Browse Source

Merge branch 'master' into tab_key

Nils ANDRÉ-CHANG 6 years ago
parent
commit
0024dd7bb5
100 changed files with 2051 additions and 1301 deletions
  1. 1 2
      .github/CODEOWNERS
  2. 0 21
      .github/ISSUE_TEMPLATE.md
  3. 18 0
      .github/ISSUE_TEMPLATE/bug_report.md
  4. 19 0
      .github/ISSUE_TEMPLATE/feature---enhancement-request.md
  5. 1 0
      .gitignore
  6. 20 4
      .travis.yml
  7. 5 0
      AUTHORS.md
  8. 96 57
      DONORS.md
  9. 11 0
      SConstruct
  10. 1 0
      core/SCsub
  11. 2 8
      core/array.cpp
  12. 46 33
      core/bind/core_bind.cpp
  13. 50 64
      core/bind/core_bind.h
  14. 15 38
      core/class_db.cpp
  15. 1 6
      core/class_db.h
  16. 8 26
      core/color.cpp
  17. 1 3
      core/color.h
  18. 0 4
      core/command_queue_mt.h
  19. 38 0
      core/crypto/SCsub
  20. 170 0
      core/crypto/crypto.cpp
  21. 105 0
      core/crypto/crypto.h
  22. 29 3
      core/crypto/crypto_core.cpp
  23. 17 3
      core/crypto/crypto_core.h
  24. 137 0
      core/crypto/hashing_context.cpp
  25. 66 0
      core/crypto/hashing_context.h
  26. 1 4
      core/engine.cpp
  27. 137 0
      core/error_macros.h
  28. 13 0
      core/func_ref.cpp
  29. 1 0
      core/func_ref.h
  30. 3 13
      core/hash_map.h
  31. 34 62
      core/image.cpp
  32. 4 2
      core/image.h
  33. 1 4
      core/input_map.cpp
  34. 3 6
      core/io/config_file.cpp
  35. 4 8
      core/io/file_access_buffered.cpp
  36. 1 4
      core/io/file_access_buffered_fa.h
  37. 2 1
      core/io/file_access_compressed.cpp
  38. 3 4
      core/io/file_access_encrypted.cpp
  39. 10 9
      core/io/file_access_pack.cpp
  40. 0 16
      core/io/image_loader.h
  41. 4 10
      core/io/ip_address.cpp
  42. 0 10
      core/io/marshalls.cpp
  43. 199 109
      core/io/multiplayer_api.cpp
  44. 34 0
      core/io/multiplayer_api.h
  45. 4 4
      core/io/packet_peer.cpp
  46. 1 4
      core/io/pck_packer.cpp
  47. 20 29
      core/io/resource_format_binary.cpp
  48. 14 31
      core/io/resource_loader.cpp
  49. 0 3
      core/io/resource_loader.h
  50. 3 6
      core/io/resource_saver.cpp
  51. 0 4
      core/io/resource_saver.h
  52. 3 65
      core/io/stream_peer_ssl.cpp
  53. 3 10
      core/io/stream_peer_ssl.h
  54. 6 19
      core/io/translation_loader_po.cpp
  55. 2 4
      core/io/xml_parser.cpp
  56. 0 3
      core/make_binders.py
  57. 1 5
      core/map.h
  58. 1 32
      core/math/SCsub
  59. 174 97
      core/math/a_star.cpp
  60. 24 13
      core/math/a_star.h
  61. 1 4
      core/math/basis.cpp
  62. 0 4
      core/math/basis.h
  63. 1 3
      core/math/bsp_tree.h
  64. 2 2
      core/math/camera_matrix.cpp
  65. 0 4
      core/math/camera_matrix.h
  66. 2 2
      core/math/delaunay.h
  67. 3 8
      core/math/expression.cpp
  68. 48 98
      core/math/geometry.cpp
  69. 102 122
      core/math/geometry.h
  70. 9 0
      core/math/math_funcs.cpp
  71. 17 6
      core/math/math_funcs.h
  72. 1 8
      core/math/octree.h
  73. 0 4
      core/math/quat.h
  74. 5 0
      core/math/transform.cpp
  75. 56 20
      core/math/transform.h
  76. 31 0
      core/math/transform_2d.h
  77. 13 0
      core/math/vector2.cpp
  78. 18 6
      core/math/vector2.h
  79. 37 3
      core/math/vector3.h
  80. 4 7
      core/message_queue.cpp
  81. 0 4
      core/method_bind.h
  82. 1 2
      core/node_path.cpp
  83. 0 4
      core/node_path.h
  84. 53 13
      core/oa_hash_map.h
  85. 28 60
      core/object.cpp
  86. 11 4
      core/object.h
  87. 0 8
      core/os/dir_access.cpp
  88. 12 5
      core/os/dir_access.h
  89. 3 5
      core/os/file_access.cpp
  90. 1 0
      core/os/input.cpp
  91. 1 0
      core/os/input.h
  92. 2 2
      core/os/input_event.cpp
  93. 0 4
      core/os/input_event.h
  94. 0 4
      core/os/keyboard.h
  95. 8 0
      core/os/main_loop.cpp
  96. 1 4
      core/os/main_loop.h
  97. 0 4
      core/os/memory.h
  98. 7 6
      core/os/os.cpp
  99. 6 4
      core/os/os.h
  100. 0 4
      core/os/semaphore.h

+ 1 - 2
.github/CODEOWNERS

@@ -30,7 +30,6 @@ doc_classes/*        @godotengine/documentation
 /modules/bullet/     @AndreaCatania
 /modules/bullet/     @AndreaCatania
 /modules/csg/        @BastiaanOlij
 /modules/csg/        @BastiaanOlij
 /modules/enet/       @godotengine/network
 /modules/enet/       @godotengine/network
-/modules/gdnative/   @karroffel
 /modules/gdnative/*arvr/ @BastiaanOlij
 /modules/gdnative/*arvr/ @BastiaanOlij
 /modules/gdscript/   @vnen @bojidar-bg
 /modules/gdscript/   @vnen @bojidar-bg
 /modules/mbedtls/    @godotengine/network
 /modules/mbedtls/    @godotengine/network
@@ -45,6 +44,6 @@ doc_classes/*        @godotengine/documentation
 /platform/uwp/       @vnen
 /platform/uwp/       @vnen
 
 
 /server/physics*/    @reduz @AndreaCatania
 /server/physics*/    @reduz @AndreaCatania
-/server/visual*/     @reduz @karroffel
+/server/visual*/     @reduz
 
 
 /thirdparty/         @akien-mga
 /thirdparty/         @akien-mga

+ 0 - 21
.github/ISSUE_TEMPLATE.md

@@ -1,21 +0,0 @@
-<!-- Please search existing issues for potential duplicates before filing yours:
-https://github.com/godotengine/godot/issues?q=is%3Aissue
--->
-
-**Godot version:**
-<!-- Specify commit hash if non-official. -->
-
-
-**OS/device including version:**
-<!-- Specify GPU model and drivers if graphics-related. -->
-
-
-**Issue description:**
-<!-- What happened, and what was expected. -->
-
-
-**Steps to reproduce:**
-
-
-**Minimal reproduction project:**
-<!-- Recommended as it greatly speeds up debugging. Drag and drop a zip archive to upload it. -->

+ 18 - 0
.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:**

+ 19 - 0
.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**

+ 1 - 0
.gitignore

@@ -1,5 +1,6 @@
 # Godot auto generated files
 # Godot auto generated files
 *.gen.*
 *.gen.*
+.import/
 
 
 # Documentation generated by doxygen or from classes.xml
 # Documentation generated by doxygen or from classes.xml
 doc/_build/
 doc/_build/

+ 20 - 4
.travis.yml

@@ -2,7 +2,10 @@ language: cpp
 
 
 # OS config, depends on actual 'os' in build matrix
 # OS config, depends on actual 'os' in build matrix
 dist: xenial
 dist: xenial
-sudo: false
+
+stages:
+  - check
+  - build
 
 
 env:
 env:
   global:
   global:
@@ -18,6 +21,7 @@ cache:
 matrix:
 matrix:
   include:
   include:
     - name: Static checks (clang-format)
     - name: Static checks (clang-format)
+      stage: check
       env: STATIC_CHECKS=yes
       env: STATIC_CHECKS=yes
       os: linux
       os: linux
       compiler: gcc
       compiler: gcc
@@ -29,6 +33,7 @@ matrix:
             - clang-format-8
             - clang-format-8
 
 
     - name: Linux editor (debug, GCC 9, with Mono)
     - 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"
       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
       os: linux
       compiler: gcc-9
       compiler: gcc-9
@@ -52,6 +57,7 @@ matrix:
           branch_pattern: coverity_scan
           branch_pattern: coverity_scan
 
 
     - name: Linux export template (release, Clang)
     - 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"
       env: PLATFORM=x11 TOOLS=no TARGET=release CACHE_NAME=${PLATFORM}-clang EXTRA_ARGS="warnings=extra werror=yes"
       os: linux
       os: linux
       compiler: clang
       compiler: clang
@@ -61,22 +67,26 @@ matrix:
             - *linux_deps
             - *linux_deps
 
 
     - name: Android export template (release_debug, Clang)
     - 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"
       env: PLATFORM=android TOOLS=no TARGET=release_debug CACHE_NAME=${PLATFORM}-clang EXTRA_ARGS="warnings=extra werror=yes"
       os: linux
       os: linux
       compiler: clang
       compiler: clang
 
 
     - name: macOS editor (debug, Clang)
     - name: macOS editor (debug, Clang)
+      stage: build
       env: PLATFORM=osx TOOLS=yes TARGET=debug CACHE_NAME=${PLATFORM}-tools-clang
       env: PLATFORM=osx TOOLS=yes TARGET=debug CACHE_NAME=${PLATFORM}-tools-clang
       os: osx
       os: osx
       compiler: clang
       compiler: clang
 
 
     - name: iOS export template (debug, Clang)
     - name: iOS export template (debug, Clang)
+      stage: build
       env: PLATFORM=iphone TOOLS=no TARGET=debug CACHE_NAME=${PLATFORM}-clang
       env: PLATFORM=iphone TOOLS=no TARGET=debug CACHE_NAME=${PLATFORM}-clang
       os: osx
       os: osx
       compiler: clang
       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
       os: linux
       compiler: gcc-9
       compiler: gcc-9
       addons:
       addons:
@@ -88,6 +98,7 @@ matrix:
             - *linux_deps
             - *linux_deps
 
 
     - name: Linux export template (release_debug, GCC 5, without 3D support)
     - 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"
       env: PLATFORM=x11 TOOLS=no TARGET=release_debug CACHE_NAME=${PLATFORM}-gcc-5 EXTRA_ARGS="disable_3d=yes"
       os: linux
       os: linux
       compiler: gcc
       compiler: gcc
@@ -124,5 +135,10 @@ script:
   - if [ "$STATIC_CHECKS" = "yes" ]; then
   - if [ "$STATIC_CHECKS" = "yes" ]; then
       sh ./misc/travis/clang-format.sh;
       sh ./misc/travis/clang-format.sh;
     else
     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
     fi

+ 5 - 0
AUTHORS.md

@@ -36,6 +36,7 @@ name is available.
     Andy Moss (MillionOstrich)
     Andy Moss (MillionOstrich)
     Anish Bhobe (KidRigger)
     Anish Bhobe (KidRigger)
     Anton Yabchinskiy (a12n)
     Anton Yabchinskiy (a12n)
+    Anutrix
     Aren Villanueva (kurikaesu)
     Aren Villanueva (kurikaesu)
     Ariel Manzur (punto-)
     Ariel Manzur (punto-)
     Bastiaan Olij (BastiaanOlij)
     Bastiaan Olij (BastiaanOlij)
@@ -56,6 +57,7 @@ name is available.
     Dharkael (lupoDharkael)
     Dharkael (lupoDharkael)
     Dmitry Koteroff (Krakean)
     Dmitry Koteroff (Krakean)
     DualMatrix
     DualMatrix
+    Emmanuel Barroga (sparkart)
     Emmanuel Leblond (touilleMan)
     Emmanuel Leblond (touilleMan)
     Eric Lasota (elasota)
     Eric Lasota (elasota)
     est31
     est31
@@ -68,6 +70,7 @@ name is available.
     Gerrit Großkopf (Grosskopf)
     Gerrit Großkopf (Grosskopf)
     Gilles Roudiere (groud)
     Gilles Roudiere (groud)
     Guilherme Felipe de C. G. da Silva (guilhermefelipecgs)
     Guilherme Felipe de C. G. da Silva (guilhermefelipecgs)
+    Hanif A (hbina)
     Hein-Pieter van Braam (hpvb)
     Hein-Pieter van Braam (hpvb)
     Hiroshi Ogawa (hi-ogawa)
     Hiroshi Ogawa (hi-ogawa)
     homer666
     homer666
@@ -104,6 +107,7 @@ name is available.
     Masoud BH (masoudbh3)
     Masoud BH (masoudbh3)
     Matthias Hölzl (hoelzl)
     Matthias Hölzl (hoelzl)
     Max Hilbrunner (mhilbrunner)
     Max Hilbrunner (mhilbrunner)
+    merumelu
     Michael Alexsander Silva Dias (YeldhamDev)
     Michael Alexsander Silva Dias (YeldhamDev)
     mrezai
     mrezai
     Nathan Warden (NathanWarden)
     Nathan Warden (NathanWarden)
@@ -128,6 +132,7 @@ name is available.
     romulox-x
     romulox-x
     Ruslan Mustakov (endragor)
     Ruslan Mustakov (endragor)
     Saniko (sanikoyes)
     Saniko (sanikoyes)
+    santouits
     SaracenOne
     SaracenOne
     sersoong
     sersoong
     Simon Wenner (swenner)
     Simon Wenner (swenner)

+ 96 - 57
DONORS.md

@@ -17,11 +17,12 @@ generous deed immortalized in the next stable release of Godot Engine.
 ## Gold sponsors
 ## Gold sponsors
 
 
     Gamblify <https://www.gamblify.com>
     Gamblify <https://www.gamblify.com>
+    Moonwards <https://www.moonwards.com>
 
 
 ## Mini sponsors
 ## Mini sponsors
 
 
+    AD Ford
     Alan Beauchamp
     Alan Beauchamp
-    Aleksandar Kordic
     Anandarup Mallik
     Anandarup Mallik
     Andrew Dunai
     Andrew Dunai
     Brandon Lamb
     Brandon Lamb
@@ -30,12 +31,16 @@ generous deed immortalized in the next stable release of Godot Engine.
     Christoph Woinke
     Christoph Woinke
     Denis Malyavin
     Denis Malyavin
     Edward Flick
     Edward Flick
+    Gamechuck
     GameDev.net
     GameDev.net
     GameDev.tv
     GameDev.tv
+    Grady
     Hein-Pieter van Braam
     Hein-Pieter van Braam
     Jacob McKenney
     Jacob McKenney
     Javary Co.
     Javary Co.
+    Jeppe Zapp
     Justin Arnold
     Justin Arnold
+    Justo Delgado Baudí
     Kyle Szklenski
     Kyle Szklenski
     Leonard Meagher
     Leonard Meagher
     Matthieu Huvé
     Matthieu Huvé
@@ -46,7 +51,6 @@ generous deed immortalized in the next stable release of Godot Engine.
     Patrick Aarstad
     Patrick Aarstad
     Slobodan Milnovic
     Slobodan Milnovic
     Stephan Lanfermann
     Stephan Lanfermann
-    Stephen Telford
     Steve
     Steve
     VilliHaukka
     VilliHaukka
     Xananax
     Xananax
@@ -56,10 +60,11 @@ generous deed immortalized in the next stable release of Godot Engine.
 
 
     Andrei
     Andrei
     cheese65536
     cheese65536
+    Daniel Hartmann
+    Dave
     David Gehrig
     David Gehrig
     Ed Morley
     Ed Morley
     Florian Krick
     Florian Krick
-    Grady
     Jakub Grzesik
     Jakub Grzesik
     K9Kraken
     K9Kraken
     Manuele Finocchiaro
     Manuele Finocchiaro
@@ -70,56 +75,62 @@ generous deed immortalized in the next stable release of Godot Engine.
     Zaven Muradyan
     Zaven Muradyan
 
 
     Alexander Trey Saunders
     Alexander Trey Saunders
-    Allen Schade
     Asher Glick
     Asher Glick
     Austen McRae
     Austen McRae
+    beVR
     Brian van der Stel
     Brian van der Stel
+    Cameron MacNair
     Carlo Cabanilla
     Carlo Cabanilla
     Daniel James
     Daniel James
     David Giardi
     David Giardi
+    David Snopek
+    Default Name
     Edward E
     Edward E
     Florian Breisch
     Florian Breisch
     Gero
     Gero
+    GiulianoB
     Javier Roman
     Javier Roman
     Jay Horton
     Jay Horton
-    Jon Smith
+    Jonathan Turner
     Jon Woodward
     Jon Woodward
-    Justo Delgado Baudí
+    Jose Fernando Alexandre
     Karl Werf
     Karl Werf
     Kommentgames
     Kommentgames
-    Krzysztof Dluzniewski
     Luke
     Luke
-    Moonwards
+    Maciej Pendolski
+    Matthew Hillier
     Mored1984
     Mored1984
-    paul gruenbacher
     Paul LaMotte
     Paul LaMotte
     Péter Magyar
     Péter Magyar
     Rob Messick
     Rob Messick
-    Ross Esmond
     Ryan Badour
     Ryan Badour
     Scott Wadden
     Scott Wadden
     Sergey
     Sergey
     Shawn Yu
     Shawn Yu
     Svenne Krap
     Svenne Krap
+    thechris
     Tom Langwaldt
     Tom Langwaldt
+    tukon
     William Wold
     William Wold
-    Wyatt Goodin
 
 
     Alex Khayrullin
     Alex Khayrullin
+    Branwyn Tylwyth
     Chris Goddard
     Chris Goddard
     Chris Serino
     Chris Serino
     Christian Padilla
     Christian Padilla
     Conrad Curry
     Conrad Curry
     Craig Smith
     Craig Smith
+    Darrian Little
     Dean Harmon
     Dean Harmon
     Ian Richard Kunert
     Ian Richard Kunert
     Ivan Trombley
     Ivan Trombley
     Joan Fons
     Joan Fons
+    Joshua Flores
     Krzysztof Jankowski
     Krzysztof Jankowski
     Lord Bloodhound
     Lord Bloodhound
     Lucas Ferreira Franca
     Lucas Ferreira Franca
-    Michele Zilli
     Nathan Lundquist
     Nathan Lundquist
+    Nicklas Breum
     Pascal Grüter
     Pascal Grüter
     Petr Malac
     Petr Malac
     Rami
     Rami
@@ -135,9 +146,10 @@ generous deed immortalized in the next stable release of Godot Engine.
     Xavier PATRICELLI
     Xavier PATRICELLI
 
 
     Adam Neumann
     Adam Neumann
-    Alessandra Pereyra
     Alexander J Maynard
     Alexander J Maynard
     Alexey Dyadchenko
     Alexey Dyadchenko
+    André Frélicot
+    andres eduardo lopez
     Andrew Bowen
     Andrew Bowen
     Asdf
     Asdf
     Ben Botwin
     Ben Botwin
@@ -147,50 +159,48 @@ generous deed immortalized in the next stable release of Godot Engine.
     Christoph Schröder
     Christoph Schröder
     Cody Parker
     Cody Parker
     D
     D
-    Daniel
     Daniel Eichler
     Daniel Eichler
     David White
     David White
-    Deadly Lampshade
     Eric
     Eric
+    Eric Churches
     Eric Monson
     Eric Monson
     Eugenio Hugo Salgüero Jáñez
     Eugenio Hugo Salgüero Jáñez
     flesk
     flesk
-    Francisco Javier Moreno Carracedo
     gavlig
     gavlig
     GGGames.org
     GGGames.org
-    Giles Montgomery
     Guilherme Felipe de C. G. da Silva
     Guilherme Felipe de C. G. da Silva
     Heath Hayes
     Heath Hayes
     Hysteria
     Hysteria
     Idzard Kwadijk
     Idzard Kwadijk
     Jared White
     Jared White
-    Jesse Nave
+    Joe Flood
     Jose Malheiro
     Jose Malheiro
-    Joshua Flores
     Joshua Lesperance
     Joshua Lesperance
     Juan T Chen
     Juan T Chen
     Juraj Móza
     Juraj Móza
     Kasper Jeppesen
     Kasper Jeppesen
+    kinfox
     Klaus The.
     Klaus The.
     Klavdij Voncina
     Klavdij Voncina
-    Leandro Voltolino
     Maarten Elings
     Maarten Elings
+    Marcelo Dornbusch Lopes
     Markus Fehr
     Markus Fehr
     Markus Wiesner
     Markus Wiesner
     Martin Eigel
     Martin Eigel
     Marvin
     Marvin
     Matt Eunson
     Matt Eunson
-    Matthew Hillier
     Max Bulai
     Max Bulai
     Max R.R. Collada
     Max R.R. Collada
     M H
     M H
     Nick Nikitin
     Nick Nikitin
     Oliver Dick
     Oliver Dick
-    Paolo Munoz
+    Patrick Ting
     Paul Hocker
     Paul Hocker
     Paul Von Zimmerman
     Paul Von Zimmerman
     Pete Goodwin
     Pete Goodwin
+    pl
     Ranoller
     Ranoller
+    Romildo Franco
     Samuel Judd
     Samuel Judd
     Scott Pilet
     Scott Pilet
     spilldata
     spilldata
@@ -198,60 +208,59 @@ generous deed immortalized in the next stable release of Godot Engine.
     Thomas Krampl
     Thomas Krampl
     Tobias Bocanegra
     Tobias Bocanegra
     Urho
     Urho
-    WytRabbit
-    Xavier Fumado Beltran
+    Zie Weaver
 
 
 ## Silver donors
 ## Silver donors
 
 
     1D_Inc
     1D_Inc
+    Abby Jones
     Abraham Haskins
     Abraham Haskins
     Adam Brunnmeier
     Adam Brunnmeier
     Adam  Carr
     Adam  Carr
     Adam Nakonieczny
     Adam Nakonieczny
     Adam Smeltzer
     Adam Smeltzer
     Adisibio
     Adisibio
+    Adrian Demetrescu
+    Aggelos Arnaoutis
     Agustinus Arya
     Agustinus Arya
     Aidan O'Flannagain
     Aidan O'Flannagain
+    Albin Jonasson Svärdsby
     Alder Stefano
     Alder Stefano
     Alessandro Senese
     Alessandro Senese
-    Alexander Gillberg
     Alexander Koppe
     Alexander Koppe
     Alex Davies-Moore
     Alex Davies-Moore
-    Alice Robinson
+    Allen Schade
     Andreas Evers
     Andreas Evers
     Andreas Krampitz
     Andreas Krampitz
+    Andreas Lundmark
     Andreas Schüle
     Andreas Schüle
-    Andrew Peart
+    André Simões
+    Andrés Rodríguez
+    Andrzej Skalski
     Anthony Bongiovanni
     Anthony Bongiovanni
     Anthony Staunton
     Anthony Staunton
+    Anton Kurkin
     Antony K. Jones
     Antony K. Jones
+    AP Condomines
     Arda Erol
     Arda Erol
-    Artem Bashev
     Arthur S. Muszynski
     Arthur S. Muszynski
-    Artistofdeath
     Aubrey Falconer
     Aubrey Falconer
     Avencherus
     Avencherus
     B A
     B A
     Balázs Batári
     Balázs Batári
-    Bastian Böhm
     Beliar
     Beliar
     Benedikt
     Benedikt
     Ben Phelan
     Ben Phelan
     Ben Vercammen
     Ben Vercammen
-    Ben Woodley
-    Berbank
     Bernd Jänichen
     Bernd Jänichen
     Black Block
     Black Block
     Blair Allen
     Blair Allen
     Bobby CC Wong
     Bobby CC Wong
-    Boyquotes
-    Branwyn Tylwyth
     Bryan Stevenson
     Bryan Stevenson
     Caleb Dumitry
     Caleb Dumitry
     Carwyn Edwards
     Carwyn Edwards
     Chris Brown
     Chris Brown
     Chris Chapin
     Chris Chapin
-    Chris Gonzales
     Christian Baune
     Christian Baune
     Christian Winter
     Christian Winter
     Christoffer Sundbom
     Christoffer Sundbom
@@ -260,18 +269,22 @@ generous deed immortalized in the next stable release of Godot Engine.
     Cobaltum
     Cobaltum
     Collin Shooltz
     Collin Shooltz
     Dag Sundin Söderström
     Dag Sundin Söderström
+    Dan H. Bentsen
     Daniel Johnson
     Daniel Johnson
+    DanielMaximiano
+    Daniel Pontillo
     Daniel Reed
     Daniel Reed
-    Danny Welch
-    Dave Watts
+    Daniel Tebbutt
     David Bullock
     David Bullock
     David Cravens
     David Cravens
     David May
     David May
-    Dimitri Stanojevic
+    David Rapisarda
+    David Woodard
     Dominic Cooney
     Dominic Cooney
     Dominik Wetzel
     Dominik Wetzel
-    DrevanTonder
+    Donovan Hutcheon
     Duobix
     Duobix
+    Eduardo Teixeira
     Edward Herbert
     Edward Herbert
     Egon Elbre
     Egon Elbre
     Ellen Marie Dash
     Ellen Marie Dash
@@ -280,18 +293,21 @@ generous deed immortalized in the next stable release of Godot Engine.
     Eric Ellingson
     Eric Ellingson
     Eric Martini
     Eric Martini
     Eric Williams
     Eric Williams
+    EugeneTel
     Evan Rose
     Evan Rose
     Felix Kollmann
     Felix Kollmann
     fengjiongmax
     fengjiongmax
     Flaredown
     Flaredown
     FuDiggity
     FuDiggity
     G3Dev sàrl
     G3Dev sàrl
+    Gadzhi Kharkharov
+    gamedev by Celio
     Gary Hulst
     Gary Hulst
-    Gerrit Großkopf
+    George Marques
     gmmath
     gmmath
-    Grant Clarke
     Greg Olson
     Greg Olson
     Greg P
     Greg P
+    Greyson Richey
     Guldoman
     Guldoman
     Hal A
     Hal A
     Heribert Hirth
     Heribert Hirth
@@ -299,7 +315,6 @@ generous deed immortalized in the next stable release of Godot Engine.
     Hunter Jones
     Hunter Jones
     Hylpher
     Hylpher
     ialex32x
     ialex32x
-    Igor Buzatovic
     Iiari
     Iiari
     IndustrialRobot
     IndustrialRobot
     Isaac Morton
     Isaac Morton
@@ -326,28 +341,33 @@ generous deed immortalized in the next stable release of Godot Engine.
     Jon Bonazza
     Jon Bonazza
     Jon Sully
     Jon Sully
     Jose Aleman
     Jose Aleman
+    Jose Andrés Mejias Rojas
     Joseph Catrambone
     Joseph Catrambone
     Josh 'Cheeseness' Bush
     Josh 'Cheeseness' Bush
     Juanfran
     Juanfran
     Juan Negrier
     Juan Negrier
+    Juan Velandia
     Judd
     Judd
     Jueast
     Jueast
     Julian Murgia
     Julian Murgia
-    Kasier Bald0
+    Justin Spedding
+    Kaiser Bald0
+    Kamuna
+    Kauzig
     KC Chan
     KC Chan
+    Keedong Park
     kickmaniac
     kickmaniac
     Kiyohiro Kawamura (kyorohiro)
     Kiyohiro Kawamura (kyorohiro)
+    Kjetil Haugland
     Klagsam
     Klagsam
     Klassix
     Klassix
     KR McGinley
     KR McGinley
     KsyTek Games
     KsyTek Games
     Kuan Cheang
     Kuan Cheang
     kycho
     kycho
-    Leviathan Hunter
     Levi Lindsey
     Levi Lindsey
     Linus Lind Lundgren
     Linus Lind Lundgren
     Lionel Gaillard
     Lionel Gaillard
-    Luis Moraes
     LunaticInAHat
     LunaticInAHat
     Lurkars
     Lurkars
     Macil
     Macil
@@ -355,61 +375,75 @@ generous deed immortalized in the next stable release of Godot Engine.
     Malcolm
     Malcolm
     Malik Ahmed
     Malik Ahmed
     Malik Nejer
     Malik Nejer
-    Marc Urlus
     Marcus Richter
     Marcus Richter
+    Markus Lohaus
     Markus Michael Egger
     Markus Michael Egger
     Martin Holas
     Martin Holas
+    Martin Liška
+    Matt Edwards
     Matthew Little
     Matthew Little
     Maxwell
     Maxwell
     medecau
     medecau
     mhilbrunner
     mhilbrunner
     Michael Dürwald
     Michael Dürwald
     Michael Gringauz
     Michael Gringauz
+    Michael Haney
     Michael Labbe
     Michael Labbe
     Mikael Olsson
     Mikael Olsson
     Mikayla Hutchinson
     Mikayla Hutchinson
+    Mike Birkhead
     Mike Cunningham
     Mike Cunningham
     Mitchell J. Wagner
     Mitchell J. Wagner
-    mlevin cantu
     MoM
     MoM
-    Moritz Laass
     MuffinManKen
     MuffinManKen
+    Nathan Fish
     Natrim
     Natrim
     nee
     nee
     Neil Blakey-Milner
     Neil Blakey-Milner
     Nerdforge
     Nerdforge
     Nicholas
     Nicholas
+    Nicholas Bettencourt
+    Nick Macholl
     Niclas Eriksen
     Niclas Eriksen
     Nicolás Montaña
     Nicolás Montaña
     Nicolas SAN AGUSTIN
     Nicolas SAN AGUSTIN
+    Nima Farid
     Nithin Jino
     Nithin Jino
     NZ
     NZ
+    Olivier
     Omar Delarosa
     Omar Delarosa
+    omzee
     Oscar Norlander
     Oscar Norlander
     Pafka
     Pafka
     Pan Ip
     Pan Ip
+    Pat LaBine
     Patrick Forringer
     Patrick Forringer
     Patrick Nafarrete
     Patrick Nafarrete
     Paul Gieske
     Paul Gieske
     Paul Mason
     Paul Mason
     Paweł Kowal
     Paweł Kowal
-    Philip O. Staiger
+    Philip Cohoe
     Pierre-Igor Berthet
     Pierre-Igor Berthet
-    Pietro Vertechi
     Pitsanu Tongprasin
     Pitsanu Tongprasin
     Point08
     Point08
     Poryg
     Poryg
     Rafa Laguna
     Rafa Laguna
     Rafal Wyszomirski
     Rafal Wyszomirski
     Raphael Leroux
     Raphael Leroux
+    Remi Rampin
     Rémi Verschelde
     Rémi Verschelde
+    Rezgi
     Ricardo Alcantara
     Ricardo Alcantara
     Robert Farr (Larington)
     Robert Farr (Larington)
     Robert Hernandez
     Robert Hernandez
+    Robert Larnach
     Rodrigo Loli
     Rodrigo Loli
     Roger Smith
     Roger Smith
     Roland Rząsa
     Roland Rząsa
     Roman Tinkov
     Roman Tinkov
+    Ronan Jouchet
+    Ryan
+    Ryan Brooks
     Ryan Groom
     Ryan Groom
     Ryan Hentz
     Ryan Hentz
     Saad Khoudmi
     Saad Khoudmi
@@ -418,46 +452,51 @@ generous deed immortalized in the next stable release of Godot Engine.
     Sasori Olkof
     Sasori Olkof
     Scott D. Yelich
     Scott D. Yelich
     Sebastian Michailidis
     Sebastian Michailidis
+    sgnsajgon
+    Shane
     Shane Sicienski
     Shane Sicienski
     Shane Spoor
     Shane Spoor
     Simon Ledam
     Simon Ledam
     Simon Wenner
     Simon Wenner
     SK
     SK
     Sootstone
     Sootstone
-    Stonepyre
-    Thibault Barbaroux
+    Taylor Fahlman
     thomas
     thomas
     Thomas Bell
     Thomas Bell
     Thomas Kelly
     Thomas Kelly
     Thomas Kurz
     Thomas Kurz
     tiansheng li
     tiansheng li
-    Tim
     Tim Drumheller
     Tim Drumheller
     Tim Gudex
     Tim Gudex
-    Timo Schmidt
     Timothy B. MacDonald
     Timothy B. MacDonald
     Tobbun
     Tobbun
+    Tom Fulp
+    Tom Glenn
     Tom Larrow
     Tom Larrow
     Torsten Crass
     Torsten Crass
     Travis O'Brien
     Travis O'Brien
     Trent Skinner
     Trent Skinner
+    Triptych
+    Troy Bonneau
     Tryggve Sollid
     Tryggve Sollid
     Turgut Temucin
     Turgut Temucin
     Tyler Stafos
     Tyler Stafos
     UltyX
     UltyX
     Vaiktorg
     Vaiktorg
-    Valeria Viana Gusmao
-    Veodok
     Victor
     Victor
     Vigilant Watch
     Vigilant Watch
+    Vincent Cloutier
     waka nya
     waka nya
+    Walter Byers
     Wayne Haak
     Wayne Haak
     werner mendizabal
     werner mendizabal
     Wiley Thompson
     Wiley Thompson
     Will
     Will
     William Hogben
     William Hogben
     Wout Standaert
     Wout Standaert
-    Yeung Si Xiang
+    Wyatt Goodin
+    Yegor
+    蕭惟允
 
 
 ## Bronze donors
 ## Bronze donors
 
 

+ 11 - 0
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)
     # must happen after the flags, so when flags are used by configure, stuff happens (ie, ssl on x11)
     detect.configure(env)
     detect.configure(env)
 
 
+    # Enable C++11 support
+    if not env.msvc:
+        env.Append(CXXFLAGS=['-std=c++11'])
+
     # Configure compiler warnings
     # Configure compiler warnings
     if env.msvc:
     if env.msvc:
         # Truncations, narrowing conversions, signed/unsigned comparisons...
         # Truncations, narrowing conversions, signed/unsigned comparisons...
@@ -480,6 +484,13 @@ if selected_platform in platform_list:
     if env['minizip']:
     if env['minizip']:
         env.Append(CPPDEFINES=['MINIZIP_ENABLED'])
         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']:
     if not env['verbose']:
         methods.no_verbose(sys, env)
         methods.no_verbose(sys, env)
 
 

+ 1 - 0
core/SCsub

@@ -159,6 +159,7 @@ env.CommandNoCache('#core/license.gen.h', ["../COPYRIGHT.txt", "../LICENSE.txt"]
 # Chain load SCsubs
 # Chain load SCsubs
 SConscript('os/SCsub')
 SConscript('os/SCsub')
 SConscript('math/SCsub')
 SConscript('math/SCsub')
+SConscript('crypto/SCsub')
 SConscript('io/SCsub')
 SConscript('io/SCsub')
 SConscript('bind/SCsub')
 SConscript('bind/SCsub')
 
 

+ 2 - 8
core/array.cpp

@@ -133,18 +133,12 @@ void Array::erase(const Variant &p_value) {
 }
 }
 
 
 Variant Array::front() const {
 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);
 	return operator[](0);
 }
 }
 
 
 Variant Array::back() const {
 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);
 	return operator[](_p->array.size() - 1);
 }
 }
 
 

+ 46 - 33
core/bind/core_bind.cpp

@@ -30,11 +30,11 @@
 
 
 #include "core_bind.h"
 #include "core_bind.h"
 
 
+#include "core/crypto/crypto_core.h"
 #include "core/io/file_access_compressed.h"
 #include "core/io/file_access_compressed.h"
 #include "core/io/file_access_encrypted.h"
 #include "core/io/file_access_encrypted.h"
 #include "core/io/json.h"
 #include "core/io/json.h"
 #include "core/io/marshalls.h"
 #include "core/io/marshalls.h"
-#include "core/math/crypto_core.h"
 #include "core/math/geometry.h"
 #include "core/math/geometry.h"
 #include "core/os/keyboard.h"
 #include "core/os/keyboard.h"
 #include "core/os/os.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;
 	Error err = OK;
 	RES ret = ResourceLoader::load(p_path, p_type_hint, p_no_cache, &err);
 	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;
 	return ret;
 }
 }
 
 
@@ -148,10 +145,7 @@ _ResourceLoader::_ResourceLoader() {
 }
 }
 
 
 Error _ResourceSaver::save(const String &p_path, const RES &p_resource, SaverFlags p_flags) {
 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);
 	return ResourceSaver::save(p_path, p_resource, p_flags);
 }
 }
 
 
@@ -191,10 +185,31 @@ _ResourceSaver::_ResourceSaver() {
 
 
 /////////////////OS
 /////////////////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 {
 Point2 _OS::get_mouse_position() const {
 
 
 	return OS::get_singleton()->get_mouse_position();
 	return OS::get_singleton()->get_mouse_position();
 }
 }
+
 void _OS::set_window_title(const String &p_title) {
 void _OS::set_window_title(const String &p_title) {
 
 
 	OS::get_singleton()->set_window_title(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 {
 String _OS::get_unique_id() const {
 	return OS::get_singleton()->get_unique_id();
 	return OS::get_singleton()->get_unique_id();
 }
 }
+
 bool _OS::has_touchscreen_ui_hint() const {
 bool _OS::has_touchscreen_ui_hint() const {
 
 
 	return OS::get_singleton()->has_touchscreen_ui_hint();
 	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);
 	OS::get_singleton()->set_clipboard(p_text);
 }
 }
+
 String _OS::get_clipboard() const {
 String _OS::get_clipboard() const {
 
 
 	return OS::get_singleton()->get_clipboard();
 	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;
 	vm.resizable = p_resizeable;
 	OS::get_singleton()->set_video_mode(vm, p_screen);
 	OS::get_singleton()->set_video_mode(vm, p_screen);
 }
 }
+
 Size2 _OS::get_video_mode(int p_screen) const {
 Size2 _OS::get_video_mode(int p_screen) const {
 
 
 	OS::VideoMode vm;
 	OS::VideoMode vm;
 	vm = OS::get_singleton()->get_video_mode(p_screen);
 	vm = OS::get_singleton()->get_video_mode(p_screen);
 	return Size2(vm.width, vm.height);
 	return Size2(vm.width, vm.height);
 }
 }
+
 bool _OS::is_video_mode_fullscreen(int p_screen) const {
 bool _OS::is_video_mode_fullscreen(int p_screen) const {
 
 
 	OS::VideoMode vm;
 	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 }
 		{ 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
 	// 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
 	// 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;
 	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("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("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_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_video_driver_name", "driver"), &_OS::get_video_driver_name);
 	ClassDB::bind_method(D_METHOD("get_current_video_driver"), &_OS::get_current_video_driver);
 	ClassDB::bind_method(D_METHOD("get_current_video_driver"), &_OS::get_current_video_driver);
@@ -1414,6 +1432,11 @@ PoolVector<Plane> _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);
 	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) {
 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);
 	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<Vector2> &p_polygon, real_t p_d
 	return ret;
 	return ret;
 }
 }
 
 
-Vector<Point2> _Geometry::transform_points_2d(const Vector<Point2> &p_points, const Transform2D &p_mat) {
-
-	return Geometry::transform_points_2d(p_points, p_mat);
-}
-
 Dictionary _Geometry::make_atlas(const Vector<Size2> &p_rects) {
 Dictionary _Geometry::make_atlas(const Vector<Size2> &p_rects) {
 
 
 	Dictionary ret;
 	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_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_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("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_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("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);
 	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_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("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);
 	ClassDB::bind_method(D_METHOD("make_atlas", "sizes"), &_Geometry::make_atlas);
 
 
 	BIND_ENUM_CONSTANT(OPERATION_UNION);
 	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() {
 _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...");
 }
 }
 /////////////////////////////////////
 /////////////////////////////////////
 
 

+ 50 - 64
core/bind/core_bind.h

@@ -109,11 +109,11 @@ public:
 	};
 	};
 
 
 	enum PowerState {
 	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 {
 	enum Weekday {
@@ -127,8 +127,8 @@ public:
 	};
 	};
 
 
 	enum Month {
 	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_JANUARY = 1,
 		MONTH_FEBRUARY,
 		MONTH_FEBRUARY,
 		MONTH_MARCH,
 		MONTH_MARCH,
@@ -143,6 +143,11 @@ public:
 		MONTH_DECEMBER
 		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;
 	Point2 get_mouse_position() const;
 	void set_window_title(const String &p_title);
 	void set_window_title(const String &p_title);
 	int get_mouse_button_state() const;
 	int get_mouse_button_state() const;
@@ -259,24 +264,6 @@ public:
 	bool is_scancode_unicode(uint32_t p_unicode) const;
 	bool is_scancode_unicode(uint32_t p_unicode) const;
 	int find_scancode_from_string(const String &p_code) 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_use_file_access_save_and_swap(bool p_enable);
 
 
 	void set_native_icon(const String &p_filename);
 	void set_native_icon(const String &p_filename);
@@ -404,6 +391,7 @@ public:
 	PoolVector<Vector3> segment_intersects_sphere(const Vector3 &p_from, const Vector3 &p_to, const Vector3 &p_sphere_pos, real_t p_sphere_radius);
 	PoolVector<Vector3> segment_intersects_sphere(const Vector3 &p_from, const Vector3 &p_to, const Vector3 &p_sphere_pos, real_t p_sphere_radius);
 	PoolVector<Vector3> segment_intersects_cylinder(const Vector3 &p_from, const Vector3 &p_to, float p_height, float p_radius);
 	PoolVector<Vector3> segment_intersects_cylinder(const Vector3 &p_from, const Vector3 &p_to, float p_height, float p_radius);
 	PoolVector<Vector3> segment_intersects_convex(const Vector3 &p_from, const Vector3 &p_to, const Vector<Plane> &p_planes);
 	PoolVector<Vector3> segment_intersects_convex(const Vector3 &p_from, const Vector3 &p_to, const Vector<Plane> &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);
 	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);
 	int get_uv84_normal_bit(const Vector3 &p_vector);
 
 
@@ -420,17 +408,17 @@ public:
 		OPERATION_INTERSECTION,
 		OPERATION_INTERSECTION,
 		OPERATION_XOR
 		OPERATION_XOR
 	};
 	};
-	// 2D polygon boolean operations
-	Array merge_polygons_2d(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b); // union (add)
-	Array clip_polygons_2d(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b); // difference (subtract)
-	Array intersect_polygons_2d(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b); // common area (multiply)
-	Array exclude_polygons_2d(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b); // all but common area (xor)
+	// 2D polygon boolean operations.
+	Array merge_polygons_2d(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b); // Union (add).
+	Array clip_polygons_2d(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b); // Difference (subtract).
+	Array intersect_polygons_2d(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b); // Common area (multiply).
+	Array exclude_polygons_2d(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b); // All but common area (xor).
 
 
-	// 2D polyline vs polygon operations
-	Array clip_polyline_with_polygon_2d(const Vector<Vector2> &p_polyline, const Vector<Vector2> &p_polygon); // cut
-	Array intersect_polyline_with_polygon_2d(const Vector<Vector2> &p_polyline, const Vector<Vector2> &p_polygon); // chop
+	// 2D polyline vs polygon operations.
+	Array clip_polyline_with_polygon_2d(const Vector<Vector2> &p_polyline, const Vector<Vector2> &p_polygon); // Cut.
+	Array intersect_polyline_with_polygon_2d(const Vector<Vector2> &p_polyline, const Vector<Vector2> &p_polygon); // Chop.
 
 
-	// 2D offset polygons/polylines
+	// 2D offset polygons/polylines.
 	enum PolyJoinType {
 	enum PolyJoinType {
 		JOIN_SQUARE,
 		JOIN_SQUARE,
 		JOIN_ROUND,
 		JOIN_ROUND,
@@ -446,8 +434,6 @@ public:
 	Array offset_polygon_2d(const Vector<Vector2> &p_polygon, real_t p_delta, PolyJoinType p_join_type = JOIN_SQUARE);
 	Array offset_polygon_2d(const Vector<Vector2> &p_polygon, real_t p_delta, PolyJoinType p_join_type = JOIN_SQUARE);
 	Array offset_polyline_2d(const Vector<Vector2> &p_polygon, real_t p_delta, PolyJoinType p_join_type = JOIN_SQUARE, PolyEndType p_end_type = END_SQUARE);
 	Array offset_polyline_2d(const Vector<Vector2> &p_polygon, real_t p_delta, PolyJoinType p_join_type = JOIN_SQUARE, PolyEndType p_end_type = END_SQUARE);
 
 
-	Vector<Point2> transform_points_2d(const Vector<Point2> &p_points, const Transform2D &p_mat);
-
 	Dictionary make_atlas(const Vector<Size2> &p_rects);
 	Dictionary make_atlas(const Vector<Size2> &p_rects);
 
 
 	_Geometry();
 	_Geometry();
@@ -486,24 +472,24 @@ public:
 	Error open_encrypted_pass(const String &p_path, ModeFlags p_mode_flags, const String &p_pass);
 	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_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;
 	float get_float() const;
 	double get_double() const;
 	double get_double() const;
@@ -511,27 +497,27 @@ public:
 
 
 	Variant get_var(bool p_allow_objects = false) const;
 	Variant get_var(bool p_allow_objects = false) const;
 
 
-	PoolVector<uint8_t> get_buffer(int p_length) const; ///< get an array of bytes
+	PoolVector<uint8_t> get_buffer(int p_length) const; // Get an array of bytes.
 	String get_line() const;
 	String get_line() const;
 	Vector<String> get_csv_line(const String &p_delim = ",") const;
 	Vector<String> get_csv_line(const String &p_delim = ",") const;
 	String get_as_text() const;
 	String get_as_text() const;
 	String get_md5(const String &p_path) const;
 	String get_md5(const String &p_path) const;
 	String get_sha256(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.
 	 * 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);
 	void set_endian_swap(bool p_swap);
 	bool get_endian_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_float(float p_dest);
 	void store_double(double p_dest);
 	void store_double(double p_dest);
@@ -544,11 +530,11 @@ public:
 	virtual void store_pascal_string(const String &p_string);
 	virtual void store_pascal_string(const String &p_string);
 	virtual String get_pascal_string();
 	virtual String get_pascal_string();
 
 
-	void store_buffer(const PoolVector<uint8_t> &p_buffer); ///< store an array of bytes
+	void store_buffer(const PoolVector<uint8_t> &p_buffer); // Store an array of bytes.
 
 
 	void store_var(const Variant &p_var, bool p_full_objects = false);
 	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;
 	uint64_t get_modified_time(const String &p_file) const;
 
 
@@ -570,18 +556,18 @@ protected:
 public:
 public:
 	Error open(const String &p_path);
 	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();
 	String get_next();
 	bool current_is_dir() const;
 	bool current_is_dir() const;
 
 
-	void list_dir_end(); ///<
+	void list_dir_end();
 
 
 	int get_drive_count();
 	int get_drive_count();
 	String get_drive(int p_drive);
 	String get_drive(int p_drive);
 	int get_current_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(String p_dir);
 	Error make_dir_recursive(String p_dir);
 	Error make_dir_recursive(String p_dir);

+ 15 - 38
core/class_db.cpp

@@ -480,6 +480,7 @@ uint64_t ClassDB::get_api_hash(APIType p_api) {
 			for (List<StringName>::Element *F = snames.front(); F; F = F->next()) {
 			for (List<StringName>::Element *F = snames.front(); F; F = F->next()) {
 
 
 				PropertySetGet *psg = t->property_setget.getptr(F->get());
 				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(F->get().hash(), hash);
 				hash = hash_djb2_one_64(psg->setter.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
 #ifdef DEBUG_METHODS_ENABLED
 	ClassInfo *check = type;
 	ClassInfo *check = type;
 	while (check) {
 	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;
 		check = check->inherits_ptr;
 	}
 	}
 #endif
 #endif
@@ -923,16 +921,11 @@ void ClassDB::add_property(StringName p_class, const PropertyInfo &p_pinfo, cons
 	if (p_setter) {
 	if (p_setter) {
 		mb_set = get_method(p_class, p_setter);
 		mb_set = get_method(p_class, p_setter);
 #ifdef DEBUG_METHODS_ENABLED
 #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
 #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);
 		mb_get = get_method(p_class, p_getter);
 #ifdef DEBUG_METHODS_ENABLED
 #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
 #endif
 	}
 	}
 
 
 #ifdef DEBUG_METHODS_ENABLED
 #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
 #endif
 
 
 	OBJTYPE_WLOCK
 	OBJTYPE_WLOCK
@@ -1239,32 +1222,26 @@ MethodBind *ClassDB::bind_methodfi(uint32_t p_flags, MethodBind *p_bind, const c
 
 
 #ifdef DEBUG_ENABLED
 #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
 #endif
 
 
 	ClassInfo *type = classes.getptr(instance_type);
 	ClassInfo *type = classes.getptr(instance_type);
 	if (!type) {
 	if (!type) {
-		ERR_PRINTS("Couldn't bind method '" + mdname + "' for instance: " + instance_type);
 		memdelete(p_bind);
 		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)) {
 	if (type->method_map.has(mdname)) {
 		memdelete(p_bind);
 		memdelete(p_bind);
 		// overloading not supported
 		// 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
 #ifdef DEBUG_METHODS_ENABLED
 
 
 	if (method_name.args.size() > p_bind->get_argument_count()) {
 	if (method_name.args.size() > p_bind->get_argument_count()) {
 		memdelete(p_bind);
 		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);
 	p_bind->set_argument_names(method_name.args);

+ 1 - 6
core/class_db.h

@@ -35,10 +35,6 @@
 #include "core/object.h"
 #include "core/object.h"
 #include "core/print_string.h"
 #include "core/print_string.h"
 
 
-/**
-	@author Juan Linietsky <[email protected]>
-*/
-
 /**	To bind more then 6 parameters include this:
 /**	To bind more then 6 parameters include this:
  *  #include "core/method_bind_ext.gen.inc"
  *  #include "core/method_bind_ext.gen.inc"
  */
  */
@@ -310,8 +306,7 @@ public:
 		if (type->method_map.has(p_name)) {
 		if (type->method_map.has(p_name)) {
 			memdelete(bind);
 			memdelete(bind);
 			// overloading not supported
 			// 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;
 		type->method_map[p_name] = bind;
 #ifdef DEBUG_METHODS_ENABLED
 #ifdef DEBUG_METHODS_ENABLED

+ 8 - 26
core/color.cpp

@@ -335,36 +335,23 @@ Color Color::html(const String &p_color) {
 	} else if (color.length() == 6) {
 	} else if (color.length() == 6) {
 		alpha = false;
 		alpha = false;
 	} else {
 	} 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;
 	int a = 255;
 	if (alpha) {
 	if (alpha) {
 		a = _parse_col(color, 0);
 		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 from = alpha ? 2 : 0;
 
 
 	int r = _parse_col(color, from + 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);
 	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);
 	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);
 	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();
 	name = name.to_lower();
 
 
 	const Map<String, Color>::Element *color = _named_colors.find(name);
 	const Map<String, Color>::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) {
 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
 // FIXME: Remove once Godot 3.1 has been released
 float Color::gray() const {
 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;
 	return (r + g + b) / 3.0;
 }
 }
 
 

+ 1 - 3
core/color.h

@@ -33,9 +33,7 @@
 
 
 #include "core/math/math_funcs.h"
 #include "core/math/math_funcs.h"
 #include "core/ustring.h"
 #include "core/ustring.h"
-/**
-	@author Juan Linietsky <[email protected]>
-*/
+
 struct Color {
 struct Color {
 
 
 	union {
 	union {

+ 0 - 4
core/command_queue_mt.h

@@ -37,10 +37,6 @@
 #include "core/simple_type.h"
 #include "core/simple_type.h"
 #include "core/typedefs.h"
 #include "core/typedefs.h"
 
 
-/**
-	@author Juan Linietsky <[email protected]>
-*/
-
 #define COMMA(N) _COMMA_##N
 #define COMMA(N) _COMMA_##N
 #define _COMMA_0
 #define _COMMA_0
 #define _COMMA_1 ,
 #define _COMMA_1 ,

+ 38 - 0
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")

+ 170 - 0
core/crypto/crypto.cpp

@@ -0,0 +1,170 @@
+/*************************************************************************/
+/*  crypto.cpp                                                           */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md)    */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#include "crypto.h"
+
+#include "core/engine.h"
+#include "core/io/certs_compressed.gen.h"
+#include "core/io/compression.h"
+
+/// Resources
+
+CryptoKey *(*CryptoKey::_create)() = NULL;
+CryptoKey *CryptoKey::create() {
+	if (_create)
+		return _create();
+	return NULL;
+}
+
+void CryptoKey::_bind_methods() {
+	ClassDB::bind_method(D_METHOD("save", "path"), &CryptoKey::save);
+	ClassDB::bind_method(D_METHOD("load", "path"), &CryptoKey::load);
+}
+
+X509Certificate *(*X509Certificate::_create)() = NULL;
+X509Certificate *X509Certificate::create() {
+	if (_create)
+		return _create();
+	return NULL;
+}
+
+void X509Certificate::_bind_methods() {
+	ClassDB::bind_method(D_METHOD("save", "path"), &X509Certificate::save);
+	ClassDB::bind_method(D_METHOD("load", "path"), &X509Certificate::load);
+}
+
+/// Crypto
+
+void (*Crypto::_load_default_certificates)(String p_path) = NULL;
+Crypto *(*Crypto::_create)() = NULL;
+Crypto *Crypto::create() {
+	if (_create)
+		return _create();
+	return memnew(Crypto);
+}
+
+void Crypto::load_default_certificates(String p_path) {
+
+	if (_load_default_certificates)
+		_load_default_certificates(p_path);
+}
+
+void Crypto::_bind_methods() {
+	ClassDB::bind_method(D_METHOD("generate_random_bytes", "size"), &Crypto::generate_random_bytes);
+	ClassDB::bind_method(D_METHOD("generate_rsa", "size"), &Crypto::generate_rsa);
+	ClassDB::bind_method(D_METHOD("generate_self_signed_certificate", "key", "issuer_name", "not_before", "not_after"), &Crypto::generate_self_signed_certificate, DEFVAL("CN=myserver,O=myorganisation,C=IT"), DEFVAL("20140101000000"), DEFVAL("20340101000000"));
+}
+
+PoolByteArray Crypto::generate_random_bytes(int p_bytes) {
+	ERR_FAIL_V_MSG(PoolByteArray(), "generate_random_bytes is not available when mbedtls module is disabled.");
+}
+
+Ref<CryptoKey> Crypto::generate_rsa(int p_bytes) {
+	ERR_FAIL_V_MSG(NULL, "generate_rsa is not available when mbedtls module is disabled.");
+}
+
+Ref<X509Certificate> Crypto::generate_self_signed_certificate(Ref<CryptoKey> p_key, String p_issuer_name, String p_not_before, String p_not_after) {
+	ERR_FAIL_V_MSG(NULL, "generate_self_signed_certificate is not available when mbedtls module is disabled.");
+}
+
+Crypto::Crypto() {
+}
+
+/// Resource loader/saver
+
+RES ResourceFormatLoaderCrypto::load(const String &p_path, const String &p_original_path, Error *r_error) {
+
+	String el = p_path.get_extension().to_lower();
+	if (el == "crt") {
+		X509Certificate *cert = X509Certificate::create();
+		if (cert)
+			cert->load(p_path);
+		return cert;
+	} else if (el == "key") {
+		CryptoKey *key = CryptoKey::create();
+		if (key)
+			key->load(p_path);
+		return key;
+	}
+	return NULL;
+}
+
+void ResourceFormatLoaderCrypto::get_recognized_extensions(List<String> *p_extensions) const {
+
+	p_extensions->push_back("crt");
+	p_extensions->push_back("key");
+}
+
+bool ResourceFormatLoaderCrypto::handles_type(const String &p_type) const {
+
+	return p_type == "X509Certificate" || p_type == "CryptoKey";
+}
+
+String ResourceFormatLoaderCrypto::get_resource_type(const String &p_path) const {
+
+	String el = p_path.get_extension().to_lower();
+	if (el == "crt")
+		return "X509Certificate";
+	else if (el == "key")
+		return "CryptoKey";
+	return "";
+}
+
+Error ResourceFormatSaverCrypto::save(const String &p_path, const RES &p_resource, uint32_t p_flags) {
+
+	Error err;
+	Ref<X509Certificate> cert = p_resource;
+	Ref<CryptoKey> key = p_resource;
+	if (cert.is_valid()) {
+		err = cert->save(p_path);
+	} else if (key.is_valid()) {
+		err = key->save(p_path);
+	} else {
+		ERR_FAIL_V(ERR_INVALID_PARAMETER);
+	}
+	ERR_FAIL_COND_V(err != OK, err);
+	return OK;
+}
+
+void ResourceFormatSaverCrypto::get_recognized_extensions(const RES &p_resource, List<String> *p_extensions) const {
+
+	const X509Certificate *cert = Object::cast_to<X509Certificate>(*p_resource);
+	const CryptoKey *key = Object::cast_to<CryptoKey>(*p_resource);
+	if (cert) {
+		p_extensions->push_back("crt");
+	}
+	if (key) {
+		p_extensions->push_back("key");
+	}
+}
+bool ResourceFormatSaverCrypto::recognize(const RES &p_resource) const {
+
+	return Object::cast_to<X509Certificate>(*p_resource) || Object::cast_to<CryptoKey>(*p_resource);
+}

+ 105 - 0
core/crypto/crypto.h

@@ -0,0 +1,105 @@
+/*************************************************************************/
+/*  crypto.h                                                             */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md)    */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#ifndef CRYPTO_H
+#define CRYPTO_H
+
+#include "core/reference.h"
+#include "core/resource.h"
+
+#include "core/io/resource_loader.h"
+#include "core/io/resource_saver.h"
+
+class CryptoKey : public Resource {
+	GDCLASS(CryptoKey, Resource);
+
+protected:
+	static void _bind_methods();
+	static CryptoKey *(*_create)();
+
+public:
+	static CryptoKey *create();
+	virtual Error load(String p_path) = 0;
+	virtual Error save(String p_path) = 0;
+};
+
+class X509Certificate : public Resource {
+	GDCLASS(X509Certificate, Resource);
+
+protected:
+	static void _bind_methods();
+	static X509Certificate *(*_create)();
+
+public:
+	static X509Certificate *create();
+	virtual Error load(String p_path) = 0;
+	virtual Error load_from_memory(const uint8_t *p_buffer, int p_len) = 0;
+	virtual Error save(String p_path) = 0;
+};
+
+class Crypto : public Reference {
+	GDCLASS(Crypto, Reference);
+
+protected:
+	static void _bind_methods();
+	static Crypto *(*_create)();
+	static void (*_load_default_certificates)(String p_path);
+
+public:
+	static Crypto *create();
+	static void load_default_certificates(String p_path);
+
+	virtual PoolByteArray generate_random_bytes(int p_bytes);
+	virtual Ref<CryptoKey> generate_rsa(int p_bytes);
+	virtual Ref<X509Certificate> generate_self_signed_certificate(Ref<CryptoKey> p_key, String p_issuer_name, String p_not_before, String p_not_after);
+
+	Crypto();
+};
+
+class ResourceFormatLoaderCrypto : public ResourceFormatLoader {
+	GDCLASS(ResourceFormatLoaderCrypto, ResourceFormatLoader);
+
+public:
+	virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = NULL);
+	virtual void get_recognized_extensions(List<String> *p_extensions) const;
+	virtual bool handles_type(const String &p_type) const;
+	virtual String get_resource_type(const String &p_path) const;
+};
+
+class ResourceFormatSaverCrypto : public ResourceFormatSaver {
+	GDCLASS(ResourceFormatSaverCrypto, ResourceFormatSaver);
+
+public:
+	virtual Error save(const String &p_path, const RES &p_resource, uint32_t p_flags = 0);
+	virtual void get_recognized_extensions(const RES &p_resource, List<String> *p_extensions) const;
+	virtual bool recognize(const RES &p_resource) const;
+};
+
+#endif // CRYPTO_H

+ 29 - 3
core/math/crypto_core.cpp → core/crypto/crypto_core.cpp

@@ -52,7 +52,7 @@ Error CryptoCore::MD5Context::start() {
 	return ret ? FAILED : OK;
 	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);
 	int ret = mbedtls_md5_update_ret((mbedtls_md5_context *)ctx, p_src, p_len);
 	return ret ? FAILED : OK;
 	return ret ? FAILED : OK;
 }
 }
@@ -62,6 +62,32 @@ Error CryptoCore::MD5Context::finish(unsigned char r_hash[16]) {
 	return ret ? FAILED : OK;
 	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
 // SHA256
 CryptoCore::SHA256Context::SHA256Context() {
 CryptoCore::SHA256Context::SHA256Context() {
 	ctx = memalloc(sizeof(mbedtls_sha256_context));
 	ctx = memalloc(sizeof(mbedtls_sha256_context));
@@ -78,12 +104,12 @@ Error CryptoCore::SHA256Context::start() {
 	return ret ? FAILED : OK;
 	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);
 	int ret = mbedtls_sha256_update_ret((mbedtls_sha256_context *)ctx, p_src, p_len);
 	return ret ? FAILED : OK;
 	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);
 	int ret = mbedtls_sha256_finish_ret((mbedtls_sha256_context *)ctx, r_hash);
 	return ret ? FAILED : OK;
 	return ret ? FAILED : OK;
 }
 }

+ 17 - 3
core/math/crypto_core.h → core/crypto/crypto_core.h

@@ -46,10 +46,24 @@ public:
 		~MD5Context();
 		~MD5Context();
 
 
 		Error start();
 		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]);
 		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 {
 	class SHA256Context {
 
 
 	private:
 	private:
@@ -60,8 +74,8 @@ public:
 		~SHA256Context();
 		~SHA256Context();
 
 
 		Error start();
 		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 {
 	class AESContext {

+ 137 - 0
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();
+}

+ 66 - 0
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

+ 1 - 4
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 {
 Object *Engine::get_singleton_object(const String &p_name) const {
 
 
 	const Map<StringName, Object *>::Element *E = singleton_ptrs.find(p_name);
 	const Map<StringName, Object *>::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();
 	return E->get();
 };
 };
 
 

+ 137 - 0
core/error_macros.h

@@ -140,6 +140,16 @@ extern bool _err_error_exists;
 		_err_error_exists = false;                                                                                  \
 		_err_error_exists = false;                                                                                  \
 	} while (0); // (*)
 	} 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.
 /** 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
 * This function returns an error value, if returning Error, please select the most
 * appropriate error condition from error_macros.h
 * appropriate error condition from error_macros.h
@@ -154,6 +164,16 @@ extern bool _err_error_exists;
 		_err_error_exists = false;                                                                                  \
 		_err_error_exists = false;                                                                                  \
 	} while (0); // (*)
 	} 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.
 /** 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
 * This function returns an error value, if returning Error, please select the most
 * appropriate error condition from error_macros.h
 * appropriate error condition from error_macros.h
@@ -168,6 +188,16 @@ extern bool _err_error_exists;
 		_err_error_exists = false;                                                                                  \
 		_err_error_exists = false;                                                                                  \
 	} while (0); // (*)
 	} 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.
 /** 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.
 *   We'll return a null reference and try to keep running.
 */
 */
@@ -179,6 +209,15 @@ extern bool _err_error_exists;
 		}                                                                                                                 \
 		}                                                                                                                 \
 	} while (0); // (*)
 	} 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().
 /** An error condition happened (m_cond tested true) (WARNING this is the opposite as assert().
   * the function will exit.
   * the function will exit.
   */
   */
@@ -192,6 +231,16 @@ extern bool _err_error_exists;
 		_err_error_exists = false;                                                                          \
 		_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)                                                                  \
 #define ERR_FAIL_NULL_V(m_param, m_retval)                                                                  \
 	{                                                                                                       \
 	{                                                                                                       \
 		if (unlikely(!m_param)) {                                                                           \
 		if (unlikely(!m_param)) {                                                                           \
@@ -201,6 +250,16 @@ extern bool _err_error_exists;
 		_err_error_exists = false;                                                                          \
 		_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().
 /** An error condition happened (m_cond tested true) (WARNING this is the opposite as assert().
  * the function will exit.
  * the function will exit.
  */
  */
@@ -214,6 +273,16 @@ extern bool _err_error_exists;
 		_err_error_exists = false;                                                                         \
 		_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.
 /** 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().
 /** An error condition happened (m_cond tested true) (WARNING this is the opposite as assert().
  * the function will exit.
  * the function will exit.
  * This function returns an error value, if returning Error, please select the most
  * 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;                                                                                                   \
 		_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().
 /** An error condition happened (m_cond tested true) (WARNING this is the opposite as assert().
  * the loop will skip to the next iteration.
  * the loop will skip to the next iteration.
  */
  */
@@ -253,6 +341,16 @@ extern bool _err_error_exists;
 		_err_error_exists = false;                                                                                       \
 		_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().
 /** An error condition happened (m_cond tested true) (WARNING this is the opposite as assert().
  * the loop will break
  * the loop will break
  */
  */
@@ -266,6 +364,16 @@ extern bool _err_error_exists;
 		_err_error_exists = false;                                                                                     \
 		_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
 /** Print an error string and return
  */
  */
 
 
@@ -276,6 +384,12 @@ extern bool _err_error_exists;
 		return;                                                                        \
 		return;                                                                        \
 	}
 	}
 
 
+#define ERR_FAIL_MSG(m_msg) \
+	{                       \
+		ERR_EXPLAIN(m_msg); \
+		ERR_FAIL();         \
+	}
+
 /** Print an error string and return with value
 /** Print an error string and return with value
  */
  */
 
 
@@ -286,6 +400,12 @@ extern bool _err_error_exists;
 		return m_value;                                                                                           \
 		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.
 /** 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                                                                         \
 		GENERATE_TRAP                                                                         \
 	}
 	}
 
 
+#define CRASH_NOW_MSG(m_msg) \
+	{                        \
+		ERR_EXPLAIN(m_msg);  \
+		CRASH_NOW();         \
+	}
+
 /** Print an error string.
 /** 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
 #endif

+ 13 - 0
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);
 	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) {
 void FuncRef::set_instance(Object *p_obj) {
 
 
 	ERR_FAIL_NULL(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_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_instance", "instance"), &FuncRef::set_instance);
 	ClassDB::bind_method(D_METHOD("set_function", "name"), &FuncRef::set_function);
 	ClassDB::bind_method(D_METHOD("set_function", "name"), &FuncRef::set_function);
 	ClassDB::bind_method(D_METHOD("is_valid"), &FuncRef::is_valid);
 	ClassDB::bind_method(D_METHOD("is_valid"), &FuncRef::is_valid);

+ 1 - 0
core/func_ref.h

@@ -44,6 +44,7 @@ protected:
 
 
 public:
 public:
 	Variant call_func(const Variant **p_args, int p_argcount, Variant::CallError &r_error);
 	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_instance(Object *p_obj);
 	void set_function(const StringName &p_func);
 	void set_function(const StringName &p_func);
 	bool is_valid() const;
 	bool is_valid() const;

+ 3 - 13
core/hash_map.h

@@ -151,11 +151,7 @@ private:
 			return;
 			return;
 
 
 		Element **new_hash_table = memnew_arr(Element *, ((uint64_t)1 << new_hash_table_power));
 		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++) {
 		for (int i = 0; i < (1 << new_hash_table_power); i++) {
 
 
@@ -208,10 +204,7 @@ private:
 
 
 		/* if element doesn't exist, create it */
 		/* if element doesn't exist, create it */
 		Element *e = memnew(Element);
 		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 hash = Hasher::hash(p_key);
 		uint32_t index = hash & ((1 << hash_table_power) - 1);
 		uint32_t index = hash & ((1 << hash_table_power) - 1);
 		e->next = hash_table[index];
 		e->next = hash_table[index];
@@ -498,10 +491,7 @@ public:
 		} else { /* get the next key */
 		} else { /* get the next key */
 
 
 			const Element *e = get_element(*p_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 (e->next) {
 				/* if there is a "next" in the list, return that */
 				/* if there is a "next" in the list, return that */
 				return &e->next->pair.key;
 				return &e->next->pair.key;

+ 34 - 62
core/image.cpp

@@ -83,6 +83,7 @@ const char *Image::format_names[Image::FORMAT_MAX] = {
 };
 };
 
 
 SavePNGFunc Image::save_png_func = NULL;
 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) {
 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) {
 	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) {
 	} 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++) {
 		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
 			// Create the kernel used by all the pixels of the column
 			for (int32_t target_x = start_x; target_x <= end_x; target_x++)
 			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++) {
 			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++) {
 		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++)
 			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++) {
 			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) {
 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 w = next_power_of_2(width);
 	int h = next_power_of_2(height);
 	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) {
 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 */;
 	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) {
 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_x < 0);
 	ERR_FAIL_COND(p_y < 0);
 	ERR_FAIL_COND(p_y < 0);
 	ERR_FAIL_COND(p_width <= 0);
 	ERR_FAIL_COND(p_width <= 0);
@@ -1163,10 +1149,7 @@ void Image::crop(int p_width, int p_height) {
 
 
 void Image::flip_y() {
 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();
 	bool used_mipmaps = has_mipmaps();
 	if (used_mipmaps) {
 	if (used_mipmaps) {
@@ -1199,10 +1182,7 @@ void Image::flip_y() {
 
 
 void Image::flip_x() {
 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();
 	bool used_mipmaps = has_mipmaps();
 	if (used_mipmaps) {
 	if (used_mipmaps) {
@@ -1461,15 +1441,9 @@ void Image::normalize() {
 
 
 Error Image::generate_mipmaps(bool p_renormalize) {
 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;
 	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 mm;
 	int size = _get_dst_image_size(p_width, p_height, p_format, mm, p_use_mipmaps ? -1 : 0);
 	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;
 	height = p_height;
 	width = p_width;
 	width = p_width;
@@ -1917,6 +1888,14 @@ Error Image::save_png(const String &p_path) const {
 	return save_png_func(p_path, Ref<Image>((Image *)this));
 	return save_png_func(p_path, Ref<Image>((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>((Image *)this), p_grayscale);
+}
+
 int Image::get_image_data_size(int p_width, int p_height, Format p_format, bool p_mipmaps) {
 int Image::get_image_data_size(int p_width, int p_height, Format p_format, bool p_mipmaps) {
 
 
 	int mm;
 	int mm;
@@ -2405,10 +2384,7 @@ Color Image::get_pixel(int p_x, int p_y) const {
 
 
 	uint8_t *ptr = write_lock.ptr();
 	uint8_t *ptr = write_lock.ptr();
 #ifdef DEBUG_ENABLED
 #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_x, width, Color());
 	ERR_FAIL_INDEX_V(p_y, height, 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]);
 			return Color::from_rgbe9995(((uint32_t *)ptr)[ofs]);
 		}
 		}
 		default: {
 		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();
 	uint8_t *ptr = write_lock.ptr();
 #ifdef DEBUG_ENABLED
 #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_x, width);
 	ERR_FAIL_INDEX(p_y, height);
 	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;
 		} break;
 		default: {
 		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("load", "path"), &Image::load);
 	ClassDB::bind_method(D_METHOD("save_png", "path"), &Image::save_png);
 	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("detect_alpha"), &Image::detect_alpha);
 	ClassDB::bind_method(D_METHOD("is_invisible"), &Image::is_invisible);
 	ClassDB::bind_method(D_METHOD("is_invisible"), &Image::is_invisible);

+ 4 - 2
core/image.h

@@ -49,11 +49,14 @@ class Image;
 typedef Error (*SavePNGFunc)(const String &p_path, const Ref<Image> &p_img);
 typedef Error (*SavePNGFunc)(const String &p_path, const Ref<Image> &p_img);
 typedef Ref<Image> (*ImageMemLoadFunc)(const uint8_t *p_png, int p_size);
 typedef Ref<Image> (*ImageMemLoadFunc)(const uint8_t *p_png, int p_size);
 
 
+typedef Error (*SaveEXRFunc)(const String &p_path, const Ref<Image> &p_img, bool p_grayscale);
+
 class Image : public Resource {
 class Image : public Resource {
 	GDCLASS(Image, Resource);
 	GDCLASS(Image, Resource);
 
 
 public:
 public:
 	static SavePNGFunc save_png_func;
 	static SavePNGFunc save_png_func;
+	static SaveEXRFunc save_exr_func;
 
 
 	enum {
 	enum {
 		MAX_WIDTH = 16384, // force a limit somehow
 		MAX_WIDTH = 16384, // force a limit somehow
@@ -217,9 +220,7 @@ public:
 
 
 	/**
 	/**
 	 * Resize the image, using the preferred interpolation method.
 	 * 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_to_po2(bool p_square = false);
 	void resize(int p_width, int p_height, Interpolation p_interpolation = INTERPOLATE_BILINEAR);
 	void resize(int p_width, int p_height, Interpolation p_interpolation = INTERPOLATE_BILINEAR);
 	void shrink_x2();
 	void shrink_x2();
@@ -258,6 +259,7 @@ public:
 
 
 	Error load(const String &p_path);
 	Error load(const String &p_path);
 	Error save_png(const String &p_path) const;
 	Error save_png(const String &p_path) const;
+	Error save_exr(const String &p_path, bool p_grayscale) const;
 
 
 	/**
 	/**
 	 * create an empty image
 	 * create an empty image

+ 1 - 4
core/input_map.cpp

@@ -192,10 +192,7 @@ bool InputMap::event_is_action(const Ref<InputEvent> &p_event, const StringName
 
 
 bool InputMap::event_get_action_status(const Ref<InputEvent> &p_event, const StringName &p_action, bool *p_pressed, float *p_strength) const {
 bool InputMap::event_get_action_status(const Ref<InputEvent> &p_event, const StringName &p_action, bool *p_pressed, float *p_strength) const {
 	Map<StringName, Action>::Element *E = input_map.find(p_action);
 	Map<StringName, Action>::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<InputEventAction> input_event_action = p_event;
 	Ref<InputEventAction> input_event_action = p_event;
 	if (input_event_action.is_valid()) {
 	if (input_event_action.is_valid()) {

+ 3 - 6
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 {
 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 (!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 p_default;
 	}
 	}
 	return values[p_section][p_key];
 	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);
 	FileAccess *f = FileAccess::open(p_path, FileAccess::READ, &err);
 
 
 	if (!f)
 	if (!f)
-		return ERR_CANT_OPEN;
+		return err;
 
 
 	return _internal_load(p_path, f);
 	return _internal_load(p_path, f);
 }
 }
@@ -271,7 +268,7 @@ Error ConfigFile::_internal_load(const String &p_path, FileAccess *f) {
 			memdelete(f);
 			memdelete(f);
 			return OK;
 			return OK;
 		} else if (err != 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);
 			memdelete(f);
 			return err;
 			return err;
 		}
 		}

+ 4 - 8
core/io/file_access_buffered.cpp

@@ -87,10 +87,8 @@ bool FileAccessBuffered::eof_reached() const {
 }
 }
 
 
 uint8_t FileAccessBuffered::get_8() 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;
 	uint8_t byte = 0;
 	if (cache_data_left() >= 1) {
 	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 {
 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) {
 	if (p_length > cache_size) {
 
 

+ 1 - 4
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 {
 	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);
 		((T *)&f)->seek(p_offset);
 
 

+ 2 - 1
core/io/file_access_compressed.cpp

@@ -208,7 +208,8 @@ void FileAccessCompressed::seek(size_t p_position) {
 		if (p_position == read_total) {
 		if (p_position == read_total) {
 			at_end = true;
 			at_end = true;
 		} else {
 		} else {
-
+			at_end = false;
+			read_eof = false;
 			int block_idx = p_position / block_size;
 			int block_idx = p_position / block_size;
 			if (block_idx != read_block) {
 			if (block_idx != read_block) {
 
 

+ 3 - 4
core/io/file_access_encrypted.cpp

@@ -30,7 +30,7 @@
 
 
 #include "file_access_encrypted.h"
 #include "file_access_encrypted.h"
 
 
-#include "core/math/crypto_core.h"
+#include "core/crypto/crypto_core.h"
 #include "core/os/copymem.h"
 #include "core/os/copymem.h"
 #include "core/print_string.h"
 #include "core/print_string.h"
 #include "core/variant.h"
 #include "core/variant.h"
@@ -94,8 +94,7 @@ Error FileAccessEncrypted::open_and_parse(FileAccess *p_base, const Vector<uint8
 		unsigned char hash[16];
 		unsigned char hash[16];
 		ERR_FAIL_COND_V(CryptoCore::md5(data.ptr(), data.size(), hash) != OK, ERR_BUG);
 		ERR_FAIL_COND_V(CryptoCore::md5(data.ptr(), data.size(), hash) != OK, ERR_BUG);
 
 
-		ERR_EXPLAIN("The MD5 sum of the decrypted file does not match the expected value. It could be that the file is corrupt, or that the provided decryption key is invalid.");
-		ERR_FAIL_COND_V(String::md5(hash) != String::md5(md5d), ERR_FILE_CORRUPT);
+		ERR_FAIL_COND_V_MSG(String::md5(hash) != String::md5(md5d), ERR_FILE_CORRUPT, "The MD5 sum of the decrypted file does not match the expected value. It could be that the file is corrupt, or that the provided decryption key is invalid.");
 
 
 		file = p_base;
 		file = p_base;
 	}
 	}
@@ -298,7 +297,7 @@ uint32_t FileAccessEncrypted::_get_unix_permissions(const String &p_file) {
 }
 }
 
 
 Error FileAccessEncrypted::_set_unix_permissions(const String &p_file, uint32_t p_permissions) {
 Error FileAccessEncrypted::_set_unix_permissions(const String &p_file, uint32_t p_permissions) {
-	ERR_PRINT("Setting UNIX permissions on encrypted files is not implemented yet");
+	ERR_PRINT("Setting UNIX permissions on encrypted files is not implemented yet.");
 	return ERR_UNAVAILABLE;
 	return ERR_UNAVAILABLE;
 }
 }
 
 

+ 10 - 9
core/io/file_access_pack.cpp

@@ -90,7 +90,7 @@ void PackedData::add_path(const String &pkg_path, const String &path, uint64_t o
 			}
 			}
 		}
 		}
 		String filename = path.get_file();
 		String filename = path.get_file();
-		// Don't add as a file if the path points to a directoryy
+		// Don't add as a file if the path points to a directory
 		if (!filename.empty()) {
 		if (!filename.empty()) {
 			cd->files.insert(filename);
 			cd->files.insert(filename);
 		}
 		}
@@ -171,10 +171,8 @@ bool PackedSourcePCK::try_open_pack(const String &p_path) {
 	uint32_t ver_minor = f->get_32();
 	uint32_t ver_minor = f->get_32();
 	f->get_32(); // ver_rev
 	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++) {
 	for (int i = 0; i < 16; i++) {
 		//reserved
 		//reserved
@@ -322,10 +320,9 @@ bool FileAccessPack::file_exists(const String &p_name) {
 FileAccessPack::FileAccessPack(const String &p_path, const PackedData::PackedFile &p_file) :
 FileAccessPack::FileAccessPack(const String &p_path, const PackedData::PackedFile &p_file) :
 		pf(p_file),
 		pf(p_file),
 		f(FileAccess::open(pf.pack, FileAccess::READ)) {
 		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);
 	f->seek(pf.offset);
 	pos = 0;
 	pos = 0;
 	eof = false;
 	eof = false;
@@ -463,11 +460,15 @@ String DirAccessPack::get_current_dir() {
 
 
 bool DirAccessPack::file_exists(String p_file) {
 bool DirAccessPack::file_exists(String p_file) {
 
 
+	p_file = fix_path(p_file);
+
 	return current->files.has(p_file);
 	return current->files.has(p_file);
 }
 }
 
 
 bool DirAccessPack::dir_exists(String p_dir) {
 bool DirAccessPack::dir_exists(String p_dir) {
 
 
+	p_dir = fix_path(p_dir);
+
 	return current->subdirs.has(p_dir);
 	return current->subdirs.has(p_dir);
 }
 }
 
 

+ 0 - 16
core/io/image_loader.h

@@ -37,24 +37,8 @@
 #include "core/os/file_access.h"
 #include "core/os/file_access.h"
 #include "core/ustring.h"
 #include "core/ustring.h"
 
 
-/**
-	@author Juan Linietsky <[email protected]>
-*/
-
-/**
- * @class ImageScanLineLoader
- * @author Juan Linietsky <[email protected]>
- *
-
- */
 class ImageLoader;
 class ImageLoader;
 
 
-/**
- * @class ImageLoader
- * Base Class and singleton for loading images from disk
- * Can load images in one go, or by scanline
- */
-
 class ImageFormatLoader {
 class ImageFormatLoader {
 	friend class ImageLoader;
 	friend class ImageLoader;
 	friend class ResourceFormatLoaderImage;
 	friend class ResourceFormatLoaderImage;

+ 4 - 10
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 == ':') {
 		} else if (c == ':') {
 			break;
 			break;
 		} else {
 		} 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 = ret << 4;
 		ret += n;
 		ret += n;
@@ -126,9 +125,7 @@ void IP_Address::_parse_ipv6(const String &p_string) {
 				++parts_count;
 				++parts_count;
 			};
 			};
 		} else {
 		} 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(".");
 	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++) {
 	for (int i = 0; i < 4; i++) {
 		p_ret[i] = ip.get_slicec('.', i).to_int();
 		p_ret[i] = ip.get_slicec('.', i).to_int();
 	}
 	}
@@ -229,7 +223,7 @@ IP_Address::IP_Address(const String &p_string) {
 		valid = true;
 		valid = true;
 
 
 	} else {
 	} else {
-		ERR_PRINT("Invalid IP address");
+		ERR_PRINT("Invalid IP address.");
 	}
 	}
 }
 }
 
 

+ 0 - 10
core/io/marshalls.cpp

@@ -377,11 +377,6 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
 			}
 			}
 
 
 		} break;
 		} 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: {
 		case Variant::_RID: {
 
 
 			r_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;
 			r_len += 4 * 4;
 
 
 		} break;
 		} 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: {
 		case Variant::_RID: {
 
 
 		} break;
 		} break;

+ 199 - 109
core/io/multiplayer_api.cpp

@@ -33,6 +33,10 @@
 #include "core/io/marshalls.h"
 #include "core/io/marshalls.h"
 #include "scene/main/node.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) {
 _FORCE_INLINE_ bool _should_call_local(MultiplayerAPI::RPCMode mode, bool is_master, bool &r_skip_rpc) {
 
 
 	switch (mode) {
 	switch (mode) {
@@ -146,8 +150,7 @@ void MultiplayerAPI::set_network_peer(const Ref<NetworkedMultiplayerPeer> &p_pee
 
 
 	network_peer = p_peer;
 	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()) {
 	if (network_peer.is_valid()) {
 		network_peer->connect("peer_connected", this, "_add_peer");
 		network_peer->connect("peer_connected", this, "_add_peer");
@@ -164,10 +167,16 @@ Ref<NetworkedMultiplayerPeer> MultiplayerAPI::get_network_peer() const {
 
 
 void MultiplayerAPI::_process_packet(int p_from, const uint8_t *p_packet, int p_packet_len) {
 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];
 	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_CALL:
 		case NETWORK_COMMAND_REMOTE_SET: {
 		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);
 			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.
 			// Detect cstring end.
 			int len_end = 5;
 			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]);
 			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;
 		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;
 		String paths;
 		paths.parse_utf8((const char *)&p_packet[ofs], p_packet_len - ofs);
 		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);
 		node = root_node->get_node(np);
 
 
 		if (!node)
 		if (!node)
-			ERR_PRINTS("Failed to get path from RPC: " + String(np));
+			ERR_PRINTS("Failed to get path from RPC: " + String(np) + ".");
 	} else {
 	} else {
 		// Use cached path.
 		// Use cached path.
 		int id = target;
 		int id = target;
 
 
 		Map<int, PathGetCache>::Element *E = path_get_cache.find(p_from);
 		Map<int, PathGetCache>::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<int, PathGetCache::NodeInfo>::Element *F = E->get().nodes.find(id);
 		Map<int, PathGetCache::NodeInfo>::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();
 		PathGetCache::NodeInfo *ni = &F->get();
 		// Do proper caching later.
 		// Do proper caching later.
 
 
 		node = root_node->get_node(ni->path);
 		node = root_node->get_node(ni->path);
 		if (!node)
 		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;
 	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) {
 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.
 	// Check that remote can call the RPC on this node.
 	RPCMode rpc_mode = RPC_MODE_DISABLED;
 	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);
 	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];
 	int argc = p_packet[p_offset];
 	Vector<Variant> args;
 	Vector<Variant> args;
@@ -295,15 +296,21 @@ void MultiplayerAPI::_process_rpc(Node *p_node, const StringName &p_name, int p_
 
 
 	p_offset++;
 	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++) {
 	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;
 		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());
 		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];
 		argp.write[i] = &args[i];
 		p_offset += vlen;
 		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) {
 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.
 	// Check that remote can call the RSET on this node.
 	RPCMode rset_mode = RPC_MODE_DISABLED;
 	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);
 	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;
 	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());
 	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;
 	bool valid;
 
 
 	p_node->set(p_name, value, &valid);
 	p_node->set(p_name, value, &valid);
 	if (!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);
 		ERR_PRINTS(error);
 	}
 	}
 }
 }
 
 
 void MultiplayerAPI::_process_simplify_path(int p_from, const uint8_t *p_packet, int p_packet_len) {
 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]);
 	int id = decode_uint32(&p_packet[1]);
 
 
 	String paths;
 	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) {
 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;
 	String paths;
 	paths.parse_utf8((const char *)&p_packet[1], p_packet_len - 1);
 	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;
 	NodePath path = paths;
 
 
 	PathSentCache *psc = path_send_cache.getptr(path);
 	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<int, bool>::Element *E = psc->confirmed_peers.find(p_from);
 	Map<int, bool>::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;
 	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) {
 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 != 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());
 	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.
 	// See if the path is cached.
 	PathSentCache *psc = path_send_cache.getptr(from_path);
 	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) {
 	if (p_set) {
 		// Set argument.
 		// Set argument.
 		Error err = encode_variant(*p_arg[0], NULL, len, allow_object_decoding || network_peer->is_object_decoding_allowed());
 		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);
 		MAKE_ROOM(ofs + len);
 		encode_variant(*p_arg[0], &(packet_cache.write[ofs]), len, allow_object_decoding || network_peer->is_object_decoding_allowed());
 		encode_variant(*p_arg[0], &(packet_cache.write[ofs]), len, allow_object_decoding || network_peer->is_object_decoding_allowed());
 		ofs += len;
 		ofs += len;
@@ -543,14 +533,21 @@ void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p
 		ofs += 1;
 		ofs += 1;
 		for (int i = 0; i < p_argcount; i++) {
 		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());
 			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);
 			MAKE_ROOM(ofs + len);
 			encode_variant(*p_arg[i], &(packet_cache.write[ofs]), len, allow_object_decoding || network_peer->is_object_decoding_allowed());
 			encode_variant(*p_arg[i], &(packet_cache.write[ofs]), len, allow_object_decoding || network_peer->is_object_decoding_allowed());
 			ofs += len;
 			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).
 	// 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);
 	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) {
 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();
 	int node_id = network_peer->get_unique_id();
 	bool skip_rpc = node_id == p_peer_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) {
 	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);
 		_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;
 		rpc_sender_id = temp_id;
 		if (ce.error != Variant::CallError::CALL_OK) {
 		if (ce.error != Variant::CallError::CALL_OK) {
 			String error = Variant::get_call_error_text(p_node, p_method, p_arg, p_argcount, ce);
 			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);
 			ERR_PRINTS(error);
 			return;
 			return;
 		}
 		}
@@ -683,24 +686,20 @@ void MultiplayerAPI::rpcp(Node *p_node, int p_peer_id, bool p_unreliable, const
 		rpc_sender_id = temp_id;
 		rpc_sender_id = temp_id;
 		if (ce.error != Variant::CallError::CALL_OK) {
 		if (ce.error != Variant::CallError::CALL_OK) {
 			String error = Variant::get_call_error_text(p_node, p_method, p_arg, p_argcount, ce);
 			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);
 			ERR_PRINTS(error);
 			return;
 			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) {
 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();
 	int node_id = network_peer->get_unique_id();
 	bool is_master = p_node->is_network_master();
 	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;
 			rpc_sender_id = temp_id;
 
 
 			if (!valid) {
 			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);
 				ERR_PRINTS(error);
 				return;
 				return;
 			}
 			}
@@ -742,7 +741,7 @@ void MultiplayerAPI::rsetp(Node *p_node, int p_peer_id, bool p_unreliable, const
 				rpc_sender_id = temp_id;
 				rpc_sender_id = temp_id;
 
 
 				if (!valid) {
 				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);
 					ERR_PRINTS(error);
 					return;
 					return;
 				}
 				}
@@ -751,11 +750,18 @@ void MultiplayerAPI::rsetp(Node *p_node, int p_peer_id, bool p_unreliable, const
 	}
 	}
 
 
 	if (skip_rset) {
 	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;
 		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;
 	const Variant *vptr = &p_value;
 
 
 	_send_rpc(p_node, p_peer_id, p_unreliable, true, p_property, &vptr, 1);
 	_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<uint8_t> p_data, int p_to, NetworkedMultiplayerPeer::TransferMode p_mode) {
 Error MultiplayerAPI::send_bytes(PoolVector<uint8_t> 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);
 	MAKE_ROOM(p_data.size() + 1);
 	PoolVector<uint8_t>::Read r = p_data.read();
 	PoolVector<uint8_t>::Read r = p_data.read();
@@ -783,8 +786,7 @@ Error MultiplayerAPI::send_bytes(PoolVector<uint8_t> p_data, int p_to, Networked
 
 
 void MultiplayerAPI::_process_raw(int p_from, const uint8_t *p_packet, int p_packet_len) {
 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<uint8_t> out;
 	PoolVector<uint8_t> out;
 	int len = p_packet_len - 1;
 	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 {
 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();
 	return network_peer->get_unique_id();
 }
 }
 
 
 bool MultiplayerAPI::is_network_server() const {
 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?
 	// 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();
 	return network_peer->is_server();
 }
 }
 
 
 void MultiplayerAPI::set_refuse_new_network_connections(bool p_refuse) {
 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);
 	network_peer->set_refuse_new_connections(p_refuse);
 }
 }
 
 
 bool MultiplayerAPI::is_refusing_new_network_connections() const {
 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();
 	return network_peer->is_refusing_new_connections();
 }
 }
 
 
 Vector<int> MultiplayerAPI::get_network_connected_peers() const {
 Vector<int> 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<int>());
+	ERR_FAIL_COND_V_MSG(!network_peer.is_valid(), Vector<int>(), "No network peer is assigned. Assume no peers are connected.");
 
 
 	Vector<int> ret;
 	Vector<int> ret;
 	for (Set<int>::Element *E = connected_peers.front(); E; E = E->next()) {
 	for (Set<int>::Element *E = connected_peers.front(); E; E = E->next()) {
@@ -848,6 +845,96 @@ bool MultiplayerAPI::is_object_decoding_allowed() const {
 	return allow_object_decoding;
 	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<ObjectID, ProfilingInfo>::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<BandwidthFrame> &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<Node>(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() {
 void MultiplayerAPI::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("set_root_node", "node"), &MultiplayerAPI::set_root_node);
 	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));
 	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) {
 		allow_object_decoding(false) {
 	rpc_sender_id = 0;
 	rpc_sender_id = 0;
 	root_node = NULL;
 	root_node = NULL;
+#ifdef DEBUG_ENABLED
+	profiling = false;
+#endif
 	clear();
 	clear();
 }
 }
 
 

+ 34 - 0
core/io/multiplayer_api.h

@@ -38,6 +38,16 @@ class MultiplayerAPI : public Reference {
 
 
 	GDCLASS(MultiplayerAPI, 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:
 private:
 	//path sent caches
 	//path sent caches
 	struct PathSentCache {
 	struct PathSentCache {
@@ -55,6 +65,23 @@ private:
 		Map<int, NodeInfo> nodes;
 		Map<int, NodeInfo> nodes;
 	};
 	};
 
 
+#ifdef DEBUG_ENABLED
+	struct BandwidthFrame {
+		uint32_t timestamp;
+		int packet_size;
+	};
+
+	int bandwidth_incoming_pointer;
+	Vector<BandwidthFrame> bandwidth_incoming_data;
+	int bandwidth_outgoing_pointer;
+	Vector<BandwidthFrame> bandwidth_outgoing_data;
+	Map<ObjectID, ProfilingInfo> profiler_frame_data;
+	bool profiling;
+
+	void _init_node_profile(ObjectID p_node);
+	int _get_bandwidth_usage(const Vector<BandwidthFrame> &p_buffer, int p_pointer);
+#endif
+
 	Ref<NetworkedMultiplayerPeer> network_peer;
 	Ref<NetworkedMultiplayerPeer> network_peer;
 	int rpc_sender_id;
 	int rpc_sender_id;
 	Set<int> connected_peers;
 	Set<int> connected_peers;
@@ -130,6 +157,13 @@ public:
 	void set_allow_object_decoding(bool p_enable);
 	void set_allow_object_decoding(bool p_enable);
 	bool is_object_decoding_allowed() const;
 	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();
 	~MultiplayerAPI();
 	~MultiplayerAPI();
 };
 };

+ 4 - 4
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 PacketPeer::_bnd_get_var(bool p_allow_objects) {
 	Variant var;
 	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;
 	return var;
-};
+}
 
 
 Error PacketPeer::_put_packet(const PoolVector<uint8_t> &p_buffer) {
 Error PacketPeer::_put_packet(const PoolVector<uint8_t> &p_buffer) {
 	return put_packet_buffer(p_buffer);
 	return put_packet_buffer(p_buffer);
@@ -279,8 +280,7 @@ Ref<StreamPeer> PacketPeerStream::get_stream_peer() const {
 void PacketPeerStream::set_input_buffer_max_size(int p_max_size) {
 void PacketPeerStream::set_input_buffer_max_size(int p_max_size) {
 
 
 	//warning may lose packets
 	//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));
 	ring_buffer.resize(nearest_shift(p_max_size + 4));
 	input_buffer.resize(next_power_of_2(p_max_size + 4));
 	input_buffer.resize(next_power_of_2(p_max_size + 4));
 }
 }

+ 1 - 4
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);
 	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;
 	alignment = p_alignment;
 
 

+ 20 - 29
core/io/resource_format_binary.cpp

@@ -490,8 +490,7 @@ Error ResourceInteractiveLoaderBinary::parse_variant(Variant &r_v) {
 #endif
 #endif
 
 
 			} else {
 			} 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();
 			w.release();
 			r_v = array;
 			r_v = array;
@@ -518,8 +517,7 @@ Error ResourceInteractiveLoaderBinary::parse_variant(Variant &r_v) {
 #endif
 #endif
 
 
 			} else {
 			} 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();
 			w.release();
 			r_v = array;
 			r_v = array;
@@ -546,8 +544,7 @@ Error ResourceInteractiveLoaderBinary::parse_variant(Variant &r_v) {
 #endif
 #endif
 
 
 			} else {
 			} 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();
 			w.release();
 			r_v = array;
 			r_v = array;
@@ -571,7 +568,7 @@ Error ResourceInteractiveLoaderBinary::parse_variant(Variant &r_v) {
 				const uint32_t current_version = 0;
 				const uint32_t current_version = 0;
 				if (format_version > current_version) {
 				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;
 					return ERR_PARSE_ERROR;
 				}
 				}
 
 
@@ -655,8 +652,7 @@ Error ResourceInteractiveLoaderBinary::poll() {
 			} else {
 			} else {
 
 
 				error = ERR_FILE_MISSING_DEPENDENCIES;
 				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 {
 		} else {
@@ -711,16 +707,15 @@ Error ResourceInteractiveLoaderBinary::poll() {
 	Object *obj = ClassDB::instance(t);
 	Object *obj = ClassDB::instance(t);
 	if (!obj) {
 	if (!obj) {
 		error = ERR_FILE_CORRUPT;
 		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<Resource>(obj);
 	Resource *r = Object::cast_to<Resource>(obj);
 	if (!r) {
 	if (!r) {
+		String obj_class = obj->get_class();
 		error = ERR_FILE_CORRUPT;
 		error = ERR_FILE_CORRUPT;
 		memdelete(obj); //bye
 		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);
 	RES res = RES(r);
@@ -850,8 +845,7 @@ void ResourceInteractiveLoaderBinary::open(FileAccess *p_f) {
 		//not normal
 		//not normal
 
 
 		error = ERR_FILE_UNRECOGNIZED;
 		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();
 	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) {
 	if (ver_format > FORMAT_VERSION || ver_major > VERSION_MAJOR) {
 
 
 		f->close();
 		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();
 	type = get_unicode_string();
@@ -926,8 +919,7 @@ void ResourceInteractiveLoaderBinary::open(FileAccess *p_f) {
 	if (f->eof_reached()) {
 	if (f->eof_reached()) {
 
 
 		error = ERR_FILE_CORRUPT;
 		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;
 		//error=ERR_FILE_UNRECOGNIZED;
 		memdelete(f);
 		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 {
 	} else {
 		fw = FileAccess::open(p_path + ".depren", FileAccess::WRITE);
 		fw = FileAccess::open(p_path + ".depren", FileAccess::WRITE);
 		if (!fw) {
 		if (!fw) {
@@ -1122,7 +1113,7 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons
 		memdelete(da);
 		memdelete(da);
 		//use the old approach
 		//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;
 		Error err;
 		f = FileAccess::open(p_path, FileAccess::READ, &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(f);
 		memdelete(fw);
 		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
 	// 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: {
 		case Variant::_RID: {
 
 
 			f->store_32(VARIANT_RID);
 			f->store_32(VARIANT_RID);
-			WARN_PRINT("Can't save RIDs");
+			WARN_PRINT("Can't save RIDs.");
 			RID val = p_property;
 			RID val = p_property;
 			f->store_32(val.get_id());
 			f->store_32(val.get_id());
 		} break;
 		} break;
@@ -1497,8 +1487,7 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia
 
 
 				if (!resource_set.has(res)) {
 				if (!resource_set.has(res)) {
 					f->store_32(OBJECT_EMPTY);
 					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);
 				f->store_32(OBJECT_INTERNAL_RESOURCE);
@@ -1629,8 +1618,7 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia
 		} break;
 		} break;
 		default: {
 		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) {
 	if (f->get_error() != OK && f->get_error() != ERR_FILE_EOF) {
 		f->close();
 		f->close();
+		memdelete(f);
 		return ERR_CANT_CREATE;
 		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) {
 	if (f->get_error() != OK && f->get_error() != ERR_FILE_EOF) {
 		f->close();
 		f->close();
+		memdelete(f);
 		return ERR_CANT_CREATE;
 		return ERR_CANT_CREATE;
 	}
 	}
 
 
 	f->close();
 	f->close();
+	memdelete(f);
 
 
 	return OK;
 	return OK;
 }
 }

+ 14 - 31
core/io/resource_loader.cpp

@@ -275,12 +275,9 @@ RES ResourceLoader::_load(const String &p_path, const String &p_original_path, c
 		return res;
 		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) {
 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);
 			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
 		//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) {
 		if (!p_no_cache) {
 			_remove_from_loading_map(local_path);
 			_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);
 	print_verbose("Loading resource: " + path);
@@ -479,10 +472,7 @@ Ref<ResourceInteractiveLoader> ResourceLoader::load_interactive(const String &p_
 	if (!p_no_cache) {
 	if (!p_no_cache) {
 
 
 		bool success = _add_to_loading_map(local_path);
 		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)) {
 		if (ResourceCache::has(local_path)) {
 
 
@@ -503,8 +493,7 @@ Ref<ResourceInteractiveLoader> ResourceLoader::load_interactive(const String &p_
 		if (!p_no_cache) {
 		if (!p_no_cache) {
 			_remove_from_loading_map(local_path);
 			_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);
 	print_verbose("Loading resource: " + path);
@@ -534,12 +523,9 @@ Ref<ResourceInteractiveLoader> ResourceLoader::load_interactive(const String &p_
 		_remove_from_loading_map(local_path);
 		_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<ResourceInteractiveLoader>());
+	ERR_FAIL_COND_V_MSG(found, Ref<ResourceInteractiveLoader>(), "Failed loading resource: " + path + ".");
+
+	ERR_FAIL_V_MSG(Ref<ResourceInteractiveLoader>(), "No loader found for resource: " + path + ".");
 }
 }
 
 
 void ResourceLoader::add_resource_format_loader(Ref<ResourceFormatLoader> p_format_loader, bool p_at_front) {
 void ResourceLoader::add_resource_format_loader(Ref<ResourceFormatLoader> 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) {
 				if (err == ERR_FILE_EOF) {
 					break;
 					break;
 				} else if (err != OK) {
 				} 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;
 					break;
 				}
 				}
 
 
@@ -932,16 +918,13 @@ bool ResourceLoader::add_custom_resource_format_loader(String script_path) {
 	Ref<Script> s = res;
 	Ref<Script> s = res;
 	StringName ibt = s->get_instance_base_type();
 	StringName ibt = s->get_instance_base_type();
 	bool valid_type = ClassDB::is_parent_class(ibt, "ResourceFormatLoader");
 	bool valid_type = ClassDB::is_parent_class(ibt, "ResourceFormatLoader");
-	ERR_EXPLAIN("Script does not inherit a CustomResourceLoader: " + script_path);
-	ERR_FAIL_COND_V(!valid_type, false);
+	ERR_FAIL_COND_V_MSG(!valid_type, false, "Script does not inherit a CustomResourceLoader: " + script_path + ".");
 
 
 	Object *obj = ClassDB::instance(ibt);
 	Object *obj = ClassDB::instance(ibt);
 
 
-	ERR_EXPLAIN("Cannot instance script as custom resource loader, expected 'ResourceFormatLoader' inheritance, got: " + String(ibt));
-	ERR_FAIL_COND_V(obj == NULL, false);
+	ERR_FAIL_COND_V_MSG(obj == NULL, false, "Cannot instance script as custom resource loader, expected 'ResourceFormatLoader' inheritance, got: " + String(ibt) + ".");
 
 
-	ResourceFormatLoader *crl = NULL;
-	crl = Object::cast_to<ResourceFormatLoader>(obj);
+	ResourceFormatLoader *crl = Object::cast_to<ResourceFormatLoader>(obj);
 	crl->set_script(s.get_ref_ptr());
 	crl->set_script(s.get_ref_ptr());
 	ResourceLoader::add_resource_format_loader(crl);
 	ResourceLoader::add_resource_format_loader(crl);
 
 

+ 0 - 3
core/io/resource_loader.h

@@ -33,9 +33,6 @@
 
 
 #include "core/os/thread.h"
 #include "core/os/thread.h"
 #include "core/resource.h"
 #include "core/resource.h"
-/**
-	@author Juan Linietsky <[email protected]>
-*/
 
 
 class ResourceInteractiveLoader : public Reference {
 class ResourceInteractiveLoader : public Reference {
 
 

+ 3 - 6
core/io/resource_saver.cpp

@@ -214,16 +214,13 @@ bool ResourceSaver::add_custom_resource_format_saver(String script_path) {
 	Ref<Script> s = res;
 	Ref<Script> s = res;
 	StringName ibt = s->get_instance_base_type();
 	StringName ibt = s->get_instance_base_type();
 	bool valid_type = ClassDB::is_parent_class(ibt, "ResourceFormatSaver");
 	bool valid_type = ClassDB::is_parent_class(ibt, "ResourceFormatSaver");
-	ERR_EXPLAIN("Script does not inherit a CustomResourceSaver: " + script_path);
-	ERR_FAIL_COND_V(!valid_type, false);
+	ERR_FAIL_COND_V_MSG(!valid_type, false, "Script does not inherit a CustomResourceSaver: " + script_path + ".");
 
 
 	Object *obj = ClassDB::instance(ibt);
 	Object *obj = ClassDB::instance(ibt);
 
 
-	ERR_EXPLAIN("Cannot instance script as custom resource saver, expected 'ResourceFormatSaver' inheritance, got: " + String(ibt));
-	ERR_FAIL_COND_V(obj == NULL, false);
+	ERR_FAIL_COND_V_MSG(obj == NULL, false, "Cannot instance script as custom resource saver, expected 'ResourceFormatSaver' inheritance, got: " + String(ibt) + ".");
 
 
-	ResourceFormatSaver *crl = NULL;
-	crl = Object::cast_to<ResourceFormatSaver>(obj);
+	ResourceFormatSaver *crl = Object::cast_to<ResourceFormatSaver>(obj);
 	crl->set_script(s.get_ref_ptr());
 	crl->set_script(s.get_ref_ptr());
 	ResourceSaver::add_resource_format_saver(crl);
 	ResourceSaver::add_resource_format_saver(crl);
 
 

+ 0 - 4
core/io/resource_saver.h

@@ -33,10 +33,6 @@
 
 
 #include "core/resource.h"
 #include "core/resource.h"
 
 
-/**
-	@author Juan Linietsky <[email protected]>
-*/
-
 class ResourceFormatSaver : public Reference {
 class ResourceFormatSaver : public Reference {
 	GDCLASS(ResourceFormatSaver, Reference);
 	GDCLASS(ResourceFormatSaver, Reference);
 
 

+ 3 - 65
core/io/stream_peer_ssl.cpp

@@ -30,10 +30,7 @@
 
 
 #include "stream_peer_ssl.h"
 #include "stream_peer_ssl.h"
 
 
-#include "core/io/certs_compressed.gen.h"
-#include "core/io/compression.h"
-#include "core/os/file_access.h"
-#include "core/project_settings.h"
+#include "core/engine.h"
 
 
 StreamPeerSSL *(*StreamPeerSSL::_create)() = NULL;
 StreamPeerSSL *(*StreamPeerSSL::_create)() = NULL;
 
 
@@ -44,22 +41,8 @@ StreamPeerSSL *StreamPeerSSL::create() {
 	return NULL;
 	return NULL;
 }
 }
 
 
-StreamPeerSSL::LoadCertsFromMemory StreamPeerSSL::load_certs_func = NULL;
 bool StreamPeerSSL::available = false;
 bool StreamPeerSSL::available = false;
 
 
-void StreamPeerSSL::load_certs_from_memory(const PoolByteArray &p_memory) {
-	if (load_certs_func)
-		load_certs_func(p_memory);
-}
-
-void StreamPeerSSL::load_certs_from_file(String p_path) {
-	if (p_path != "") {
-		PoolByteArray certs = get_cert_file_as_array(p_path);
-		if (certs.size() > 0)
-			load_certs_func(certs);
-	}
-}
-
 bool StreamPeerSSL::is_available() {
 bool StreamPeerSSL::is_available() {
 	return available;
 	return available;
 }
 }
@@ -72,56 +55,11 @@ bool StreamPeerSSL::is_blocking_handshake_enabled() const {
 	return blocking_handshake;
 	return blocking_handshake;
 }
 }
 
 
-PoolByteArray StreamPeerSSL::get_cert_file_as_array(String p_path) {
-
-	PoolByteArray out;
-	FileAccess *f = FileAccess::open(p_path, FileAccess::READ);
-	if (f) {
-		int flen = f->get_len();
-		out.resize(flen + 1);
-		PoolByteArray::Write w = out.write();
-		f->get_buffer(w.ptr(), flen);
-		w[flen] = 0; // Make sure it ends with string terminator
-		memdelete(f);
-#ifdef DEBUG_ENABLED
-		print_verbose(vformat("Loaded certs from '%s'.", p_path));
-#endif
-	}
-
-	return out;
-}
-
-PoolByteArray StreamPeerSSL::get_project_cert_array() {
-
-	PoolByteArray out;
-	String certs_path = GLOBAL_DEF("network/ssl/certificates", "");
-	ProjectSettings::get_singleton()->set_custom_property_info("network/ssl/certificates", PropertyInfo(Variant::STRING, "network/ssl/certificates", PROPERTY_HINT_FILE, "*.crt"));
-
-	if (certs_path != "") {
-		// Use certs defined in project settings.
-		return get_cert_file_as_array(certs_path);
-	}
-#ifdef BUILTIN_CERTS_ENABLED
-	else {
-		// Use builtin certs only if user did not override it in project settings.
-		out.resize(_certs_uncompressed_size + 1);
-		PoolByteArray::Write w = out.write();
-		Compression::decompress(w.ptr(), _certs_uncompressed_size, _certs_compressed, _certs_compressed_size, Compression::MODE_DEFLATE);
-		w[_certs_uncompressed_size] = 0; // Make sure it ends with string terminator
-#ifdef DEBUG_ENABLED
-		print_verbose("Loaded builtin certs");
-#endif
-	}
-#endif
-
-	return out;
-}
-
 void StreamPeerSSL::_bind_methods() {
 void StreamPeerSSL::_bind_methods() {
 
 
 	ClassDB::bind_method(D_METHOD("poll"), &StreamPeerSSL::poll);
 	ClassDB::bind_method(D_METHOD("poll"), &StreamPeerSSL::poll);
-	ClassDB::bind_method(D_METHOD("accept_stream", "base"), &StreamPeerSSL::accept_stream);
-	ClassDB::bind_method(D_METHOD("connect_to_stream", "stream", "validate_certs", "for_hostname"), &StreamPeerSSL::connect_to_stream, DEFVAL(false), DEFVAL(String()));
+	ClassDB::bind_method(D_METHOD("accept_stream", "stream", "private_key", "certificate", "chain"), &StreamPeerSSL::accept_stream, DEFVAL(Ref<X509Certificate>()));
+	ClassDB::bind_method(D_METHOD("connect_to_stream", "stream", "validate_certs", "for_hostname", "valid_certificate"), &StreamPeerSSL::connect_to_stream, DEFVAL(false), DEFVAL(String()), DEFVAL(Ref<X509Certificate>()));
 	ClassDB::bind_method(D_METHOD("get_status"), &StreamPeerSSL::get_status);
 	ClassDB::bind_method(D_METHOD("get_status"), &StreamPeerSSL::get_status);
 	ClassDB::bind_method(D_METHOD("disconnect_from_stream"), &StreamPeerSSL::disconnect_from_stream);
 	ClassDB::bind_method(D_METHOD("disconnect_from_stream"), &StreamPeerSSL::disconnect_from_stream);
 	ClassDB::bind_method(D_METHOD("set_blocking_handshake_enabled", "enabled"), &StreamPeerSSL::set_blocking_handshake_enabled);
 	ClassDB::bind_method(D_METHOD("set_blocking_handshake_enabled", "enabled"), &StreamPeerSSL::set_blocking_handshake_enabled);

+ 3 - 10
core/io/stream_peer_ssl.h

@@ -31,19 +31,16 @@
 #ifndef STREAM_PEER_SSL_H
 #ifndef STREAM_PEER_SSL_H
 #define STREAM_PEER_SSL_H
 #define STREAM_PEER_SSL_H
 
 
+#include "core/crypto/crypto.h"
 #include "core/io/stream_peer.h"
 #include "core/io/stream_peer.h"
 
 
 class StreamPeerSSL : public StreamPeer {
 class StreamPeerSSL : public StreamPeer {
 	GDCLASS(StreamPeerSSL, StreamPeer);
 	GDCLASS(StreamPeerSSL, StreamPeer);
 
 
-public:
-	typedef void (*LoadCertsFromMemory)(const PoolByteArray &p_certs);
-
 protected:
 protected:
 	static StreamPeerSSL *(*_create)();
 	static StreamPeerSSL *(*_create)();
 	static void _bind_methods();
 	static void _bind_methods();
 
 
-	static LoadCertsFromMemory load_certs_func;
 	static bool available;
 	static bool available;
 
 
 	bool blocking_handshake;
 	bool blocking_handshake;
@@ -61,18 +58,14 @@ public:
 	bool is_blocking_handshake_enabled() const;
 	bool is_blocking_handshake_enabled() const;
 
 
 	virtual void poll() = 0;
 	virtual void poll() = 0;
-	virtual Error accept_stream(Ref<StreamPeer> p_base) = 0;
-	virtual Error connect_to_stream(Ref<StreamPeer> p_base, bool p_validate_certs = false, const String &p_for_hostname = String()) = 0;
+	virtual Error accept_stream(Ref<StreamPeer> p_base, Ref<CryptoKey> p_key, Ref<X509Certificate> p_cert, Ref<X509Certificate> p_ca_chain = Ref<X509Certificate>()) = 0;
+	virtual Error connect_to_stream(Ref<StreamPeer> p_base, bool p_validate_certs = false, const String &p_for_hostname = String(), Ref<X509Certificate> p_valid_cert = Ref<X509Certificate>()) = 0;
 	virtual Status get_status() const = 0;
 	virtual Status get_status() const = 0;
 
 
 	virtual void disconnect_from_stream() = 0;
 	virtual void disconnect_from_stream() = 0;
 
 
 	static StreamPeerSSL *create();
 	static StreamPeerSSL *create();
 
 
-	static PoolByteArray get_cert_file_as_array(String p_path);
-	static PoolByteArray get_project_cert_array();
-	static void load_certs_from_file(String p_path);
-	static void load_certs_from_memory(const PoolByteArray &p_memory);
 	static bool is_available();
 	static bool is_available();
 
 
 	StreamPeerSSL();
 	StreamPeerSSL();

+ 6 - 19
core/io/translation_loader_po.cpp

@@ -67,8 +67,7 @@ RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error, const S
 
 
 			if (status == STATUS_READING_ID) {
 			if (status == STATUS_READING_ID) {
 				memdelete(f);
 				memdelete(f);
-				ERR_EXPLAIN(p_path + ":" + itos(line) + " Unexpected EOF while reading 'msgid' at file: ");
-				ERR_FAIL_V(RES());
+				ERR_FAIL_V_MSG(RES(), p_path + ":" + itos(line) + " Unexpected EOF while reading 'msgid' at file: ");
 			} else {
 			} else {
 				break;
 				break;
 			}
 			}
@@ -79,8 +78,7 @@ RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error, const S
 			if (status == STATUS_READING_ID) {
 			if (status == STATUS_READING_ID) {
 
 
 				memdelete(f);
 				memdelete(f);
-				ERR_EXPLAIN(p_path + ":" + itos(line) + " Unexpected 'msgid', was expecting 'msgstr' while parsing: ");
-				ERR_FAIL_V(RES());
+				ERR_FAIL_V_MSG(RES(), p_path + ":" + itos(line) + " Unexpected 'msgid', was expecting 'msgstr' while parsing: ");
 			}
 			}
 
 
 			if (msg_id != "") {
 			if (msg_id != "") {
@@ -102,8 +100,7 @@ RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error, const S
 			if (status != STATUS_READING_ID) {
 			if (status != STATUS_READING_ID) {
 
 
 				memdelete(f);
 				memdelete(f);
-				ERR_EXPLAIN(p_path + ":" + itos(line) + " Unexpected 'msgstr', was expecting 'msgid' while parsing: ");
-				ERR_FAIL_V(RES());
+				ERR_FAIL_V_MSG(RES(), p_path + ":" + itos(line) + " Unexpected 'msgstr', was expecting 'msgid' while parsing: ");
 			}
 			}
 
 
 			l = l.substr(6, l.length()).strip_edges();
 			l = l.substr(6, l.length()).strip_edges();
@@ -118,11 +115,7 @@ RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error, const S
 			continue; //nothing to read or comment
 			continue; //nothing to read or comment
 		}
 		}
 
 
-		if (!l.begins_with("\"") || status == STATUS_NONE) {
-			//not a string? failure!
-			ERR_EXPLAIN(p_path + ":" + itos(line) + " Invalid line '" + l + "' while parsing: ");
-			ERR_FAIL_V(RES());
-		}
+		ERR_FAIL_COND_V_MSG(!l.begins_with("\"") || status == STATUS_NONE, RES(), p_path + ":" + itos(line) + " Invalid line '" + l + "' while parsing: ");
 
 
 		l = l.substr(1, l.length());
 		l = l.substr(1, l.length());
 		//find final quote
 		//find final quote
@@ -135,10 +128,7 @@ RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error, const S
 			}
 			}
 		}
 		}
 
 
-		if (end_pos == -1) {
-			ERR_EXPLAIN(p_path + ":" + itos(line) + " Expected '\"' at end of message while parsing file: ");
-			ERR_FAIL_V(RES());
-		}
+		ERR_FAIL_COND_V_MSG(end_pos == -1, RES(), p_path + ":" + itos(line) + " Expected '\"' at end of message while parsing file: ");
 
 
 		l = l.substr(0, end_pos);
 		l = l.substr(0, end_pos);
 		l = l.c_unescape();
 		l = l.c_unescape();
@@ -163,10 +153,7 @@ RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error, const S
 			config = msg_str;
 			config = msg_str;
 	}
 	}
 
 
-	if (config == "") {
-		ERR_EXPLAIN("No config found in file: " + p_path);
-		ERR_FAIL_V(RES());
-	}
+	ERR_FAIL_COND_V_MSG(config == "", RES(), "No config found in file: " + p_path + ".");
 
 
 	Vector<String> configs = config.split("\n");
 	Vector<String> configs = config.split("\n");
 	for (int i = 0; i < configs.size(); i++) {
 	for (int i = 0; i < configs.size(); i++) {

+ 2 - 4
core/io/xml_parser.cpp

@@ -443,10 +443,8 @@ String XMLParser::get_attribute_value(const String &p_name) const {
 		}
 		}
 	}
 	}
 
 
-	if (idx < 0) {
-		ERR_EXPLAIN("Attribute not found: " + p_name);
-	}
-	ERR_FAIL_COND_V(idx < 0, "");
+	ERR_FAIL_COND_V_MSG(idx < 0, "", "Attribute not found: " + p_name + ".");
+
 	return attributes[idx].value;
 	return attributes[idx].value;
 }
 }
 
 

+ 0 - 3
core/make_binders.py

@@ -334,9 +334,6 @@ def make_version(template, nargs, argmax, const, ret):
         elif (cmd == "noarg"):
         elif (cmd == "noarg"):
             for i in range(nargs + 1, argmax + 1):
             for i in range(nargs + 1, argmax + 1):
                 outtext += data.replace("@", str(i))
                 outtext += data.replace("@", str(i))
-        elif (cmd == "noarg"):
-            for i in range(nargs + 1, argmax + 1):
-                outtext += data.replace("@", str(i))
 
 
         from_pos = end + 1
         from_pos = end + 1
 
 

+ 1 - 5
core/map.h

@@ -33,12 +33,8 @@
 
 
 #include "core/set.h"
 #include "core/set.h"
 
 
-/**
-	@author Juan Linietsky <[email protected]>
-*/
-
 // based on the very nice implementation of rb-trees by:
 // based on the very nice implementation of rb-trees by:
-// http://web.mit.edu/~emin/www/source_code/red_black_tree/index.html
+// https://web.archive.org/web/20120507164830/http://web.mit.edu/~emin/www/source_code/red_black_tree/index.html
 
 
 template <class K, class V, class C = Comparator<K>, class A = DefaultAllocator>
 template <class K, class V, class C = Comparator<K>, class A = DefaultAllocator>
 class Map {
 class Map {

+ 1 - 32
core/math/SCsub

@@ -2,37 +2,6 @@
 
 
 Import('env')
 Import('env')
 
 
-env_math = env.Clone() # Maybe make one specific for crypto?
-
-is_builtin = env["builtin_mbedtls"]
-has_module = env["module_mbedtls_enabled"]
-
-if is_builtin or not has_module:
-    # Use our headers for builtin or if the module is not going to be compiled.
-    # We decided not to depend on system mbedtls just for these few files that can
-    # be easily extracted.
-    env_math.Prepend(CPPPATH=["#thirdparty/mbedtls/include"])
-
-# MbedTLS core functions (for CryptoCore).
-# If the mbedtls module is compiled we don't need to add the .c files with our
-# custom config since they will be built by the module itself.
-# Only if the module is not enabled, we must compile here the required sources
-# to make a "light" build with only the necessary mbedtls files.
-if not has_module:
-    env_thirdparty = env_math.Clone()
-    env_thirdparty.disable_warnings()
-    # Custom config file
-    env_thirdparty.Append(CPPDEFINES=[('MBEDTLS_CONFIG_FILE', '\\"thirdparty/mbedtls/include/godot_core_mbedtls_config.h\\"')])
-    thirdparty_mbedtls_dir = "#thirdparty/mbedtls/library/"
-    thirdparty_mbedtls_sources = [
-        "aes.c",
-        "base64.c",
-        "md5.c",
-        "sha1.c",
-        "sha256.c",
-        "godot_core_mbedtls_platform.c"
-    ]
-    thirdparty_mbedtls_sources = [thirdparty_mbedtls_dir + file for file in thirdparty_mbedtls_sources]
-    env_thirdparty.add_source_files(env.core_sources, thirdparty_mbedtls_sources)
+env_math = env.Clone()
 
 
 env_math.add_source_files(env.core_sources, "*.cpp")
 env_math.add_source_files(env.core_sources, "*.cpp")

+ 174 - 97
core/math/a_star.cpp

@@ -40,7 +40,17 @@ int AStar::get_available_point_id() const {
 		return 1;
 		return 1;
 	}
 	}
 
 
-	return points.back()->key() + 1;
+	// calculate our new next available point id if bigger than before or next id already contained in set of points.
+	if (points.has(last_free_id)) {
+		int cur_new_id = last_free_id;
+		while (points.has(cur_new_id)) {
+			cur_new_id++;
+		}
+		int &non_const = const_cast<int &>(last_free_id);
+		non_const = cur_new_id;
+	}
+
+	return last_free_id;
 }
 }
 
 
 void AStar::add_point(int p_id, const Vector3 &p_pos, real_t p_weight_scale) {
 void AStar::add_point(int p_id, const Vector3 &p_pos, real_t p_weight_scale) {
@@ -48,7 +58,10 @@ void AStar::add_point(int p_id, const Vector3 &p_pos, real_t p_weight_scale) {
 	ERR_FAIL_COND(p_id < 0);
 	ERR_FAIL_COND(p_id < 0);
 	ERR_FAIL_COND(p_weight_scale < 1);
 	ERR_FAIL_COND(p_weight_scale < 1);
 
 
-	if (!points.has(p_id)) {
+	Point *found_pt;
+	bool p_exists = points.lookup(p_id, found_pt);
+
+	if (!p_exists) {
 		Point *pt = memnew(Point);
 		Point *pt = memnew(Point);
 		pt->id = p_id;
 		pt->id = p_id;
 		pt->pos = p_pos;
 		pt->pos = p_pos;
@@ -57,84 +70,98 @@ void AStar::add_point(int p_id, const Vector3 &p_pos, real_t p_weight_scale) {
 		pt->open_pass = 0;
 		pt->open_pass = 0;
 		pt->closed_pass = 0;
 		pt->closed_pass = 0;
 		pt->enabled = true;
 		pt->enabled = true;
-		points[p_id] = pt;
+		points.set(p_id, pt);
 	} else {
 	} else {
-		points[p_id]->pos = p_pos;
-		points[p_id]->weight_scale = p_weight_scale;
+		found_pt->pos = p_pos;
+		found_pt->weight_scale = p_weight_scale;
 	}
 	}
 }
 }
 
 
 Vector3 AStar::get_point_position(int p_id) const {
 Vector3 AStar::get_point_position(int p_id) const {
 
 
-	ERR_FAIL_COND_V(!points.has(p_id), Vector3());
+	Point *p;
+	bool p_exists = points.lookup(p_id, p);
+	ERR_FAIL_COND_V(!p_exists, Vector3());
 
 
-	return points[p_id]->pos;
+	return p->pos;
 }
 }
 
 
 void AStar::set_point_position(int p_id, const Vector3 &p_pos) {
 void AStar::set_point_position(int p_id, const Vector3 &p_pos) {
 
 
-	ERR_FAIL_COND(!points.has(p_id));
+	Point *p;
+	bool p_exists = points.lookup(p_id, p);
+	ERR_FAIL_COND(!p_exists);
 
 
-	points[p_id]->pos = p_pos;
+	p->pos = p_pos;
 }
 }
 
 
 real_t AStar::get_point_weight_scale(int p_id) const {
 real_t AStar::get_point_weight_scale(int p_id) const {
 
 
-	ERR_FAIL_COND_V(!points.has(p_id), 0);
+	Point *p;
+	bool p_exists = points.lookup(p_id, p);
+	ERR_FAIL_COND_V(!p_exists, 0);
 
 
-	return points[p_id]->weight_scale;
+	return p->weight_scale;
 }
 }
 
 
 void AStar::set_point_weight_scale(int p_id, real_t p_weight_scale) {
 void AStar::set_point_weight_scale(int p_id, real_t p_weight_scale) {
 
 
-	ERR_FAIL_COND(!points.has(p_id));
+	Point *p;
+	bool p_exists = points.lookup(p_id, p);
+	ERR_FAIL_COND(!p_exists);
 	ERR_FAIL_COND(p_weight_scale < 1);
 	ERR_FAIL_COND(p_weight_scale < 1);
 
 
-	points[p_id]->weight_scale = p_weight_scale;
+	p->weight_scale = p_weight_scale;
 }
 }
 
 
 void AStar::remove_point(int p_id) {
 void AStar::remove_point(int p_id) {
 
 
-	ERR_FAIL_COND(!points.has(p_id));
-
-	Point *p = points[p_id];
+	Point *p;
+	bool p_exists = points.lookup(p_id, p);
+	ERR_FAIL_COND(!p_exists);
 
 
-	for (Set<Point *>::Element *E = p->neighbours.front(); E; E = E->next()) {
+	for (OAHashMap<int, Point *>::Iterator it = p->neighbours.iter(); it.valid; it = p->neighbours.next_iter(it)) {
 
 
-		Segment s(p_id, E->get()->id);
+		Segment s(p_id, (*it.key));
 		segments.erase(s);
 		segments.erase(s);
 
 
-		E->get()->neighbours.erase(p);
-		E->get()->unlinked_neighbours.erase(p);
+		(*it.value)->neighbours.remove(p->id);
+		(*it.value)->unlinked_neighbours.remove(p->id);
 	}
 	}
 
 
-	for (Set<Point *>::Element *E = p->unlinked_neighbours.front(); E; E = E->next()) {
+	for (OAHashMap<int, Point *>::Iterator it = p->unlinked_neighbours.iter(); it.valid; it = p->unlinked_neighbours.next_iter(it)) {
 
 
-		Segment s(p_id, E->get()->id);
+		Segment s(p_id, (*it.key));
 		segments.erase(s);
 		segments.erase(s);
 
 
-		E->get()->neighbours.erase(p);
-		E->get()->unlinked_neighbours.erase(p);
+		(*it.value)->neighbours.remove(p->id);
+		(*it.value)->unlinked_neighbours.remove(p->id);
 	}
 	}
 
 
 	memdelete(p);
 	memdelete(p);
-	points.erase(p_id);
+	points.remove(p_id);
+	last_free_id = p_id;
 }
 }
 
 
 void AStar::connect_points(int p_id, int p_with_id, bool bidirectional) {
 void AStar::connect_points(int p_id, int p_with_id, bool bidirectional) {
 
 
-	ERR_FAIL_COND(!points.has(p_id));
-	ERR_FAIL_COND(!points.has(p_with_id));
 	ERR_FAIL_COND(p_id == p_with_id);
 	ERR_FAIL_COND(p_id == p_with_id);
 
 
-	Point *a = points[p_id];
-	Point *b = points[p_with_id];
-	a->neighbours.insert(b);
+	Point *a;
+	bool from_exists = points.lookup(p_id, a);
+	ERR_FAIL_COND(!from_exists);
+
+	Point *b;
+	bool to_exists = points.lookup(p_with_id, b);
+	ERR_FAIL_COND(!to_exists);
 
 
-	if (bidirectional)
-		b->neighbours.insert(a);
-	else
-		b->unlinked_neighbours.insert(a);
+	a->neighbours.set(b->id, b);
+
+	if (bidirectional) {
+		b->neighbours.set(a->id, a);
+	} else {
+		b->unlinked_neighbours.set(a->id, a);
+	}
 
 
 	Segment s(p_id, p_with_id);
 	Segment s(p_id, p_with_id);
 	if (s.from == p_id) {
 	if (s.from == p_id) {
@@ -147,6 +174,7 @@ void AStar::connect_points(int p_id, int p_with_id, bool bidirectional) {
 
 
 	segments.insert(s);
 	segments.insert(s);
 }
 }
+
 void AStar::disconnect_points(int p_id, int p_with_id) {
 void AStar::disconnect_points(int p_id, int p_with_id) {
 
 
 	Segment s(p_id, p_with_id);
 	Segment s(p_id, p_with_id);
@@ -154,12 +182,18 @@ void AStar::disconnect_points(int p_id, int p_with_id) {
 
 
 	segments.erase(s);
 	segments.erase(s);
 
 
-	Point *a = points[p_id];
-	Point *b = points[p_with_id];
-	a->neighbours.erase(b);
-	a->unlinked_neighbours.erase(b);
-	b->neighbours.erase(a);
-	b->unlinked_neighbours.erase(a);
+	Point *a;
+	bool a_exists = points.lookup(p_id, a);
+	CRASH_COND(!a_exists);
+
+	Point *b;
+	bool b_exists = points.lookup(p_with_id, b);
+	CRASH_COND(!b_exists);
+
+	a->neighbours.remove(b->id);
+	a->unlinked_neighbours.remove(b->id);
+	b->neighbours.remove(a->id);
+	b->unlinked_neighbours.remove(a->id);
 }
 }
 
 
 bool AStar::has_point(int p_id) const {
 bool AStar::has_point(int p_id) const {
@@ -171,8 +205,8 @@ Array AStar::get_points() {
 
 
 	Array point_list;
 	Array point_list;
 
 
-	for (const Map<int, Point *>::Element *E = points.front(); E; E = E->next()) {
-		point_list.push_back(E->key());
+	for (OAHashMap<int, Point *>::Iterator it = points.iter(); it.valid; it = points.next_iter(it)) {
+		point_list.push_back(*(it.key));
 	}
 	}
 
 
 	return point_list;
 	return point_list;
@@ -180,14 +214,14 @@ Array AStar::get_points() {
 
 
 PoolVector<int> AStar::get_point_connections(int p_id) {
 PoolVector<int> AStar::get_point_connections(int p_id) {
 
 
-	ERR_FAIL_COND_V(!points.has(p_id), PoolVector<int>());
+	Point *p;
+	bool p_exists = points.lookup(p_id, p);
+	ERR_FAIL_COND_V(!p_exists, PoolVector<int>());
 
 
 	PoolVector<int> point_list;
 	PoolVector<int> point_list;
 
 
-	Point *p = points[p_id];
-
-	for (Set<Point *>::Element *E = p->neighbours.front(); E; E = E->next()) {
-		point_list.push_back(E->get()->id);
+	for (OAHashMap<int, Point *>::Iterator it = p->neighbours.iter(); it.valid; it = p->neighbours.next_iter(it)) {
+		point_list.push_back((*it.key));
 	}
 	}
 
 
 	return point_list;
 	return point_list;
@@ -201,27 +235,41 @@ bool AStar::are_points_connected(int p_id, int p_with_id) const {
 
 
 void AStar::clear() {
 void AStar::clear() {
 
 
-	for (const Map<int, Point *>::Element *E = points.front(); E; E = E->next()) {
-
-		memdelete(E->get());
+	last_free_id = 0;
+	for (OAHashMap<int, Point *>::Iterator it = points.iter(); it.valid; it = points.next_iter(it)) {
+		memdelete(*(it.value));
 	}
 	}
 	segments.clear();
 	segments.clear();
 	points.clear();
 	points.clear();
 }
 }
 
 
+int AStar::get_point_count() const {
+	return points.get_num_elements();
+}
+
+int AStar::get_point_capacity() const {
+	return points.get_capacity();
+}
+
+void AStar::reserve_space(int p_num_nodes) {
+	ERR_FAIL_COND_MSG(p_num_nodes <= 0, "New capacity must be greater than 0, was: " + itos(p_num_nodes) + ".");
+	ERR_FAIL_COND_MSG((uint32_t)p_num_nodes < points.get_capacity(), "New capacity must be greater than current capacity: " + itos(points.get_capacity()) + ", new was: " + itos(p_num_nodes) + ".");
+	points.reserve(p_num_nodes);
+}
+
 int AStar::get_closest_point(const Vector3 &p_point) const {
 int AStar::get_closest_point(const Vector3 &p_point) const {
 
 
 	int closest_id = -1;
 	int closest_id = -1;
 	real_t closest_dist = 1e20;
 	real_t closest_dist = 1e20;
 
 
-	for (const Map<int, Point *>::Element *E = points.front(); E; E = E->next()) {
+	for (OAHashMap<int, Point *>::Iterator it = points.iter(); it.valid; it = points.next_iter(it)) {
+
+		if (!(*it.value)->enabled) continue; // Disabled points should not be considered.
 
 
-		if (!E->get()->enabled)
-			continue; //Disabled points should not be considered
-		real_t d = p_point.distance_squared_to(E->get()->pos);
+		real_t d = p_point.distance_squared_to((*it.value)->pos);
 		if (closest_id < 0 || d < closest_dist) {
 		if (closest_id < 0 || d < closest_dist) {
 			closest_dist = d;
 			closest_dist = d;
-			closest_id = E->key();
+			closest_id = *(it.key);
 		}
 		}
 	}
 	}
 
 
@@ -230,8 +278,8 @@ int AStar::get_closest_point(const Vector3 &p_point) const {
 
 
 Vector3 AStar::get_closest_position_in_segment(const Vector3 &p_point) const {
 Vector3 AStar::get_closest_position_in_segment(const Vector3 &p_point) const {
 
 
-	real_t closest_dist = 1e20;
 	bool found = false;
 	bool found = false;
+	real_t closest_dist = 1e20;
 	Vector3 closest_point;
 	Vector3 closest_point;
 
 
 	for (const Set<Segment>::Element *E = segments.front(); E; E = E->next()) {
 	for (const Set<Segment>::Element *E = segments.front(); E; E = E->next()) {
@@ -262,8 +310,7 @@ bool AStar::_solve(Point *begin_point, Point *end_point) {
 
 
 	pass++;
 	pass++;
 
 
-	if (!end_point->enabled)
-		return false;
+	if (!end_point->enabled) return false;
 
 
 	bool found_route = false;
 	bool found_route = false;
 
 
@@ -272,13 +319,9 @@ bool AStar::_solve(Point *begin_point, Point *end_point) {
 
 
 	begin_point->g_score = 0;
 	begin_point->g_score = 0;
 	begin_point->f_score = _estimate_cost(begin_point->id, end_point->id);
 	begin_point->f_score = _estimate_cost(begin_point->id, end_point->id);
-
 	open_list.push_back(begin_point);
 	open_list.push_back(begin_point);
 
 
-	while (true) {
-
-		if (open_list.size() == 0) // No path found
-			break;
+	while (!open_list.empty()) {
 
 
 		Point *p = open_list[0]; // The currently processed point
 		Point *p = open_list[0]; // The currently processed point
 
 
@@ -291,24 +334,23 @@ bool AStar::_solve(Point *begin_point, Point *end_point) {
 		open_list.remove(open_list.size() - 1);
 		open_list.remove(open_list.size() - 1);
 		p->closed_pass = pass; // Mark the point as closed
 		p->closed_pass = pass; // Mark the point as closed
 
 
-		for (Set<Point *>::Element *E = p->neighbours.front(); E; E = E->next()) {
+		for (OAHashMap<int, Point *>::Iterator it = p->neighbours.iter(); it.valid; it = p->neighbours.next_iter(it)) {
 
 
-			Point *e = E->get(); // The neighbour point
+			Point *e = *(it.value); // The neighbour point
 
 
-			if (!e->enabled || e->closed_pass == pass)
+			if (!e->enabled || e->closed_pass == pass) {
 				continue;
 				continue;
+			}
 
 
 			real_t tentative_g_score = p->g_score + _compute_cost(p->id, e->id) * e->weight_scale;
 			real_t tentative_g_score = p->g_score + _compute_cost(p->id, e->id) * e->weight_scale;
 
 
 			bool new_point = false;
 			bool new_point = false;
 
 
-			if (e->open_pass != pass) { // The point wasn't inside the open list
-
+			if (e->open_pass != pass) { // The point wasn't inside the open list.
 				e->open_pass = pass;
 				e->open_pass = pass;
 				open_list.push_back(e);
 				open_list.push_back(e);
 				new_point = true;
 				new_point = true;
-			} else if (tentative_g_score >= e->g_score) { // The new path is worse than the previous
-
+			} else if (tentative_g_score >= e->g_score) { // The new path is worse than the previous.
 				continue;
 				continue;
 			}
 			}
 
 
@@ -316,10 +358,11 @@ bool AStar::_solve(Point *begin_point, Point *end_point) {
 			e->g_score = tentative_g_score;
 			e->g_score = tentative_g_score;
 			e->f_score = e->g_score + _estimate_cost(e->id, end_point->id);
 			e->f_score = e->g_score + _estimate_cost(e->id, end_point->id);
 
 
-			if (new_point) // The position of the new points is already known
+			if (new_point) { // The position of the new points is already known.
 				sorter.push_heap(0, open_list.size() - 1, 0, e, open_list.ptrw());
 				sorter.push_heap(0, open_list.size() - 1, 0, e, open_list.ptrw());
-			else
+			} else {
 				sorter.push_heap(0, open_list.find(e), 0, e, open_list.ptrw());
 				sorter.push_heap(0, open_list.find(e), 0, e, open_list.ptrw());
+			}
 		}
 		}
 	}
 	}
 
 
@@ -331,7 +374,15 @@ float AStar::_estimate_cost(int p_from_id, int p_to_id) {
 	if (get_script_instance() && get_script_instance()->has_method(SceneStringNames::get_singleton()->_estimate_cost))
 	if (get_script_instance() && get_script_instance()->has_method(SceneStringNames::get_singleton()->_estimate_cost))
 		return get_script_instance()->call(SceneStringNames::get_singleton()->_estimate_cost, p_from_id, p_to_id);
 		return get_script_instance()->call(SceneStringNames::get_singleton()->_estimate_cost, p_from_id, p_to_id);
 
 
-	return points[p_from_id]->pos.distance_to(points[p_to_id]->pos);
+	Point *from_point;
+	bool from_exists = points.lookup(p_from_id, from_point);
+	CRASH_COND(!from_exists);
+
+	Point *to_point;
+	bool to_exists = points.lookup(p_to_id, to_point);
+	CRASH_COND(!to_exists);
+
+	return from_point->pos.distance_to(to_point->pos);
 }
 }
 
 
 float AStar::_compute_cost(int p_from_id, int p_to_id) {
 float AStar::_compute_cost(int p_from_id, int p_to_id) {
@@ -339,16 +390,26 @@ float AStar::_compute_cost(int p_from_id, int p_to_id) {
 	if (get_script_instance() && get_script_instance()->has_method(SceneStringNames::get_singleton()->_compute_cost))
 	if (get_script_instance() && get_script_instance()->has_method(SceneStringNames::get_singleton()->_compute_cost))
 		return get_script_instance()->call(SceneStringNames::get_singleton()->_compute_cost, p_from_id, p_to_id);
 		return get_script_instance()->call(SceneStringNames::get_singleton()->_compute_cost, p_from_id, p_to_id);
 
 
-	return points[p_from_id]->pos.distance_to(points[p_to_id]->pos);
+	Point *from_point;
+	bool from_exists = points.lookup(p_from_id, from_point);
+	CRASH_COND(!from_exists);
+
+	Point *to_point;
+	bool to_exists = points.lookup(p_to_id, to_point);
+	CRASH_COND(!to_exists);
+
+	return from_point->pos.distance_to(to_point->pos);
 }
 }
 
 
 PoolVector<Vector3> AStar::get_point_path(int p_from_id, int p_to_id) {
 PoolVector<Vector3> AStar::get_point_path(int p_from_id, int p_to_id) {
 
 
-	ERR_FAIL_COND_V(!points.has(p_from_id), PoolVector<Vector3>());
-	ERR_FAIL_COND_V(!points.has(p_to_id), PoolVector<Vector3>());
+	Point *a;
+	bool from_exists = points.lookup(p_from_id, a);
+	ERR_FAIL_COND_V(!from_exists, PoolVector<Vector3>());
 
 
-	Point *a = points[p_from_id];
-	Point *b = points[p_to_id];
+	Point *b;
+	bool to_exists = points.lookup(p_to_id, b);
+	ERR_FAIL_COND_V(!to_exists, PoolVector<Vector3>());
 
 
 	if (a == b) {
 	if (a == b) {
 		PoolVector<Vector3> ret;
 		PoolVector<Vector3> ret;
@@ -360,11 +421,8 @@ PoolVector<Vector3> AStar::get_point_path(int p_from_id, int p_to_id) {
 	Point *end_point = b;
 	Point *end_point = b;
 
 
 	bool found_route = _solve(begin_point, end_point);
 	bool found_route = _solve(begin_point, end_point);
+	if (!found_route) return PoolVector<Vector3>();
 
 
-	if (!found_route)
-		return PoolVector<Vector3>();
-
-	// Midpoints
 	Point *p = end_point;
 	Point *p = end_point;
 	int pc = 1; // Begin point
 	int pc = 1; // Begin point
 	while (p != begin_point) {
 	while (p != begin_point) {
@@ -393,11 +451,13 @@ PoolVector<Vector3> AStar::get_point_path(int p_from_id, int p_to_id) {
 
 
 PoolVector<int> AStar::get_id_path(int p_from_id, int p_to_id) {
 PoolVector<int> AStar::get_id_path(int p_from_id, int p_to_id) {
 
 
-	ERR_FAIL_COND_V(!points.has(p_from_id), PoolVector<int>());
-	ERR_FAIL_COND_V(!points.has(p_to_id), PoolVector<int>());
+	Point *a;
+	bool from_exists = points.lookup(p_from_id, a);
+	ERR_FAIL_COND_V(!from_exists, PoolVector<int>());
 
 
-	Point *a = points[p_from_id];
-	Point *b = points[p_to_id];
+	Point *b;
+	bool to_exists = points.lookup(p_to_id, b);
+	ERR_FAIL_COND_V(!to_exists, PoolVector<int>());
 
 
 	if (a == b) {
 	if (a == b) {
 		PoolVector<int> ret;
 		PoolVector<int> ret;
@@ -409,11 +469,8 @@ PoolVector<int> AStar::get_id_path(int p_from_id, int p_to_id) {
 	Point *end_point = b;
 	Point *end_point = b;
 
 
 	bool found_route = _solve(begin_point, end_point);
 	bool found_route = _solve(begin_point, end_point);
+	if (!found_route) return PoolVector<int>();
 
 
-	if (!found_route)
-		return PoolVector<int>();
-
-	// Midpoints
 	Point *p = end_point;
 	Point *p = end_point;
 	int pc = 1; // Begin point
 	int pc = 1; // Begin point
 	while (p != begin_point) {
 	while (p != begin_point) {
@@ -442,16 +499,20 @@ PoolVector<int> AStar::get_id_path(int p_from_id, int p_to_id) {
 
 
 void AStar::set_point_disabled(int p_id, bool p_disabled) {
 void AStar::set_point_disabled(int p_id, bool p_disabled) {
 
 
-	ERR_FAIL_COND(!points.has(p_id));
+	Point *p;
+	bool p_exists = points.lookup(p_id, p);
+	ERR_FAIL_COND(!p_exists);
 
 
-	points[p_id]->enabled = !p_disabled;
+	p->enabled = !p_disabled;
 }
 }
 
 
 bool AStar::is_point_disabled(int p_id) const {
 bool AStar::is_point_disabled(int p_id) const {
 
 
-	ERR_FAIL_COND_V(!points.has(p_id), false);
+	Point *p;
+	bool p_exists = points.lookup(p_id, p);
+	ERR_FAIL_COND_V(!p_exists, false);
 
 
-	return !points[p_id]->enabled;
+	return !p->enabled;
 }
 }
 
 
 void AStar::_bind_methods() {
 void AStar::_bind_methods() {
@@ -474,6 +535,9 @@ void AStar::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("disconnect_points", "id", "to_id"), &AStar::disconnect_points);
 	ClassDB::bind_method(D_METHOD("disconnect_points", "id", "to_id"), &AStar::disconnect_points);
 	ClassDB::bind_method(D_METHOD("are_points_connected", "id", "to_id"), &AStar::are_points_connected);
 	ClassDB::bind_method(D_METHOD("are_points_connected", "id", "to_id"), &AStar::are_points_connected);
 
 
+	ClassDB::bind_method(D_METHOD("get_point_count"), &AStar::get_point_count);
+	ClassDB::bind_method(D_METHOD("get_point_capacity"), &AStar::get_point_capacity);
+	ClassDB::bind_method(D_METHOD("reserve_space", "num_nodes"), &AStar::reserve_space);
 	ClassDB::bind_method(D_METHOD("clear"), &AStar::clear);
 	ClassDB::bind_method(D_METHOD("clear"), &AStar::clear);
 
 
 	ClassDB::bind_method(D_METHOD("get_closest_point", "to_position"), &AStar::get_closest_point);
 	ClassDB::bind_method(D_METHOD("get_closest_point", "to_position"), &AStar::get_closest_point);
@@ -487,13 +551,11 @@ void AStar::_bind_methods() {
 }
 }
 
 
 AStar::AStar() {
 AStar::AStar() {
-
+	last_free_id = 0;
 	pass = 1;
 	pass = 1;
 }
 }
 
 
 AStar::~AStar() {
 AStar::~AStar() {
-
-	pass = 1;
 	clear();
 	clear();
 }
 }
 
 
@@ -560,10 +622,22 @@ bool AStar2D::are_points_connected(int p_id, int p_with_id) const {
 	return astar.are_points_connected(p_id, p_with_id);
 	return astar.are_points_connected(p_id, p_with_id);
 }
 }
 
 
+int AStar2D::get_point_count() const {
+	return astar.get_point_count();
+}
+
+int AStar2D::get_point_capacity() const {
+	return astar.get_point_capacity();
+}
+
 void AStar2D::clear() {
 void AStar2D::clear() {
 	astar.clear();
 	astar.clear();
 }
 }
 
 
+void AStar2D::reserve_space(int p_num_nodes) {
+	astar.reserve_space(p_num_nodes);
+}
+
 int AStar2D::get_closest_point(const Vector2 &p_point) const {
 int AStar2D::get_closest_point(const Vector2 &p_point) const {
 	return astar.get_closest_point(Vector3(p_point.x, p_point.y, 0));
 	return astar.get_closest_point(Vector3(p_point.x, p_point.y, 0));
 }
 }
@@ -614,6 +688,9 @@ void AStar2D::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("disconnect_points", "id", "to_id"), &AStar2D::disconnect_points);
 	ClassDB::bind_method(D_METHOD("disconnect_points", "id", "to_id"), &AStar2D::disconnect_points);
 	ClassDB::bind_method(D_METHOD("are_points_connected", "id", "to_id"), &AStar2D::are_points_connected);
 	ClassDB::bind_method(D_METHOD("are_points_connected", "id", "to_id"), &AStar2D::are_points_connected);
 
 
+	ClassDB::bind_method(D_METHOD("get_point_count"), &AStar2D::get_point_count);
+	ClassDB::bind_method(D_METHOD("get_point_capacity"), &AStar2D::get_point_capacity);
+	ClassDB::bind_method(D_METHOD("reserve_space", "num_nodes"), &AStar2D::reserve_space);
 	ClassDB::bind_method(D_METHOD("clear"), &AStar2D::clear);
 	ClassDB::bind_method(D_METHOD("clear"), &AStar2D::clear);
 
 
 	ClassDB::bind_method(D_METHOD("get_closest_point", "to_position"), &AStar2D::get_closest_point);
 	ClassDB::bind_method(D_METHOD("get_closest_point", "to_position"), &AStar2D::get_closest_point);

+ 24 - 13
core/math/a_star.h

@@ -31,8 +31,8 @@
 #ifndef ASTAR_H
 #ifndef ASTAR_H
 #define ASTAR_H
 #define ASTAR_H
 
 
+#include "core/oa_hash_map.h"
 #include "core/reference.h"
 #include "core/reference.h"
-#include "core/self_list.h"
 
 
 /**
 /**
 	A* pathfinding algorithm
 	A* pathfinding algorithm
@@ -44,19 +44,21 @@ class AStar : public Reference {
 
 
 	GDCLASS(AStar, Reference);
 	GDCLASS(AStar, Reference);
 
 
-	uint64_t pass;
-
 	struct Point {
 	struct Point {
 
 
+		Point() :
+				neighbours(4u),
+				unlinked_neighbours(4u) {}
+
 		int id;
 		int id;
 		Vector3 pos;
 		Vector3 pos;
 		real_t weight_scale;
 		real_t weight_scale;
 		bool enabled;
 		bool enabled;
 
 
-		Set<Point *> neighbours;
-		Set<Point *> unlinked_neighbours;
+		OAHashMap<int, Point *> neighbours;
+		OAHashMap<int, Point *> unlinked_neighbours;
 
 
-		// Used for pathfinding
+		// Used for pathfinding.
 		Point *prev_point;
 		Point *prev_point;
 		real_t g_score;
 		real_t g_score;
 		real_t f_score;
 		real_t f_score;
@@ -64,16 +66,15 @@ class AStar : public Reference {
 		uint64_t closed_pass;
 		uint64_t closed_pass;
 	};
 	};
 
 
-	Map<int, Point *> points;
-
 	struct SortPoints {
 	struct SortPoints {
-		_FORCE_INLINE_ bool operator()(const Point *A, const Point *B) const { // Returns true when the Point A is worse than Point B
-			if (A->f_score > B->f_score)
+		_FORCE_INLINE_ bool operator()(const Point *A, const Point *B) const { // Returns true when the Point A is worse than Point B.
+			if (A->f_score > B->f_score) {
 				return true;
 				return true;
-			else if (A->f_score < B->f_score)
+			} else if (A->f_score < B->f_score) {
 				return false;
 				return false;
-			else
-				return A->g_score < B->g_score; // If the f_costs are the same then prioritize the points that are further away from the start
+			} else {
+				return A->g_score < B->g_score; // If the f_costs are the same then prioritize the points that are further away from the start.
+			}
 		}
 		}
 	};
 	};
 
 
@@ -101,6 +102,10 @@ class AStar : public Reference {
 		}
 		}
 	};
 	};
 
 
+	int last_free_id;
+	uint64_t pass;
+
+	OAHashMap<int, Point *> points;
 	Set<Segment> segments;
 	Set<Segment> segments;
 
 
 	bool _solve(Point *begin_point, Point *end_point);
 	bool _solve(Point *begin_point, Point *end_point);
@@ -131,6 +136,9 @@ public:
 	void disconnect_points(int p_id, int p_with_id);
 	void disconnect_points(int p_id, int p_with_id);
 	bool are_points_connected(int p_id, int p_with_id) const;
 	bool are_points_connected(int p_id, int p_with_id) const;
 
 
+	int get_point_count() const;
+	int get_point_capacity() const;
+	void reserve_space(int p_num_nodes);
 	void clear();
 	void clear();
 
 
 	int get_closest_point(const Vector3 &p_point) const;
 	int get_closest_point(const Vector3 &p_point) const;
@@ -170,6 +178,9 @@ public:
 	void disconnect_points(int p_id, int p_with_id);
 	void disconnect_points(int p_id, int p_with_id);
 	bool are_points_connected(int p_id, int p_with_id) const;
 	bool are_points_connected(int p_id, int p_with_id) const;
 
 
+	int get_point_count() const;
+	int get_point_capacity() const;
+	void reserve_space(int p_num_nodes);
 	void clear();
 	void clear();
 
 
 	int get_closest_point(const Vector2 &p_point) const;
 	int get_closest_point(const Vector2 &p_point) const;

+ 1 - 4
core/math/basis.cpp

@@ -618,10 +618,7 @@ Basis::operator String() const {
 Quat Basis::get_quat() const {
 Quat Basis::get_quat() const {
 
 
 #ifdef MATH_CHECKS
 #ifdef MATH_CHECKS
-	if (!is_rotation()) {
-		ERR_EXPLAIN("Basis must be normalized in order to be casted to a Quaternion. Use get_rotation_quat() or call orthonormalized() instead.");
-		ERR_FAIL_V(Quat());
-	}
+	ERR_FAIL_COND_V_MSG(!is_rotation(), Quat(), "Basis must be normalized in order to be casted to a Quaternion. Use get_rotation_quat() or call orthonormalized() instead.");
 #endif
 #endif
 	/* Allow getting a quaternion from an unnormalized transform */
 	/* Allow getting a quaternion from an unnormalized transform */
 	Basis m = *this;
 	Basis m = *this;

+ 0 - 4
core/math/basis.h

@@ -36,10 +36,6 @@
 
 
 #include "core/math/quat.h"
 #include "core/math/quat.h"
 
 
-/**
-	@author Juan Linietsky <[email protected]>
-*/
-
 class Basis {
 class Basis {
 public:
 public:
 	Vector3 elements[3];
 	Vector3 elements[3];

+ 1 - 3
core/math/bsp_tree.h

@@ -38,9 +38,7 @@
 #include "core/pool_vector.h"
 #include "core/pool_vector.h"
 #include "core/variant.h"
 #include "core/variant.h"
 #include "core/vector.h"
 #include "core/vector.h"
-/**
-	@author Juan Linietsky <[email protected]>
-*/
+
 class BSP_Tree {
 class BSP_Tree {
 public:
 public:
 	enum {
 	enum {

+ 2 - 2
core/math/camera_matrix.cpp

@@ -302,8 +302,8 @@ Vector<Plane> CameraMatrix::get_projection_planes(const Transform &p_transform)
 
 
 	/** Fast Plane Extraction from combined modelview/projection matrices.
 	/** Fast Plane Extraction from combined modelview/projection matrices.
 	 * References:
 	 * References:
-	 * http://www.markmorley.com/opengl/frustumculling.html
-	 * http://www2.ravensoft.com/users/ggribb/plane%20extraction.pdf
+	 * https://web.archive.org/web/20011221205252/http://www.markmorley.com/opengl/frustumculling.html
+	 * https://web.archive.org/web/20061020020112/http://www2.ravensoft.com/users/ggribb/plane%20extraction.pdf
 	 */
 	 */
 
 
 	Vector<Plane> planes;
 	Vector<Plane> planes;

+ 0 - 4
core/math/camera_matrix.h

@@ -34,10 +34,6 @@
 #include "core/math/rect2.h"
 #include "core/math/rect2.h"
 #include "core/math/transform.h"
 #include "core/math/transform.h"
 
 
-/**
-	@author Juan Linietsky <[email protected]>
-*/
-
 struct CameraMatrix {
 struct CameraMatrix {
 
 
 	enum Planes {
 	enum Planes {

+ 2 - 2
core/math/delaunay.h

@@ -80,11 +80,11 @@ public:
 	}
 	}
 
 
 	static bool edge_compare(const Vector<Vector2> &p_vertices, const Edge &p_a, const Edge &p_b) {
 	static bool edge_compare(const Vector<Vector2> &p_vertices, const Edge &p_a, const Edge &p_b) {
-		if (Math::is_zero_approx(p_vertices[p_a.edge[0]].distance_to(p_vertices[p_b.edge[0]])) && Math::is_zero_approx(p_vertices[p_a.edge[1]].distance_to(p_vertices[p_b.edge[1]]))) {
+		if (p_vertices[p_a.edge[0]] == p_vertices[p_b.edge[0]] && p_vertices[p_a.edge[1]] == p_vertices[p_b.edge[1]]) {
 			return true;
 			return true;
 		}
 		}
 
 
-		if (Math::is_zero_approx(p_vertices[p_a.edge[0]].distance_to(p_vertices[p_b.edge[1]])) && Math::is_zero_approx(p_vertices[p_a.edge[1]].distance_to(p_vertices[p_b.edge[0]]))) {
+		if (p_vertices[p_a.edge[0]] == p_vertices[p_b.edge[1]] && p_vertices[p_a.edge[1]] == p_vertices[p_b.edge[0]]) {
 			return true;
 			return true;
 		}
 		}
 
 

+ 3 - 8
core/math/expression.cpp

@@ -2161,10 +2161,8 @@ Error Expression::parse(const String &p_expression, const Vector<String> &p_inpu
 }
 }
 
 
 Variant Expression::execute(Array p_inputs, Object *p_base, bool p_show_error) {
 Variant Expression::execute(Array p_inputs, Object *p_base, bool p_show_error) {
-	if (error_set) {
-		ERR_EXPLAIN("There was previously a parse error: " + error_str);
-		ERR_FAIL_V(Variant());
-	}
+
+	ERR_FAIL_COND_V_MSG(error_set, Variant(), "There was previously a parse error: " + error_str + ".");
 
 
 	execution_error = false;
 	execution_error = false;
 	Variant output;
 	Variant output;
@@ -2173,10 +2171,7 @@ Variant Expression::execute(Array p_inputs, Object *p_base, bool p_show_error) {
 	if (err) {
 	if (err) {
 		execution_error = true;
 		execution_error = true;
 		error_str = error_txt;
 		error_str = error_txt;
-		if (p_show_error) {
-			ERR_EXPLAIN(error_str);
-			ERR_FAIL_V(Variant());
-		}
+		ERR_FAIL_COND_V_MSG(p_show_error, Variant(), error_str);
 	}
 	}
 
 
 	return output;
 	return output;

+ 48 - 98
core/math/geometry.cpp

@@ -34,9 +34,10 @@
 #include "thirdparty/misc/clipper.hpp"
 #include "thirdparty/misc/clipper.hpp"
 #include "thirdparty/misc/triangulator.h"
 #include "thirdparty/misc/triangulator.h"
 
 
-#define SCALE_FACTOR 100000.0 // based on CMP_EPSILON
+#define SCALE_FACTOR 100000.0 // Based on CMP_EPSILON.
 
 
-/* this implementation is very inefficient, commenting unless bugs happen. See the other one.
+// This implementation is very inefficient, commenting unless bugs happen. See the other one.
+/*
 bool Geometry::is_point_in_polygon(const Vector2 &p_point, const Vector<Vector2> &p_polygon) {
 bool Geometry::is_point_in_polygon(const Vector2 &p_point, const Vector<Vector2> &p_polygon) {
 
 
 	Vector<int> indices = Geometry::triangulate_polygon(p_polygon);
 	Vector<int> indices = Geometry::triangulate_polygon(p_polygon);
@@ -124,8 +125,8 @@ struct _FaceClassify {
 };
 };
 
 
 static bool _connect_faces(_FaceClassify *p_faces, int len, int p_group) {
 static bool _connect_faces(_FaceClassify *p_faces, int len, int p_group) {
-	/* connect faces, error will occur if an edge is shared between more than 2 faces */
-	/* clear connections */
+	// Connect faces, error will occur if an edge is shared between more than 2 faces.
+	// Clear connections.
 
 
 	bool error = false;
 	bool error = false;
 
 
@@ -195,13 +196,6 @@ static bool _connect_faces(_FaceClassify *p_faces, int len, int p_group) {
 			if (p_faces[i].links[j].face == -1)
 			if (p_faces[i].links[j].face == -1)
 				p_faces[i].valid = false;
 				p_faces[i].valid = false;
 		}
 		}
-		/*printf("face %i is valid: %i, group %i. connected to %i:%i,%i:%i,%i:%i\n",i,p_faces[i].valid,p_faces[i].group,
-			p_faces[i].links[0].face,
-			p_faces[i].links[0].edge,
-			p_faces[i].links[1].face,
-			p_faces[i].links[1].edge,
-			p_faces[i].links[2].face,
-			p_faces[i].links[2].edge);*/
 	}
 	}
 	return error;
 	return error;
 }
 }
@@ -249,10 +243,10 @@ PoolVector<PoolVector<Face3> > Geometry::separate_objects(PoolVector<Face3> p_ar
 
 
 	if (error) {
 	if (error) {
 
 
-		ERR_FAIL_COND_V(error, PoolVector<PoolVector<Face3> >()); // invalid geometry
+		ERR_FAIL_COND_V(error, PoolVector<PoolVector<Face3> >()); // Invalid geometry.
 	}
 	}
 
 
-	/* group connected faces in separate objects */
+	// Group connected faces in separate objects.
 
 
 	int group = 0;
 	int group = 0;
 	for (int i = 0; i < len; i++) {
 	for (int i = 0; i < len; i++) {
@@ -264,7 +258,7 @@ PoolVector<PoolVector<Face3> > Geometry::separate_objects(PoolVector<Face3> p_ar
 		}
 		}
 	}
 	}
 
 
-	/* group connected faces in separate objects */
+	// Group connected faces in separate objects.
 
 
 	for (int i = 0; i < len; i++) {
 	for (int i = 0; i < len; i++) {
 
 
@@ -376,7 +370,7 @@ static inline void _plot_face(uint8_t ***p_cell_status, int x, int y, int z, int
 static inline void _mark_outside(uint8_t ***p_cell_status, int x, int y, int z, int len_x, int len_y, int len_z) {
 static inline void _mark_outside(uint8_t ***p_cell_status, int x, int y, int z, int len_x, int len_y, int len_z) {
 
 
 	if (p_cell_status[x][y][z] & 3)
 	if (p_cell_status[x][y][z] & 3)
-		return; // nothing to do, already used and/or visited
+		return; // Nothing to do, already used and/or visited.
 
 
 	p_cell_status[x][y][z] = _CELL_PREV_FIRST;
 	p_cell_status[x][y][z] = _CELL_PREV_FIRST;
 
 
@@ -384,29 +378,20 @@ static inline void _mark_outside(uint8_t ***p_cell_status, int x, int y, int z,
 
 
 		uint8_t &c = p_cell_status[x][y][z];
 		uint8_t &c = p_cell_status[x][y][z];
 
 
-		//printf("at %i,%i,%i\n",x,y,z);
-
 		if ((c & _CELL_STEP_MASK) == _CELL_STEP_NONE) {
 		if ((c & _CELL_STEP_MASK) == _CELL_STEP_NONE) {
-			/* Haven't been in here, mark as outside */
+			// Haven't been in here, mark as outside.
 			p_cell_status[x][y][z] |= _CELL_EXTERIOR;
 			p_cell_status[x][y][z] |= _CELL_EXTERIOR;
-			//printf("not marked as anything, marking exterior\n");
 		}
 		}
 
 
-		//printf("cell step is %i\n",(c&_CELL_STEP_MASK));
-
 		if ((c & _CELL_STEP_MASK) != _CELL_STEP_DONE) {
 		if ((c & _CELL_STEP_MASK) != _CELL_STEP_DONE) {
-			/* if not done, increase step */
+			// If not done, increase step.
 			c += 1 << 2;
 			c += 1 << 2;
-			//printf("incrementing cell step\n");
 		}
 		}
 
 
 		if ((c & _CELL_STEP_MASK) == _CELL_STEP_DONE) {
 		if ((c & _CELL_STEP_MASK) == _CELL_STEP_DONE) {
-			/* Go back */
-			//printf("done, going back a cell\n");
-
+			// Go back.
 			switch (c & _CELL_PREV_MASK) {
 			switch (c & _CELL_PREV_MASK) {
 				case _CELL_PREV_FIRST: {
 				case _CELL_PREV_FIRST: {
-					//printf("at end, finished marking\n");
 					return;
 					return;
 				} break;
 				} break;
 				case _CELL_PREV_Y_POS: {
 				case _CELL_PREV_Y_POS: {
@@ -440,8 +425,6 @@ static inline void _mark_outside(uint8_t ***p_cell_status, int x, int y, int z,
 			continue;
 			continue;
 		}
 		}
 
 
-		//printf("attempting new cell!\n");
-
 		int next_x = x, next_y = y, next_z = z;
 		int next_x = x, next_y = y, next_z = z;
 		uint8_t prev = 0;
 		uint8_t prev = 0;
 
 
@@ -475,8 +458,6 @@ static inline void _mark_outside(uint8_t ***p_cell_status, int x, int y, int z,
 			default: ERR_FAIL();
 			default: ERR_FAIL();
 		}
 		}
 
 
-		//printf("testing if new cell will be ok...!\n");
-
 		if (next_x < 0 || next_x >= len_x)
 		if (next_x < 0 || next_x >= len_x)
 			continue;
 			continue;
 		if (next_y < 0 || next_y >= len_y)
 		if (next_y < 0 || next_y >= len_y)
@@ -484,13 +465,9 @@ static inline void _mark_outside(uint8_t ***p_cell_status, int x, int y, int z,
 		if (next_z < 0 || next_z >= len_z)
 		if (next_z < 0 || next_z >= len_z)
 			continue;
 			continue;
 
 
-		//printf("testing if new cell is traversable\n");
-
 		if (p_cell_status[next_x][next_y][next_z] & 3)
 		if (p_cell_status[next_x][next_y][next_z] & 3)
 			continue;
 			continue;
 
 
-		//printf("move to it\n");
-
 		x = next_x;
 		x = next_x;
 		y = next_y;
 		y = next_y;
 		z = next_z;
 		z = next_z;
@@ -507,17 +484,6 @@ static inline void _build_faces(uint8_t ***p_cell_status, int x, int y, int z, i
 	if (p_cell_status[x][y][z] & _CELL_EXTERIOR)
 	if (p_cell_status[x][y][z] & _CELL_EXTERIOR)
 		return;
 		return;
 
 
-/*	static const Vector3 vertices[8]={
-		Vector3(0,0,0),
-		Vector3(0,0,1),
-		Vector3(0,1,0),
-		Vector3(0,1,1),
-		Vector3(1,0,0),
-		Vector3(1,0,1),
-		Vector3(1,1,0),
-		Vector3(1,1,1),
-	};
-*/
 #define vert(m_idx) Vector3(((m_idx)&4) >> 2, ((m_idx)&2) >> 1, (m_idx)&1)
 #define vert(m_idx) Vector3(((m_idx)&4) >> 2, ((m_idx)&2) >> 1, (m_idx)&1)
 
 
 	static const uint8_t indices[6][4] = {
 	static const uint8_t indices[6][4] = {
@@ -529,22 +495,6 @@ static inline void _build_faces(uint8_t ***p_cell_status, int x, int y, int z, i
 		{ 0, 4, 6, 2 },
 		{ 0, 4, 6, 2 },
 
 
 	};
 	};
-	/*
-
-		{0,1,2,3},
-		{0,1,4,5},
-		{0,2,4,6},
-		{4,5,6,7},
-		{2,3,7,6},
-		{1,3,5,7},
-
-		{0,2,3,1},
-		{0,1,5,4},
-		{0,4,6,2},
-		{7,6,4,5},
-		{7,3,2,6},
-		{7,5,1,3},
-*/
 
 
 	for (int i = 0; i < 6; i++) {
 	for (int i = 0; i < 6; i++) {
 
 
@@ -607,9 +557,9 @@ PoolVector<Face3> Geometry::wrap_geometry(PoolVector<Face3> p_array, real_t *p_e
 		}
 		}
 	}
 	}
 
 
-	global_aabb.grow_by(0.01); // avoid numerical error
+	global_aabb.grow_by(0.01); // Avoid numerical error.
 
 
-	// determine amount of cells in grid axis
+	// Determine amount of cells in grid axis.
 	int div_x, div_y, div_z;
 	int div_x, div_y, div_z;
 
 
 	if (global_aabb.size.x / _MIN_SIZE < _MAX_LENGTH)
 	if (global_aabb.size.x / _MIN_SIZE < _MAX_LENGTH)
@@ -632,7 +582,7 @@ PoolVector<Face3> Geometry::wrap_geometry(PoolVector<Face3> p_array, real_t *p_e
 	voxelsize.y /= div_y;
 	voxelsize.y /= div_y;
 	voxelsize.z /= div_z;
 	voxelsize.z /= div_z;
 
 
-	// create and initialize cells to zero
+	// Create and initialize cells to zero.
 
 
 	uint8_t ***cell_status = memnew_arr(uint8_t **, div_x);
 	uint8_t ***cell_status = memnew_arr(uint8_t **, div_x);
 	for (int i = 0; i < div_x; i++) {
 	for (int i = 0; i < div_x; i++) {
@@ -650,7 +600,7 @@ PoolVector<Face3> Geometry::wrap_geometry(PoolVector<Face3> p_array, real_t *p_e
 		}
 		}
 	}
 	}
 
 
-	// plot faces into cells
+	// Plot faces into cells.
 
 
 	for (int i = 0; i < face_count; i++) {
 	for (int i = 0; i < face_count; i++) {
 
 
@@ -662,7 +612,7 @@ PoolVector<Face3> Geometry::wrap_geometry(PoolVector<Face3> p_array, real_t *p_e
 		_plot_face(cell_status, 0, 0, 0, div_x, div_y, div_z, voxelsize, f);
 		_plot_face(cell_status, 0, 0, 0, div_x, div_y, div_z, voxelsize, f);
 	}
 	}
 
 
-	// determine which cells connect to the outside by traversing the outside and recursively flood-fill marking
+	// Determine which cells connect to the outside by traversing the outside and recursively flood-fill marking.
 
 
 	for (int i = 0; i < div_x; i++) {
 	for (int i = 0; i < div_x; i++) {
 
 
@@ -691,7 +641,7 @@ PoolVector<Face3> Geometry::wrap_geometry(PoolVector<Face3> p_array, real_t *p_e
 		}
 		}
 	}
 	}
 
 
-	// build faces for the inside-outside cell divisors
+	// Build faces for the inside-outside cell divisors.
 
 
 	PoolVector<Face3> wrapped_faces;
 	PoolVector<Face3> wrapped_faces;
 
 
@@ -706,7 +656,7 @@ PoolVector<Face3> Geometry::wrap_geometry(PoolVector<Face3> p_array, real_t *p_e
 		}
 		}
 	}
 	}
 
 
-	// transform face vertices to global coords
+	// Transform face vertices to global coords.
 
 
 	int wrapped_faces_count = wrapped_faces.size();
 	int wrapped_faces_count = wrapped_faces.size();
 	PoolVector<Face3>::Write wrapped_facesw = wrapped_faces.write();
 	PoolVector<Face3>::Write wrapped_facesw = wrapped_faces.write();
@@ -753,7 +703,7 @@ Vector<Vector<Vector2> > Geometry::decompose_polygon_in_convex(Vector<Point2> po
 	inp.SetOrientation(TRIANGULATOR_CCW);
 	inp.SetOrientation(TRIANGULATOR_CCW);
 	in_poly.push_back(inp);
 	in_poly.push_back(inp);
 	TriangulatorPartition tpart;
 	TriangulatorPartition tpart;
-	if (tpart.ConvexPartition_HM(&in_poly, &out_poly) == 0) { //failed!
+	if (tpart.ConvexPartition_HM(&in_poly, &out_poly) == 0) { // Failed.
 		ERR_PRINT("Convex decomposing failed!");
 		ERR_PRINT("Convex decomposing failed!");
 		return decomp;
 		return decomp;
 	}
 	}
@@ -781,7 +731,7 @@ Geometry::MeshData Geometry::build_convex_mesh(const PoolVector<Plane> &p_planes
 
 
 #define SUBPLANE_SIZE 1024.0
 #define SUBPLANE_SIZE 1024.0
 
 
-	real_t subplane_size = 1024.0; // should compute this from the actual plane
+	real_t subplane_size = 1024.0; // Should compute this from the actual plane.
 	for (int i = 0; i < p_planes.size(); i++) {
 	for (int i = 0; i < p_planes.size(); i++) {
 
 
 		Plane p = p_planes[i];
 		Plane p = p_planes[i];
@@ -789,7 +739,7 @@ Geometry::MeshData Geometry::build_convex_mesh(const PoolVector<Plane> &p_planes
 		Vector3 ref = Vector3(0.0, 1.0, 0.0);
 		Vector3 ref = Vector3(0.0, 1.0, 0.0);
 
 
 		if (ABS(p.normal.dot(ref)) > 0.95)
 		if (ABS(p.normal.dot(ref)) > 0.95)
-			ref = Vector3(0.0, 0.0, 1.0); // change axis
+			ref = Vector3(0.0, 0.0, 1.0); // Change axis.
 
 
 		Vector3 right = p.normal.cross(ref).normalized();
 		Vector3 right = p.normal.cross(ref).normalized();
 		Vector3 up = p.normal.cross(right).normalized();
 		Vector3 up = p.normal.cross(right).normalized();
@@ -827,20 +777,20 @@ Geometry::MeshData Geometry::build_convex_mesh(const PoolVector<Plane> &p_planes
 				real_t dist0 = clip.distance_to(edge0_A);
 				real_t dist0 = clip.distance_to(edge0_A);
 				real_t dist1 = clip.distance_to(edge1_A);
 				real_t dist1 = clip.distance_to(edge1_A);
 
 
-				if (dist0 <= 0) { // behind plane
+				if (dist0 <= 0) { // Behind plane.
 
 
 					new_vertices.push_back(vertices[k]);
 					new_vertices.push_back(vertices[k]);
 				}
 				}
 
 
-				// check for different sides and non coplanar
+				// Check for different sides and non coplanar.
 				if ((dist0 * dist1) < 0) {
 				if ((dist0 * dist1) < 0) {
 
 
-					// calculate intersection
+					// Calculate intersection.
 					Vector3 rel = edge1_A - edge0_A;
 					Vector3 rel = edge1_A - edge0_A;
 
 
 					real_t den = clip.normal.dot(rel);
 					real_t den = clip.normal.dot(rel);
 					if (Math::is_zero_approx(den))
 					if (Math::is_zero_approx(den))
-						continue; // point too short
+						continue; // Point too short.
 
 
 					real_t dist = -(clip.normal.dot(edge0_A) - clip.d) / den;
 					real_t dist = -(clip.normal.dot(edge0_A) - clip.d) / den;
 					Vector3 inters = edge0_A + rel * dist;
 					Vector3 inters = edge0_A + rel * dist;
@@ -854,11 +804,11 @@ Geometry::MeshData Geometry::build_convex_mesh(const PoolVector<Plane> &p_planes
 		if (vertices.size() < 3)
 		if (vertices.size() < 3)
 			continue;
 			continue;
 
 
-		//result is a clockwise face
+		// Result is a clockwise face.
 
 
 		MeshData::Face face;
 		MeshData::Face face;
 
 
-		// add face indices
+		// Add face indices.
 		for (int j = 0; j < vertices.size(); j++) {
 		for (int j = 0; j < vertices.size(); j++) {
 
 
 			int idx = -1;
 			int idx = -1;
@@ -882,7 +832,7 @@ Geometry::MeshData Geometry::build_convex_mesh(const PoolVector<Plane> &p_planes
 		face.plane = p;
 		face.plane = p;
 		mesh.faces.push_back(face);
 		mesh.faces.push_back(face);
 
 
-		//add edge
+		// Add edge.
 
 
 		for (int j = 0; j < face.indices.size(); j++) {
 		for (int j = 0; j < face.indices.size(); j++) {
 
 
@@ -972,7 +922,7 @@ PoolVector<Plane> Geometry::build_sphere_planes(real_t p_radius, int p_lats, int
 
 
 		for (int j = 1; j <= p_lats; j++) {
 		for (int j = 1; j <= p_lats; j++) {
 
 
-			//todo this is stupid, fix
+			// FIXME: This is stupid.
 			Vector3 angle = normal.linear_interpolate(axis, j / (real_t)p_lats).normalized();
 			Vector3 angle = normal.linear_interpolate(axis, j / (real_t)p_lats).normalized();
 			Vector3 pos = angle * p_radius;
 			Vector3 pos = angle * p_radius;
 			planes.push_back(Plane(pos, angle));
 			planes.push_back(Plane(pos, angle));
@@ -1032,12 +982,12 @@ struct _AtlasWorkRectResult {
 
 
 void Geometry::make_atlas(const Vector<Size2i> &p_rects, Vector<Point2i> &r_result, Size2i &r_size) {
 void Geometry::make_atlas(const Vector<Size2i> &p_rects, Vector<Point2i> &r_result, Size2i &r_size) {
 
 
-	//super simple, almost brute force scanline stacking fitter
-	//it's pretty basic for now, but it tries to make sure that the aspect ratio of the
-	//resulting atlas is somehow square. This is necessary because video cards have limits
-	//on texture size (usually 2048 or 4096), so the more square a texture, the more chances
-	//it will work in every hardware.
-	// for example, it will prioritize a 1024x1024 atlas (works everywhere) instead of a
+	// Super simple, almost brute force scanline stacking fitter.
+	// It's pretty basic for now, but it tries to make sure that the aspect ratio of the
+	// resulting atlas is somehow square. This is necessary because video cards have limits.
+	// On texture size (usually 2048 or 4096), so the more square a texture, the more chances.
+	// It will work in every hardware.
+	// For example, it will prioritize a 1024x1024 atlas (works everywhere) instead of a
 	// 256x8192 atlas (won't work anywhere).
 	// 256x8192 atlas (won't work anywhere).
 
 
 	ERR_FAIL_COND(p_rects.size() == 0);
 	ERR_FAIL_COND(p_rects.size() == 0);
@@ -1066,7 +1016,7 @@ void Geometry::make_atlas(const Vector<Size2i> &p_rects, Vector<Point2i> &r_resu
 		for (int j = 0; j < w; j++)
 		for (int j = 0; j < w; j++)
 			hmax.write[j] = 0;
 			hmax.write[j] = 0;
 
 
-		//place them
+		// Place them.
 		int ofs = 0;
 		int ofs = 0;
 		int limit_h = 0;
 		int limit_h = 0;
 		for (int j = 0; j < wrects.size(); j++) {
 		for (int j = 0; j < wrects.size(); j++) {
@@ -1101,7 +1051,7 @@ void Geometry::make_atlas(const Vector<Size2i> &p_rects, Vector<Point2i> &r_resu
 			if (end_w > max_w)
 			if (end_w > max_w)
 				max_w = end_w;
 				max_w = end_w;
 
 
-			if (ofs == 0 || end_h > limit_h) //while h limit not reached, keep stacking
+			if (ofs == 0 || end_h > limit_h) // While h limit not reached, keep stacking.
 				ofs += wrects[j].s.width;
 				ofs += wrects[j].s.width;
 		}
 		}
 
 
@@ -1112,7 +1062,7 @@ void Geometry::make_atlas(const Vector<Size2i> &p_rects, Vector<Point2i> &r_resu
 		results.push_back(result);
 		results.push_back(result);
 	}
 	}
 
 
-	//find the result with the best aspect ratio
+	// Find the result with the best aspect ratio.
 
 
 	int best = -1;
 	int best = -1;
 	real_t best_aspect = 1e20;
 	real_t best_aspect = 1e20;
@@ -1152,7 +1102,7 @@ Vector<Vector<Point2> > Geometry::_polypaths_do_operation(PolyBooleanOperation p
 	}
 	}
 	Path path_a, path_b;
 	Path path_a, path_b;
 
 
-	// Need to scale points (Clipper's requirement for robust computation)
+	// Need to scale points (Clipper's requirement for robust computation).
 	for (int i = 0; i != p_polypath_a.size(); ++i) {
 	for (int i = 0; i != p_polypath_a.size(); ++i) {
 		path_a << IntPoint(p_polypath_a[i].x * SCALE_FACTOR, p_polypath_a[i].y * SCALE_FACTOR);
 		path_a << IntPoint(p_polypath_a[i].x * SCALE_FACTOR, p_polypath_a[i].y * SCALE_FACTOR);
 	}
 	}
@@ -1160,19 +1110,19 @@ Vector<Vector<Point2> > Geometry::_polypaths_do_operation(PolyBooleanOperation p
 		path_b << IntPoint(p_polypath_b[i].x * SCALE_FACTOR, p_polypath_b[i].y * SCALE_FACTOR);
 		path_b << IntPoint(p_polypath_b[i].x * SCALE_FACTOR, p_polypath_b[i].y * SCALE_FACTOR);
 	}
 	}
 	Clipper clp;
 	Clipper clp;
-	clp.AddPath(path_a, ptSubject, !is_a_open); // forward compatible with Clipper 10.0.0
-	clp.AddPath(path_b, ptClip, true); // polylines cannot be set as clip
+	clp.AddPath(path_a, ptSubject, !is_a_open); // Forward compatible with Clipper 10.0.0.
+	clp.AddPath(path_b, ptClip, true); // Polylines cannot be set as clip.
 
 
 	Paths paths;
 	Paths paths;
 
 
 	if (is_a_open) {
 	if (is_a_open) {
-		PolyTree tree; // needed to populate polylines
+		PolyTree tree; // Needed to populate polylines.
 		clp.Execute(op, tree);
 		clp.Execute(op, tree);
 		OpenPathsFromPolyTree(tree, paths);
 		OpenPathsFromPolyTree(tree, paths);
 	} else {
 	} else {
-		clp.Execute(op, paths); // works on closed polygons only
+		clp.Execute(op, paths); // Works on closed polygons only.
 	}
 	}
-	// Have to scale points down now
+	// Have to scale points down now.
 	Vector<Vector<Point2> > polypaths;
 	Vector<Vector<Point2> > polypaths;
 
 
 	for (Paths::size_type i = 0; i < paths.size(); ++i) {
 	for (Paths::size_type i = 0; i < paths.size(); ++i) {
@@ -1214,16 +1164,16 @@ Vector<Vector<Point2> > Geometry::_polypath_offset(const Vector<Point2> &p_polyp
 	ClipperOffset co;
 	ClipperOffset co;
 	Path path;
 	Path path;
 
 
-	// Need to scale points (Clipper's requirement for robust computation)
+	// Need to scale points (Clipper's requirement for robust computation).
 	for (int i = 0; i != p_polypath.size(); ++i) {
 	for (int i = 0; i != p_polypath.size(); ++i) {
 		path << IntPoint(p_polypath[i].x * SCALE_FACTOR, p_polypath[i].y * SCALE_FACTOR);
 		path << IntPoint(p_polypath[i].x * SCALE_FACTOR, p_polypath[i].y * SCALE_FACTOR);
 	}
 	}
 	co.AddPath(path, jt, et);
 	co.AddPath(path, jt, et);
 
 
 	Paths paths;
 	Paths paths;
-	co.Execute(paths, p_delta * SCALE_FACTOR); // inflate/deflate
+	co.Execute(paths, p_delta * SCALE_FACTOR); // Inflate/deflate.
 
 
-	// Have to scale points down now
+	// Have to scale points down now.
 	Vector<Vector<Point2> > polypaths;
 	Vector<Vector<Point2> > polypaths;
 
 
 	for (Paths::size_type i = 0; i < paths.size(); ++i) {
 	for (Paths::size_type i = 0; i < paths.size(); ++i) {

+ 102 - 122
core/math/geometry.h

@@ -41,47 +41,43 @@
 #include "core/print_string.h"
 #include "core/print_string.h"
 #include "core/vector.h"
 #include "core/vector.h"
 
 
-/**
-	@author Juan Linietsky <[email protected]>
-*/
-
 class Geometry {
 class Geometry {
 	Geometry();
 	Geometry();
 
 
 public:
 public:
 	static real_t get_closest_points_between_segments(const Vector2 &p1, const Vector2 &q1, const Vector2 &p2, const Vector2 &q2, Vector2 &c1, Vector2 &c2) {
 	static real_t get_closest_points_between_segments(const Vector2 &p1, const Vector2 &q1, const Vector2 &p2, const Vector2 &q2, Vector2 &c1, Vector2 &c2) {
 
 
-		Vector2 d1 = q1 - p1; // Direction vector of segment S1
-		Vector2 d2 = q2 - p2; // Direction vector of segment S2
+		Vector2 d1 = q1 - p1; // Direction vector of segment S1.
+		Vector2 d2 = q2 - p2; // Direction vector of segment S2.
 		Vector2 r = p1 - p2;
 		Vector2 r = p1 - p2;
-		real_t a = d1.dot(d1); // Squared length of segment S1, always nonnegative
-		real_t e = d2.dot(d2); // Squared length of segment S2, always nonnegative
+		real_t a = d1.dot(d1); // Squared length of segment S1, always nonnegative.
+		real_t e = d2.dot(d2); // Squared length of segment S2, always nonnegative.
 		real_t f = d2.dot(r);
 		real_t f = d2.dot(r);
 		real_t s, t;
 		real_t s, t;
-		// Check if either or both segments degenerate into points
+		// Check if either or both segments degenerate into points.
 		if (a <= CMP_EPSILON && e <= CMP_EPSILON) {
 		if (a <= CMP_EPSILON && e <= CMP_EPSILON) {
-			// Both segments degenerate into points
+			// Both segments degenerate into points.
 			c1 = p1;
 			c1 = p1;
 			c2 = p2;
 			c2 = p2;
 			return Math::sqrt((c1 - c2).dot(c1 - c2));
 			return Math::sqrt((c1 - c2).dot(c1 - c2));
 		}
 		}
 		if (a <= CMP_EPSILON) {
 		if (a <= CMP_EPSILON) {
-			// First segment degenerates into a point
+			// First segment degenerates into a point.
 			s = 0.0;
 			s = 0.0;
 			t = f / e; // s = 0 => t = (b*s + f) / e = f / e
 			t = f / e; // s = 0 => t = (b*s + f) / e = f / e
 			t = CLAMP(t, 0.0, 1.0);
 			t = CLAMP(t, 0.0, 1.0);
 		} else {
 		} else {
 			real_t c = d1.dot(r);
 			real_t c = d1.dot(r);
 			if (e <= CMP_EPSILON) {
 			if (e <= CMP_EPSILON) {
-				// Second segment degenerates into a point
+				// Second segment degenerates into a point.
 				t = 0.0;
 				t = 0.0;
 				s = CLAMP(-c / a, 0.0, 1.0); // t = 0 => s = (b*t - c) / a = -c / a
 				s = CLAMP(-c / a, 0.0, 1.0); // t = 0 => s = (b*t - c) / a = -c / a
 			} else {
 			} else {
-				// The general nondegenerate case starts here
+				// The general nondegenerate case starts here.
 				real_t b = d1.dot(d2);
 				real_t b = d1.dot(d2);
-				real_t denom = a * e - b * b; // Always nonnegative
+				real_t denom = a * e - b * b; // Always nonnegative.
 				// If segments not parallel, compute closest point on L1 to L2 and
 				// If segments not parallel, compute closest point on L1 to L2 and
-				// clamp to segment S1. Else pick arbitrary s (here 0)
+				// clamp to segment S1. Else pick arbitrary s (here 0).
 				if (denom != 0.0) {
 				if (denom != 0.0) {
 					s = CLAMP((b * f - c * e) / denom, 0.0, 1.0);
 					s = CLAMP((b * f - c * e) / denom, 0.0, 1.0);
 				} else
 				} else
@@ -92,7 +88,7 @@ public:
 
 
 				//If t in [0,1] done. Else clamp t, recompute s for the new value
 				//If t in [0,1] done. Else clamp t, recompute s for the new value
 				// of t using s = Dot((P2 + D2*t) - P1,D1) / Dot(D1,D1)= (t*b - c) / a
 				// of t using s = Dot((P2 + D2*t) - P1,D1) / Dot(D1,D1)= (t*b - c) / a
-				// and clamp s to [0, 1]
+				// and clamp s to [0, 1].
 				if (t < 0.0) {
 				if (t < 0.0) {
 					t = 0.0;
 					t = 0.0;
 					s = CLAMP(-c / a, 0.0, 1.0);
 					s = CLAMP(-c / a, 0.0, 1.0);
@@ -109,14 +105,14 @@ public:
 
 
 	static void get_closest_points_between_segments(const Vector3 &p1, const Vector3 &p2, const Vector3 &q1, const Vector3 &q2, Vector3 &c1, Vector3 &c2) {
 	static void get_closest_points_between_segments(const Vector3 &p1, const Vector3 &p2, const Vector3 &q1, const Vector3 &q2, Vector3 &c1, Vector3 &c2) {
 
 
-//do the function 'd' as defined by pb. I think is is dot product of some sort
+// Do the function 'd' as defined by pb. I think is is dot product of some sort.
 #define d_of(m, n, o, p) ((m.x - n.x) * (o.x - p.x) + (m.y - n.y) * (o.y - p.y) + (m.z - n.z) * (o.z - p.z))
 #define d_of(m, n, o, p) ((m.x - n.x) * (o.x - p.x) + (m.y - n.y) * (o.y - p.y) + (m.z - n.z) * (o.z - p.z))
 
 
-		//calculate the parametric position on the 2 curves, mua and mub
+		// Calculate the parametric position on the 2 curves, mua and mub.
 		real_t mua = (d_of(p1, q1, q2, q1) * d_of(q2, q1, p2, p1) - d_of(p1, q1, p2, p1) * d_of(q2, q1, q2, q1)) / (d_of(p2, p1, p2, p1) * d_of(q2, q1, q2, q1) - d_of(q2, q1, p2, p1) * d_of(q2, q1, p2, p1));
 		real_t mua = (d_of(p1, q1, q2, q1) * d_of(q2, q1, p2, p1) - d_of(p1, q1, p2, p1) * d_of(q2, q1, q2, q1)) / (d_of(p2, p1, p2, p1) * d_of(q2, q1, q2, q1) - d_of(q2, q1, p2, p1) * d_of(q2, q1, p2, p1));
 		real_t mub = (d_of(p1, q1, q2, q1) + mua * d_of(q2, q1, p2, p1)) / d_of(q2, q1, q2, q1);
 		real_t mub = (d_of(p1, q1, q2, q1) + mua * d_of(q2, q1, p2, p1)) / d_of(q2, q1, q2, q1);
 
 
-		//clip the value between [0..1] constraining the solution to lie on the original curves
+		// Clip the value between [0..1] constraining the solution to lie on the original curves.
 		if (mua < 0) mua = 0;
 		if (mua < 0) mua = 0;
 		if (mub < 0) mub = 0;
 		if (mub < 0) mub = 0;
 		if (mua > 1) mua = 1;
 		if (mua > 1) mua = 1;
@@ -129,38 +125,38 @@ public:
 		Vector3 u = p_to_a - p_from_a;
 		Vector3 u = p_to_a - p_from_a;
 		Vector3 v = p_to_b - p_from_b;
 		Vector3 v = p_to_b - p_from_b;
 		Vector3 w = p_from_a - p_to_a;
 		Vector3 w = p_from_a - p_to_a;
-		real_t a = u.dot(u); // always >= 0
+		real_t a = u.dot(u); // Always >= 0
 		real_t b = u.dot(v);
 		real_t b = u.dot(v);
-		real_t c = v.dot(v); // always >= 0
+		real_t c = v.dot(v); // Always >= 0
 		real_t d = u.dot(w);
 		real_t d = u.dot(w);
 		real_t e = v.dot(w);
 		real_t e = v.dot(w);
-		real_t D = a * c - b * b; // always >= 0
+		real_t D = a * c - b * b; // Always >= 0
 		real_t sc, sN, sD = D; // sc = sN / sD, default sD = D >= 0
 		real_t sc, sN, sD = D; // sc = sN / sD, default sD = D >= 0
 		real_t tc, tN, tD = D; // tc = tN / tD, default tD = D >= 0
 		real_t tc, tN, tD = D; // tc = tN / tD, default tD = D >= 0
 
 
-		// compute the line parameters of the two closest points
-		if (D < CMP_EPSILON) { // the lines are almost parallel
-			sN = 0.0; // force using point P0 on segment S1
-			sD = 1.0; // to prevent possible division by 0.0 later
+		// Compute the line parameters of the two closest points.
+		if (D < CMP_EPSILON) { // The lines are almost parallel.
+			sN = 0.0; // Force using point P0 on segment S1
+			sD = 1.0; // to prevent possible division by 0.0 later.
 			tN = e;
 			tN = e;
 			tD = c;
 			tD = c;
-		} else { // get the closest points on the infinite lines
+		} else { // Get the closest points on the infinite lines
 			sN = (b * e - c * d);
 			sN = (b * e - c * d);
 			tN = (a * e - b * d);
 			tN = (a * e - b * d);
-			if (sN < 0.0) { // sc < 0 => the s=0 edge is visible
+			if (sN < 0.0) { // sc < 0 => the s=0 edge is visible.
 				sN = 0.0;
 				sN = 0.0;
 				tN = e;
 				tN = e;
 				tD = c;
 				tD = c;
-			} else if (sN > sD) { // sc > 1 => the s=1 edge is visible
+			} else if (sN > sD) { // sc > 1 => the s=1 edge is visible.
 				sN = sD;
 				sN = sD;
 				tN = e + b;
 				tN = e + b;
 				tD = c;
 				tD = c;
 			}
 			}
 		}
 		}
 
 
-		if (tN < 0.0) { // tc < 0 => the t=0 edge is visible
+		if (tN < 0.0) { // tc < 0 => the t=0 edge is visible.
 			tN = 0.0;
 			tN = 0.0;
-			// recompute sc for this edge
+			// Recompute sc for this edge.
 			if (-d < 0.0)
 			if (-d < 0.0)
 				sN = 0.0;
 				sN = 0.0;
 			else if (-d > a)
 			else if (-d > a)
@@ -169,9 +165,9 @@ public:
 				sN = -d;
 				sN = -d;
 				sD = a;
 				sD = a;
 			}
 			}
-		} else if (tN > tD) { // tc > 1 => the t=1 edge is visible
+		} else if (tN > tD) { // tc > 1 => the t=1 edge is visible.
 			tN = tD;
 			tN = tD;
-			// recompute sc for this edge
+			// Recompute sc for this edge.
 			if ((-d + b) < 0.0)
 			if ((-d + b) < 0.0)
 				sN = 0;
 				sN = 0;
 			else if ((-d + b) > a)
 			else if ((-d + b) > a)
@@ -181,14 +177,14 @@ public:
 				sD = a;
 				sD = a;
 			}
 			}
 		}
 		}
-		// finally do the division to get sc and tc
+		// Finally do the division to get sc and tc.
 		sc = (Math::is_zero_approx(sN) ? 0.0 : sN / sD);
 		sc = (Math::is_zero_approx(sN) ? 0.0 : sN / sD);
 		tc = (Math::is_zero_approx(tN) ? 0.0 : tN / tD);
 		tc = (Math::is_zero_approx(tN) ? 0.0 : tN / tD);
 
 
-		// get the difference of the two closest points
+		// Get the difference of the two closest points.
 		Vector3 dP = w + (sc * u) - (tc * v); // = S1(sc) - S2(tc)
 		Vector3 dP = w + (sc * u) - (tc * v); // = S1(sc) - S2(tc)
 
 
-		return dP.length(); // return the closest distance
+		return dP.length(); // Return the closest distance.
 	}
 	}
 
 
 	static inline bool ray_intersects_triangle(const Vector3 &p_from, const Vector3 &p_dir, const Vector3 &p_v0, const Vector3 &p_v1, const Vector3 &p_v2, Vector3 *r_res = 0) {
 	static inline bool ray_intersects_triangle(const Vector3 &p_from, const Vector3 &p_dir, const Vector3 &p_v0, const Vector3 &p_v1, const Vector3 &p_v2, Vector3 *r_res = 0) {
@@ -196,7 +192,7 @@ public:
 		Vector3 e2 = p_v2 - p_v0;
 		Vector3 e2 = p_v2 - p_v0;
 		Vector3 h = p_dir.cross(e2);
 		Vector3 h = p_dir.cross(e2);
 		real_t a = e1.dot(h);
 		real_t a = e1.dot(h);
-		if (Math::is_zero_approx(a)) // parallel test
+		if (Math::is_zero_approx(a)) // Parallel test.
 			return false;
 			return false;
 
 
 		real_t f = 1.0 / a;
 		real_t f = 1.0 / a;
@@ -214,16 +210,15 @@ public:
 		if (v < 0.0 || u + v > 1.0)
 		if (v < 0.0 || u + v > 1.0)
 			return false;
 			return false;
 
 
-		// at this stage we can compute t to find out where
-		// the intersection point is on the line
+		// At this stage we can compute t to find out where
+		// the intersection point is on the line.
 		real_t t = f * e2.dot(q);
 		real_t t = f * e2.dot(q);
 
 
 		if (t > 0.00001) { // ray intersection
 		if (t > 0.00001) { // ray intersection
 			if (r_res)
 			if (r_res)
 				*r_res = p_from + p_dir * t;
 				*r_res = p_from + p_dir * t;
 			return true;
 			return true;
-		} else // this means that there is a line intersection
-			// but not a ray intersection
+		} else // This means that there is a line intersection but not a ray intersection.
 			return false;
 			return false;
 	}
 	}
 
 
@@ -234,7 +229,7 @@ public:
 		Vector3 e2 = p_v2 - p_v0;
 		Vector3 e2 = p_v2 - p_v0;
 		Vector3 h = rel.cross(e2);
 		Vector3 h = rel.cross(e2);
 		real_t a = e1.dot(h);
 		real_t a = e1.dot(h);
-		if (Math::is_zero_approx(a)) // parallel test
+		if (Math::is_zero_approx(a)) // Parallel test.
 			return false;
 			return false;
 
 
 		real_t f = 1.0 / a;
 		real_t f = 1.0 / a;
@@ -252,16 +247,15 @@ public:
 		if (v < 0.0 || u + v > 1.0)
 		if (v < 0.0 || u + v > 1.0)
 			return false;
 			return false;
 
 
-		// at this stage we can compute t to find out where
-		// the intersection point is on the line
+		// At this stage we can compute t to find out where
+		// the intersection point is on the line.
 		real_t t = f * e2.dot(q);
 		real_t t = f * e2.dot(q);
 
 
-		if (t > CMP_EPSILON && t <= 1.0) { // ray intersection
+		if (t > CMP_EPSILON && t <= 1.0) { // Ray intersection.
 			if (r_res)
 			if (r_res)
 				*r_res = p_from + rel * t;
 				*r_res = p_from + rel * t;
 			return true;
 			return true;
-		} else // this means that there is a line intersection
-			// but not a ray intersection
+		} else // This means that there is a line intersection but not a ray intersection.
 			return false;
 			return false;
 	}
 	}
 
 
@@ -271,13 +265,11 @@ public:
 		Vector3 rel = (p_to - p_from);
 		Vector3 rel = (p_to - p_from);
 		real_t rel_l = rel.length();
 		real_t rel_l = rel.length();
 		if (rel_l < CMP_EPSILON)
 		if (rel_l < CMP_EPSILON)
-			return false; // both points are the same
+			return false; // Both points are the same.
 		Vector3 normal = rel / rel_l;
 		Vector3 normal = rel / rel_l;
 
 
 		real_t sphere_d = normal.dot(sphere_pos);
 		real_t sphere_d = normal.dot(sphere_pos);
 
 
-		//Vector3 ray_closest=normal*sphere_d;
-
 		real_t ray_distance = sphere_pos.distance_to(normal * sphere_d);
 		real_t ray_distance = sphere_pos.distance_to(normal * sphere_d);
 
 
 		if (ray_distance >= p_sphere_radius)
 		if (ray_distance >= p_sphere_radius)
@@ -289,7 +281,7 @@ public:
 		if (inters_d2 >= CMP_EPSILON)
 		if (inters_d2 >= CMP_EPSILON)
 			inters_d -= Math::sqrt(inters_d2);
 			inters_d -= Math::sqrt(inters_d2);
 
 
-		// check in segment
+		// Check in segment.
 		if (inters_d < 0 || inters_d > rel_l)
 		if (inters_d < 0 || inters_d > rel_l)
 			return false;
 			return false;
 
 
@@ -308,9 +300,9 @@ public:
 		Vector3 rel = (p_to - p_from);
 		Vector3 rel = (p_to - p_from);
 		real_t rel_l = rel.length();
 		real_t rel_l = rel.length();
 		if (rel_l < CMP_EPSILON)
 		if (rel_l < CMP_EPSILON)
-			return false; // both points are the same
+			return false; // Both points are the same.
 
 
-		// first check if they are parallel
+		// First check if they are parallel.
 		Vector3 normal = (rel / rel_l);
 		Vector3 normal = (rel / rel_l);
 		Vector3 crs = normal.cross(Vector3(0, 0, 1));
 		Vector3 crs = normal.cross(Vector3(0, 0, 1));
 		real_t crs_l = crs.length();
 		real_t crs_l = crs.length();
@@ -318,8 +310,7 @@ public:
 		Vector3 z_dir;
 		Vector3 z_dir;
 
 
 		if (crs_l < CMP_EPSILON) {
 		if (crs_l < CMP_EPSILON) {
-			//blahblah parallel
-			z_dir = Vector3(1, 0, 0); //any x/y vector ok
+			z_dir = Vector3(1, 0, 0); // Any x/y vector OK.
 		} else {
 		} else {
 			z_dir = crs / crs_l;
 			z_dir = crs / crs_l;
 		}
 		}
@@ -327,12 +318,12 @@ public:
 		real_t dist = z_dir.dot(p_from);
 		real_t dist = z_dir.dot(p_from);
 
 
 		if (dist >= p_radius)
 		if (dist >= p_radius)
-			return false; // too far away
+			return false; // Too far away.
 
 
-		// convert to 2D
+		// Convert to 2D.
 		real_t w2 = p_radius * p_radius - dist * dist;
 		real_t w2 = p_radius * p_radius - dist * dist;
 		if (w2 < CMP_EPSILON)
 		if (w2 < CMP_EPSILON)
-			return false; //avoid numerical error
+			return false; // Avoid numerical error.
 		Size2 size(Math::sqrt(w2), p_height * 0.5);
 		Size2 size(Math::sqrt(w2), p_height * 0.5);
 
 
 		Vector3 x_dir = z_dir.cross(Vector3(0, 0, 1)).normalized();
 		Vector3 x_dir = z_dir.cross(Vector3(0, 0, 1)).normalized();
@@ -379,7 +370,7 @@ public:
 				return false;
 				return false;
 		}
 		}
 
 
-		// convert to 3D again
+		// Convert to 3D again.
 		Vector3 result = p_from + (rel * min);
 		Vector3 result = p_from + (rel * min);
 		Vector3 res_normal = result;
 		Vector3 res_normal = result;
 
 
@@ -420,19 +411,18 @@ public:
 
 
 			real_t den = p.normal.dot(dir);
 			real_t den = p.normal.dot(dir);
 
 
-			//printf("den is %i\n",den);
 			if (Math::abs(den) <= CMP_EPSILON)
 			if (Math::abs(den) <= CMP_EPSILON)
-				continue; // ignore parallel plane
+				continue; // Ignore parallel plane.
 
 
 			real_t dist = -p.distance_to(p_from) / den;
 			real_t dist = -p.distance_to(p_from) / den;
 
 
 			if (den > 0) {
 			if (den > 0) {
-				//backwards facing plane
+				// Backwards facing plane.
 				if (dist < max)
 				if (dist < max)
 					max = dist;
 					max = dist;
 			} else {
 			} else {
 
 
-				//front facing plane
+				// Front facing plane.
 				if (dist > min) {
 				if (dist > min) {
 					min = dist;
 					min = dist;
 					min_index = i;
 					min_index = i;
@@ -440,8 +430,8 @@ public:
 			}
 			}
 		}
 		}
 
 
-		if (max <= min || min < 0 || min > rel_l || min_index == -1) // exit conditions
-			return false; // no intersection
+		if (max <= min || min < 0 || min > rel_l || min_index == -1) // Exit conditions.
+			return false; // No intersection.
 
 
 		if (p_res)
 		if (p_res)
 			*p_res = p_from + dir * min;
 			*p_res = p_from + dir * min;
@@ -457,16 +447,16 @@ public:
 		Vector3 n = p_segment[1] - p_segment[0];
 		Vector3 n = p_segment[1] - p_segment[0];
 		real_t l2 = n.length_squared();
 		real_t l2 = n.length_squared();
 		if (l2 < 1e-20)
 		if (l2 < 1e-20)
-			return p_segment[0]; // both points are the same, just give any
+			return p_segment[0]; // Both points are the same, just give any.
 
 
 		real_t d = n.dot(p) / l2;
 		real_t d = n.dot(p) / l2;
 
 
 		if (d <= 0.0)
 		if (d <= 0.0)
-			return p_segment[0]; // before first point
+			return p_segment[0]; // Before first point.
 		else if (d >= 1.0)
 		else if (d >= 1.0)
-			return p_segment[1]; // after first point
+			return p_segment[1]; // After first point.
 		else
 		else
-			return p_segment[0] + n * d; // inside
+			return p_segment[0] + n * d; // Inside.
 	}
 	}
 
 
 	static Vector3 get_closest_point_to_segment_uncapped(const Vector3 &p_point, const Vector3 *p_segment) {
 	static Vector3 get_closest_point_to_segment_uncapped(const Vector3 &p_point, const Vector3 *p_segment) {
@@ -475,11 +465,11 @@ public:
 		Vector3 n = p_segment[1] - p_segment[0];
 		Vector3 n = p_segment[1] - p_segment[0];
 		real_t l2 = n.length_squared();
 		real_t l2 = n.length_squared();
 		if (l2 < 1e-20)
 		if (l2 < 1e-20)
-			return p_segment[0]; // both points are the same, just give any
+			return p_segment[0]; // Both points are the same, just give any.
 
 
 		real_t d = n.dot(p) / l2;
 		real_t d = n.dot(p) / l2;
 
 
-		return p_segment[0] + n * d; // inside
+		return p_segment[0] + n * d; // Inside.
 	}
 	}
 
 
 	static Vector2 get_closest_point_to_segment_2d(const Vector2 &p_point, const Vector2 *p_segment) {
 	static Vector2 get_closest_point_to_segment_2d(const Vector2 &p_point, const Vector2 *p_segment) {
@@ -488,16 +478,16 @@ public:
 		Vector2 n = p_segment[1] - p_segment[0];
 		Vector2 n = p_segment[1] - p_segment[0];
 		real_t l2 = n.length_squared();
 		real_t l2 = n.length_squared();
 		if (l2 < 1e-20)
 		if (l2 < 1e-20)
-			return p_segment[0]; // both points are the same, just give any
+			return p_segment[0]; // Both points are the same, just give any.
 
 
 		real_t d = n.dot(p) / l2;
 		real_t d = n.dot(p) / l2;
 
 
 		if (d <= 0.0)
 		if (d <= 0.0)
-			return p_segment[0]; // before first point
+			return p_segment[0]; // Before first point.
 		else if (d >= 1.0)
 		else if (d >= 1.0)
-			return p_segment[1]; // after first point
+			return p_segment[1]; // After first point.
 		else
 		else
-			return p_segment[0] + n * d; // inside
+			return p_segment[0] + n * d; // Inside.
 	}
 	}
 
 
 	static bool is_point_in_triangle(const Vector2 &s, const Vector2 &a, const Vector2 &b, const Vector2 &c) {
 	static bool is_point_in_triangle(const Vector2 &s, const Vector2 &a, const Vector2 &b, const Vector2 &c) {
@@ -512,27 +502,25 @@ public:
 		return (cn.cross(an) > 0) == orientation;
 		return (cn.cross(an) > 0) == orientation;
 	}
 	}
 
 
-	//static bool is_point_in_polygon(const Vector2 &p_point, const Vector<Vector2> &p_polygon);
-
 	static Vector2 get_closest_point_to_segment_uncapped_2d(const Vector2 &p_point, const Vector2 *p_segment) {
 	static Vector2 get_closest_point_to_segment_uncapped_2d(const Vector2 &p_point, const Vector2 *p_segment) {
 
 
 		Vector2 p = p_point - p_segment[0];
 		Vector2 p = p_point - p_segment[0];
 		Vector2 n = p_segment[1] - p_segment[0];
 		Vector2 n = p_segment[1] - p_segment[0];
 		real_t l2 = n.length_squared();
 		real_t l2 = n.length_squared();
 		if (l2 < 1e-20)
 		if (l2 < 1e-20)
-			return p_segment[0]; // both points are the same, just give any
+			return p_segment[0]; // Both points are the same, just give any.
 
 
 		real_t d = n.dot(p) / l2;
 		real_t d = n.dot(p) / l2;
 
 
-		return p_segment[0] + n * d; // inside
+		return p_segment[0] + n * d; // Inside.
 	}
 	}
 
 
 	static bool line_intersects_line_2d(const Vector2 &p_from_a, const Vector2 &p_dir_a, const Vector2 &p_from_b, const Vector2 &p_dir_b, Vector2 &r_result) {
 	static bool line_intersects_line_2d(const Vector2 &p_from_a, const Vector2 &p_dir_a, const Vector2 &p_from_b, const Vector2 &p_dir_b, Vector2 &r_result) {
 
 
-		// see http://paulbourke.net/geometry/pointlineplane/
+		// See http://paulbourke.net/geometry/pointlineplane/
 
 
 		const real_t denom = p_dir_b.y * p_dir_a.x - p_dir_b.x * p_dir_a.y;
 		const real_t denom = p_dir_b.y * p_dir_a.x - p_dir_b.x * p_dir_a.y;
-		if (Math::is_zero_approx(denom)) { // parallel?
+		if (Math::is_zero_approx(denom)) { // Parallel?
 			return false;
 			return false;
 		}
 		}
 
 
@@ -560,11 +548,11 @@ public:
 
 
 		real_t ABpos = D.x + (C.x - D.x) * D.y / (D.y - C.y);
 		real_t ABpos = D.x + (C.x - D.x) * D.y / (D.y - C.y);
 
 
-		//  Fail if segment C-D crosses line A-B outside of segment A-B.
+		// Fail if segment C-D crosses line A-B outside of segment A-B.
 		if (ABpos < 0 || ABpos > 1.0)
 		if (ABpos < 0 || ABpos > 1.0)
 			return false;
 			return false;
 
 
-		//  (4) Apply the discovered position to line A-B in the original coordinate system.
+		// (4) Apply the discovered position to line A-B in the original coordinate system.
 		if (r_result)
 		if (r_result)
 			*r_result = p_from_a + B * ABpos;
 			*r_result = p_from_a + B * ABpos;
 
 
@@ -597,7 +585,7 @@ public:
 
 
 		real_t d = p_normal.dot(p_sphere_pos) - p_normal.dot(p_triangle[0]);
 		real_t d = p_normal.dot(p_sphere_pos) - p_normal.dot(p_triangle[0]);
 
 
-		if (d > p_sphere_radius || d < -p_sphere_radius) // not touching the plane of the face, return
+		if (d > p_sphere_radius || d < -p_sphere_radius) // Not touching the plane of the face, return.
 			return false;
 			return false;
 
 
 		Vector3 contact = p_sphere_pos - (p_normal * d);
 		Vector3 contact = p_sphere_pos - (p_normal * d);
@@ -617,25 +605,25 @@ public:
 
 
 		for (int i = 0; i < 3; i++) {
 		for (int i = 0; i < 3; i++) {
 
 
-			// check edge cylinder
+			// Check edge cylinder.
 
 
 			Vector3 n1 = verts[i] - verts[i + 1];
 			Vector3 n1 = verts[i] - verts[i + 1];
 			Vector3 n2 = p_sphere_pos - verts[i + 1];
 			Vector3 n2 = p_sphere_pos - verts[i + 1];
 
 
-			///@TODO i could discard by range here to make the algorithm quicker? dunno..
+			///@TODO Maybe discard by range here to make the algorithm quicker.
 
 
-			// check point within cylinder radius
+			// Check point within cylinder radius.
 			Vector3 axis = n1.cross(n2).cross(n1);
 			Vector3 axis = n1.cross(n2).cross(n1);
-			axis.normalize(); // ugh
+			axis.normalize();
 
 
 			real_t ad = axis.dot(n2);
 			real_t ad = axis.dot(n2);
 
 
 			if (ABS(ad) > p_sphere_radius) {
 			if (ABS(ad) > p_sphere_radius) {
-				// no chance with this edge, too far away
+				// No chance with this edge, too far away.
 				continue;
 				continue;
 			}
 			}
 
 
-			// check point within edge capsule cylinder
+			// Check point within edge capsule cylinder.
 			/** 4th TEST INSIDE EDGE POINTS **/
 			/** 4th TEST INSIDE EDGE POINTS **/
 
 
 			real_t sphere_at = n1.dot(n2);
 			real_t sphere_at = n1.dot(n2);
@@ -644,8 +632,7 @@ public:
 
 
 				r_triangle_contact = p_sphere_pos - axis * (axis.dot(n2));
 				r_triangle_contact = p_sphere_pos - axis * (axis.dot(n2));
 				r_sphere_contact = p_sphere_pos - axis * p_sphere_radius;
 				r_sphere_contact = p_sphere_pos - axis * p_sphere_radius;
-				// point inside here
-				//printf("solved inside edge\n");
+				// Point inside here.
 				return true;
 				return true;
 			}
 			}
 
 
@@ -655,48 +642,51 @@ public:
 
 
 				Vector3 n = (p_sphere_pos - verts[i + 1]).normalized();
 				Vector3 n = (p_sphere_pos - verts[i + 1]).normalized();
 
 
-				//r_triangle_contact=verts[i+1]+n*p_sphere_radius;p_sphere_pos+axis*(p_sphere_radius-axis.dot(n2));
 				r_triangle_contact = verts[i + 1];
 				r_triangle_contact = verts[i + 1];
 				r_sphere_contact = p_sphere_pos - n * p_sphere_radius;
 				r_sphere_contact = p_sphere_pos - n * p_sphere_radius;
-				//printf("solved inside point segment 1\n");
 				return true;
 				return true;
 			}
 			}
 
 
 			if (n2.distance_squared_to(n1) < r2) {
 			if (n2.distance_squared_to(n1) < r2) {
 				Vector3 n = (p_sphere_pos - verts[i]).normalized();
 				Vector3 n = (p_sphere_pos - verts[i]).normalized();
 
 
-				//r_triangle_contact=verts[i]+n*p_sphere_radius;p_sphere_pos+axis*(p_sphere_radius-axis.dot(n2));
 				r_triangle_contact = verts[i];
 				r_triangle_contact = verts[i];
 				r_sphere_contact = p_sphere_pos - n * p_sphere_radius;
 				r_sphere_contact = p_sphere_pos - n * p_sphere_radius;
-				//printf("solved inside point segment 1\n");
 				return true;
 				return true;
 			}
 			}
 
 
-			break; // It's pointless to continue at this point, so save some cpu cycles
+			break; // It's pointless to continue at this point, so save some CPU cycles.
 		}
 		}
 
 
 		return false;
 		return false;
 	}
 	}
 
 
+	static inline bool is_point_in_circle(const Vector2 &p_point, const Vector2 &p_circle_pos, real_t p_circle_radius) {
+
+		return p_point.distance_squared_to(p_circle_pos) <= p_circle_radius * p_circle_radius;
+	}
+
 	static real_t segment_intersects_circle(const Vector2 &p_from, const Vector2 &p_to, const Vector2 &p_circle_pos, real_t p_circle_radius) {
 	static real_t segment_intersects_circle(const Vector2 &p_from, const Vector2 &p_to, const Vector2 &p_circle_pos, real_t p_circle_radius) {
 
 
 		Vector2 line_vec = p_to - p_from;
 		Vector2 line_vec = p_to - p_from;
 		Vector2 vec_to_line = p_from - p_circle_pos;
 		Vector2 vec_to_line = p_from - p_circle_pos;
 
 
-		/* create a quadratic formula of the form ax^2 + bx + c = 0 */
+		// Create a quadratic formula of the form ax^2 + bx + c = 0
 		real_t a, b, c;
 		real_t a, b, c;
 
 
 		a = line_vec.dot(line_vec);
 		a = line_vec.dot(line_vec);
 		b = 2 * vec_to_line.dot(line_vec);
 		b = 2 * vec_to_line.dot(line_vec);
 		c = vec_to_line.dot(vec_to_line) - p_circle_radius * p_circle_radius;
 		c = vec_to_line.dot(vec_to_line) - p_circle_radius * p_circle_radius;
 
 
-		/* solve for t */
+		// Solve for t.
 		real_t sqrtterm = b * b - 4 * a * c;
 		real_t sqrtterm = b * b - 4 * a * c;
 
 
-		/* if the term we intend to square root is less than 0 then the answer won't be real, so it definitely won't be t in the range 0 to 1 */
+		// If the term we intend to square root is less than 0 then the answer won't be real,
+		// so it definitely won't be t in the range 0 to 1.
 		if (sqrtterm < 0) return -1;
 		if (sqrtterm < 0) return -1;
 
 
-		/* if we can assume that the line segment starts outside the circle (e.g. for continuous time collision detection) then the following can be skipped and we can just return the equivalent of res1 */
+		// If we can assume that the line segment starts outside the circle (e.g. for continuous time collision detection)
+		// then the following can be skipped and we can just return the equivalent of res1.
 		sqrtterm = Math::sqrt(sqrtterm);
 		sqrtterm = Math::sqrt(sqrtterm);
 		real_t res1 = (-b - sqrtterm) / (2 * a);
 		real_t res1 = (-b - sqrtterm) / (2 * a);
 		real_t res2 = (-b + sqrtterm) / (2 * a);
 		real_t res2 = (-b + sqrtterm) / (2 * a);
@@ -722,7 +712,6 @@ public:
 		int outside_count = 0;
 		int outside_count = 0;
 
 
 		for (int a = 0; a < polygon.size(); a++) {
 		for (int a = 0; a < polygon.size(); a++) {
-			//real_t p_plane.d = (*this) * polygon[a];
 			real_t dist = p_plane.distance_to(polygon[a]);
 			real_t dist = p_plane.distance_to(polygon[a]);
 			if (dist < -CMP_POINT_IN_PLANE_EPSILON) {
 			if (dist < -CMP_POINT_IN_PLANE_EPSILON) {
 				location_cache[a] = LOC_INSIDE;
 				location_cache[a] = LOC_INSIDE;
@@ -739,11 +728,11 @@ public:
 
 
 		if (outside_count == 0) {
 		if (outside_count == 0) {
 
 
-			return polygon; // no changes
+			return polygon; // No changes.
 
 
 		} else if (inside_count == 0) {
 		} else if (inside_count == 0) {
 
 
-			return Vector<Vector3>(); //empty
+			return Vector<Vector3>(); // Empty.
 		}
 		}
 
 
 		long previous = polygon.size() - 1;
 		long previous = polygon.size() - 1;
@@ -838,22 +827,11 @@ public:
 
 
 	static Vector<Vector<Point2> > offset_polyline_2d(const Vector<Vector2> &p_polygon, real_t p_delta, PolyJoinType p_join_type, PolyEndType p_end_type) {
 	static Vector<Vector<Point2> > offset_polyline_2d(const Vector<Vector2> &p_polygon, real_t p_delta, PolyJoinType p_join_type, PolyEndType p_end_type) {
 
 
-		ERR_EXPLAIN("Attempt to offset a polyline like a polygon (use offset_polygon_2d instead).");
-		ERR_FAIL_COND_V(p_end_type == END_POLYGON, Vector<Vector<Point2> >());
+		ERR_FAIL_COND_V_MSG(p_end_type == END_POLYGON, Vector<Vector<Point2> >(), "Attempt to offset a polyline like a polygon (use offset_polygon_2d instead).");
 
 
 		return _polypath_offset(p_polygon, p_delta, p_join_type, p_end_type);
 		return _polypath_offset(p_polygon, p_delta, p_join_type, p_end_type);
 	}
 	}
 
 
-	static Vector<Point2> transform_points_2d(const Vector<Point2> &p_points, const Transform2D &p_mat) {
-
-		Vector<Point2> points;
-
-		for (int i = 0; i < p_points.size(); ++i) {
-			points.push_back(p_mat.xform(p_points[i]));
-		}
-		return points;
-	}
-
 	static Vector<int> triangulate_delaunay_2d(const Vector<Vector2> &p_points) {
 	static Vector<int> triangulate_delaunay_2d(const Vector<Vector2> &p_points) {
 
 
 		Vector<Delaunay2D::Triangle> tr = Delaunay2D::triangulate(p_points);
 		Vector<Delaunay2D::Triangle> tr = Delaunay2D::triangulate(p_points);
@@ -899,7 +877,7 @@ public:
 		return sum > 0.0f;
 		return sum > 0.0f;
 	}
 	}
 
 
-	/* alternate implementation that should be faster */
+	// Alternate implementation that should be faster.
 	static bool is_point_in_polygon(const Vector2 &p_point, const Vector<Vector2> &p_polygon) {
 	static bool is_point_in_polygon(const Vector2 &p_point, const Vector<Vector2> &p_polygon) {
 		int c = p_polygon.size();
 		int c = p_polygon.size();
 		if (c < 3)
 		if (c < 3)
@@ -915,7 +893,8 @@ public:
 			further_away_opposite.y = MIN(p[i].y, further_away_opposite.y);
 			further_away_opposite.y = MIN(p[i].y, further_away_opposite.y);
 		}
 		}
 
 
-		further_away += (further_away - further_away_opposite) * Vector2(1.221313, 1.512312); // make point outside that won't intersect with points in segment from p_point
+		// Make point outside that won't intersect with points in segment from p_point.
+		further_away += (further_away - further_away_opposite) * Vector2(1.221313, 1.512312);
 
 
 		int intersections = 0;
 		int intersections = 0;
 		for (int i = 0; i < c; i++) {
 		for (int i = 0; i < c; i++) {
@@ -931,7 +910,8 @@ public:
 
 
 	static PoolVector<PoolVector<Face3> > separate_objects(PoolVector<Face3> p_array);
 	static PoolVector<PoolVector<Face3> > separate_objects(PoolVector<Face3> p_array);
 
 
-	static PoolVector<Face3> wrap_geometry(PoolVector<Face3> p_array, real_t *p_error = NULL); ///< create a "wrap" that encloses the given geometry
+	// Create a "wrap" that encloses the given geometry.
+	static PoolVector<Face3> wrap_geometry(PoolVector<Face3> p_array, real_t *p_error = NULL);
 
 
 	struct MeshData {
 	struct MeshData {
 
 
@@ -1013,17 +993,17 @@ public:
 		Vector<Point2> H;
 		Vector<Point2> H;
 		H.resize(2 * n);
 		H.resize(2 * n);
 
 
-		// Sort points lexicographically
+		// Sort points lexicographically.
 		P.sort();
 		P.sort();
 
 
-		// Build lower hull
+		// Build lower hull.
 		for (int i = 0; i < n; ++i) {
 		for (int i = 0; i < n; ++i) {
 			while (k >= 2 && vec2_cross(H[k - 2], H[k - 1], P[i]) <= 0)
 			while (k >= 2 && vec2_cross(H[k - 2], H[k - 1], P[i]) <= 0)
 				k--;
 				k--;
 			H.write[k++] = P[i];
 			H.write[k++] = P[i];
 		}
 		}
 
 
-		// Build upper hull
+		// Build upper hull.
 		for (int i = n - 2, t = k + 1; i >= 0; i--) {
 		for (int i = n - 2, t = k + 1; i >= 0; i--) {
 			while (k >= t && vec2_cross(H[k - 2], H[k - 1], P[i]) <= 0)
 			while (k >= t && vec2_cross(H[k - 2], H[k - 1], P[i]) <= 0)
 				k--;
 				k--;

+ 9 - 0
core/math/math_funcs.cpp

@@ -79,6 +79,15 @@ int Math::step_decimals(double p_step) {
 	return 0;
 	return 0;
 }
 }
 
 
+// Only meant for editor usage in float ranges, where a step of 0
+// means that decimal digits should not be limited in String::num.
+int Math::range_step_decimals(double p_step) {
+	if (p_step < 0.0000000000001) {
+		return 16; // Max value hardcoded in String::num
+	}
+	return step_decimals(p_step);
+}
+
 double Math::dectime(double p_value, double p_amount, double p_step) {
 double Math::dectime(double p_value, double p_amount, double p_step) {
 	double sgn = p_value < 0 ? -1.0 : 1.0;
 	double sgn = p_value < 0 ? -1.0 : 1.0;
 	double val = Math::abs(p_value);
 	double val = Math::abs(p_value);

+ 17 - 6
core/math/math_funcs.h

@@ -255,21 +255,22 @@ public:
 	static _ALWAYS_INLINE_ float round(float p_val) { return (p_val >= 0) ? Math::floor(p_val + 0.5) : -Math::floor(-p_val + 0.5); }
 	static _ALWAYS_INLINE_ float round(float p_val) { return (p_val >= 0) ? Math::floor(p_val + 0.5) : -Math::floor(-p_val + 0.5); }
 
 
 	static _ALWAYS_INLINE_ int64_t wrapi(int64_t value, int64_t min, int64_t max) {
 	static _ALWAYS_INLINE_ int64_t wrapi(int64_t value, int64_t min, int64_t max) {
-		int64_t rng = max - min;
-		return (rng != 0) ? min + ((((value - min) % rng) + rng) % rng) : min;
+		int64_t range = max - min;
+		return range == 0 ? min : min + ((((value - min) % range) + range) % range);
 	}
 	}
 	static _ALWAYS_INLINE_ double wrapf(double value, double min, double max) {
 	static _ALWAYS_INLINE_ double wrapf(double value, double min, double max) {
-		double rng = max - min;
-		return (!is_equal_approx(rng, 0.0)) ? value - (rng * Math::floor((value - min) / rng)) : min;
+		double range = max - min;
+		return is_zero_approx(range) ? min : value - (range * Math::floor((value - min) / range));
 	}
 	}
 	static _ALWAYS_INLINE_ float wrapf(float value, float min, float max) {
 	static _ALWAYS_INLINE_ float wrapf(float value, float min, float max) {
-		float rng = max - min;
-		return (!is_equal_approx(rng, 0.0f)) ? value - (rng * Math::floor((value - min) / rng)) : min;
+		float range = max - min;
+		return is_zero_approx(range) ? min : value - (range * Math::floor((value - min) / range));
 	}
 	}
 
 
 	// double only, as these functions are mainly used by the editor and not performance-critical,
 	// double only, as these functions are mainly used by the editor and not performance-critical,
 	static double ease(double p_x, double p_c);
 	static double ease(double p_x, double p_c);
 	static int step_decimals(double p_step);
 	static int step_decimals(double p_step);
+	static int range_step_decimals(double p_step);
 	static double stepify(double p_value, double p_step);
 	static double stepify(double p_value, double p_step);
 	static double dectime(double p_value, double p_amount, double p_step);
 	static double dectime(double p_value, double p_amount, double p_step);
 
 
@@ -299,6 +300,11 @@ public:
 	}
 	}
 
 
 	static _ALWAYS_INLINE_ bool is_equal_approx(real_t a, real_t b) {
 	static _ALWAYS_INLINE_ bool is_equal_approx(real_t a, real_t b) {
+		// Check for exact equality first, required to handle "infinity" values.
+		if (a == b) {
+			return true;
+		}
+		// Then check for approximate equality.
 		real_t tolerance = CMP_EPSILON * abs(a);
 		real_t tolerance = CMP_EPSILON * abs(a);
 		if (tolerance < CMP_EPSILON) {
 		if (tolerance < CMP_EPSILON) {
 			tolerance = CMP_EPSILON;
 			tolerance = CMP_EPSILON;
@@ -307,6 +313,11 @@ public:
 	}
 	}
 
 
 	static _ALWAYS_INLINE_ bool is_equal_approx(real_t a, real_t b, real_t tolerance) {
 	static _ALWAYS_INLINE_ bool is_equal_approx(real_t a, real_t b, real_t tolerance) {
+		// Check for exact equality first, required to handle "infinity" values.
+		if (a == b) {
+			return true;
+		}
+		// Then check for approximate equality.
 		return abs(a - b) < tolerance;
 		return abs(a - b) < tolerance;
 	}
 	}
 
 

+ 1 - 8
core/math/octree.h

@@ -38,10 +38,6 @@
 #include "core/print_string.h"
 #include "core/print_string.h"
 #include "core/variant.h"
 #include "core/variant.h"
 
 
-/**
-	@author Juan Linietsky <[email protected]>
-*/
-
 typedef uint32_t OctreeElementID;
 typedef uint32_t OctreeElementID;
 
 
 #define OCTREE_ELEMENT_INVALID_ID 0
 #define OCTREE_ELEMENT_INVALID_ID 0
@@ -568,10 +564,7 @@ void Octree<T, use_pairs, AL>::_ensure_valid_root(const AABB &p_aabb) {
 
 
 		while (!base.encloses(p_aabb)) {
 		while (!base.encloses(p_aabb)) {
 
 
-			if (base.size.x > OCTREE_SIZE_LIMIT) {
-				ERR_EXPLAIN("Octree upper size limit reeached, does the AABB supplied contain NAN?");
-				ERR_FAIL();
-			}
+			ERR_FAIL_COND_MSG(base.size.x > OCTREE_SIZE_LIMIT, "Octree upper size limit reached, does the AABB supplied contain NAN?");
 
 
 			Octant *gp = memnew_allocator(Octant, AL);
 			Octant *gp = memnew_allocator(Octant, AL);
 			octant_count++;
 			octant_count++;

+ 0 - 4
core/math/quat.h

@@ -38,10 +38,6 @@
 #include "core/math/math_funcs.h"
 #include "core/math/math_funcs.h"
 #include "core/ustring.h"
 #include "core/ustring.h"
 
 
-/**
-	@author Juan Linietsky <[email protected]>
-*/
-
 class Quat {
 class Quat {
 public:
 public:
 	real_t x, y, z, w;
 	real_t x, y, z, w;

+ 5 - 0
core/math/transform.cpp

@@ -213,3 +213,8 @@ Transform::Transform(const Basis &p_basis, const Vector3 &p_origin) :
 		basis(p_basis),
 		basis(p_basis),
 		origin(p_origin) {
 		origin(p_origin) {
 }
 }
+
+Transform::Transform(real_t xx, real_t xy, real_t xz, real_t yx, real_t yy, real_t yz, real_t zx, real_t zy, real_t zz, real_t ox, real_t oy, real_t oz) {
+	basis = Basis(xx, xy, xz, yx, yy, yz, zx, zy, zz);
+	origin = Vector3(ox, oy, oz);
+}

+ 56 - 20
core/math/transform.h

@@ -34,10 +34,7 @@
 #include "core/math/aabb.h"
 #include "core/math/aabb.h"
 #include "core/math/basis.h"
 #include "core/math/basis.h"
 #include "core/math/plane.h"
 #include "core/math/plane.h"
-
-/**
-	@author Juan Linietsky <[email protected]>
-*/
+#include "core/pool_vector.h"
 
 
 class Transform {
 class Transform {
 public:
 public:
@@ -86,6 +83,9 @@ public:
 	_FORCE_INLINE_ AABB xform(const AABB &p_aabb) const;
 	_FORCE_INLINE_ AABB xform(const AABB &p_aabb) const;
 	_FORCE_INLINE_ AABB xform_inv(const AABB &p_aabb) const;
 	_FORCE_INLINE_ AABB xform_inv(const AABB &p_aabb) const;
 
 
+	_FORCE_INLINE_ PoolVector<Vector3> xform(const PoolVector<Vector3> &p_array) const;
+	_FORCE_INLINE_ PoolVector<Vector3> xform_inv(const PoolVector<Vector3> &p_array) const;
+
 	void operator*=(const Transform &p_transform);
 	void operator*=(const Transform &p_transform);
 	Transform operator*(const Transform &p_transform) const;
 	Transform operator*(const Transform &p_transform) const;
 
 
@@ -108,6 +108,7 @@ public:
 
 
 	operator String() const;
 	operator String() const;
 
 
+	Transform(real_t xx, real_t xy, real_t xz, real_t yx, real_t yy, real_t yz, real_t zx, real_t zy, real_t zz, real_t ox, real_t oy, real_t oz);
 	Transform(const Basis &p_basis, const Vector3 &p_origin = Vector3());
 	Transform(const Basis &p_basis, const Vector3 &p_origin = Vector3());
 	Transform() {}
 	Transform() {}
 };
 };
@@ -157,22 +158,29 @@ _FORCE_INLINE_ Plane Transform::xform_inv(const Plane &p_plane) const {
 }
 }
 
 
 _FORCE_INLINE_ AABB Transform::xform(const AABB &p_aabb) const {
 _FORCE_INLINE_ AABB Transform::xform(const AABB &p_aabb) const {
-	/* define vertices */
-	Vector3 x = basis.get_axis(0) * p_aabb.size.x;
-	Vector3 y = basis.get_axis(1) * p_aabb.size.y;
-	Vector3 z = basis.get_axis(2) * p_aabb.size.z;
-	Vector3 pos = xform(p_aabb.position);
-	//could be even further optimized
-	AABB new_aabb;
-	new_aabb.position = pos;
-	new_aabb.expand_to(pos + x);
-	new_aabb.expand_to(pos + y);
-	new_aabb.expand_to(pos + z);
-	new_aabb.expand_to(pos + x + y);
-	new_aabb.expand_to(pos + x + z);
-	new_aabb.expand_to(pos + y + z);
-	new_aabb.expand_to(pos + x + y + z);
-	return new_aabb;
+
+	/* http://dev.theomader.com/transform-bounding-boxes/ */
+	Vector3 min = p_aabb.position;
+	Vector3 max = p_aabb.position + p_aabb.size;
+	Vector3 tmin, tmax;
+	for (int i = 0; i < 3; i++) {
+		tmin[i] = tmax[i] = origin[i];
+		for (int j = 0; j < 3; j++) {
+			real_t e = basis[i][j] * min[j];
+			real_t f = basis[i][j] * max[j];
+			if (e < f) {
+				tmin[i] += e;
+				tmax[i] += f;
+			} else {
+				tmin[i] += f;
+				tmax[i] += e;
+			}
+		}
+	}
+	AABB r_aabb;
+	r_aabb.position = tmin;
+	r_aabb.size = tmax - tmin;
+	return r_aabb;
 }
 }
 
 
 _FORCE_INLINE_ AABB Transform::xform_inv(const AABB &p_aabb) const {
 _FORCE_INLINE_ AABB Transform::xform_inv(const AABB &p_aabb) const {
@@ -201,4 +209,32 @@ _FORCE_INLINE_ AABB Transform::xform_inv(const AABB &p_aabb) const {
 	return ret;
 	return ret;
 }
 }
 
 
+PoolVector<Vector3> Transform::xform(const PoolVector<Vector3> &p_array) const {
+
+	PoolVector<Vector3> array;
+	array.resize(p_array.size());
+
+	PoolVector<Vector3>::Read r = p_array.read();
+	PoolVector<Vector3>::Write w = array.write();
+
+	for (int i = 0; i < p_array.size(); ++i) {
+		w[i] = xform(r[i]);
+	}
+	return array;
+}
+
+PoolVector<Vector3> Transform::xform_inv(const PoolVector<Vector3> &p_array) const {
+
+	PoolVector<Vector3> array;
+	array.resize(p_array.size());
+
+	PoolVector<Vector3>::Read r = p_array.read();
+	PoolVector<Vector3>::Write w = array.write();
+
+	for (int i = 0; i < p_array.size(); ++i) {
+		w[i] = xform_inv(r[i]);
+	}
+	return array;
+}
+
 #endif // TRANSFORM_H
 #endif // TRANSFORM_H

+ 31 - 0
core/math/transform_2d.h

@@ -32,6 +32,7 @@
 #define TRANSFORM_2D_H
 #define TRANSFORM_2D_H
 
 
 #include "core/math/rect2.h" // also includes vector2, math_funcs, and ustring
 #include "core/math/rect2.h" // also includes vector2, math_funcs, and ustring
+#include "core/pool_vector.h"
 
 
 struct Transform2D {
 struct Transform2D {
 	// Warning #1: basis of Transform2D is stored differently from Basis. In terms of elements array, the basis matrix looks like "on paper":
 	// Warning #1: basis of Transform2D is stored differently from Basis. In terms of elements array, the basis matrix looks like "on paper":
@@ -110,6 +111,8 @@ struct Transform2D {
 	_FORCE_INLINE_ Vector2 xform_inv(const Vector2 &p_vec) const;
 	_FORCE_INLINE_ Vector2 xform_inv(const Vector2 &p_vec) const;
 	_FORCE_INLINE_ Rect2 xform(const Rect2 &p_rect) const;
 	_FORCE_INLINE_ Rect2 xform(const Rect2 &p_rect) const;
 	_FORCE_INLINE_ Rect2 xform_inv(const Rect2 &p_rect) const;
 	_FORCE_INLINE_ Rect2 xform_inv(const Rect2 &p_rect) const;
+	_FORCE_INLINE_ PoolVector<Vector2> xform(const PoolVector<Vector2> &p_array) const;
+	_FORCE_INLINE_ PoolVector<Vector2> xform_inv(const PoolVector<Vector2> &p_array) const;
 
 
 	operator String() const;
 	operator String() const;
 
 
@@ -199,4 +202,32 @@ Rect2 Transform2D::xform_inv(const Rect2 &p_rect) const {
 	return new_rect;
 	return new_rect;
 }
 }
 
 
+PoolVector<Vector2> Transform2D::xform(const PoolVector<Vector2> &p_array) const {
+
+	PoolVector<Vector2> array;
+	array.resize(p_array.size());
+
+	PoolVector<Vector2>::Read r = p_array.read();
+	PoolVector<Vector2>::Write w = array.write();
+
+	for (int i = 0; i < p_array.size(); ++i) {
+		w[i] = xform(r[i]);
+	}
+	return array;
+}
+
+PoolVector<Vector2> Transform2D::xform_inv(const PoolVector<Vector2> &p_array) const {
+
+	PoolVector<Vector2> array;
+	array.resize(p_array.size());
+
+	PoolVector<Vector2>::Read r = p_array.read();
+	PoolVector<Vector2>::Write w = array.write();
+
+	for (int i = 0; i < p_array.size(); ++i) {
+		w[i] = xform_inv(r[i]);
+	}
+	return array;
+}
+
 #endif // TRANSFORM_2D_H
 #endif // TRANSFORM_2D_H

+ 13 - 0
core/math/vector2.cpp

@@ -98,6 +98,11 @@ real_t Vector2::cross(const Vector2 &p_other) const {
 	return x * p_other.y - y * p_other.x;
 	return x * p_other.y - y * p_other.x;
 }
 }
 
 
+Vector2 Vector2::sign() const {
+
+	return Vector2(SGN(x), SGN(y));
+}
+
 Vector2 Vector2::floor() const {
 Vector2 Vector2::floor() const {
 
 
 	return Vector2(Math::floor(x), Math::floor(y));
 	return Vector2(Math::floor(x), Math::floor(y));
@@ -121,6 +126,14 @@ Vector2 Vector2::rotated(real_t p_by) const {
 	return v;
 	return v;
 }
 }
 
 
+Vector2 Vector2::posmod(const real_t p_mod) const {
+	return Vector2(Math::fposmod(x, p_mod), Math::fposmod(y, p_mod));
+}
+
+Vector2 Vector2::posmodv(const Vector2 &p_modv) const {
+	return Vector2(Math::fposmod(x, p_modv.x), Math::fposmod(y, p_modv.y));
+}
+
 Vector2 Vector2::project(const Vector2 &p_b) const {
 Vector2 Vector2::project(const Vector2 &p_b) const {
 	return p_b * (dot(p_b) / p_b.length_squared());
 	return p_b * (dot(p_b) / p_b.length_squared());
 }
 }

+ 18 - 6
core/math/vector2.h

@@ -38,6 +38,11 @@ struct Vector2i;
 
 
 struct Vector2 {
 struct Vector2 {
 
 
+	enum Axis {
+		AXIS_X,
+		AXIS_Y,
+	};
+
 	union {
 	union {
 		real_t x;
 		real_t x;
 		real_t width;
 		real_t width;
@@ -69,6 +74,8 @@ struct Vector2 {
 
 
 	real_t dot(const Vector2 &p_other) const;
 	real_t dot(const Vector2 &p_other) const;
 	real_t cross(const Vector2 &p_other) const;
 	real_t cross(const Vector2 &p_other) const;
+	Vector2 posmod(const real_t p_mod) const;
+	Vector2 posmodv(const Vector2 &p_modv) const;
 	Vector2 project(const Vector2 &p_b) const;
 	Vector2 project(const Vector2 &p_b) const;
 
 
 	Vector2 plane_project(real_t p_d, const Vector2 &p_vec) const;
 	Vector2 plane_project(real_t p_d, const Vector2 &p_vec) const;
@@ -107,8 +114,10 @@ struct Vector2 {
 	bool operator==(const Vector2 &p_vec2) const;
 	bool operator==(const Vector2 &p_vec2) const;
 	bool operator!=(const Vector2 &p_vec2) const;
 	bool operator!=(const Vector2 &p_vec2) const;
 
 
-	bool operator<(const Vector2 &p_vec2) const { return (Math::is_equal_approx(x, p_vec2.x)) ? (y < p_vec2.y) : (x < p_vec2.x); }
-	bool operator<=(const Vector2 &p_vec2) const { return (Math::is_equal_approx(x, p_vec2.x)) ? (y <= p_vec2.y) : (x < p_vec2.x); }
+	bool operator<(const Vector2 &p_vec2) const { return Math::is_equal_approx(x, p_vec2.x) ? (y < p_vec2.y) : (x < p_vec2.x); }
+	bool operator>(const Vector2 &p_vec2) const { return Math::is_equal_approx(x, p_vec2.x) ? (y > p_vec2.y) : (x > p_vec2.x); }
+	bool operator<=(const Vector2 &p_vec2) const { return Math::is_equal_approx(x, p_vec2.x) ? (y <= p_vec2.y) : (x < p_vec2.x); }
+	bool operator>=(const Vector2 &p_vec2) const { return Math::is_equal_approx(x, p_vec2.x) ? (y >= p_vec2.y) : (x > p_vec2.x); }
 
 
 	real_t angle() const;
 	real_t angle() const;
 
 
@@ -129,6 +138,7 @@ struct Vector2 {
 		return Vector2(y, -x);
 		return Vector2(y, -x);
 	}
 	}
 
 
+	Vector2 sign() const;
 	Vector2 floor() const;
 	Vector2 floor() const;
 	Vector2 ceil() const;
 	Vector2 ceil() const;
 	Vector2 round() const;
 	Vector2 round() const;
@@ -141,10 +151,7 @@ struct Vector2 {
 		x = p_x;
 		x = p_x;
 		y = p_y;
 		y = p_y;
 	}
 	}
-	_FORCE_INLINE_ Vector2() {
-		x = 0;
-		y = 0;
-	}
+	_FORCE_INLINE_ Vector2() { x = y = 0; }
 };
 };
 
 
 _FORCE_INLINE_ Vector2 Vector2::plane_project(real_t p_d, const Vector2 &p_vec) const {
 _FORCE_INLINE_ Vector2 Vector2::plane_project(real_t p_d, const Vector2 &p_vec) const {
@@ -262,6 +269,11 @@ typedef Vector2 Point2;
 
 
 struct Vector2i {
 struct Vector2i {
 
 
+	enum Axis {
+		AXIS_X,
+		AXIS_Y,
+	};
+
 	union {
 	union {
 		int x;
 		int x;
 		int width;
 		int width;

+ 37 - 3
core/math/vector3.h

@@ -31,9 +31,7 @@
 #ifndef VECTOR3_H
 #ifndef VECTOR3_H
 #define VECTOR3_H
 #define VECTOR3_H
 
 
-#include "core/math/math_defs.h"
 #include "core/math/math_funcs.h"
 #include "core/math/math_funcs.h"
-#include "core/typedefs.h"
 #include "core/ustring.h"
 #include "core/ustring.h"
 
 
 class Basis;
 class Basis;
@@ -110,6 +108,8 @@ struct Vector3 {
 	_FORCE_INLINE_ real_t distance_to(const Vector3 &p_b) const;
 	_FORCE_INLINE_ real_t distance_to(const Vector3 &p_b) const;
 	_FORCE_INLINE_ real_t distance_squared_to(const Vector3 &p_b) const;
 	_FORCE_INLINE_ real_t distance_squared_to(const Vector3 &p_b) const;
 
 
+	_FORCE_INLINE_ Vector3 posmod(const real_t p_mod) const;
+	_FORCE_INLINE_ Vector3 posmodv(const Vector3 &p_modv) const;
 	_FORCE_INLINE_ Vector3 project(const Vector3 &p_b) const;
 	_FORCE_INLINE_ Vector3 project(const Vector3 &p_b) const;
 
 
 	_FORCE_INLINE_ real_t angle_to(const Vector3 &p_b) const;
 	_FORCE_INLINE_ real_t angle_to(const Vector3 &p_b) const;
@@ -141,15 +141,17 @@ struct Vector3 {
 	_FORCE_INLINE_ bool operator!=(const Vector3 &p_v) const;
 	_FORCE_INLINE_ bool operator!=(const Vector3 &p_v) const;
 	_FORCE_INLINE_ bool operator<(const Vector3 &p_v) const;
 	_FORCE_INLINE_ bool operator<(const Vector3 &p_v) const;
 	_FORCE_INLINE_ bool operator<=(const Vector3 &p_v) const;
 	_FORCE_INLINE_ bool operator<=(const Vector3 &p_v) const;
+	_FORCE_INLINE_ bool operator>(const Vector3 &p_v) const;
+	_FORCE_INLINE_ bool operator>=(const Vector3 &p_v) const;
 
 
 	operator String() const;
 	operator String() const;
 
 
-	_FORCE_INLINE_ Vector3() { x = y = z = 0; }
 	_FORCE_INLINE_ Vector3(real_t p_x, real_t p_y, real_t p_z) {
 	_FORCE_INLINE_ Vector3(real_t p_x, real_t p_y, real_t p_z) {
 		x = p_x;
 		x = p_x;
 		y = p_y;
 		y = p_y;
 		z = p_z;
 		z = p_z;
 	}
 	}
+	_FORCE_INLINE_ Vector3() { x = y = z = 0; }
 };
 };
 
 
 // Should be included after class definition, otherwise we get circular refs
 // Should be included after class definition, otherwise we get circular refs
@@ -233,6 +235,14 @@ real_t Vector3::distance_squared_to(const Vector3 &p_b) const {
 	return (p_b - *this).length_squared();
 	return (p_b - *this).length_squared();
 }
 }
 
 
+Vector3 Vector3::posmod(const real_t p_mod) const {
+	return Vector3(Math::fposmod(x, p_mod), Math::fposmod(y, p_mod), Math::fposmod(z, p_mod));
+}
+
+Vector3 Vector3::posmodv(const Vector3 &p_modv) const {
+	return Vector3(Math::fposmod(x, p_modv.x), Math::fposmod(y, p_modv.y), Math::fposmod(z, p_modv.z));
+}
+
 Vector3 Vector3::project(const Vector3 &p_b) const {
 Vector3 Vector3::project(const Vector3 &p_b) const {
 	return p_b * (dot(p_b) / p_b.length_squared());
 	return p_b * (dot(p_b) / p_b.length_squared());
 }
 }
@@ -357,6 +367,18 @@ bool Vector3::operator<(const Vector3 &p_v) const {
 	}
 	}
 }
 }
 
 
+bool Vector3::operator>(const Vector3 &p_v) const {
+
+	if (Math::is_equal_approx(x, p_v.x)) {
+		if (Math::is_equal_approx(y, p_v.y))
+			return z > p_v.z;
+		else
+			return y > p_v.y;
+	} else {
+		return x > p_v.x;
+	}
+}
+
 bool Vector3::operator<=(const Vector3 &p_v) const {
 bool Vector3::operator<=(const Vector3 &p_v) const {
 
 
 	if (Math::is_equal_approx(x, p_v.x)) {
 	if (Math::is_equal_approx(x, p_v.x)) {
@@ -369,6 +391,18 @@ bool Vector3::operator<=(const Vector3 &p_v) const {
 	}
 	}
 }
 }
 
 
+bool Vector3::operator>=(const Vector3 &p_v) const {
+
+	if (Math::is_equal_approx(x, p_v.x)) {
+		if (Math::is_equal_approx(y, p_v.y))
+			return z >= p_v.z;
+		else
+			return y > p_v.y;
+	} else {
+		return x > p_v.x;
+	}
+}
+
 _FORCE_INLINE_ Vector3 vec3_cross(const Vector3 &p_a, const Vector3 &p_b) {
 _FORCE_INLINE_ Vector3 vec3_cross(const Vector3 &p_a, const Vector3 &p_b) {
 
 
 	return p_a.cross(p_b);
 	return p_a.cross(p_b);

+ 4 - 7
core/message_queue.cpp

@@ -52,8 +52,7 @@ Error MessageQueue::push_call(ObjectID p_id, const StringName &p_method, const V
 			type = ObjectDB::get_instance(p_id)->get_class();
 			type = ObjectDB::get_instance(p_id)->get_class();
 		print_line("Failed method: " + type + ":" + p_method + " target ID: " + itos(p_id));
 		print_line("Failed method: " + type + ":" + p_method + " target ID: " + itos(p_id));
 		statistics();
 		statistics();
-		ERR_EXPLAIN("Message queue out of memory. Try increasing 'message_queue_size_kb' in project settings.");
-		ERR_FAIL_V(ERR_OUT_OF_MEMORY);
+		ERR_FAIL_V_MSG(ERR_OUT_OF_MEMORY, "Message queue out of memory. Try increasing 'message_queue_size_kb' in project settings.");
 	}
 	}
 
 
 	Message *msg = memnew_placement(&buffer[buffer_end], Message);
 	Message *msg = memnew_placement(&buffer[buffer_end], Message);
@@ -103,8 +102,7 @@ Error MessageQueue::push_set(ObjectID p_id, const StringName &p_prop, const Vari
 			type = ObjectDB::get_instance(p_id)->get_class();
 			type = ObjectDB::get_instance(p_id)->get_class();
 		print_line("Failed set: " + type + ":" + p_prop + " target ID: " + itos(p_id));
 		print_line("Failed set: " + type + ":" + p_prop + " target ID: " + itos(p_id));
 		statistics();
 		statistics();
-		ERR_EXPLAIN("Message queue out of memory. Try increasing 'message_queue_size_kb' in project settings.");
-		ERR_FAIL_V(ERR_OUT_OF_MEMORY);
+		ERR_FAIL_V_MSG(ERR_OUT_OF_MEMORY, "Message queue out of memory. Try increasing 'message_queue_size_kb' in project settings.");
 	}
 	}
 
 
 	Message *msg = memnew_placement(&buffer[buffer_end], Message);
 	Message *msg = memnew_placement(&buffer[buffer_end], Message);
@@ -136,8 +134,7 @@ Error MessageQueue::push_notification(ObjectID p_id, int p_notification) {
 			type = ObjectDB::get_instance(p_id)->get_class();
 			type = ObjectDB::get_instance(p_id)->get_class();
 		print_line("Failed notification: " + itos(p_notification) + " target ID: " + itos(p_id));
 		print_line("Failed notification: " + itos(p_notification) + " target ID: " + itos(p_id));
 		statistics();
 		statistics();
-		ERR_EXPLAIN("Message queue out of memory. Try increasing 'message_queue_size_kb' in project settings.");
-		ERR_FAIL_V(ERR_OUT_OF_MEMORY);
+		ERR_FAIL_V_MSG(ERR_OUT_OF_MEMORY, "Message queue out of memory. Try increasing 'message_queue_size_kb' in project settings.");
 	}
 	}
 
 
 	Message *msg = memnew_placement(&buffer[buffer_end], Message);
 	Message *msg = memnew_placement(&buffer[buffer_end], Message);
@@ -256,7 +253,7 @@ void MessageQueue::_call_function(Object *p_target, const StringName &p_func, co
 	p_target->call(p_func, argptrs, p_argcount, ce);
 	p_target->call(p_func, argptrs, p_argcount, ce);
 	if (p_show_error && ce.error != Variant::CallError::CALL_OK) {
 	if (p_show_error && ce.error != Variant::CallError::CALL_OK) {
 
 
-		ERR_PRINTS("Error calling deferred method: " + Variant::get_call_error_text(p_target, p_func, argptrs, p_argcount, ce));
+		ERR_PRINTS("Error calling deferred method: " + Variant::get_call_error_text(p_target, p_func, argptrs, p_argcount, ce) + ".");
 	}
 	}
 }
 }
 
 

+ 0 - 4
core/method_bind.h

@@ -38,10 +38,6 @@
 
 
 #include <stdio.h>
 #include <stdio.h>
 
 
-/**
-	@author Juan Linietsky <[email protected]>
-*/
-
 #ifdef DEBUG_ENABLED
 #ifdef DEBUG_ENABLED
 #define DEBUG_METHODS_ENABLED
 #define DEBUG_METHODS_ENABLED
 #endif
 #endif

+ 1 - 2
core/node_path.cpp

@@ -375,8 +375,7 @@ NodePath::NodePath(const String &p_path) {
 				if (str == "") {
 				if (str == "") {
 					if (path[i] == 0) continue; // Allow end-of-path :
 					if (path[i] == 0) continue; // Allow end-of-path :
 
 
-					ERR_EXPLAIN("Invalid NodePath: " + p_path);
-					ERR_FAIL();
+					ERR_FAIL_MSG("Invalid NodePath: " + p_path + ".");
 				}
 				}
 				subpath.push_back(str);
 				subpath.push_back(str);
 
 

+ 0 - 4
core/node_path.h

@@ -34,10 +34,6 @@
 #include "core/string_name.h"
 #include "core/string_name.h"
 #include "core/ustring.h"
 #include "core/ustring.h"
 
 
-/**
-	@author Juan Linietsky <[email protected]>
-*/
-
 class NodePath {
 class NodePath {
 
 
 	struct Data {
 	struct Data {

+ 53 - 13
core/oa_hash_map.h

@@ -62,7 +62,7 @@ private:
 	static const uint32_t EMPTY_HASH = 0;
 	static const uint32_t EMPTY_HASH = 0;
 	static const uint32_t DELETED_HASH_BIT = 1 << 31;
 	static const uint32_t DELETED_HASH_BIT = 1 << 31;
 
 
-	_FORCE_INLINE_ uint32_t _hash(const TKey &p_key) {
+	_FORCE_INLINE_ uint32_t _hash(const TKey &p_key) const {
 		uint32_t hash = Hasher::hash(p_key);
 		uint32_t hash = Hasher::hash(p_key);
 
 
 		if (hash == EMPTY_HASH) {
 		if (hash == EMPTY_HASH) {
@@ -74,12 +74,11 @@ private:
 		return hash;
 		return hash;
 	}
 	}
 
 
-	_FORCE_INLINE_ uint32_t _get_probe_length(uint32_t p_pos, uint32_t p_hash) {
+	_FORCE_INLINE_ uint32_t _get_probe_length(uint32_t p_pos, uint32_t p_hash) const {
 		p_hash = p_hash & ~DELETED_HASH_BIT; // we don't care if it was deleted or not
 		p_hash = p_hash & ~DELETED_HASH_BIT; // we don't care if it was deleted or not
 
 
 		uint32_t original_pos = p_hash % capacity;
 		uint32_t original_pos = p_hash % capacity;
-
-		return p_pos - original_pos;
+		return (p_pos - original_pos) % capacity;
 	}
 	}
 
 
 	_FORCE_INLINE_ void _construct(uint32_t p_pos, uint32_t p_hash, const TKey &p_key, const TValue &p_value) {
 	_FORCE_INLINE_ void _construct(uint32_t p_pos, uint32_t p_hash, const TKey &p_key, const TValue &p_value) {
@@ -90,7 +89,7 @@ private:
 		num_elements++;
 		num_elements++;
 	}
 	}
 
 
-	bool _lookup_pos(const TKey &p_key, uint32_t &r_pos) {
+	bool _lookup_pos(const TKey &p_key, uint32_t &r_pos) const {
 		uint32_t hash = _hash(p_key);
 		uint32_t hash = _hash(p_key);
 		uint32_t pos = hash % capacity;
 		uint32_t pos = hash % capacity;
 		uint32_t distance = 0;
 		uint32_t distance = 0;
@@ -151,17 +150,17 @@ private:
 			distance++;
 			distance++;
 		}
 		}
 	}
 	}
-	void _resize_and_rehash() {
+
+	void _resize_and_rehash(uint32_t p_new_capacity) {
+
+		uint32_t old_capacity = capacity;
+		capacity = p_new_capacity;
 
 
 		TKey *old_keys = keys;
 		TKey *old_keys = keys;
 		TValue *old_values = values;
 		TValue *old_values = values;
 		uint32_t *old_hashes = hashes;
 		uint32_t *old_hashes = hashes;
 
 
-		uint32_t old_capacity = capacity;
-
-		capacity = old_capacity * 2;
 		num_elements = 0;
 		num_elements = 0;
-
 		keys = memnew_arr(TKey, capacity);
 		keys = memnew_arr(TKey, capacity);
 		values = memnew_arr(TValue, capacity);
 		values = memnew_arr(TValue, capacity);
 		hashes = memnew_arr(uint32_t, capacity);
 		hashes = memnew_arr(uint32_t, capacity);
@@ -186,10 +185,38 @@ private:
 		memdelete_arr(old_hashes);
 		memdelete_arr(old_hashes);
 	}
 	}
 
 
+	void _resize_and_rehash() {
+		_resize_and_rehash(capacity * 2);
+	}
+
 public:
 public:
 	_FORCE_INLINE_ uint32_t get_capacity() const { return capacity; }
 	_FORCE_INLINE_ uint32_t get_capacity() const { return capacity; }
 	_FORCE_INLINE_ uint32_t get_num_elements() const { return num_elements; }
 	_FORCE_INLINE_ uint32_t get_num_elements() const { return num_elements; }
 
 
+	bool empty() const {
+		return num_elements == 0;
+	}
+
+	void clear() {
+
+		for (uint32_t i = 0; i < capacity; i++) {
+
+			if (hashes[i] == EMPTY_HASH) {
+				continue;
+			}
+
+			if (hashes[i] & DELETED_HASH_BIT) {
+				continue;
+			}
+
+			hashes[i] = EMPTY_HASH;
+			values[i].~TValue();
+			keys[i].~TKey();
+		}
+
+		num_elements = 0;
+	}
+
 	void insert(const TKey &p_key, const TValue &p_value) {
 	void insert(const TKey &p_key, const TValue &p_value) {
 
 
 		if ((float)num_elements / (float)capacity > 0.9) {
 		if ((float)num_elements / (float)capacity > 0.9) {
@@ -219,7 +246,7 @@ public:
 	 * if r_data is not NULL then the value will be written to the object
 	 * if r_data is not NULL then the value will be written to the object
 	 * it points to.
 	 * it points to.
 	 */
 	 */
-	bool lookup(const TKey &p_key, TValue &r_data) {
+	bool lookup(const TKey &p_key, TValue &r_data) const {
 		uint32_t pos = 0;
 		uint32_t pos = 0;
 		bool exists = _lookup_pos(p_key, pos);
 		bool exists = _lookup_pos(p_key, pos);
 
 
@@ -232,7 +259,7 @@ public:
 		return false;
 		return false;
 	}
 	}
 
 
-	_FORCE_INLINE_ bool has(const TKey &p_key) {
+	_FORCE_INLINE_ bool has(const TKey &p_key) const {
 		uint32_t _pos = 0;
 		uint32_t _pos = 0;
 		return _lookup_pos(p_key, _pos);
 		return _lookup_pos(p_key, _pos);
 	}
 	}
@@ -251,6 +278,16 @@ public:
 		num_elements--;
 		num_elements--;
 	}
 	}
 
 
+	/**
+	 * reserves space for a number of elements, useful to avoid many resizes and rehashes
+	 *  if adding a known (possibly large) number of elements at once, must be larger than old
+	 *  capacity.
+	 **/
+	void reserve(uint32_t p_new_capacity) {
+		ERR_FAIL_COND(p_new_capacity < capacity);
+		_resize_and_rehash(p_new_capacity);
+	}
+
 	struct Iterator {
 	struct Iterator {
 		bool valid;
 		bool valid;
 
 
@@ -302,6 +339,9 @@ public:
 		return it;
 		return it;
 	}
 	}
 
 
+	OAHashMap(const OAHashMap &) = delete; // Delete the copy constructor so we don't get unexpected copies and dangling pointers.
+	OAHashMap &operator=(const OAHashMap &) = delete; // Same for assignment operator.
+
 	OAHashMap(uint32_t p_initial_capacity = 64) {
 	OAHashMap(uint32_t p_initial_capacity = 64) {
 
 
 		capacity = p_initial_capacity;
 		capacity = p_initial_capacity;
@@ -312,7 +352,7 @@ public:
 		hashes = memnew_arr(uint32_t, p_initial_capacity);
 		hashes = memnew_arr(uint32_t, p_initial_capacity);
 
 
 		for (uint32_t i = 0; i < p_initial_capacity; i++) {
 		for (uint32_t i = 0; i < p_initial_capacity; i++) {
-			hashes[i] = 0;
+			hashes[i] = EMPTY_HASH;
 		}
 		}
 	}
 	}
 
 

+ 28 - 60
core/object.cpp

@@ -635,12 +635,9 @@ void Object::get_property_list(List<PropertyInfo> *p_list, bool p_reversed) cons
 #endif
 #endif
 		p_list->push_back(PropertyInfo(Variant::OBJECT, "script", PROPERTY_HINT_RESOURCE_TYPE, "Script", PROPERTY_USAGE_DEFAULT));
 		p_list->push_back(PropertyInfo(Variant::OBJECT, "script", PROPERTY_HINT_RESOURCE_TYPE, "Script", PROPERTY_USAGE_DEFAULT));
 	}
 	}
-
-#ifdef TOOLS_ENABLED
-	p_list->push_back(PropertyInfo(Variant::NIL, "Metadata", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP));
-#endif
-	p_list->push_back(PropertyInfo(Variant::DICTIONARY, "__meta__", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT));
-
+	if (!metadata.empty()) {
+		p_list->push_back(PropertyInfo(Variant::DICTIONARY, "__meta__", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL));
+	}
 	if (script_instance && !p_reversed) {
 	if (script_instance && !p_reversed) {
 		p_list->push_back(PropertyInfo(Variant::NIL, "Script Variables", PROPERTY_HINT_NONE, String(), PROPERTY_USAGE_CATEGORY));
 		p_list->push_back(PropertyInfo(Variant::NIL, "Script Variables", PROPERTY_HINT_NONE, String(), PROPERTY_USAGE_CATEGORY));
 		script_instance->get_property_list(p_list);
 		script_instance->get_property_list(p_list);
@@ -712,20 +709,17 @@ static void _test_call_error(const StringName &p_func, const Variant::CallError
 			break;
 			break;
 		case Variant::CallError::CALL_ERROR_INVALID_ARGUMENT: {
 		case Variant::CallError::CALL_ERROR_INVALID_ARGUMENT: {
 
 
-			ERR_EXPLAIN("Error Calling Function: " + String(p_func) + " - Invalid type for argument " + itos(error.argument) + ", expected " + Variant::get_type_name(error.expected));
-			ERR_FAIL();
+			ERR_FAIL_MSG("Error calling function: " + String(p_func) + " - Invalid type for argument " + itos(error.argument) + ", expected " + Variant::get_type_name(error.expected) + ".");
 			break;
 			break;
 		}
 		}
 		case Variant::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS: {
 		case Variant::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS: {
 
 
-			ERR_EXPLAIN("Error Calling Function: " + String(p_func) + " - Too many arguments, expected " + itos(error.argument));
-			ERR_FAIL();
+			ERR_FAIL_MSG("Error calling function: " + String(p_func) + " - Too many arguments, expected " + itos(error.argument) + ".");
 			break;
 			break;
 		}
 		}
 		case Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS: {
 		case Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS: {
 
 
-			ERR_EXPLAIN("Error Calling Function: " + String(p_func) + " - Too few arguments, expected " + itos(error.argument));
-			ERR_FAIL();
+			ERR_FAIL_MSG("Error calling function: " + String(p_func) + " - Too few arguments, expected " + itos(error.argument) + ".");
 			break;
 			break;
 		}
 		}
 		case Variant::CallError::CALL_ERROR_INSTANCE_IS_NULL:
 		case Variant::CallError::CALL_ERROR_INSTANCE_IS_NULL:
@@ -742,15 +736,9 @@ void Object::call_multilevel(const StringName &p_method, const Variant **p_args,
 
 
 	if (p_method == CoreStringNames::get_singleton()->_free) {
 	if (p_method == CoreStringNames::get_singleton()->_free) {
 #ifdef DEBUG_ENABLED
 #ifdef DEBUG_ENABLED
-		if (Object::cast_to<Reference>(this)) {
-			ERR_EXPLAIN("Can't 'free' a reference.");
-			ERR_FAIL();
-		}
+		ERR_FAIL_COND_MSG(Object::cast_to<Reference>(this), "Can't 'free' a reference.");
 
 
-		if (_lock_index.get() > 1) {
-			ERR_EXPLAIN("Object is locked and can't be freed.");
-			ERR_FAIL();
-		}
+		ERR_FAIL_COND_MSG(_lock_index.get() > 1, "Object is locked and can't be freed.");
 #endif
 #endif
 
 
 		//must be here, must be before everything,
 		//must be here, must be before everything,
@@ -838,8 +826,7 @@ Variant Object::callv(const StringName &p_method, const Array &p_args) {
 	Variant::CallError ce;
 	Variant::CallError ce;
 	Variant ret = call(p_method, argptrs, p_args.size(), ce);
 	Variant ret = call(p_method, argptrs, p_args.size(), ce);
 	if (ce.error != Variant::CallError::CALL_OK) {
 	if (ce.error != Variant::CallError::CALL_OK) {
-		ERR_EXPLAIN("Error calling method from 'callv': " + Variant::get_call_error_text(this, p_method, argptrs, p_args.size(), ce));
-		ERR_FAIL_V(Variant());
+		ERR_FAIL_V_MSG(Variant(), "Error calling method from 'callv': " + Variant::get_call_error_text(this, p_method, argptrs, p_args.size(), ce) + ".");
 	}
 	}
 	return ret;
 	return ret;
 }
 }
@@ -891,15 +878,13 @@ Variant Object::call(const StringName &p_method, const Variant **p_args, int p_a
 		if (Object::cast_to<Reference>(this)) {
 		if (Object::cast_to<Reference>(this)) {
 			r_error.argument = 0;
 			r_error.argument = 0;
 			r_error.error = Variant::CallError::CALL_ERROR_INVALID_METHOD;
 			r_error.error = Variant::CallError::CALL_ERROR_INVALID_METHOD;
-			ERR_EXPLAIN("Can't 'free' a reference.");
-			ERR_FAIL_V(Variant());
+			ERR_FAIL_V_MSG(Variant(), "Can't 'free' a reference.");
 		}
 		}
 
 
 		if (_lock_index.get() > 1) {
 		if (_lock_index.get() > 1) {
 			r_error.argument = 0;
 			r_error.argument = 0;
 			r_error.error = Variant::CallError::CALL_ERROR_INVALID_METHOD;
 			r_error.error = Variant::CallError::CALL_ERROR_INVALID_METHOD;
-			ERR_EXPLAIN("Object is locked and can't be freed.");
-			ERR_FAIL_V(Variant());
+			ERR_FAIL_V_MSG(Variant(), "Object is locked and can't be freed.");
 		}
 		}
 
 
 #endif
 #endif
@@ -1175,10 +1160,7 @@ Error Object::emit_signal(const StringName &p_name, const Variant **p_args, int
 #ifdef DEBUG_ENABLED
 #ifdef DEBUG_ENABLED
 		bool signal_is_valid = ClassDB::has_signal(get_class_name(), p_name);
 		bool signal_is_valid = ClassDB::has_signal(get_class_name(), p_name);
 		//check in script
 		//check in script
-		if (!signal_is_valid && !script.is_null() && !Ref<Script>(script)->has_script_signal(p_name)) {
-			ERR_EXPLAIN("Can't emit non-existing signal " + String("\"") + p_name + "\".");
-			ERR_FAIL_V(ERR_UNAVAILABLE);
-		}
+		ERR_FAIL_COND_V_MSG(!signal_is_valid && !script.is_null() && !Ref<Script>(script)->has_script_signal(p_name), ERR_UNAVAILABLE, "Can't emit non-existing signal " + String("\"") + p_name + "\".");
 #endif
 #endif
 		//not connected? just return
 		//not connected? just return
 		return ERR_UNAVAILABLE;
 		return ERR_UNAVAILABLE;
@@ -1243,7 +1225,7 @@ Error Object::emit_signal(const StringName &p_name, const Variant **p_args, int
 				if (ce.error == Variant::CallError::CALL_ERROR_INVALID_METHOD && !ClassDB::class_exists(target->get_class_name())) {
 				if (ce.error == Variant::CallError::CALL_ERROR_INVALID_METHOD && !ClassDB::class_exists(target->get_class_name())) {
 					//most likely object is not initialized yet, do not throw error.
 					//most likely object is not initialized yet, do not throw error.
 				} else {
 				} else {
-					ERR_PRINTS("Error calling method from signal '" + String(p_name) + "': " + Variant::get_call_error_text(target, c.method, args, argc, ce));
+					ERR_PRINTS("Error calling method from signal '" + String(p_name) + "': " + Variant::get_call_error_text(target, c.method, args, argc, ce) + ".");
 					err = ERR_METHOD_NOT_FOUND;
 					err = ERR_METHOD_NOT_FOUND;
 				}
 				}
 			}
 			}
@@ -1418,8 +1400,9 @@ void Object::get_signal_connection_list(const StringName &p_signal, List<Connect
 		p_connections->push_back(s->slot_map.getv(i).conn);
 		p_connections->push_back(s->slot_map.getv(i).conn);
 }
 }
 
 
-bool Object::has_persistent_signal_connections() const {
+int Object::get_persistent_signal_connection_count() const {
 
 
+	int count = 0;
 	const StringName *S = NULL;
 	const StringName *S = NULL;
 
 
 	while ((S = signal_map.next(S))) {
 	while ((S = signal_map.next(S))) {
@@ -1427,13 +1410,13 @@ bool Object::has_persistent_signal_connections() const {
 		const Signal *s = &signal_map[*S];
 		const Signal *s = &signal_map[*S];
 
 
 		for (int i = 0; i < s->slot_map.size(); i++) {
 		for (int i = 0; i < s->slot_map.size(); i++) {
-
-			if (s->slot_map.getv(i).conn.flags & CONNECT_PERSIST)
-				return true;
+			if (s->slot_map.getv(i).conn.flags & CONNECT_PERSIST) {
+				count += 1;
+			}
 		}
 		}
 	}
 	}
 
 
-	return false;
+	return count;
 }
 }
 
 
 void Object::get_signals_connected_to_this(List<Connection> *p_connections) const {
 void Object::get_signals_connected_to_this(List<Connection> *p_connections) const {
@@ -1466,10 +1449,8 @@ Error Object::connect(const StringName &p_signal, Object *p_to_object, const Str
 #endif
 #endif
 		}
 		}
 
 
-		if (!signal_is_valid) {
-			ERR_EXPLAIN("In Object of type '" + String(get_class()) + "': Attempt to connect nonexistent signal '" + p_signal + "' to method '" + p_to_object->get_class() + "." + p_to_method + "'");
-			ERR_FAIL_V(ERR_INVALID_PARAMETER);
-		}
+		ERR_FAIL_COND_V_MSG(!signal_is_valid, ERR_INVALID_PARAMETER, "In Object of type '" + String(get_class()) + "': Attempt to connect nonexistent signal '" + p_signal + "' to method '" + p_to_object->get_class() + "." + p_to_method + "'.");
+
 		signal_map[p_signal] = Signal();
 		signal_map[p_signal] = Signal();
 		s = &signal_map[p_signal];
 		s = &signal_map[p_signal];
 	}
 	}
@@ -1480,8 +1461,7 @@ Error Object::connect(const StringName &p_signal, Object *p_to_object, const Str
 			s->slot_map[target].reference_count++;
 			s->slot_map[target].reference_count++;
 			return OK;
 			return OK;
 		} else {
 		} else {
-			ERR_EXPLAIN("Signal '" + p_signal + "' is already connected to given method '" + p_to_method + "' in that object.");
-			ERR_FAIL_V(ERR_INVALID_PARAMETER);
+			ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, "Signal '" + p_signal + "' is already connected to given method '" + p_to_method + "' in that object.");
 		}
 		}
 	}
 	}
 
 
@@ -1517,8 +1497,7 @@ bool Object::is_connected(const StringName &p_signal, Object *p_to_object, const
 		if (!script.is_null() && Ref<Script>(script)->has_script_signal(p_signal))
 		if (!script.is_null() && Ref<Script>(script)->has_script_signal(p_signal))
 			return false;
 			return false;
 
 
-		ERR_EXPLAIN("Nonexistent signal: " + p_signal);
-		ERR_FAIL_V(false);
+		ERR_FAIL_V_MSG(false, "Nonexistent signal: " + p_signal + ".");
 	}
 	}
 
 
 	Signal::Target target(p_to_object->get_instance_id(), p_to_method);
 	Signal::Target target(p_to_object->get_instance_id(), p_to_method);
@@ -1536,21 +1515,13 @@ void Object::_disconnect(const StringName &p_signal, Object *p_to_object, const
 
 
 	ERR_FAIL_NULL(p_to_object);
 	ERR_FAIL_NULL(p_to_object);
 	Signal *s = signal_map.getptr(p_signal);
 	Signal *s = signal_map.getptr(p_signal);
-	if (!s) {
-		ERR_EXPLAIN("Nonexistent signal: " + p_signal);
-		ERR_FAIL();
-	}
-	if (s->lock > 0) {
-		ERR_EXPLAIN("Attempt to disconnect signal '" + p_signal + "' while emitting (locks: " + itos(s->lock) + ")");
-		ERR_FAIL();
-	}
+	ERR_FAIL_COND_MSG(!s, "Nonexistent signal: " + p_signal + ".");
+
+	ERR_FAIL_COND_MSG(s->lock > 0, "Attempt to disconnect signal '" + p_signal + "' while emitting (locks: " + itos(s->lock) + ").");
 
 
 	Signal::Target target(p_to_object->get_instance_id(), p_to_method);
 	Signal::Target target(p_to_object->get_instance_id(), p_to_method);
 
 
-	if (!s->slot_map.has(target)) {
-		ERR_EXPLAIN("Disconnecting nonexistent signal '" + p_signal + "', slot: " + itos(target._id) + ":" + target.method);
-		ERR_FAIL();
-	}
+	ERR_FAIL_COND_MSG(!s->slot_map.has(target), "Disconnecting nonexistent signal '" + p_signal + "', slot: " + itos(target._id) + ":" + target.method + ".");
 
 
 	Signal::Slot *slot = &s->slot_map[target];
 	Signal::Slot *slot = &s->slot_map[target];
 
 
@@ -1977,10 +1948,7 @@ Object::~Object() {
 
 
 		Signal *s = &signal_map[*S];
 		Signal *s = &signal_map[*S];
 
 
-		if (s->lock) {
-			ERR_EXPLAIN("Attempt to delete an object in the middle of a signal emission from it");
-			ERR_CONTINUE(s->lock > 0);
-		}
+		ERR_CONTINUE_MSG(s->lock > 0, "Attempt to delete an object in the middle of a signal emission from it.");
 
 
 		//brute force disconnect for performance
 		//brute force disconnect for performance
 		int slot_count = s->slot_map.size();
 		int slot_count = s->slot_map.size();

+ 11 - 4
core/object.h

@@ -58,7 +58,7 @@ enum PropertyHint {
 	PROPERTY_HINT_ENUM, ///< hint_text= "val1,val2,val3,etc"
 	PROPERTY_HINT_ENUM, ///< hint_text= "val1,val2,val3,etc"
 	PROPERTY_HINT_EXP_EASING, /// exponential easing function (Math::ease) use "attenuation" hint string to revert (flip h), "full" to also include in/out. (ie: "attenuation,inout")
 	PROPERTY_HINT_EXP_EASING, /// exponential easing function (Math::ease) use "attenuation" hint string to revert (flip h), "full" to also include in/out. (ie: "attenuation,inout")
 	PROPERTY_HINT_LENGTH, ///< hint_text= "length" (as integer)
 	PROPERTY_HINT_LENGTH, ///< hint_text= "length" (as integer)
-	PROPERTY_HINT_SPRITE_FRAME,
+	PROPERTY_HINT_SPRITE_FRAME, // FIXME: Obsolete: drop whenever we can break compat. Keeping now for GDNative compat.
 	PROPERTY_HINT_KEY_ACCEL, ///< hint_text= "length" (as integer)
 	PROPERTY_HINT_KEY_ACCEL, ///< hint_text= "length" (as integer)
 	PROPERTY_HINT_FLAGS, ///< hint_text= "flag1,flag2,etc" (as bit flags)
 	PROPERTY_HINT_FLAGS, ///< hint_text= "flag1,flag2,etc" (as bit flags)
 	PROPERTY_HINT_LAYERS_2D_RENDER,
 	PROPERTY_HINT_LAYERS_2D_RENDER,
@@ -104,7 +104,8 @@ enum PropertyUsageFlags {
 	PROPERTY_USAGE_INTERNATIONALIZED = 64, //hint for internationalized strings
 	PROPERTY_USAGE_INTERNATIONALIZED = 64, //hint for internationalized strings
 	PROPERTY_USAGE_GROUP = 128, //used for grouping props in the editor
 	PROPERTY_USAGE_GROUP = 128, //used for grouping props in the editor
 	PROPERTY_USAGE_CATEGORY = 256,
 	PROPERTY_USAGE_CATEGORY = 256,
-	//those below are deprecated thanks to ClassDB's now class value cache
+	// FIXME: Drop in 4.0, possibly reorder other flags?
+	// Those below are deprecated thanks to ClassDB's now class value cache
 	//PROPERTY_USAGE_STORE_IF_NONZERO = 512, //only store if nonzero
 	//PROPERTY_USAGE_STORE_IF_NONZERO = 512, //only store if nonzero
 	//PROPERTY_USAGE_STORE_IF_NONONE = 1024, //only store if false
 	//PROPERTY_USAGE_STORE_IF_NONONE = 1024, //only store if false
 	PROPERTY_USAGE_NO_INSTANCE_STATE = 2048,
 	PROPERTY_USAGE_NO_INSTANCE_STATE = 2048,
@@ -121,6 +122,7 @@ enum PropertyUsageFlags {
 	PROPERTY_USAGE_HIGH_END_GFX = 1 << 22,
 	PROPERTY_USAGE_HIGH_END_GFX = 1 << 22,
 	PROPERTY_USAGE_NODE_PATH_FROM_SCENE_ROOT = 1 << 23,
 	PROPERTY_USAGE_NODE_PATH_FROM_SCENE_ROOT = 1 << 23,
 	PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT = 1 << 24,
 	PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT = 1 << 24,
+	PROPERTY_USAGE_KEYING_INCREMENTS = 1 << 25, // Used in inspector to increment property when keyed in animation player
 
 
 	PROPERTY_USAGE_DEFAULT = PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_NETWORK,
 	PROPERTY_USAGE_DEFAULT = PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_NETWORK,
 	PROPERTY_USAGE_DEFAULT_INTL = PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_NETWORK | PROPERTY_USAGE_INTERNATIONALIZED,
 	PROPERTY_USAGE_DEFAULT_INTL = PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_NETWORK | PROPERTY_USAGE_INTERNATIONALIZED,
@@ -705,7 +707,7 @@ public:
 	void get_signal_list(List<MethodInfo> *p_signals) const;
 	void get_signal_list(List<MethodInfo> *p_signals) const;
 	void get_signal_connection_list(const StringName &p_signal, List<Connection> *p_connections) const;
 	void get_signal_connection_list(const StringName &p_signal, List<Connection> *p_connections) const;
 	void get_all_signal_connections(List<Connection> *p_connections) const;
 	void get_all_signal_connections(List<Connection> *p_connections) const;
-	bool has_persistent_signal_connections() const;
+	int get_persistent_signal_connection_count() const;
 	void get_signals_connected_to_this(List<Connection> *p_connections) const;
 	void get_signals_connected_to_this(List<Connection> *p_connections) const;
 
 
 	Error connect(const StringName &p_signal, Object *p_to_object, const StringName &p_to_method, const Vector<Variant> &p_binds = Vector<Variant>(), uint32_t p_flags = 0);
 	Error connect(const StringName &p_signal, Object *p_to_object, const StringName &p_to_method, const Vector<Variant> &p_binds = Vector<Variant>(), uint32_t p_flags = 0);
@@ -792,8 +794,13 @@ public:
 	static int get_object_count();
 	static int get_object_count();
 
 
 	_FORCE_INLINE_ static bool instance_validate(Object *p_ptr) {
 	_FORCE_INLINE_ static bool instance_validate(Object *p_ptr) {
+		rw_lock->read_lock();
 
 
-		return instance_checks.has(p_ptr);
+		bool exists = instance_checks.has(p_ptr);
+
+		rw_lock->read_unlock();
+
+		return exists;
 	}
 	}
 };
 };
 
 

+ 0 - 8
core/os/dir_access.cpp

@@ -179,14 +179,6 @@ Error DirAccess::make_dir_recursive(String p_dir) {
 	return OK;
 	return OK;
 }
 }
 
 
-String DirAccess::get_next(bool *p_is_dir) {
-
-	String next = get_next();
-	if (p_is_dir)
-		*p_is_dir = current_is_dir();
-	return next;
-}
-
 String DirAccess::fix_path(String p_path) const {
 String DirAccess::fix_path(String p_path) const {
 
 
 	switch (_access_type) {
 	switch (_access_type) {

+ 12 - 5
core/os/dir_access.h

@@ -34,10 +34,6 @@
 #include "core/typedefs.h"
 #include "core/typedefs.h"
 #include "core/ustring.h"
 #include "core/ustring.h"
 
 
-/**
-	@author Juan Linietsky <[email protected]>
-*/
-
 //@ TODO, excellent candidate for THREAD_SAFE MACRO, should go through all these and add THREAD_SAFE where it applies
 //@ TODO, excellent candidate for THREAD_SAFE MACRO, should go through all these and add THREAD_SAFE where it applies
 class DirAccess {
 class DirAccess {
 public:
 public:
@@ -71,7 +67,6 @@ protected:
 
 
 public:
 public:
 	virtual Error list_dir_begin() = 0; ///< This starts dir listing
 	virtual Error list_dir_begin() = 0; ///< This starts dir listing
-	virtual String get_next(bool *p_is_dir); // compatibility
 	virtual String get_next() = 0;
 	virtual String get_next() = 0;
 	virtual bool current_is_dir() const = 0;
 	virtual bool current_is_dir() const = 0;
 	virtual bool current_is_hidden() const = 0;
 	virtual bool current_is_hidden() const = 0;
@@ -98,6 +93,18 @@ public:
 	virtual Error rename(String p_from, String p_to) = 0;
 	virtual Error rename(String p_from, String p_to) = 0;
 	virtual Error remove(String p_name) = 0;
 	virtual Error remove(String p_name) = 0;
 
 
+	// Meant for editor code when we want to quickly remove a file without custom
+	// handling (e.g. removing a cache file).
+	static void remove_file_or_error(String p_path) {
+		DirAccess *da = create(ACCESS_FILESYSTEM);
+		if (da->file_exists(p_path)) {
+			if (da->remove(p_path) != OK) {
+				ERR_FAIL_MSG("Cannot remove file or directory: " + p_path);
+			}
+		}
+		memdelete(da);
+	}
+
 	virtual String get_filesystem_type() const = 0;
 	virtual String get_filesystem_type() const = 0;
 	static String get_full_path(const String &p_path, AccessType p_access);
 	static String get_full_path(const String &p_path, AccessType p_access);
 	static DirAccess *create_for_path(const String &p_path);
 	static DirAccess *create_for_path(const String &p_path);

+ 3 - 5
core/os/file_access.cpp

@@ -30,9 +30,9 @@
 
 
 #include "file_access.h"
 #include "file_access.h"
 
 
+#include "core/crypto/crypto_core.h"
 #include "core/io/file_access_pack.h"
 #include "core/io/file_access_pack.h"
 #include "core/io/marshalls.h"
 #include "core/io/marshalls.h"
-#include "core/math/crypto_core.h"
 #include "core/os/os.h"
 #include "core/os/os.h"
 #include "core/project_settings.h"
 #include "core/project_settings.h"
 
 
@@ -599,8 +599,7 @@ Vector<uint8_t> FileAccess::get_file_as_array(const String &p_path, Error *r_err
 		if (r_error) { // if error requested, do not throw error
 		if (r_error) { // if error requested, do not throw error
 			return Vector<uint8_t>();
 			return Vector<uint8_t>();
 		}
 		}
-		ERR_EXPLAIN("Can't open file from path: " + String(p_path));
-		ERR_FAIL_V(Vector<uint8_t>());
+		ERR_FAIL_V_MSG(Vector<uint8_t>(), "Can't open file from path: " + String(p_path) + ".");
 	}
 	}
 	Vector<uint8_t> data;
 	Vector<uint8_t> data;
 	data.resize(f->get_len());
 	data.resize(f->get_len());
@@ -620,8 +619,7 @@ String FileAccess::get_file_as_string(const String &p_path, Error *r_error) {
 		if (r_error) {
 		if (r_error) {
 			return String();
 			return String();
 		}
 		}
-		ERR_EXPLAIN("Can't get file as string from path: " + String(p_path));
-		ERR_FAIL_V(String());
+		ERR_FAIL_V_MSG(String(), "Can't get file as string from path: " + String(p_path) + ".");
 	}
 	}
 
 
 	String ret;
 	String ret;

+ 1 - 0
core/os/input.cpp

@@ -80,6 +80,7 @@ void Input::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("get_joy_axis_index_from_string", "axis"), &Input::get_joy_axis_index_from_string);
 	ClassDB::bind_method(D_METHOD("get_joy_axis_index_from_string", "axis"), &Input::get_joy_axis_index_from_string);
 	ClassDB::bind_method(D_METHOD("start_joy_vibration", "device", "weak_magnitude", "strong_magnitude", "duration"), &Input::start_joy_vibration, DEFVAL(0));
 	ClassDB::bind_method(D_METHOD("start_joy_vibration", "device", "weak_magnitude", "strong_magnitude", "duration"), &Input::start_joy_vibration, DEFVAL(0));
 	ClassDB::bind_method(D_METHOD("stop_joy_vibration", "device"), &Input::stop_joy_vibration);
 	ClassDB::bind_method(D_METHOD("stop_joy_vibration", "device"), &Input::stop_joy_vibration);
+	ClassDB::bind_method(D_METHOD("vibrate_handheld", "duration_ms"), &Input::vibrate_handheld, DEFVAL(500));
 	ClassDB::bind_method(D_METHOD("get_gravity"), &Input::get_gravity);
 	ClassDB::bind_method(D_METHOD("get_gravity"), &Input::get_gravity);
 	ClassDB::bind_method(D_METHOD("get_accelerometer"), &Input::get_accelerometer);
 	ClassDB::bind_method(D_METHOD("get_accelerometer"), &Input::get_accelerometer);
 	ClassDB::bind_method(D_METHOD("get_magnetometer"), &Input::get_magnetometer);
 	ClassDB::bind_method(D_METHOD("get_magnetometer"), &Input::get_magnetometer);

+ 1 - 0
core/os/input.h

@@ -100,6 +100,7 @@ public:
 	virtual uint64_t get_joy_vibration_timestamp(int p_device) = 0;
 	virtual uint64_t get_joy_vibration_timestamp(int p_device) = 0;
 	virtual void start_joy_vibration(int p_device, float p_weak_magnitude, float p_strong_magnitude, float p_duration = 0) = 0;
 	virtual void start_joy_vibration(int p_device, float p_weak_magnitude, float p_strong_magnitude, float p_duration = 0) = 0;
 	virtual void stop_joy_vibration(int p_device) = 0;
 	virtual void stop_joy_vibration(int p_device) = 0;
+	virtual void vibrate_handheld(int p_duration_ms = 500) = 0;
 
 
 	virtual Point2 get_mouse_position() const = 0;
 	virtual Point2 get_mouse_position() const = 0;
 	virtual Point2 get_last_mouse_speed() const = 0;
 	virtual Point2 get_last_mouse_speed() const = 0;

+ 2 - 2
core/os/input_event.cpp

@@ -450,7 +450,7 @@ bool InputEventMouseButton::is_doubleclick() const {
 
 
 Ref<InputEvent> InputEventMouseButton::xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs) const {
 Ref<InputEvent> InputEventMouseButton::xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs) const {
 
 
-	Vector2 g = p_xform.xform(get_global_position());
+	Vector2 g = get_global_position();
 	Vector2 l = p_xform.xform(get_position() + p_local_ofs);
 	Vector2 l = p_xform.xform(get_position() + p_local_ofs);
 
 
 	Ref<InputEventMouseButton> mb;
 	Ref<InputEventMouseButton> mb;
@@ -577,7 +577,7 @@ Vector2 InputEventMouseMotion::get_speed() const {
 
 
 Ref<InputEvent> InputEventMouseMotion::xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs) const {
 Ref<InputEvent> InputEventMouseMotion::xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs) const {
 
 
-	Vector2 g = p_xform.xform(get_global_position());
+	Vector2 g = get_global_position();
 	Vector2 l = p_xform.xform(get_position() + p_local_ofs);
 	Vector2 l = p_xform.xform(get_position() + p_local_ofs);
 	Vector2 r = p_xform.basis_xform(get_relative());
 	Vector2 r = p_xform.basis_xform(get_relative());
 	Vector2 s = p_xform.basis_xform(get_speed());
 	Vector2 s = p_xform.basis_xform(get_speed());

+ 0 - 4
core/os/input_event.h

@@ -37,10 +37,6 @@
 #include "core/typedefs.h"
 #include "core/typedefs.h"
 #include "core/ustring.h"
 #include "core/ustring.h"
 
 
-/**
-	@author Juan Linietsky <[email protected]>
-*/
-
 /**
 /**
  * Input Event classes. These are used in the main loop.
  * Input Event classes. These are used in the main loop.
  * The events are pretty obvious.
  * The events are pretty obvious.

+ 0 - 4
core/os/keyboard.h

@@ -33,10 +33,6 @@
 
 
 #include "core/ustring.h"
 #include "core/ustring.h"
 
 
-/**
-	@author Juan Linietsky <[email protected]>
-*/
-
 /*
 /*
 	Special Key:
 	Special Key:
 
 

+ 8 - 0
core/os/main_loop.cpp

@@ -49,6 +49,8 @@ void MainLoop::_bind_methods() {
 	BIND_VMETHOD(MethodInfo("_drop_files", PropertyInfo(Variant::POOL_STRING_ARRAY, "files"), PropertyInfo(Variant::INT, "from_screen")));
 	BIND_VMETHOD(MethodInfo("_drop_files", PropertyInfo(Variant::POOL_STRING_ARRAY, "files"), PropertyInfo(Variant::INT, "from_screen")));
 	BIND_VMETHOD(MethodInfo("_finalize"));
 	BIND_VMETHOD(MethodInfo("_finalize"));
 
 
+	BIND_VMETHOD(MethodInfo("_global_menu_action", PropertyInfo(Variant::NIL, "id"), PropertyInfo(Variant::NIL, "meta")));
+
 	BIND_CONSTANT(NOTIFICATION_WM_MOUSE_ENTER);
 	BIND_CONSTANT(NOTIFICATION_WM_MOUSE_ENTER);
 	BIND_CONSTANT(NOTIFICATION_WM_MOUSE_EXIT);
 	BIND_CONSTANT(NOTIFICATION_WM_MOUSE_EXIT);
 	BIND_CONSTANT(NOTIFICATION_WM_FOCUS_IN);
 	BIND_CONSTANT(NOTIFICATION_WM_FOCUS_IN);
@@ -115,6 +117,12 @@ void MainLoop::drop_files(const Vector<String> &p_files, int p_from_screen) {
 		get_script_instance()->call("_drop_files", p_files, p_from_screen);
 		get_script_instance()->call("_drop_files", p_files, p_from_screen);
 }
 }
 
 
+void MainLoop::global_menu_action(const Variant &p_id, const Variant &p_meta) {
+
+	if (get_script_instance())
+		get_script_instance()->call("_global_menu_action", p_id, p_meta);
+}
+
 void MainLoop::finish() {
 void MainLoop::finish() {
 
 
 	if (get_script_instance()) {
 	if (get_script_instance()) {

+ 1 - 4
core/os/main_loop.h

@@ -35,10 +35,6 @@
 #include "core/reference.h"
 #include "core/reference.h"
 #include "core/script_language.h"
 #include "core/script_language.h"
 
 
-/**
-	@author Juan Linietsky <[email protected]>
-*/
-
 class MainLoop : public Object {
 class MainLoop : public Object {
 
 
 	GDCLASS(MainLoop, Object);
 	GDCLASS(MainLoop, Object);
@@ -75,6 +71,7 @@ public:
 	virtual void finish();
 	virtual void finish();
 
 
 	virtual void drop_files(const Vector<String> &p_files, int p_from_screen = 0);
 	virtual void drop_files(const Vector<String> &p_files, int p_from_screen = 0);
+	virtual void global_menu_action(const Variant &p_id, const Variant &p_meta);
 
 
 	void set_init_script(const Ref<Script> &p_init_script);
 	void set_init_script(const Ref<Script> &p_init_script);
 
 

+ 0 - 4
core/os/memory.h

@@ -35,10 +35,6 @@
 
 
 #include <stddef.h>
 #include <stddef.h>
 
 
-/**
-	@author Juan Linietsky <[email protected]>
-*/
-
 #ifndef PAD_ALIGN
 #ifndef PAD_ALIGN
 #define PAD_ALIGN 16 //must always be greater than this at much
 #define PAD_ALIGN 16 //must always be greater than this at much
 #endif
 #endif

+ 7 - 6
core/os/os.cpp

@@ -186,6 +186,11 @@ int OS::get_process_id() const {
 	return -1;
 	return -1;
 };
 };
 
 
+void OS::vibrate_handheld(int p_duration_ms) {
+
+	WARN_PRINTS("vibrate_handheld() only works with Android and iOS");
+}
+
 bool OS::is_stdout_verbose() const {
 bool OS::is_stdout_verbose() const {
 
 
 	return _verbose_stdout;
 	return _verbose_stdout;
@@ -268,8 +273,7 @@ void OS::print_all_resources(String p_to_file) {
 		_OSPRF = FileAccess::open(p_to_file, FileAccess::WRITE, &err);
 		_OSPRF = FileAccess::open(p_to_file, FileAccess::WRITE, &err);
 		if (err != OK) {
 		if (err != OK) {
 			_OSPRF = NULL;
 			_OSPRF = NULL;
-			ERR_EXPLAIN("Can't print all resources to file: " + String(p_to_file));
-			ERR_FAIL();
+			ERR_FAIL_MSG("Can't print all resources to file: " + String(p_to_file) + ".");
 		}
 		}
 	}
 	}
 
 
@@ -487,10 +491,7 @@ void OS::_ensure_user_data_dir() {
 
 
 	da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
 	da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
 	Error err = da->make_dir_recursive(dd);
 	Error err = da->make_dir_recursive(dd);
-	if (err != OK) {
-		ERR_EXPLAIN("Error attempting to create data dir: " + dd);
-	}
-	ERR_FAIL_COND(err != OK);
+	ERR_FAIL_COND_MSG(err != OK, "Error attempting to create data dir: " + dd + ".");
 
 
 	memdelete(da);
 	memdelete(da);
 }
 }

+ 6 - 4
core/os/os.h

@@ -41,10 +41,6 @@
 
 
 #include <stdarg.h>
 #include <stdarg.h>
 
 
-/**
-	@author Juan Linietsky <[email protected]>
-*/
-
 class Mutex;
 class Mutex;
 
 
 class OS {
 class OS {
@@ -147,6 +143,11 @@ public:
 
 
 	static OS *get_singleton();
 	static OS *get_singleton();
 
 
+	virtual void global_menu_add_item(const String &p_menu, const String &p_label, const Variant &p_signal, const Variant &p_meta){};
+	virtual void global_menu_add_separator(const String &p_menu){};
+	virtual void global_menu_remove_item(const String &p_menu, int p_idx){};
+	virtual void global_menu_clear(const String &p_menu){};
+
 	void print_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, Logger::ErrorType p_type = Logger::ERR_ERROR);
 	void print_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, Logger::ErrorType p_type = Logger::ERR_ERROR);
 	void print(const char *p_format, ...) _PRINTF_FORMAT_ATTRIBUTE_2_3;
 	void print(const char *p_format, ...) _PRINTF_FORMAT_ATTRIBUTE_2_3;
 	void printerr(const char *p_format, ...) _PRINTF_FORMAT_ATTRIBUTE_2_3;
 	void printerr(const char *p_format, ...) _PRINTF_FORMAT_ATTRIBUTE_2_3;
@@ -269,6 +270,7 @@ public:
 	virtual Error execute(const String &p_path, const List<String> &p_arguments, bool p_blocking, ProcessID *r_child_id = NULL, String *r_pipe = NULL, int *r_exitcode = NULL, bool read_stderr = false, Mutex *p_pipe_mutex = NULL) = 0;
 	virtual Error execute(const String &p_path, const List<String> &p_arguments, bool p_blocking, ProcessID *r_child_id = NULL, String *r_pipe = NULL, int *r_exitcode = NULL, bool read_stderr = false, Mutex *p_pipe_mutex = NULL) = 0;
 	virtual Error kill(const ProcessID &p_pid) = 0;
 	virtual Error kill(const ProcessID &p_pid) = 0;
 	virtual int get_process_id() const;
 	virtual int get_process_id() const;
+	virtual void vibrate_handheld(int p_duration_ms = 500);
 
 
 	virtual Error shell_open(String p_uri);
 	virtual Error shell_open(String p_uri);
 	virtual Error set_cwd(const String &p_cwd);
 	virtual Error set_cwd(const String &p_cwd);

+ 0 - 4
core/os/semaphore.h

@@ -33,10 +33,6 @@
 
 
 #include "core/error_list.h"
 #include "core/error_list.h"
 
 
-/**
-	@author Juan Linietsky <[email protected]>
-*/
-
 class Semaphore {
 class Semaphore {
 protected:
 protected:
 	static Semaphore *(*create_func)();
 	static Semaphore *(*create_func)();

Some files were not shown because too many files changed in this diff