Merge branch 'master' into tab_key
This commit is contained in:
commit
0024dd7bb5
1139 changed files with 102964 additions and 41115 deletions
3
.github/CODEOWNERS
vendored
3
.github/CODEOWNERS
vendored
|
@ -30,7 +30,6 @@ doc_classes/* @godotengine/documentation
|
|||
/modules/bullet/ @AndreaCatania
|
||||
/modules/csg/ @BastiaanOlij
|
||||
/modules/enet/ @godotengine/network
|
||||
/modules/gdnative/ @karroffel
|
||||
/modules/gdnative/*arvr/ @BastiaanOlij
|
||||
/modules/gdscript/ @vnen @bojidar-bg
|
||||
/modules/mbedtls/ @godotengine/network
|
||||
|
@ -45,6 +44,6 @@ doc_classes/* @godotengine/documentation
|
|||
/platform/uwp/ @vnen
|
||||
|
||||
/server/physics*/ @reduz @AndreaCatania
|
||||
/server/visual*/ @reduz @karroffel
|
||||
/server/visual*/ @reduz
|
||||
|
||||
/thirdparty/ @akien-mga
|
||||
|
|
21
.github/ISSUE_TEMPLATE.md
vendored
21
.github/ISSUE_TEMPLATE.md
vendored
|
@ -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
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
18
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
|
@ -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
.github/ISSUE_TEMPLATE/feature---enhancement-request.md
vendored
Normal file
19
.github/ISSUE_TEMPLATE/feature---enhancement-request.md
vendored
Normal file
|
@ -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
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,5 +1,6 @@
|
|||
# Godot auto generated files
|
||||
*.gen.*
|
||||
.import/
|
||||
|
||||
# Documentation generated by doxygen or from classes.xml
|
||||
doc/_build/
|
||||
|
|
24
.travis.yml
24
.travis.yml
|
@ -2,7 +2,10 @@ language: cpp
|
|||
|
||||
# OS config, depends on actual 'os' in build matrix
|
||||
dist: xenial
|
||||
sudo: false
|
||||
|
||||
stages:
|
||||
- check
|
||||
- build
|
||||
|
||||
env:
|
||||
global:
|
||||
|
@ -18,6 +21,7 @@ cache:
|
|||
matrix:
|
||||
include:
|
||||
- name: Static checks (clang-format)
|
||||
stage: check
|
||||
env: STATIC_CHECKS=yes
|
||||
os: linux
|
||||
compiler: gcc
|
||||
|
@ -29,6 +33,7 @@ matrix:
|
|||
- clang-format-8
|
||||
|
||||
- name: Linux editor (debug, GCC 9, with Mono)
|
||||
stage: build
|
||||
env: PLATFORM=x11 TOOLS=yes TARGET=debug CACHE_NAME=${PLATFORM}-tools-mono-gcc-9 MATRIX_EVAL="CC=gcc-9 && CXX=g++-9" EXTRA_ARGS="module_mono_enabled=yes mono_glue=no warnings=extra werror=yes"
|
||||
os: linux
|
||||
compiler: gcc-9
|
||||
|
@ -52,6 +57,7 @@ matrix:
|
|||
branch_pattern: coverity_scan
|
||||
|
||||
- name: Linux export template (release, Clang)
|
||||
stage: build
|
||||
env: PLATFORM=x11 TOOLS=no TARGET=release CACHE_NAME=${PLATFORM}-clang EXTRA_ARGS="warnings=extra werror=yes"
|
||||
os: linux
|
||||
compiler: clang
|
||||
|
@ -61,22 +67,26 @@ matrix:
|
|||
- *linux_deps
|
||||
|
||||
- name: Android export template (release_debug, Clang)
|
||||
stage: build
|
||||
env: PLATFORM=android TOOLS=no TARGET=release_debug CACHE_NAME=${PLATFORM}-clang EXTRA_ARGS="warnings=extra werror=yes"
|
||||
os: linux
|
||||
compiler: clang
|
||||
|
||||
- name: macOS editor (debug, Clang)
|
||||
stage: build
|
||||
env: PLATFORM=osx TOOLS=yes TARGET=debug CACHE_NAME=${PLATFORM}-tools-clang
|
||||
os: osx
|
||||
compiler: clang
|
||||
|
||||
- name: iOS export template (debug, Clang)
|
||||
stage: build
|
||||
env: PLATFORM=iphone TOOLS=no TARGET=debug CACHE_NAME=${PLATFORM}-clang
|
||||
os: osx
|
||||
compiler: clang
|
||||
|
||||
- name: Linux headless editor (release_debug, GCC 9)
|
||||
env: PLATFORM=server TOOLS=yes TARGET=release_debug CACHE_NAME=${PLATFORM}-tools-gcc-9 MATRIX_EVAL="CC=gcc-9 && CXX=g++-9" EXTRA_ARGS="warnings=extra werror=yes"
|
||||
- name: Linux headless editor (release_debug, GCC 9, testing project exporting and script running)
|
||||
stage: build
|
||||
env: PLATFORM=server TOOLS=yes TARGET=release_debug CACHE_NAME=${PLATFORM}-tools-gcc-9 MATRIX_EVAL="CC=gcc-9 && CXX=g++-9" EXTRA_ARGS="warnings=extra werror=yes" TEST_PROJECT=yes
|
||||
os: linux
|
||||
compiler: gcc-9
|
||||
addons:
|
||||
|
@ -88,6 +98,7 @@ matrix:
|
|||
- *linux_deps
|
||||
|
||||
- name: Linux export template (release_debug, GCC 5, without 3D support)
|
||||
stage: build
|
||||
env: PLATFORM=x11 TOOLS=no TARGET=release_debug CACHE_NAME=${PLATFORM}-gcc-5 EXTRA_ARGS="disable_3d=yes"
|
||||
os: linux
|
||||
compiler: gcc
|
||||
|
@ -124,5 +135,10 @@ script:
|
|||
- if [ "$STATIC_CHECKS" = "yes" ]; then
|
||||
sh ./misc/travis/clang-format.sh;
|
||||
else
|
||||
scons -j2 CC=$CC CXX=$CXX platform=$PLATFORM tools=$TOOLS target=$TARGET $OPTIONS $EXTRA_ARGS;
|
||||
scons -j2 CC=$CC CXX=$CXX platform=$PLATFORM tools=$TOOLS target=$TARGET $OPTIONS $EXTRA_ARGS &&
|
||||
if [ "$TEST_PROJECT" = "yes" ]; then
|
||||
git clone --depth 1 "https://github.com/godotengine/godot-tests.git";
|
||||
sed -i "s:custom_template/release=\"\":custom_template/release=\"$(readlink -e bin/godot_server.x11.opt.tools.64)\":" godot-tests/tests/project_export/export_presets.cfg;
|
||||
godot-tests/tests/project_export/test_project.sh "bin/godot_server.x11.opt.tools.64";
|
||||
fi
|
||||
fi
|
||||
|
|
|
@ -36,6 +36,7 @@ name is available.
|
|||
Andy Moss (MillionOstrich)
|
||||
Anish Bhobe (KidRigger)
|
||||
Anton Yabchinskiy (a12n)
|
||||
Anutrix
|
||||
Aren Villanueva (kurikaesu)
|
||||
Ariel Manzur (punto-)
|
||||
Bastiaan Olij (BastiaanOlij)
|
||||
|
@ -56,6 +57,7 @@ name is available.
|
|||
Dharkael (lupoDharkael)
|
||||
Dmitry Koteroff (Krakean)
|
||||
DualMatrix
|
||||
Emmanuel Barroga (sparkart)
|
||||
Emmanuel Leblond (touilleMan)
|
||||
Eric Lasota (elasota)
|
||||
est31
|
||||
|
@ -68,6 +70,7 @@ name is available.
|
|||
Gerrit Großkopf (Grosskopf)
|
||||
Gilles Roudiere (groud)
|
||||
Guilherme Felipe de C. G. da Silva (guilhermefelipecgs)
|
||||
Hanif A (hbina)
|
||||
Hein-Pieter van Braam (hpvb)
|
||||
Hiroshi Ogawa (hi-ogawa)
|
||||
homer666
|
||||
|
@ -104,6 +107,7 @@ name is available.
|
|||
Masoud BH (masoudbh3)
|
||||
Matthias Hölzl (hoelzl)
|
||||
Max Hilbrunner (mhilbrunner)
|
||||
merumelu
|
||||
Michael Alexsander Silva Dias (YeldhamDev)
|
||||
mrezai
|
||||
Nathan Warden (NathanWarden)
|
||||
|
@ -128,6 +132,7 @@ name is available.
|
|||
romulox-x
|
||||
Ruslan Mustakov (endragor)
|
||||
Saniko (sanikoyes)
|
||||
santouits
|
||||
SaracenOne
|
||||
sersoong
|
||||
Simon Wenner (swenner)
|
||||
|
|
153
DONORS.md
153
DONORS.md
|
@ -17,11 +17,12 @@ generous deed immortalized in the next stable release of Godot Engine.
|
|||
## Gold sponsors
|
||||
|
||||
Gamblify <https://www.gamblify.com>
|
||||
Moonwards <https://www.moonwards.com>
|
||||
|
||||
## Mini sponsors
|
||||
|
||||
AD Ford
|
||||
Alan Beauchamp
|
||||
Aleksandar Kordic
|
||||
Anandarup Mallik
|
||||
Andrew Dunai
|
||||
Brandon Lamb
|
||||
|
@ -30,12 +31,16 @@ generous deed immortalized in the next stable release of Godot Engine.
|
|||
Christoph Woinke
|
||||
Denis Malyavin
|
||||
Edward Flick
|
||||
Gamechuck
|
||||
GameDev.net
|
||||
GameDev.tv
|
||||
Grady
|
||||
Hein-Pieter van Braam
|
||||
Jacob McKenney
|
||||
Javary Co.
|
||||
Jeppe Zapp
|
||||
Justin Arnold
|
||||
Justo Delgado Baudí
|
||||
Kyle Szklenski
|
||||
Leonard Meagher
|
||||
Matthieu Huvé
|
||||
|
@ -46,7 +51,6 @@ generous deed immortalized in the next stable release of Godot Engine.
|
|||
Patrick Aarstad
|
||||
Slobodan Milnovic
|
||||
Stephan Lanfermann
|
||||
Stephen Telford
|
||||
Steve
|
||||
VilliHaukka
|
||||
Xananax
|
||||
|
@ -56,10 +60,11 @@ generous deed immortalized in the next stable release of Godot Engine.
|
|||
|
||||
Andrei
|
||||
cheese65536
|
||||
Daniel Hartmann
|
||||
Dave
|
||||
David Gehrig
|
||||
Ed Morley
|
||||
Florian Krick
|
||||
Grady
|
||||
Jakub Grzesik
|
||||
K9Kraken
|
||||
Manuele Finocchiaro
|
||||
|
@ -70,56 +75,62 @@ generous deed immortalized in the next stable release of Godot Engine.
|
|||
Zaven Muradyan
|
||||
|
||||
Alexander Trey Saunders
|
||||
Allen Schade
|
||||
Asher Glick
|
||||
Austen McRae
|
||||
beVR
|
||||
Brian van der Stel
|
||||
Cameron MacNair
|
||||
Carlo Cabanilla
|
||||
Daniel James
|
||||
David Giardi
|
||||
David Snopek
|
||||
Default Name
|
||||
Edward E
|
||||
Florian Breisch
|
||||
Gero
|
||||
GiulianoB
|
||||
Javier Roman
|
||||
Jay Horton
|
||||
Jon Smith
|
||||
Jonathan Turner
|
||||
Jon Woodward
|
||||
Justo Delgado Baudí
|
||||
Jose Fernando Alexandre
|
||||
Karl Werf
|
||||
Kommentgames
|
||||
Krzysztof Dluzniewski
|
||||
Luke
|
||||
Moonwards
|
||||
Maciej Pendolski
|
||||
Matthew Hillier
|
||||
Mored1984
|
||||
paul gruenbacher
|
||||
Paul LaMotte
|
||||
Péter Magyar
|
||||
Rob Messick
|
||||
Ross Esmond
|
||||
Ryan Badour
|
||||
Scott Wadden
|
||||
Sergey
|
||||
Shawn Yu
|
||||
Svenne Krap
|
||||
thechris
|
||||
Tom Langwaldt
|
||||
tukon
|
||||
William Wold
|
||||
Wyatt Goodin
|
||||
|
||||
Alex Khayrullin
|
||||
Branwyn Tylwyth
|
||||
Chris Goddard
|
||||
Chris Serino
|
||||
Christian Padilla
|
||||
Conrad Curry
|
||||
Craig Smith
|
||||
Darrian Little
|
||||
Dean Harmon
|
||||
Ian Richard Kunert
|
||||
Ivan Trombley
|
||||
Joan Fons
|
||||
Joshua Flores
|
||||
Krzysztof Jankowski
|
||||
Lord Bloodhound
|
||||
Lucas Ferreira Franca
|
||||
Michele Zilli
|
||||
Nathan Lundquist
|
||||
Nicklas Breum
|
||||
Pascal Grüter
|
||||
Petr Malac
|
||||
Rami
|
||||
|
@ -135,9 +146,10 @@ generous deed immortalized in the next stable release of Godot Engine.
|
|||
Xavier PATRICELLI
|
||||
|
||||
Adam Neumann
|
||||
Alessandra Pereyra
|
||||
Alexander J Maynard
|
||||
Alexey Dyadchenko
|
||||
André Frélicot
|
||||
andres eduardo lopez
|
||||
Andrew Bowen
|
||||
Asdf
|
||||
Ben Botwin
|
||||
|
@ -147,50 +159,48 @@ generous deed immortalized in the next stable release of Godot Engine.
|
|||
Christoph Schröder
|
||||
Cody Parker
|
||||
D
|
||||
Daniel
|
||||
Daniel Eichler
|
||||
David White
|
||||
Deadly Lampshade
|
||||
Eric
|
||||
Eric Churches
|
||||
Eric Monson
|
||||
Eugenio Hugo Salgüero Jáñez
|
||||
flesk
|
||||
Francisco Javier Moreno Carracedo
|
||||
gavlig
|
||||
GGGames.org
|
||||
Giles Montgomery
|
||||
Guilherme Felipe de C. G. da Silva
|
||||
Heath Hayes
|
||||
Hysteria
|
||||
Idzard Kwadijk
|
||||
Jared White
|
||||
Jesse Nave
|
||||
Joe Flood
|
||||
Jose Malheiro
|
||||
Joshua Flores
|
||||
Joshua Lesperance
|
||||
Juan T Chen
|
||||
Juraj Móza
|
||||
Kasper Jeppesen
|
||||
kinfox
|
||||
Klaus The.
|
||||
Klavdij Voncina
|
||||
Leandro Voltolino
|
||||
Maarten Elings
|
||||
Marcelo Dornbusch Lopes
|
||||
Markus Fehr
|
||||
Markus Wiesner
|
||||
Martin Eigel
|
||||
Marvin
|
||||
Matt Eunson
|
||||
Matthew Hillier
|
||||
Max Bulai
|
||||
Max R.R. Collada
|
||||
M H
|
||||
Nick Nikitin
|
||||
Oliver Dick
|
||||
Paolo Munoz
|
||||
Patrick Ting
|
||||
Paul Hocker
|
||||
Paul Von Zimmerman
|
||||
Pete Goodwin
|
||||
pl
|
||||
Ranoller
|
||||
Romildo Franco
|
||||
Samuel Judd
|
||||
Scott Pilet
|
||||
spilldata
|
||||
|
@ -198,60 +208,59 @@ generous deed immortalized in the next stable release of Godot Engine.
|
|||
Thomas Krampl
|
||||
Tobias Bocanegra
|
||||
Urho
|
||||
WytRabbit
|
||||
Xavier Fumado Beltran
|
||||
Zie Weaver
|
||||
|
||||
## Silver donors
|
||||
|
||||
1D_Inc
|
||||
Abby Jones
|
||||
Abraham Haskins
|
||||
Adam Brunnmeier
|
||||
Adam Carr
|
||||
Adam Nakonieczny
|
||||
Adam Smeltzer
|
||||
Adisibio
|
||||
Adrian Demetrescu
|
||||
Aggelos Arnaoutis
|
||||
Agustinus Arya
|
||||
Aidan O'Flannagain
|
||||
Albin Jonasson Svärdsby
|
||||
Alder Stefano
|
||||
Alessandro Senese
|
||||
Alexander Gillberg
|
||||
Alexander Koppe
|
||||
Alex Davies-Moore
|
||||
Alice Robinson
|
||||
Allen Schade
|
||||
Andreas Evers
|
||||
Andreas Krampitz
|
||||
Andreas Lundmark
|
||||
Andreas Schüle
|
||||
Andrew Peart
|
||||
André Simões
|
||||
Andrés Rodríguez
|
||||
Andrzej Skalski
|
||||
Anthony Bongiovanni
|
||||
Anthony Staunton
|
||||
Anton Kurkin
|
||||
Antony K. Jones
|
||||
AP Condomines
|
||||
Arda Erol
|
||||
Artem Bashev
|
||||
Arthur S. Muszynski
|
||||
Artistofdeath
|
||||
Aubrey Falconer
|
||||
Avencherus
|
||||
B A
|
||||
Balázs Batári
|
||||
Bastian Böhm
|
||||
Beliar
|
||||
Benedikt
|
||||
Ben Phelan
|
||||
Ben Vercammen
|
||||
Ben Woodley
|
||||
Berbank
|
||||
Bernd Jänichen
|
||||
Black Block
|
||||
Blair Allen
|
||||
Bobby CC Wong
|
||||
Boyquotes
|
||||
Branwyn Tylwyth
|
||||
Bryan Stevenson
|
||||
Caleb Dumitry
|
||||
Carwyn Edwards
|
||||
Chris Brown
|
||||
Chris Chapin
|
||||
Chris Gonzales
|
||||
Christian Baune
|
||||
Christian Winter
|
||||
Christoffer Sundbom
|
||||
|
@ -260,18 +269,22 @@ generous deed immortalized in the next stable release of Godot Engine.
|
|||
Cobaltum
|
||||
Collin Shooltz
|
||||
Dag Sundin Söderström
|
||||
Dan H. Bentsen
|
||||
Daniel Johnson
|
||||
DanielMaximiano
|
||||
Daniel Pontillo
|
||||
Daniel Reed
|
||||
Danny Welch
|
||||
Dave Watts
|
||||
Daniel Tebbutt
|
||||
David Bullock
|
||||
David Cravens
|
||||
David May
|
||||
Dimitri Stanojevic
|
||||
David Rapisarda
|
||||
David Woodard
|
||||
Dominic Cooney
|
||||
Dominik Wetzel
|
||||
DrevanTonder
|
||||
Donovan Hutcheon
|
||||
Duobix
|
||||
Eduardo Teixeira
|
||||
Edward Herbert
|
||||
Egon Elbre
|
||||
Ellen Marie Dash
|
||||
|
@ -280,18 +293,21 @@ generous deed immortalized in the next stable release of Godot Engine.
|
|||
Eric Ellingson
|
||||
Eric Martini
|
||||
Eric Williams
|
||||
EugeneTel
|
||||
Evan Rose
|
||||
Felix Kollmann
|
||||
fengjiongmax
|
||||
Flaredown
|
||||
FuDiggity
|
||||
G3Dev sàrl
|
||||
Gadzhi Kharkharov
|
||||
gamedev by Celio
|
||||
Gary Hulst
|
||||
Gerrit Großkopf
|
||||
George Marques
|
||||
gmmath
|
||||
Grant Clarke
|
||||
Greg Olson
|
||||
Greg P
|
||||
Greyson Richey
|
||||
Guldoman
|
||||
Hal A
|
||||
Heribert Hirth
|
||||
|
@ -299,7 +315,6 @@ generous deed immortalized in the next stable release of Godot Engine.
|
|||
Hunter Jones
|
||||
Hylpher
|
||||
ialex32x
|
||||
Igor Buzatovic
|
||||
Iiari
|
||||
IndustrialRobot
|
||||
Isaac Morton
|
||||
|
@ -326,28 +341,33 @@ generous deed immortalized in the next stable release of Godot Engine.
|
|||
Jon Bonazza
|
||||
Jon Sully
|
||||
Jose Aleman
|
||||
Jose Andrés Mejias Rojas
|
||||
Joseph Catrambone
|
||||
Josh 'Cheeseness' Bush
|
||||
Juanfran
|
||||
Juan Negrier
|
||||
Juan Velandia
|
||||
Judd
|
||||
Jueast
|
||||
Julian Murgia
|
||||
Kasier Bald0
|
||||
Justin Spedding
|
||||
Kaiser Bald0
|
||||
Kamuna
|
||||
Kauzig
|
||||
KC Chan
|
||||
Keedong Park
|
||||
kickmaniac
|
||||
Kiyohiro Kawamura (kyorohiro)
|
||||
Kjetil Haugland
|
||||
Klagsam
|
||||
Klassix
|
||||
KR McGinley
|
||||
KsyTek Games
|
||||
Kuan Cheang
|
||||
kycho
|
||||
Leviathan Hunter
|
||||
Levi Lindsey
|
||||
Linus Lind Lundgren
|
||||
Lionel Gaillard
|
||||
Luis Moraes
|
||||
LunaticInAHat
|
||||
Lurkars
|
||||
Macil
|
||||
|
@ -355,61 +375,75 @@ generous deed immortalized in the next stable release of Godot Engine.
|
|||
Malcolm
|
||||
Malik Ahmed
|
||||
Malik Nejer
|
||||
Marc Urlus
|
||||
Marcus Richter
|
||||
Markus Lohaus
|
||||
Markus Michael Egger
|
||||
Martin Holas
|
||||
Martin Liška
|
||||
Matt Edwards
|
||||
Matthew Little
|
||||
Maxwell
|
||||
medecau
|
||||
mhilbrunner
|
||||
Michael Dürwald
|
||||
Michael Gringauz
|
||||
Michael Haney
|
||||
Michael Labbe
|
||||
Mikael Olsson
|
||||
Mikayla Hutchinson
|
||||
Mike Birkhead
|
||||
Mike Cunningham
|
||||
Mitchell J. Wagner
|
||||
mlevin cantu
|
||||
MoM
|
||||
Moritz Laass
|
||||
MuffinManKen
|
||||
Nathan Fish
|
||||
Natrim
|
||||
nee
|
||||
Neil Blakey-Milner
|
||||
Nerdforge
|
||||
Nicholas
|
||||
Nicholas Bettencourt
|
||||
Nick Macholl
|
||||
Niclas Eriksen
|
||||
Nicolás Montaña
|
||||
Nicolas SAN AGUSTIN
|
||||
Nima Farid
|
||||
Nithin Jino
|
||||
NZ
|
||||
Olivier
|
||||
Omar Delarosa
|
||||
omzee
|
||||
Oscar Norlander
|
||||
Pafka
|
||||
Pan Ip
|
||||
Pat LaBine
|
||||
Patrick Forringer
|
||||
Patrick Nafarrete
|
||||
Paul Gieske
|
||||
Paul Mason
|
||||
Paweł Kowal
|
||||
Philip O. Staiger
|
||||
Philip Cohoe
|
||||
Pierre-Igor Berthet
|
||||
Pietro Vertechi
|
||||
Pitsanu Tongprasin
|
||||
Point08
|
||||
Poryg
|
||||
Rafa Laguna
|
||||
Rafal Wyszomirski
|
||||
Raphael Leroux
|
||||
Remi Rampin
|
||||
Rémi Verschelde
|
||||
Rezgi
|
||||
Ricardo Alcantara
|
||||
Robert Farr (Larington)
|
||||
Robert Hernandez
|
||||
Robert Larnach
|
||||
Rodrigo Loli
|
||||
Roger Smith
|
||||
Roland Rząsa
|
||||
Roman Tinkov
|
||||
Ronan Jouchet
|
||||
Ryan
|
||||
Ryan Brooks
|
||||
Ryan Groom
|
||||
Ryan Hentz
|
||||
Saad Khoudmi
|
||||
|
@ -418,46 +452,51 @@ generous deed immortalized in the next stable release of Godot Engine.
|
|||
Sasori Olkof
|
||||
Scott D. Yelich
|
||||
Sebastian Michailidis
|
||||
sgnsajgon
|
||||
Shane
|
||||
Shane Sicienski
|
||||
Shane Spoor
|
||||
Simon Ledam
|
||||
Simon Wenner
|
||||
SK
|
||||
Sootstone
|
||||
Stonepyre
|
||||
Thibault Barbaroux
|
||||
Taylor Fahlman
|
||||
thomas
|
||||
Thomas Bell
|
||||
Thomas Kelly
|
||||
Thomas Kurz
|
||||
tiansheng li
|
||||
Tim
|
||||
Tim Drumheller
|
||||
Tim Gudex
|
||||
Timo Schmidt
|
||||
Timothy B. MacDonald
|
||||
Tobbun
|
||||
Tom Fulp
|
||||
Tom Glenn
|
||||
Tom Larrow
|
||||
Torsten Crass
|
||||
Travis O'Brien
|
||||
Trent Skinner
|
||||
Triptych
|
||||
Troy Bonneau
|
||||
Tryggve Sollid
|
||||
Turgut Temucin
|
||||
Tyler Stafos
|
||||
UltyX
|
||||
Vaiktorg
|
||||
Valeria Viana Gusmao
|
||||
Veodok
|
||||
Victor
|
||||
Vigilant Watch
|
||||
Vincent Cloutier
|
||||
waka nya
|
||||
Walter Byers
|
||||
Wayne Haak
|
||||
werner mendizabal
|
||||
Wiley Thompson
|
||||
Will
|
||||
William Hogben
|
||||
Wout Standaert
|
||||
Yeung Si Xiang
|
||||
Wyatt Goodin
|
||||
Yegor
|
||||
蕭惟允
|
||||
|
||||
## Bronze donors
|
||||
|
||||
|
|
11
SConstruct
11
SConstruct
|
@ -311,6 +311,10 @@ if selected_platform in platform_list:
|
|||
# must happen after the flags, so when flags are used by configure, stuff happens (ie, ssl on x11)
|
||||
detect.configure(env)
|
||||
|
||||
# Enable C++11 support
|
||||
if not env.msvc:
|
||||
env.Append(CXXFLAGS=['-std=c++11'])
|
||||
|
||||
# Configure compiler warnings
|
||||
if env.msvc:
|
||||
# Truncations, narrowing conversions, signed/unsigned comparisons...
|
||||
|
@ -480,6 +484,13 @@ if selected_platform in platform_list:
|
|||
if env['minizip']:
|
||||
env.Append(CPPDEFINES=['MINIZIP_ENABLED'])
|
||||
|
||||
editor_module_list = ['regex']
|
||||
for x in editor_module_list:
|
||||
if not env['module_' + x + '_enabled']:
|
||||
if env['tools']:
|
||||
print("Build option 'module_" + x + "_enabled=no' cannot be used with 'tools=yes' (editor), only with 'tools=no' (export template).")
|
||||
sys.exit(255)
|
||||
|
||||
if not env['verbose']:
|
||||
methods.no_verbose(sys, env)
|
||||
|
||||
|
|
|
@ -159,6 +159,7 @@ env.CommandNoCache('#core/license.gen.h', ["../COPYRIGHT.txt", "../LICENSE.txt"]
|
|||
# Chain load SCsubs
|
||||
SConscript('os/SCsub')
|
||||
SConscript('math/SCsub')
|
||||
SConscript('crypto/SCsub')
|
||||
SConscript('io/SCsub')
|
||||
SConscript('bind/SCsub')
|
||||
|
||||
|
|
|
@ -133,18 +133,12 @@ void Array::erase(const Variant &p_value) {
|
|||
}
|
||||
|
||||
Variant Array::front() const {
|
||||
if (_p->array.size() == 0) {
|
||||
ERR_EXPLAIN("Can't take value from empty array");
|
||||
ERR_FAIL_V(Variant());
|
||||
}
|
||||
ERR_FAIL_COND_V_MSG(_p->array.size() == 0, Variant(), "Can't take value from empty array.");
|
||||
return operator[](0);
|
||||
}
|
||||
|
||||
Variant Array::back() const {
|
||||
if (_p->array.size() == 0) {
|
||||
ERR_EXPLAIN("Can't take value from empty array");
|
||||
ERR_FAIL_V(Variant());
|
||||
}
|
||||
ERR_FAIL_COND_V_MSG(_p->array.size() == 0, Variant(), "Can't take value from empty array.");
|
||||
return operator[](_p->array.size() - 1);
|
||||
}
|
||||
|
||||
|
|
|
@ -30,11 +30,11 @@
|
|||
|
||||
#include "core_bind.h"
|
||||
|
||||
#include "core/crypto/crypto_core.h"
|
||||
#include "core/io/file_access_compressed.h"
|
||||
#include "core/io/file_access_encrypted.h"
|
||||
#include "core/io/json.h"
|
||||
#include "core/io/marshalls.h"
|
||||
#include "core/math/crypto_core.h"
|
||||
#include "core/math/geometry.h"
|
||||
#include "core/os/keyboard.h"
|
||||
#include "core/os/os.h"
|
||||
|
@ -73,10 +73,7 @@ RES _ResourceLoader::load(const String &p_path, const String &p_type_hint, bool
|
|||
Error err = OK;
|
||||
RES ret = ResourceLoader::load(p_path, p_type_hint, p_no_cache, &err);
|
||||
|
||||
if (err != OK) {
|
||||
ERR_EXPLAIN("Error loading resource: '" + p_path + "'");
|
||||
ERR_FAIL_V(ret);
|
||||
}
|
||||
ERR_FAIL_COND_V_MSG(err != OK, ret, "Error loading resource: '" + p_path + "'.");
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -148,10 +145,7 @@ _ResourceLoader::_ResourceLoader() {
|
|||
}
|
||||
|
||||
Error _ResourceSaver::save(const String &p_path, const RES &p_resource, SaverFlags p_flags) {
|
||||
if (p_resource.is_null()) {
|
||||
ERR_EXPLAIN("Can't save empty resource to path: " + String(p_path))
|
||||
ERR_FAIL_V(ERR_INVALID_PARAMETER);
|
||||
}
|
||||
ERR_FAIL_COND_V_MSG(p_resource.is_null(), ERR_INVALID_PARAMETER, "Can't save empty resource to path: " + String(p_path) + ".");
|
||||
return ResourceSaver::save(p_path, p_resource, p_flags);
|
||||
}
|
||||
|
||||
|
@ -191,10 +185,31 @@ _ResourceSaver::_ResourceSaver() {
|
|||
|
||||
/////////////////OS
|
||||
|
||||
void _OS::global_menu_add_item(const String &p_menu, const String &p_label, const Variant &p_signal, const Variant &p_meta) {
|
||||
|
||||
OS::get_singleton()->global_menu_add_item(p_menu, p_label, p_signal, p_meta);
|
||||
}
|
||||
|
||||
void _OS::global_menu_add_separator(const String &p_menu) {
|
||||
|
||||
OS::get_singleton()->global_menu_add_separator(p_menu);
|
||||
}
|
||||
|
||||
void _OS::global_menu_remove_item(const String &p_menu, int p_idx) {
|
||||
|
||||
OS::get_singleton()->global_menu_remove_item(p_menu, p_idx);
|
||||
}
|
||||
|
||||
void _OS::global_menu_clear(const String &p_menu) {
|
||||
|
||||
OS::get_singleton()->global_menu_clear(p_menu);
|
||||
}
|
||||
|
||||
Point2 _OS::get_mouse_position() const {
|
||||
|
||||
return OS::get_singleton()->get_mouse_position();
|
||||
}
|
||||
|
||||
void _OS::set_window_title(const String &p_title) {
|
||||
|
||||
OS::get_singleton()->set_window_title(p_title);
|
||||
|
@ -208,6 +223,7 @@ int _OS::get_mouse_button_state() const {
|
|||
String _OS::get_unique_id() const {
|
||||
return OS::get_singleton()->get_unique_id();
|
||||
}
|
||||
|
||||
bool _OS::has_touchscreen_ui_hint() const {
|
||||
|
||||
return OS::get_singleton()->has_touchscreen_ui_hint();
|
||||
|
@ -217,6 +233,7 @@ void _OS::set_clipboard(const String &p_text) {
|
|||
|
||||
OS::get_singleton()->set_clipboard(p_text);
|
||||
}
|
||||
|
||||
String _OS::get_clipboard() const {
|
||||
|
||||
return OS::get_singleton()->get_clipboard();
|
||||
|
@ -263,12 +280,14 @@ void _OS::set_video_mode(const Size2 &p_size, bool p_fullscreen, bool p_resizeab
|
|||
vm.resizable = p_resizeable;
|
||||
OS::get_singleton()->set_video_mode(vm, p_screen);
|
||||
}
|
||||
|
||||
Size2 _OS::get_video_mode(int p_screen) const {
|
||||
|
||||
OS::VideoMode vm;
|
||||
vm = OS::get_singleton()->get_video_mode(p_screen);
|
||||
return Size2(vm.width, vm.height);
|
||||
}
|
||||
|
||||
bool _OS::is_video_mode_fullscreen(int p_screen) const {
|
||||
|
||||
OS::VideoMode vm;
|
||||
|
@ -727,22 +746,16 @@ int64_t _OS::get_unix_time_from_datetime(Dictionary datetime) const {
|
|||
{ 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
|
||||
};
|
||||
|
||||
ERR_EXPLAIN("Invalid second value of: " + itos(second));
|
||||
ERR_FAIL_COND_V(second > 59, 0);
|
||||
ERR_FAIL_COND_V_MSG(second > 59, 0, "Invalid second value of: " + itos(second) + ".");
|
||||
|
||||
ERR_EXPLAIN("Invalid minute value of: " + itos(minute));
|
||||
ERR_FAIL_COND_V(minute > 59, 0);
|
||||
ERR_FAIL_COND_V_MSG(minute > 59, 0, "Invalid minute value of: " + itos(minute) + ".");
|
||||
|
||||
ERR_EXPLAIN("Invalid hour value of: " + itos(hour));
|
||||
ERR_FAIL_COND_V(hour > 23, 0);
|
||||
ERR_FAIL_COND_V_MSG(hour > 23, 0, "Invalid hour value of: " + itos(hour) + ".");
|
||||
|
||||
ERR_EXPLAIN("Invalid month value of: " + itos(month));
|
||||
ERR_FAIL_COND_V(month > 12 || month == 0, 0);
|
||||
ERR_FAIL_COND_V_MSG(month > 12 || month == 0, 0, "Invalid month value of: " + itos(month) + ".");
|
||||
|
||||
// Do this check after month is tested as valid
|
||||
ERR_EXPLAIN("Invalid day value of: " + itos(day) + " which is larger than " + itos(MONTH_DAYS_TABLE[LEAPYEAR(year)][month - 1]) + " or 0");
|
||||
ERR_FAIL_COND_V(day > MONTH_DAYS_TABLE[LEAPYEAR(year)][month - 1] || day == 0, 0);
|
||||
|
||||
ERR_FAIL_COND_V_MSG(day > MONTH_DAYS_TABLE[LEAPYEAR(year)][month - 1] || day == 0, 0, "Invalid day value of: " + itos(day) + " which is larger than " + itos(MONTH_DAYS_TABLE[LEAPYEAR(year)][month - 1]) + " or 0.");
|
||||
// Calculate all the seconds from months past in this year
|
||||
uint64_t SECONDS_FROM_MONTHS_PAST_THIS_YEAR = DAYS_PAST_THIS_YEAR_TABLE[LEAPYEAR(year)][month - 1] * SECONDS_PER_DAY;
|
||||
|
||||
|
@ -1137,6 +1150,11 @@ void _OS::_bind_methods() {
|
|||
//ClassDB::bind_method(D_METHOD("is_video_mode_resizable","screen"),&_OS::is_video_mode_resizable,DEFVAL(0));
|
||||
//ClassDB::bind_method(D_METHOD("get_fullscreen_mode_list","screen"),&_OS::get_fullscreen_mode_list,DEFVAL(0));
|
||||
|
||||
ClassDB::bind_method(D_METHOD("global_menu_add_item", "menu", "label", "id", "meta"), &_OS::global_menu_add_item);
|
||||
ClassDB::bind_method(D_METHOD("global_menu_add_separator", "menu"), &_OS::global_menu_add_separator);
|
||||
ClassDB::bind_method(D_METHOD("global_menu_remove_item", "menu", "idx"), &_OS::global_menu_remove_item);
|
||||
ClassDB::bind_method(D_METHOD("global_menu_clear", "menu"), &_OS::global_menu_clear);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_video_driver_count"), &_OS::get_video_driver_count);
|
||||
ClassDB::bind_method(D_METHOD("get_video_driver_name", "driver"), &_OS::get_video_driver_name);
|
||||
ClassDB::bind_method(D_METHOD("get_current_video_driver"), &_OS::get_current_video_driver);
|
||||
|
@ -1414,6 +1432,11 @@ PoolVector<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);
|
||||
}
|
||||
|
||||
bool _Geometry::is_point_in_circle(const Vector2 &p_point, const Vector2 &p_circle_pos, real_t p_circle_radius) {
|
||||
|
||||
return Geometry::is_point_in_circle(p_point, p_circle_pos, p_circle_radius);
|
||||
}
|
||||
|
||||
real_t _Geometry::segment_intersects_circle(const Vector2 &p_from, const Vector2 &p_to, const Vector2 &p_circle_pos, real_t p_circle_radius) {
|
||||
|
||||
return Geometry::segment_intersects_circle(p_from, p_to, p_circle_pos, p_circle_radius);
|
||||
|
@ -1666,11 +1689,6 @@ Array _Geometry::offset_polyline_2d(const Vector<Vector2> &p_polygon, real_t p_d
|
|||
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 ret;
|
||||
|
@ -1709,6 +1727,7 @@ void _Geometry::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("build_box_planes", "extents"), &_Geometry::build_box_planes);
|
||||
ClassDB::bind_method(D_METHOD("build_cylinder_planes", "radius", "height", "sides", "axis"), &_Geometry::build_cylinder_planes, DEFVAL(Vector3::AXIS_Z));
|
||||
ClassDB::bind_method(D_METHOD("build_capsule_planes", "radius", "height", "sides", "lats", "axis"), &_Geometry::build_capsule_planes, DEFVAL(Vector3::AXIS_Z));
|
||||
ClassDB::bind_method(D_METHOD("is_point_in_circle", "point", "circle_position", "circle_radius"), &_Geometry::is_point_in_circle);
|
||||
ClassDB::bind_method(D_METHOD("segment_intersects_circle", "segment_from", "segment_to", "circle_position", "circle_radius"), &_Geometry::segment_intersects_circle);
|
||||
ClassDB::bind_method(D_METHOD("segment_intersects_segment_2d", "from_a", "to_a", "from_b", "to_b"), &_Geometry::segment_intersects_segment_2d);
|
||||
ClassDB::bind_method(D_METHOD("line_intersects_line_2d", "from_a", "dir_a", "from_b", "dir_b"), &_Geometry::line_intersects_line_2d);
|
||||
|
@ -1749,8 +1768,6 @@ void _Geometry::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("offset_polygon_2d", "polygon", "delta", "join_type"), &_Geometry::offset_polygon_2d, DEFVAL(JOIN_SQUARE));
|
||||
ClassDB::bind_method(D_METHOD("offset_polyline_2d", "polyline", "delta", "join_type", "end_type"), &_Geometry::offset_polyline_2d, DEFVAL(JOIN_SQUARE), DEFVAL(END_SQUARE));
|
||||
|
||||
ClassDB::bind_method(D_METHOD("transform_points_2d", "points", "transform"), &_Geometry::transform_points_2d);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("make_atlas", "sizes"), &_Geometry::make_atlas);
|
||||
|
||||
BIND_ENUM_CONSTANT(OPERATION_UNION);
|
||||
|
@ -2621,8 +2638,7 @@ void _Thread::_start_func(void *ud) {
|
|||
}
|
||||
}
|
||||
|
||||
ERR_EXPLAIN("Could not call function '" + t->target_method.operator String() + "'' starting thread ID: " + t->get_id() + " Reason: " + reason);
|
||||
ERR_FAIL();
|
||||
ERR_FAIL_MSG("Could not call function '" + t->target_method.operator String() + "'' starting thread ID: " + t->get_id() + " Reason: " + reason + ".");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2704,10 +2720,7 @@ _Thread::_Thread() {
|
|||
|
||||
_Thread::~_Thread() {
|
||||
|
||||
if (active) {
|
||||
ERR_EXPLAIN("Reference to a Thread object object was lost while the thread is still running...");
|
||||
}
|
||||
ERR_FAIL_COND(active);
|
||||
ERR_FAIL_COND_MSG(active, "Reference to a Thread object object was lost while the thread is still running...");
|
||||
}
|
||||
/////////////////////////////////////
|
||||
|
||||
|
|
|
@ -109,11 +109,11 @@ public:
|
|||
};
|
||||
|
||||
enum PowerState {
|
||||
POWERSTATE_UNKNOWN, /**< cannot determine power status */
|
||||
POWERSTATE_ON_BATTERY, /**< Not plugged in, running on the battery */
|
||||
POWERSTATE_NO_BATTERY, /**< Plugged in, no battery available */
|
||||
POWERSTATE_CHARGING, /**< Plugged in, charging battery */
|
||||
POWERSTATE_CHARGED /**< Plugged in, battery charged */
|
||||
POWERSTATE_UNKNOWN, // Cannot determine power status.
|
||||
POWERSTATE_ON_BATTERY, // Not plugged in, running on the battery.
|
||||
POWERSTATE_NO_BATTERY, // Plugged in, no battery available.
|
||||
POWERSTATE_CHARGING, // Plugged in, charging battery.
|
||||
POWERSTATE_CHARGED // Plugged in, battery charged.
|
||||
};
|
||||
|
||||
enum Weekday {
|
||||
|
@ -127,8 +127,8 @@ public:
|
|||
};
|
||||
|
||||
enum Month {
|
||||
/// Start at 1 to follow Windows SYSTEMTIME structure
|
||||
/// https://msdn.microsoft.com/en-us/library/windows/desktop/ms724950(v=vs.85).aspx
|
||||
// Start at 1 to follow Windows SYSTEMTIME structure
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms724950(v=vs.85).aspx
|
||||
MONTH_JANUARY = 1,
|
||||
MONTH_FEBRUARY,
|
||||
MONTH_MARCH,
|
||||
|
@ -143,6 +143,11 @@ public:
|
|||
MONTH_DECEMBER
|
||||
};
|
||||
|
||||
void global_menu_add_item(const String &p_menu, const String &p_label, const Variant &p_signal, const Variant &p_meta);
|
||||
void global_menu_add_separator(const String &p_menu);
|
||||
void global_menu_remove_item(const String &p_menu, int p_idx);
|
||||
void global_menu_clear(const String &p_menu);
|
||||
|
||||
Point2 get_mouse_position() const;
|
||||
void set_window_title(const String &p_title);
|
||||
int get_mouse_button_state() const;
|
||||
|
@ -259,24 +264,6 @@ public:
|
|||
bool is_scancode_unicode(uint32_t p_unicode) const;
|
||||
int find_scancode_from_string(const String &p_code) const;
|
||||
|
||||
/*
|
||||
struct Date {
|
||||
|
||||
int year;
|
||||
Month month;
|
||||
int day;
|
||||
Weekday weekday;
|
||||
bool dst;
|
||||
};
|
||||
|
||||
struct Time {
|
||||
|
||||
int hour;
|
||||
int min;
|
||||
int sec;
|
||||
};
|
||||
*/
|
||||
|
||||
void set_use_file_access_save_and_swap(bool p_enable);
|
||||
|
||||
void set_native_icon(const String &p_filename);
|
||||
|
@ -404,6 +391,7 @@ public:
|
|||
PoolVector<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_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);
|
||||
int get_uv84_normal_bit(const Vector3 &p_vector);
|
||||
|
||||
|
@ -420,17 +408,17 @@ public:
|
|||
OPERATION_INTERSECTION,
|
||||
OPERATION_XOR
|
||||
};
|
||||
// 2D polygon boolean operations
|
||||
Array merge_polygons_2d(const Vector<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 {
|
||||
JOIN_SQUARE,
|
||||
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_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);
|
||||
|
||||
_Geometry();
|
||||
|
@ -486,24 +472,24 @@ public:
|
|||
Error open_encrypted_pass(const String &p_path, ModeFlags p_mode_flags, const String &p_pass);
|
||||
Error open_compressed(const String &p_path, ModeFlags p_mode_flags, CompressionMode p_compress_mode = COMPRESSION_FASTLZ);
|
||||
|
||||
Error open(const String &p_path, ModeFlags p_mode_flags); ///< open a file
|
||||
void close(); ///< close a file
|
||||
bool is_open() const; ///< true when file is open
|
||||
Error open(const String &p_path, ModeFlags p_mode_flags); // open a file.
|
||||
void close(); // Close a file.
|
||||
bool is_open() const; // True when file is open.
|
||||
|
||||
String get_path() const; /// returns the path for the current open file
|
||||
String get_path_absolute() const; /// returns the absolute path for the current open file
|
||||
String get_path() const; // Returns the path for the current open file.
|
||||
String get_path_absolute() const; // Returns the absolute path for the current open file.
|
||||
|
||||
void seek(int64_t p_position); ///< seek to a given position
|
||||
void seek_end(int64_t p_position = 0); ///< seek from the end of file
|
||||
int64_t get_position() const; ///< get position in the file
|
||||
int64_t get_len() const; ///< get size of the file
|
||||
void seek(int64_t p_position); // Seek to a given position.
|
||||
void seek_end(int64_t p_position = 0); // Seek from the end of file.
|
||||
int64_t get_position() const; // Get position in the file.
|
||||
int64_t get_len() const; // Get size of the file.
|
||||
|
||||
bool eof_reached() const; ///< reading passed EOF
|
||||
bool eof_reached() const; // Reading passed EOF.
|
||||
|
||||
uint8_t get_8() const; ///< get a byte
|
||||
uint16_t get_16() const; ///< get 16 bits uint
|
||||
uint32_t get_32() const; ///< get 32 bits uint
|
||||
uint64_t get_64() const; ///< get 64 bits uint
|
||||
uint8_t get_8() const; // Get a byte.
|
||||
uint16_t get_16() const; // Get 16 bits uint.
|
||||
uint32_t get_32() const; // Get 32 bits uint.
|
||||
uint64_t get_64() const; // Get 64 bits uint.
|
||||
|
||||
float get_float() const;
|
||||
double get_double() const;
|
||||
|
@ -511,27 +497,27 @@ public:
|
|||
|
||||
Variant get_var(bool p_allow_objects = false) const;
|
||||
|
||||
PoolVector<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;
|
||||
Vector<String> get_csv_line(const String &p_delim = ",") const;
|
||||
String get_as_text() const;
|
||||
String get_md5(const String &p_path) const;
|
||||
String get_sha256(const String &p_path) const;
|
||||
|
||||
/**< use this for files WRITTEN in _big_ endian machines (ie, amiga/mac)
|
||||
/* Use this for files WRITTEN in _big_ endian machines (ie, amiga/mac).
|
||||
* It's not about the current CPU type but file formats.
|
||||
* this flags get reset to false (little endian) on each open
|
||||
* This flags get reset to false (little endian) on each open.
|
||||
*/
|
||||
|
||||
void set_endian_swap(bool p_swap);
|
||||
bool get_endian_swap();
|
||||
|
||||
Error get_error() const; ///< get last error
|
||||
Error get_error() const; // Get last error.
|
||||
|
||||
void store_8(uint8_t p_dest); ///< store a byte
|
||||
void store_16(uint16_t p_dest); ///< store 16 bits uint
|
||||
void store_32(uint32_t p_dest); ///< store 32 bits uint
|
||||
void store_64(uint64_t p_dest); ///< store 64 bits uint
|
||||
void store_8(uint8_t p_dest); // Store a byte.
|
||||
void store_16(uint16_t p_dest); // Store 16 bits uint.
|
||||
void store_32(uint32_t p_dest); // Store 32 bits uint.
|
||||
void store_64(uint64_t p_dest); // Store 64 bits uint.
|
||||
|
||||
void store_float(float p_dest);
|
||||
void store_double(double p_dest);
|
||||
|
@ -544,11 +530,11 @@ public:
|
|||
virtual void store_pascal_string(const String &p_string);
|
||||
virtual String get_pascal_string();
|
||||
|
||||
void store_buffer(const PoolVector<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);
|
||||
|
||||
bool file_exists(const String &p_name) const; ///< return true if a file exists
|
||||
bool file_exists(const String &p_name) const; // Return true if a file exists.
|
||||
|
||||
uint64_t get_modified_time(const String &p_file) const;
|
||||
|
||||
|
@ -570,18 +556,18 @@ protected:
|
|||
public:
|
||||
Error open(const String &p_path);
|
||||
|
||||
Error list_dir_begin(bool p_skip_navigational = false, bool p_skip_hidden = false); ///< This starts dir listing
|
||||
Error list_dir_begin(bool p_skip_navigational = false, bool p_skip_hidden = false); // This starts dir listing.
|
||||
String get_next();
|
||||
bool current_is_dir() const;
|
||||
|
||||
void list_dir_end(); ///<
|
||||
void list_dir_end();
|
||||
|
||||
int get_drive_count();
|
||||
String get_drive(int p_drive);
|
||||
int get_current_drive();
|
||||
|
||||
Error change_dir(String p_dir); ///< can be relative or absolute, return false on success
|
||||
String get_current_dir(); ///< return current dir location
|
||||
Error change_dir(String p_dir); // Can be relative or absolute, return false on success.
|
||||
String get_current_dir(); // Return current dir location.
|
||||
|
||||
Error make_dir(String p_dir);
|
||||
Error make_dir_recursive(String p_dir);
|
||||
|
|
|
@ -480,6 +480,7 @@ uint64_t ClassDB::get_api_hash(APIType p_api) {
|
|||
for (List<StringName>::Element *F = snames.front(); F; F = F->next()) {
|
||||
|
||||
PropertySetGet *psg = t->property_setget.getptr(F->get());
|
||||
ERR_FAIL_COND_V(!psg, 0);
|
||||
|
||||
hash = hash_djb2_one_64(F->get().hash(), hash);
|
||||
hash = hash_djb2_one_64(psg->setter.hash(), hash);
|
||||
|
@ -835,10 +836,7 @@ void ClassDB::add_signal(StringName p_class, const MethodInfo &p_signal) {
|
|||
#ifdef DEBUG_METHODS_ENABLED
|
||||
ClassInfo *check = type;
|
||||
while (check) {
|
||||
if (check->signal_map.has(sname)) {
|
||||
ERR_EXPLAIN("Type " + String(p_class) + " already has signal: " + String(sname));
|
||||
ERR_FAIL();
|
||||
}
|
||||
ERR_FAIL_COND_MSG(check->signal_map.has(sname), "Type " + String(p_class) + " already has signal: " + String(sname) + ".");
|
||||
check = check->inherits_ptr;
|
||||
}
|
||||
#endif
|
||||
|
@ -923,16 +921,11 @@ void ClassDB::add_property(StringName p_class, const PropertyInfo &p_pinfo, cons
|
|||
if (p_setter) {
|
||||
mb_set = get_method(p_class, p_setter);
|
||||
#ifdef DEBUG_METHODS_ENABLED
|
||||
if (!mb_set) {
|
||||
ERR_EXPLAIN("Invalid Setter: " + p_class + "::" + p_setter + " for property: " + p_pinfo.name);
|
||||
ERR_FAIL();
|
||||
} else {
|
||||
int exp_args = 1 + (p_index >= 0 ? 1 : 0);
|
||||
if (mb_set->get_argument_count() != exp_args) {
|
||||
ERR_EXPLAIN("Invalid Function for Setter: " + p_class + "::" + p_setter + " for property: " + p_pinfo.name);
|
||||
ERR_FAIL();
|
||||
}
|
||||
}
|
||||
|
||||
ERR_FAIL_COND_MSG(!mb_set, "Invalid setter: " + p_class + "::" + p_setter + " for property: " + p_pinfo.name + ".");
|
||||
|
||||
int exp_args = 1 + (p_index >= 0 ? 1 : 0);
|
||||
ERR_FAIL_COND_MSG(mb_set->get_argument_count() != exp_args, "Invalid function for setter: " + p_class + "::" + p_setter + " for property: " + p_pinfo.name + ".");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -942,25 +935,15 @@ void ClassDB::add_property(StringName p_class, const PropertyInfo &p_pinfo, cons
|
|||
mb_get = get_method(p_class, p_getter);
|
||||
#ifdef DEBUG_METHODS_ENABLED
|
||||
|
||||
if (!mb_get) {
|
||||
ERR_EXPLAIN("Invalid Getter: " + p_class + "::" + p_getter + " for property: " + p_pinfo.name);
|
||||
ERR_FAIL();
|
||||
} else {
|
||||
ERR_FAIL_COND_MSG(!mb_get, "Invalid getter: " + p_class + "::" + p_getter + " for property: " + p_pinfo.name + ".");
|
||||
|
||||
int exp_args = 0 + (p_index >= 0 ? 1 : 0);
|
||||
if (mb_get->get_argument_count() != exp_args) {
|
||||
ERR_EXPLAIN("Invalid Function for Getter: " + p_class + "::" + p_getter + " for property: " + p_pinfo.name);
|
||||
ERR_FAIL();
|
||||
}
|
||||
}
|
||||
int exp_args = 0 + (p_index >= 0 ? 1 : 0);
|
||||
ERR_FAIL_COND_MSG(mb_get->get_argument_count() != exp_args, "Invalid function for getter: " + p_class + "::" + p_getter + " for property: " + p_pinfo.name + ".");
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef DEBUG_METHODS_ENABLED
|
||||
if (type->property_setget.has(p_pinfo.name)) {
|
||||
ERR_EXPLAIN("Object " + p_class + " already has property: " + p_pinfo.name);
|
||||
ERR_FAIL();
|
||||
}
|
||||
ERR_FAIL_COND_MSG(type->property_setget.has(p_pinfo.name), "Object " + p_class + " already has property: " + p_pinfo.name + ".");
|
||||
#endif
|
||||
|
||||
OBJTYPE_WLOCK
|
||||
|
@ -1239,32 +1222,26 @@ MethodBind *ClassDB::bind_methodfi(uint32_t p_flags, MethodBind *p_bind, const c
|
|||
|
||||
#ifdef DEBUG_ENABLED
|
||||
|
||||
if (has_method(instance_type, mdname)) {
|
||||
ERR_EXPLAIN("Class " + String(instance_type) + " already has a method " + String(mdname));
|
||||
ERR_FAIL_V(NULL);
|
||||
}
|
||||
ERR_FAIL_COND_V_MSG(has_method(instance_type, mdname), NULL, "Class " + String(instance_type) + " already has a method " + String(mdname) + ".");
|
||||
#endif
|
||||
|
||||
ClassInfo *type = classes.getptr(instance_type);
|
||||
if (!type) {
|
||||
ERR_PRINTS("Couldn't bind method '" + mdname + "' for instance: " + instance_type);
|
||||
memdelete(p_bind);
|
||||
ERR_FAIL_V(NULL);
|
||||
ERR_FAIL_V_MSG(NULL, "Couldn't bind method '" + mdname + "' for instance: " + instance_type + ".");
|
||||
}
|
||||
|
||||
if (type->method_map.has(mdname)) {
|
||||
memdelete(p_bind);
|
||||
// overloading not supported
|
||||
ERR_EXPLAIN("Method already bound: " + instance_type + "::" + mdname);
|
||||
ERR_FAIL_V(NULL);
|
||||
ERR_FAIL_V_MSG(NULL, "Method already bound: " + instance_type + "::" + mdname + ".");
|
||||
}
|
||||
|
||||
#ifdef DEBUG_METHODS_ENABLED
|
||||
|
||||
if (method_name.args.size() > p_bind->get_argument_count()) {
|
||||
memdelete(p_bind);
|
||||
ERR_EXPLAIN("Method definition provides more arguments than the method actually has: " + instance_type + "::" + mdname);
|
||||
ERR_FAIL_V(NULL);
|
||||
ERR_FAIL_V_MSG(NULL, "Method definition provides more arguments than the method actually has: " + instance_type + "::" + mdname + ".");
|
||||
}
|
||||
|
||||
p_bind->set_argument_names(method_name.args);
|
||||
|
|
|
@ -35,10 +35,6 @@
|
|||
#include "core/object.h"
|
||||
#include "core/print_string.h"
|
||||
|
||||
/**
|
||||
@author Juan Linietsky <reduzio@gmail.com>
|
||||
*/
|
||||
|
||||
/** To bind more then 6 parameters include this:
|
||||
* #include "core/method_bind_ext.gen.inc"
|
||||
*/
|
||||
|
@ -310,8 +306,7 @@ public:
|
|||
if (type->method_map.has(p_name)) {
|
||||
memdelete(bind);
|
||||
// overloading not supported
|
||||
ERR_EXPLAIN("Method already bound: " + instance_type + "::" + p_name);
|
||||
ERR_FAIL_V(NULL);
|
||||
ERR_FAIL_V_MSG(NULL, "Method already bound: " + instance_type + "::" + p_name + ".");
|
||||
}
|
||||
type->method_map[p_name] = bind;
|
||||
#ifdef DEBUG_METHODS_ENABLED
|
||||
|
|
|
@ -335,36 +335,23 @@ Color Color::html(const String &p_color) {
|
|||
} else if (color.length() == 6) {
|
||||
alpha = false;
|
||||
} else {
|
||||
ERR_EXPLAIN("Invalid Color Code: " + p_color);
|
||||
ERR_FAIL_V(Color());
|
||||
ERR_FAIL_V_MSG(Color(), "Invalid color code: " + p_color + ".");
|
||||
}
|
||||
|
||||
int a = 255;
|
||||
if (alpha) {
|
||||
a = _parse_col(color, 0);
|
||||
if (a < 0) {
|
||||
ERR_EXPLAIN("Invalid Color Code: " + p_color);
|
||||
ERR_FAIL_V(Color());
|
||||
}
|
||||
ERR_FAIL_COND_V_MSG(a < 0, Color(), "Invalid color code: " + p_color + ".");
|
||||
}
|
||||
|
||||
int from = alpha ? 2 : 0;
|
||||
|
||||
int r = _parse_col(color, from + 0);
|
||||
if (r < 0) {
|
||||
ERR_EXPLAIN("Invalid Color Code: " + p_color);
|
||||
ERR_FAIL_V(Color());
|
||||
}
|
||||
ERR_FAIL_COND_V_MSG(r < 0, Color(), "Invalid color code: " + p_color + ".");
|
||||
int g = _parse_col(color, from + 2);
|
||||
if (g < 0) {
|
||||
ERR_EXPLAIN("Invalid Color Code: " + p_color);
|
||||
ERR_FAIL_V(Color());
|
||||
}
|
||||
ERR_FAIL_COND_V_MSG(g < 0, Color(), "Invalid color code: " + p_color + ".");
|
||||
int b = _parse_col(color, from + 4);
|
||||
if (b < 0) {
|
||||
ERR_EXPLAIN("Invalid Color Code: " + p_color);
|
||||
ERR_FAIL_V(Color());
|
||||
}
|
||||
ERR_FAIL_COND_V_MSG(b < 0, Color(), "Invalid color code: " + p_color + ".");
|
||||
|
||||
return Color(r / 255.0, g / 255.0, b / 255.0, a / 255.0);
|
||||
}
|
||||
|
@ -425,12 +412,8 @@ Color Color::named(const String &p_name) {
|
|||
name = name.to_lower();
|
||||
|
||||
const Map<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) {
|
||||
|
@ -523,8 +506,7 @@ Color Color::from_hsv(float p_h, float p_s, float p_v, float p_a) const {
|
|||
// FIXME: Remove once Godot 3.1 has been released
|
||||
float Color::gray() const {
|
||||
|
||||
ERR_EXPLAIN("Color.gray() is deprecated and will be removed in a future version. Use Color.get_v() for a better grayscale approximation.");
|
||||
WARN_DEPRECATED;
|
||||
WARN_DEPRECATED_MSG("Color.gray() is deprecated and will be removed in a future version. Use Color.get_v() for a better grayscale approximation.");
|
||||
return (r + g + b) / 3.0;
|
||||
}
|
||||
|
||||
|
|
|
@ -33,9 +33,7 @@
|
|||
|
||||
#include "core/math/math_funcs.h"
|
||||
#include "core/ustring.h"
|
||||
/**
|
||||
@author Juan Linietsky <reduzio@gmail.com>
|
||||
*/
|
||||
|
||||
struct Color {
|
||||
|
||||
union {
|
||||
|
|
|
@ -37,10 +37,6 @@
|
|||
#include "core/simple_type.h"
|
||||
#include "core/typedefs.h"
|
||||
|
||||
/**
|
||||
@author Juan Linietsky <reduzio@gmail.com>
|
||||
*/
|
||||
|
||||
#define COMMA(N) _COMMA_##N
|
||||
#define _COMMA_0
|
||||
#define _COMMA_1 ,
|
||||
|
|
38
core/crypto/SCsub
Normal file
38
core/crypto/SCsub
Normal file
|
@ -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
core/crypto/crypto.cpp
Normal file
170
core/crypto/crypto.cpp
Normal file
|
@ -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
core/crypto/crypto.h
Normal file
105
core/crypto/crypto.h
Normal file
|
@ -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
|
|
@ -52,7 +52,7 @@ Error CryptoCore::MD5Context::start() {
|
|||
return ret ? FAILED : OK;
|
||||
}
|
||||
|
||||
Error CryptoCore::MD5Context::update(uint8_t *p_src, size_t p_len) {
|
||||
Error CryptoCore::MD5Context::update(const uint8_t *p_src, size_t p_len) {
|
||||
int ret = mbedtls_md5_update_ret((mbedtls_md5_context *)ctx, p_src, p_len);
|
||||
return ret ? FAILED : OK;
|
||||
}
|
||||
|
@ -62,6 +62,32 @@ Error CryptoCore::MD5Context::finish(unsigned char r_hash[16]) {
|
|||
return ret ? FAILED : OK;
|
||||
}
|
||||
|
||||
// SHA1
|
||||
CryptoCore::SHA1Context::SHA1Context() {
|
||||
ctx = memalloc(sizeof(mbedtls_sha1_context));
|
||||
mbedtls_sha1_init((mbedtls_sha1_context *)ctx);
|
||||
}
|
||||
|
||||
CryptoCore::SHA1Context::~SHA1Context() {
|
||||
mbedtls_sha1_free((mbedtls_sha1_context *)ctx);
|
||||
memfree((mbedtls_sha1_context *)ctx);
|
||||
}
|
||||
|
||||
Error CryptoCore::SHA1Context::start() {
|
||||
int ret = mbedtls_sha1_starts_ret((mbedtls_sha1_context *)ctx);
|
||||
return ret ? FAILED : OK;
|
||||
}
|
||||
|
||||
Error CryptoCore::SHA1Context::update(const uint8_t *p_src, size_t p_len) {
|
||||
int ret = mbedtls_sha1_update_ret((mbedtls_sha1_context *)ctx, p_src, p_len);
|
||||
return ret ? FAILED : OK;
|
||||
}
|
||||
|
||||
Error CryptoCore::SHA1Context::finish(unsigned char r_hash[20]) {
|
||||
int ret = mbedtls_sha1_finish_ret((mbedtls_sha1_context *)ctx, r_hash);
|
||||
return ret ? FAILED : OK;
|
||||
}
|
||||
|
||||
// SHA256
|
||||
CryptoCore::SHA256Context::SHA256Context() {
|
||||
ctx = memalloc(sizeof(mbedtls_sha256_context));
|
||||
|
@ -78,12 +104,12 @@ Error CryptoCore::SHA256Context::start() {
|
|||
return ret ? FAILED : OK;
|
||||
}
|
||||
|
||||
Error CryptoCore::SHA256Context::update(uint8_t *p_src, size_t p_len) {
|
||||
Error CryptoCore::SHA256Context::update(const uint8_t *p_src, size_t p_len) {
|
||||
int ret = mbedtls_sha256_update_ret((mbedtls_sha256_context *)ctx, p_src, p_len);
|
||||
return ret ? FAILED : OK;
|
||||
}
|
||||
|
||||
Error CryptoCore::SHA256Context::finish(unsigned char r_hash[16]) {
|
||||
Error CryptoCore::SHA256Context::finish(unsigned char r_hash[32]) {
|
||||
int ret = mbedtls_sha256_finish_ret((mbedtls_sha256_context *)ctx, r_hash);
|
||||
return ret ? FAILED : OK;
|
||||
}
|
|
@ -46,10 +46,24 @@ public:
|
|||
~MD5Context();
|
||||
|
||||
Error start();
|
||||
Error update(uint8_t *p_src, size_t p_len);
|
||||
Error update(const uint8_t *p_src, size_t p_len);
|
||||
Error finish(unsigned char r_hash[16]);
|
||||
};
|
||||
|
||||
class SHA1Context {
|
||||
|
||||
private:
|
||||
void *ctx; // To include, or not to include...
|
||||
|
||||
public:
|
||||
SHA1Context();
|
||||
~SHA1Context();
|
||||
|
||||
Error start();
|
||||
Error update(const uint8_t *p_src, size_t p_len);
|
||||
Error finish(unsigned char r_hash[20]);
|
||||
};
|
||||
|
||||
class SHA256Context {
|
||||
|
||||
private:
|
||||
|
@ -60,8 +74,8 @@ public:
|
|||
~SHA256Context();
|
||||
|
||||
Error start();
|
||||
Error update(uint8_t *p_src, size_t p_len);
|
||||
Error finish(unsigned char r_hash[16]);
|
||||
Error update(const uint8_t *p_src, size_t p_len);
|
||||
Error finish(unsigned char r_hash[32]);
|
||||
};
|
||||
|
||||
class AESContext {
|
137
core/crypto/hashing_context.cpp
Normal file
137
core/crypto/hashing_context.cpp
Normal file
|
@ -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
core/crypto/hashing_context.h
Normal file
66
core/crypto/hashing_context.h
Normal file
|
@ -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
|
|
@ -197,10 +197,7 @@ void Engine::add_singleton(const Singleton &p_singleton) {
|
|||
Object *Engine::get_singleton_object(const String &p_name) const {
|
||||
|
||||
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();
|
||||
};
|
||||
|
||||
|
|
|
@ -140,6 +140,16 @@ extern bool _err_error_exists;
|
|||
_err_error_exists = false; \
|
||||
} while (0); // (*)
|
||||
|
||||
#define ERR_FAIL_INDEX_MSG(m_index, m_size, m_msg) \
|
||||
do { \
|
||||
if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \
|
||||
ERR_EXPLAIN(m_msg); \
|
||||
_err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size)); \
|
||||
return; \
|
||||
} \
|
||||
_err_error_exists = false; \
|
||||
} while (0); // (*)
|
||||
|
||||
/** An index has failed if m_index<0 or m_index >=m_size, the function exits.
|
||||
* This function returns an error value, if returning Error, please select the most
|
||||
* appropriate error condition from error_macros.h
|
||||
|
@ -154,6 +164,16 @@ extern bool _err_error_exists;
|
|||
_err_error_exists = false; \
|
||||
} while (0); // (*)
|
||||
|
||||
#define ERR_FAIL_INDEX_V_MSG(m_index, m_size, m_retval, m_msg) \
|
||||
do { \
|
||||
if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \
|
||||
ERR_EXPLAIN(m_msg); \
|
||||
_err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size)); \
|
||||
return m_retval; \
|
||||
} \
|
||||
_err_error_exists = false; \
|
||||
} while (0); // (*)
|
||||
|
||||
/** An index has failed if m_index >=m_size, the function exits.
|
||||
* This function returns an error value, if returning Error, please select the most
|
||||
* appropriate error condition from error_macros.h
|
||||
|
@ -168,6 +188,16 @@ extern bool _err_error_exists;
|
|||
_err_error_exists = false; \
|
||||
} while (0); // (*)
|
||||
|
||||
#define ERR_FAIL_UNSIGNED_INDEX_V_MSG(m_index, m_size, m_retval, m_msg) \
|
||||
do { \
|
||||
if (unlikely((m_index) >= (m_size))) { \
|
||||
ERR_EXPLAIN(m_msg); \
|
||||
_err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size)); \
|
||||
return m_retval; \
|
||||
} \
|
||||
_err_error_exists = false; \
|
||||
} while (0); // (*)
|
||||
|
||||
/** Use this one if there is no sensible fallback, that is, the error is unrecoverable.
|
||||
* We'll return a null reference and try to keep running.
|
||||
*/
|
||||
|
@ -179,6 +209,15 @@ extern bool _err_error_exists;
|
|||
} \
|
||||
} while (0); // (*)
|
||||
|
||||
#define CRASH_BAD_INDEX_MSG(m_index, m_size, m_msg) \
|
||||
do { \
|
||||
if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \
|
||||
ERR_EXPLAIN(m_msg); \
|
||||
_err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), true); \
|
||||
GENERATE_TRAP \
|
||||
} \
|
||||
} while (0); // (*)
|
||||
|
||||
/** An error condition happened (m_cond tested true) (WARNING this is the opposite as assert().
|
||||
* the function will exit.
|
||||
*/
|
||||
|
@ -192,6 +231,16 @@ extern bool _err_error_exists;
|
|||
_err_error_exists = false; \
|
||||
}
|
||||
|
||||
#define ERR_FAIL_NULL_MSG(m_param, m_msg) \
|
||||
{ \
|
||||
if (unlikely(!m_param)) { \
|
||||
ERR_EXPLAIN(m_msg); \
|
||||
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter ' " _STR(m_param) " ' is null."); \
|
||||
return; \
|
||||
} \
|
||||
_err_error_exists = false; \
|
||||
}
|
||||
|
||||
#define ERR_FAIL_NULL_V(m_param, m_retval) \
|
||||
{ \
|
||||
if (unlikely(!m_param)) { \
|
||||
|
@ -201,6 +250,16 @@ extern bool _err_error_exists;
|
|||
_err_error_exists = false; \
|
||||
}
|
||||
|
||||
#define ERR_FAIL_NULL_V_MSG(m_param, m_retval, m_msg) \
|
||||
{ \
|
||||
if (unlikely(!m_param)) { \
|
||||
ERR_EXPLAIN(m_msg); \
|
||||
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter ' " _STR(m_param) " ' is null."); \
|
||||
return m_retval; \
|
||||
} \
|
||||
_err_error_exists = false; \
|
||||
}
|
||||
|
||||
/** An error condition happened (m_cond tested true) (WARNING this is the opposite as assert().
|
||||
* the function will exit.
|
||||
*/
|
||||
|
@ -214,6 +273,16 @@ extern bool _err_error_exists;
|
|||
_err_error_exists = false; \
|
||||
}
|
||||
|
||||
#define ERR_FAIL_COND_MSG(m_cond, m_msg) \
|
||||
{ \
|
||||
if (unlikely(m_cond)) { \
|
||||
ERR_EXPLAIN(m_msg); \
|
||||
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition ' " _STR(m_cond) " ' is true."); \
|
||||
return; \
|
||||
} \
|
||||
_err_error_exists = false; \
|
||||
}
|
||||
|
||||
/** Use this one if there is no sensible fallback, that is, the error is unrecoverable.
|
||||
*/
|
||||
|
||||
|
@ -225,6 +294,15 @@ extern bool _err_error_exists;
|
|||
} \
|
||||
}
|
||||
|
||||
#define CRASH_COND_MSG(m_cond, m_msg) \
|
||||
{ \
|
||||
if (unlikely(m_cond)) { \
|
||||
ERR_EXPLAIN(m_msg); \
|
||||
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: Condition ' " _STR(m_cond) " ' is true."); \
|
||||
GENERATE_TRAP \
|
||||
} \
|
||||
}
|
||||
|
||||
/** An error condition happened (m_cond tested true) (WARNING this is the opposite as assert().
|
||||
* the function will exit.
|
||||
* This function returns an error value, if returning Error, please select the most
|
||||
|
@ -240,6 +318,16 @@ extern bool _err_error_exists;
|
|||
_err_error_exists = false; \
|
||||
}
|
||||
|
||||
#define ERR_FAIL_COND_V_MSG(m_cond, m_retval, m_msg) \
|
||||
{ \
|
||||
if (unlikely(m_cond)) { \
|
||||
ERR_EXPLAIN(m_msg); \
|
||||
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition ' " _STR(m_cond) " ' is true. returned: " _STR(m_retval)); \
|
||||
return m_retval; \
|
||||
} \
|
||||
_err_error_exists = false; \
|
||||
}
|
||||
|
||||
/** An error condition happened (m_cond tested true) (WARNING this is the opposite as assert().
|
||||
* the loop will skip to the next iteration.
|
||||
*/
|
||||
|
@ -253,6 +341,16 @@ extern bool _err_error_exists;
|
|||
_err_error_exists = false; \
|
||||
}
|
||||
|
||||
#define ERR_CONTINUE_MSG(m_cond, m_msg) \
|
||||
{ \
|
||||
if (unlikely(m_cond)) { \
|
||||
ERR_EXPLAIN(m_msg); \
|
||||
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition ' " _STR(m_cond) " ' is true. Continuing..:"); \
|
||||
continue; \
|
||||
} \
|
||||
_err_error_exists = false; \
|
||||
}
|
||||
|
||||
/** An error condition happened (m_cond tested true) (WARNING this is the opposite as assert().
|
||||
* the loop will break
|
||||
*/
|
||||
|
@ -266,6 +364,16 @@ extern bool _err_error_exists;
|
|||
_err_error_exists = false; \
|
||||
}
|
||||
|
||||
#define ERR_BREAK_MSG(m_cond, m_msg) \
|
||||
{ \
|
||||
if (unlikely(m_cond)) { \
|
||||
ERR_EXPLAIN(m_msg); \
|
||||
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition ' " _STR(m_cond) " ' is true. Breaking..:"); \
|
||||
break; \
|
||||
} \
|
||||
_err_error_exists = false; \
|
||||
}
|
||||
|
||||
/** Print an error string and return
|
||||
*/
|
||||
|
||||
|
@ -276,6 +384,12 @@ extern bool _err_error_exists;
|
|||
return; \
|
||||
}
|
||||
|
||||
#define ERR_FAIL_MSG(m_msg) \
|
||||
{ \
|
||||
ERR_EXPLAIN(m_msg); \
|
||||
ERR_FAIL(); \
|
||||
}
|
||||
|
||||
/** Print an error string and return with value
|
||||
*/
|
||||
|
||||
|
@ -286,6 +400,12 @@ extern bool _err_error_exists;
|
|||
return m_value; \
|
||||
}
|
||||
|
||||
#define ERR_FAIL_V_MSG(m_value, m_msg) \
|
||||
{ \
|
||||
ERR_EXPLAIN(m_msg); \
|
||||
ERR_FAIL_V(m_value); \
|
||||
}
|
||||
|
||||
/** Use this one if there is no sensible fallback, that is, the error is unrecoverable.
|
||||
*/
|
||||
|
||||
|
@ -295,6 +415,12 @@ extern bool _err_error_exists;
|
|||
GENERATE_TRAP \
|
||||
}
|
||||
|
||||
#define CRASH_NOW_MSG(m_msg) \
|
||||
{ \
|
||||
ERR_EXPLAIN(m_msg); \
|
||||
CRASH_NOW(); \
|
||||
}
|
||||
|
||||
/** Print an error string.
|
||||
*/
|
||||
|
||||
|
@ -355,4 +481,15 @@ extern bool _err_error_exists;
|
|||
} \
|
||||
}
|
||||
|
||||
#define WARN_DEPRECATED_MSG(m_msg) \
|
||||
{ \
|
||||
static volatile bool warning_shown = false; \
|
||||
if (!warning_shown) { \
|
||||
ERR_EXPLAIN(m_msg); \
|
||||
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "This method has been deprecated and will be removed in the future", ERR_HANDLER_WARNING); \
|
||||
_err_error_exists = false; \
|
||||
warning_shown = true; \
|
||||
} \
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -46,6 +46,17 @@ Variant FuncRef::call_func(const Variant **p_args, int p_argcount, Variant::Call
|
|||
return obj->call(function, p_args, p_argcount, r_error);
|
||||
}
|
||||
|
||||
Variant FuncRef::call_funcv(const Array &p_args) {
|
||||
|
||||
ERR_FAIL_COND_V(id == 0, Variant());
|
||||
|
||||
Object *obj = ObjectDB::get_instance(id);
|
||||
|
||||
ERR_FAIL_COND_V(!obj, Variant());
|
||||
|
||||
return obj->callv(function, p_args);
|
||||
}
|
||||
|
||||
void FuncRef::set_instance(Object *p_obj) {
|
||||
|
||||
ERR_FAIL_NULL(p_obj);
|
||||
|
@ -77,6 +88,8 @@ void FuncRef::_bind_methods() {
|
|||
ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "call_func", &FuncRef::call_func, mi, defargs);
|
||||
}
|
||||
|
||||
ClassDB::bind_method(D_METHOD("call_funcv", "arg_array"), &FuncRef::call_funcv);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_instance", "instance"), &FuncRef::set_instance);
|
||||
ClassDB::bind_method(D_METHOD("set_function", "name"), &FuncRef::set_function);
|
||||
ClassDB::bind_method(D_METHOD("is_valid"), &FuncRef::is_valid);
|
||||
|
|
|
@ -44,6 +44,7 @@ protected:
|
|||
|
||||
public:
|
||||
Variant call_func(const Variant **p_args, int p_argcount, Variant::CallError &r_error);
|
||||
Variant call_funcv(const Array &p_args);
|
||||
void set_instance(Object *p_obj);
|
||||
void set_function(const StringName &p_func);
|
||||
bool is_valid() const;
|
||||
|
|
|
@ -151,11 +151,7 @@ private:
|
|||
return;
|
||||
|
||||
Element **new_hash_table = memnew_arr(Element *, ((uint64_t)1 << new_hash_table_power));
|
||||
if (!new_hash_table) {
|
||||
|
||||
ERR_PRINT("Out of Memory");
|
||||
return;
|
||||
}
|
||||
ERR_FAIL_COND_MSG(!new_hash_table, "Out of memory.");
|
||||
|
||||
for (int i = 0; i < (1 << new_hash_table_power); i++) {
|
||||
|
||||
|
@ -208,10 +204,7 @@ private:
|
|||
|
||||
/* if element doesn't exist, create it */
|
||||
Element *e = memnew(Element);
|
||||
if (!e) {
|
||||
ERR_EXPLAIN("Out of memory");
|
||||
ERR_FAIL_V(NULL);
|
||||
}
|
||||
ERR_FAIL_COND_V_MSG(!e, NULL, "Out of memory.");
|
||||
uint32_t hash = Hasher::hash(p_key);
|
||||
uint32_t index = hash & ((1 << hash_table_power) - 1);
|
||||
e->next = hash_table[index];
|
||||
|
@ -498,10 +491,7 @@ public:
|
|||
} else { /* get the next key */
|
||||
|
||||
const Element *e = get_element(*p_key);
|
||||
if (!e) {
|
||||
ERR_EXPLAIN("Invalid key supplied")
|
||||
ERR_FAIL_V(NULL);
|
||||
}
|
||||
ERR_FAIL_COND_V_MSG(!e, NULL, "Invalid key supplied.");
|
||||
if (e->next) {
|
||||
/* if there is a "next" in the list, return that */
|
||||
return &e->next->pair.key;
|
||||
|
|
|
@ -83,6 +83,7 @@ const char *Image::format_names[Image::FORMAT_MAX] = {
|
|||
};
|
||||
|
||||
SavePNGFunc Image::save_png_func = NULL;
|
||||
SaveEXRFunc Image::save_exr_func = NULL;
|
||||
|
||||
void Image::_put_pixelb(int p_x, int p_y, uint32_t p_pixelsize, uint8_t *p_data, const uint8_t *p_pixel) {
|
||||
|
||||
|
@ -422,8 +423,7 @@ void Image::convert(Format p_new_format) {
|
|||
|
||||
if (format > FORMAT_RGBE9995 || p_new_format > FORMAT_RGBE9995) {
|
||||
|
||||
ERR_EXPLAIN("Cannot convert to <-> from compressed formats. Use compress() and decompress() instead.");
|
||||
ERR_FAIL();
|
||||
ERR_FAIL_MSG("Cannot convert to <-> from compressed formats. Use compress() and decompress() instead.");
|
||||
|
||||
} else if (format > FORMAT_RGBA8 || p_new_format > FORMAT_RGBA8) {
|
||||
|
||||
|
@ -753,15 +753,14 @@ static void _scale_lanczos(const uint8_t *__restrict p_src, uint8_t *__restrict
|
|||
|
||||
for (int32_t buffer_x = 0; buffer_x < dst_width; buffer_x++) {
|
||||
|
||||
float src_real_x = buffer_x * x_scale;
|
||||
int32_t src_x = src_real_x;
|
||||
|
||||
int32_t start_x = MAX(0, src_x - half_kernel + 1);
|
||||
int32_t end_x = MIN(src_width - 1, src_x + half_kernel);
|
||||
// The corresponding point on the source image
|
||||
float src_x = (buffer_x + 0.5f) * x_scale; // Offset by 0.5 so it uses the pixel's center
|
||||
int32_t start_x = MAX(0, int32_t(src_x) - half_kernel + 1);
|
||||
int32_t end_x = MIN(src_width - 1, int32_t(src_x) + half_kernel);
|
||||
|
||||
// Create the kernel used by all the pixels of the column
|
||||
for (int32_t target_x = start_x; target_x <= end_x; target_x++)
|
||||
kernel[target_x - start_x] = _lanczos((src_real_x - target_x) / scale_factor);
|
||||
kernel[target_x - start_x] = _lanczos((target_x + 0.5f - src_x) / scale_factor);
|
||||
|
||||
for (int32_t buffer_y = 0; buffer_y < src_height; buffer_y++) {
|
||||
|
||||
|
@ -804,14 +803,12 @@ static void _scale_lanczos(const uint8_t *__restrict p_src, uint8_t *__restrict
|
|||
|
||||
for (int32_t dst_y = 0; dst_y < dst_height; dst_y++) {
|
||||
|
||||
float buffer_real_y = dst_y * y_scale;
|
||||
int32_t buffer_y = buffer_real_y;
|
||||
|
||||
int32_t start_y = MAX(0, buffer_y - half_kernel + 1);
|
||||
int32_t end_y = MIN(src_height - 1, buffer_y + half_kernel);
|
||||
float buffer_y = (dst_y + 0.5f) * y_scale;
|
||||
int32_t start_y = MAX(0, int32_t(buffer_y) - half_kernel + 1);
|
||||
int32_t end_y = MIN(src_height - 1, int32_t(buffer_y) + half_kernel);
|
||||
|
||||
for (int32_t target_y = start_y; target_y <= end_y; target_y++)
|
||||
kernel[target_y - start_y] = _lanczos((buffer_real_y - target_y) / scale_factor);
|
||||
kernel[target_y - start_y] = _lanczos((target_y + 0.5f - buffer_y) / scale_factor);
|
||||
|
||||
for (int32_t dst_x = 0; dst_x < dst_width; dst_x++) {
|
||||
|
||||
|
@ -866,10 +863,7 @@ bool Image::is_size_po2() const {
|
|||
|
||||
void Image::resize_to_po2(bool p_square) {
|
||||
|
||||
if (!_can_modify(format)) {
|
||||
ERR_EXPLAIN("Cannot resize in indexed, compressed or custom image formats.");
|
||||
ERR_FAIL();
|
||||
}
|
||||
ERR_FAIL_COND_MSG(!_can_modify(format), "Cannot resize in compressed or custom image formats.");
|
||||
|
||||
int w = next_power_of_2(width);
|
||||
int h = next_power_of_2(height);
|
||||
|
@ -885,15 +879,9 @@ void Image::resize_to_po2(bool p_square) {
|
|||
|
||||
void Image::resize(int p_width, int p_height, Interpolation p_interpolation) {
|
||||
|
||||
if (data.size() == 0) {
|
||||
ERR_EXPLAIN("Cannot resize image before creating it, use create() or create_from_data() first.");
|
||||
ERR_FAIL();
|
||||
}
|
||||
ERR_FAIL_COND_MSG(data.size() == 0, "Cannot resize image before creating it, use create() or create_from_data() first.");
|
||||
|
||||
if (!_can_modify(format)) {
|
||||
ERR_EXPLAIN("Cannot resize in indexed, compressed or custom image formats.");
|
||||
ERR_FAIL();
|
||||
}
|
||||
ERR_FAIL_COND_MSG(!_can_modify(format), "Cannot resize in compressed or custom image formats.");
|
||||
|
||||
bool mipmap_aware = p_interpolation == INTERPOLATE_TRILINEAR /* || p_interpolation == INTERPOLATE_TRICUBIC */;
|
||||
|
||||
|
@ -1106,10 +1094,8 @@ void Image::resize(int p_width, int p_height, Interpolation p_interpolation) {
|
|||
|
||||
void Image::crop_from_point(int p_x, int p_y, int p_width, int p_height) {
|
||||
|
||||
if (!_can_modify(format)) {
|
||||
ERR_EXPLAIN("Cannot crop in indexed, compressed or custom image formats.");
|
||||
ERR_FAIL();
|
||||
}
|
||||
ERR_FAIL_COND_MSG(!_can_modify(format), "Cannot crop in compressed or custom image formats.");
|
||||
|
||||
ERR_FAIL_COND(p_x < 0);
|
||||
ERR_FAIL_COND(p_y < 0);
|
||||
ERR_FAIL_COND(p_width <= 0);
|
||||
|
@ -1163,10 +1149,7 @@ void Image::crop(int p_width, int p_height) {
|
|||
|
||||
void Image::flip_y() {
|
||||
|
||||
if (!_can_modify(format)) {
|
||||
ERR_EXPLAIN("Cannot flip_y in indexed, compressed or custom image formats.");
|
||||
ERR_FAIL();
|
||||
}
|
||||
ERR_FAIL_COND_MSG(!_can_modify(format), "Cannot flip_y in compressed or custom image formats.");
|
||||
|
||||
bool used_mipmaps = has_mipmaps();
|
||||
if (used_mipmaps) {
|
||||
|
@ -1199,10 +1182,7 @@ void Image::flip_y() {
|
|||
|
||||
void Image::flip_x() {
|
||||
|
||||
if (!_can_modify(format)) {
|
||||
ERR_EXPLAIN("Cannot flip_x in indexed, compressed or custom image formats.");
|
||||
ERR_FAIL();
|
||||
}
|
||||
ERR_FAIL_COND_MSG(!_can_modify(format), "Cannot flip_x in compressed or custom image formats.");
|
||||
|
||||
bool used_mipmaps = has_mipmaps();
|
||||
if (used_mipmaps) {
|
||||
|
@ -1461,15 +1441,9 @@ void Image::normalize() {
|
|||
|
||||
Error Image::generate_mipmaps(bool p_renormalize) {
|
||||
|
||||
if (!_can_modify(format)) {
|
||||
ERR_EXPLAIN("Cannot generate mipmaps in indexed, compressed or custom image formats.");
|
||||
ERR_FAIL_V(ERR_UNAVAILABLE);
|
||||
}
|
||||
ERR_FAIL_COND_V_MSG(!_can_modify(format), ERR_UNAVAILABLE, "Cannot generate mipmaps in compressed or custom image formats.");
|
||||
|
||||
if (width == 0 || height == 0) {
|
||||
ERR_EXPLAIN("Cannot generate mipmaps with width or height equal to 0.");
|
||||
ERR_FAIL_V(ERR_UNCONFIGURED);
|
||||
}
|
||||
ERR_FAIL_COND_V_MSG(width == 0 || height == 0, ERR_UNCONFIGURED, "Cannot generate mipmaps with width or height equal to 0.");
|
||||
|
||||
int mmcount;
|
||||
|
||||
|
@ -1620,10 +1594,7 @@ void Image::create(int p_width, int p_height, bool p_use_mipmaps, Format p_forma
|
|||
int mm;
|
||||
int size = _get_dst_image_size(p_width, p_height, p_format, mm, p_use_mipmaps ? -1 : 0);
|
||||
|
||||
if (size != p_data.size()) {
|
||||
ERR_EXPLAIN("Expected data size of " + itos(size) + " bytes in Image::create(), got instead " + itos(p_data.size()) + " bytes.");
|
||||
ERR_FAIL_COND(p_data.size() != size);
|
||||
}
|
||||
ERR_FAIL_COND_MSG(p_data.size() != size, "Expected data size of " + itos(size) + " bytes in Image::create(), got instead " + itos(p_data.size()) + " bytes.");
|
||||
|
||||
height = p_height;
|
||||
width = p_width;
|
||||
|
@ -1917,6 +1888,14 @@ Error Image::save_png(const String &p_path) const {
|
|||
return save_png_func(p_path, Ref<Image>((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 mm;
|
||||
|
@ -2405,10 +2384,7 @@ Color Image::get_pixel(int p_x, int p_y) const {
|
|||
|
||||
uint8_t *ptr = write_lock.ptr();
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (!ptr) {
|
||||
ERR_EXPLAIN("Image must be locked with 'lock()' before using get_pixel()");
|
||||
ERR_FAIL_V(Color());
|
||||
}
|
||||
ERR_FAIL_COND_V_MSG(!ptr, Color(), "Image must be locked with 'lock()' before using get_pixel().");
|
||||
|
||||
ERR_FAIL_INDEX_V(p_x, width, Color());
|
||||
ERR_FAIL_INDEX_V(p_y, height, Color());
|
||||
|
@ -2524,8 +2500,7 @@ Color Image::get_pixel(int p_x, int p_y) const {
|
|||
return Color::from_rgbe9995(((uint32_t *)ptr)[ofs]);
|
||||
}
|
||||
default: {
|
||||
ERR_EXPLAIN("Can't get_pixel() on compressed image, sorry.");
|
||||
ERR_FAIL_V(Color());
|
||||
ERR_FAIL_V_MSG(Color(), "Can't get_pixel() on compressed image, sorry.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2538,10 +2513,7 @@ void Image::set_pixel(int p_x, int p_y, const Color &p_color) {
|
|||
|
||||
uint8_t *ptr = write_lock.ptr();
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (!ptr) {
|
||||
ERR_EXPLAIN("Image must be locked with 'lock()' before using set_pixel()");
|
||||
ERR_FAIL();
|
||||
}
|
||||
ERR_FAIL_COND_MSG(!ptr, "Image must be locked with 'lock()' before using set_pixel().");
|
||||
|
||||
ERR_FAIL_INDEX(p_x, width);
|
||||
ERR_FAIL_INDEX(p_y, height);
|
||||
|
@ -2653,8 +2625,7 @@ void Image::set_pixel(int p_x, int p_y, const Color &p_color) {
|
|||
|
||||
} break;
|
||||
default: {
|
||||
ERR_EXPLAIN("Can't set_pixel() on compressed image, sorry.");
|
||||
ERR_FAIL();
|
||||
ERR_FAIL_MSG("Can't set_pixel() on compressed image, sorry.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2746,6 +2717,7 @@ void Image::_bind_methods() {
|
|||
|
||||
ClassDB::bind_method(D_METHOD("load", "path"), &Image::load);
|
||||
ClassDB::bind_method(D_METHOD("save_png", "path"), &Image::save_png);
|
||||
ClassDB::bind_method(D_METHOD("save_exr", "path", "grayscale"), &Image::save_exr, DEFVAL(false));
|
||||
|
||||
ClassDB::bind_method(D_METHOD("detect_alpha"), &Image::detect_alpha);
|
||||
ClassDB::bind_method(D_METHOD("is_invisible"), &Image::is_invisible);
|
||||
|
|
|
@ -49,11 +49,14 @@ class Image;
|
|||
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 Error (*SaveEXRFunc)(const String &p_path, const Ref<Image> &p_img, bool p_grayscale);
|
||||
|
||||
class Image : public Resource {
|
||||
GDCLASS(Image, Resource);
|
||||
|
||||
public:
|
||||
static SavePNGFunc save_png_func;
|
||||
static SaveEXRFunc save_exr_func;
|
||||
|
||||
enum {
|
||||
MAX_WIDTH = 16384, // force a limit somehow
|
||||
|
@ -217,9 +220,7 @@ public:
|
|||
|
||||
/**
|
||||
* Resize the image, using the preferred interpolation method.
|
||||
* Indexed-Color images always use INTERPOLATE_NEAREST.
|
||||
*/
|
||||
|
||||
void resize_to_po2(bool p_square = false);
|
||||
void resize(int p_width, int p_height, Interpolation p_interpolation = INTERPOLATE_BILINEAR);
|
||||
void shrink_x2();
|
||||
|
@ -258,6 +259,7 @@ public:
|
|||
|
||||
Error load(const String &p_path);
|
||||
Error save_png(const String &p_path) const;
|
||||
Error save_exr(const String &p_path, bool p_grayscale) const;
|
||||
|
||||
/**
|
||||
* create an empty image
|
||||
|
|
|
@ -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 {
|
||||
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;
|
||||
if (input_event_action.is_valid()) {
|
||||
|
|
|
@ -86,10 +86,7 @@ void ConfigFile::set_value(const String &p_section, const String &p_key, const V
|
|||
Variant ConfigFile::get_value(const String &p_section, const String &p_key, Variant p_default) const {
|
||||
|
||||
if (!values.has(p_section) || !values[p_section].has(p_key)) {
|
||||
if (p_default.get_type() == Variant::NIL) {
|
||||
ERR_EXPLAIN("Couldn't find the given section/key and no default was given");
|
||||
ERR_FAIL_V(p_default);
|
||||
}
|
||||
ERR_FAIL_COND_V_MSG(p_default.get_type() == Variant::NIL, p_default, "Couldn't find the given section/key and no default was given.");
|
||||
return p_default;
|
||||
}
|
||||
return values[p_section][p_key];
|
||||
|
@ -204,7 +201,7 @@ Error ConfigFile::load(const String &p_path) {
|
|||
FileAccess *f = FileAccess::open(p_path, FileAccess::READ, &err);
|
||||
|
||||
if (!f)
|
||||
return ERR_CANT_OPEN;
|
||||
return err;
|
||||
|
||||
return _internal_load(p_path, f);
|
||||
}
|
||||
|
@ -271,7 +268,7 @@ Error ConfigFile::_internal_load(const String &p_path, FileAccess *f) {
|
|||
memdelete(f);
|
||||
return OK;
|
||||
} else if (err != OK) {
|
||||
ERR_PRINTS("ConfgFile::load - " + p_path + ":" + itos(lines) + " error: " + error_text);
|
||||
ERR_PRINTS("ConfgFile::load - " + p_path + ":" + itos(lines) + " error: " + error_text + ".");
|
||||
memdelete(f);
|
||||
return err;
|
||||
}
|
||||
|
|
|
@ -87,10 +87,8 @@ bool FileAccessBuffered::eof_reached() const {
|
|||
}
|
||||
|
||||
uint8_t FileAccessBuffered::get_8() const {
|
||||
if (!file.open) {
|
||||
ERR_EXPLAIN("Can't get data, when file is not opened.");
|
||||
ERR_FAIL_V(0);
|
||||
}
|
||||
|
||||
ERR_FAIL_COND_V_MSG(!file.open, 0, "Can't get data, when file is not opened.");
|
||||
|
||||
uint8_t byte = 0;
|
||||
if (cache_data_left() >= 1) {
|
||||
|
@ -104,10 +102,8 @@ uint8_t FileAccessBuffered::get_8() const {
|
|||
}
|
||||
|
||||
int FileAccessBuffered::get_buffer(uint8_t *p_dest, int p_length) const {
|
||||
if (!file.open) {
|
||||
ERR_EXPLAIN("Can't get buffer, when file is not opened.");
|
||||
ERR_FAIL_V(-1);
|
||||
}
|
||||
|
||||
ERR_FAIL_COND_V_MSG(!file.open, -1, "Can't get buffer, when file is not opened.");
|
||||
|
||||
if (p_length > cache_size) {
|
||||
|
||||
|
|
|
@ -40,10 +40,7 @@ class FileAccessBufferedFA : public FileAccessBuffered {
|
|||
|
||||
int read_data_block(int p_offset, int p_size, uint8_t *p_dest = 0) const {
|
||||
|
||||
if (!f.is_open()) {
|
||||
ERR_EXPLAIN("Can't read data block, when file is not opened.");
|
||||
ERR_FAIL_V(-1);
|
||||
}
|
||||
ERR_FAIL_COND_V_MSG(!f.is_open(), -1, "Can't read data block when file is not opened.");
|
||||
|
||||
((T *)&f)->seek(p_offset);
|
||||
|
||||
|
|
|
@ -208,7 +208,8 @@ void FileAccessCompressed::seek(size_t p_position) {
|
|||
if (p_position == read_total) {
|
||||
at_end = true;
|
||||
} else {
|
||||
|
||||
at_end = false;
|
||||
read_eof = false;
|
||||
int block_idx = p_position / block_size;
|
||||
if (block_idx != read_block) {
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
|
||||
#include "file_access_encrypted.h"
|
||||
|
||||
#include "core/math/crypto_core.h"
|
||||
#include "core/crypto/crypto_core.h"
|
||||
#include "core/os/copymem.h"
|
||||
#include "core/print_string.h"
|
||||
#include "core/variant.h"
|
||||
|
@ -94,8 +94,7 @@ Error FileAccessEncrypted::open_and_parse(FileAccess *p_base, const Vector<uint8
|
|||
unsigned char hash[16];
|
||||
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;
|
||||
}
|
||||
|
@ -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) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -90,7 +90,7 @@ void PackedData::add_path(const String &pkg_path, const String &path, uint64_t o
|
|||
}
|
||||
}
|
||||
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()) {
|
||||
cd->files.insert(filename);
|
||||
}
|
||||
|
@ -171,10 +171,8 @@ bool PackedSourcePCK::try_open_pack(const String &p_path) {
|
|||
uint32_t ver_minor = f->get_32();
|
||||
f->get_32(); // ver_rev
|
||||
|
||||
ERR_EXPLAIN("Pack version unsupported: " + itos(version));
|
||||
ERR_FAIL_COND_V(version != PACK_VERSION, false);
|
||||
ERR_EXPLAIN("Pack created with a newer version of the engine: " + itos(ver_major) + "." + itos(ver_minor));
|
||||
ERR_FAIL_COND_V(ver_major > VERSION_MAJOR || (ver_major == VERSION_MAJOR && ver_minor > VERSION_MINOR), false);
|
||||
ERR_FAIL_COND_V_MSG(version != PACK_VERSION, false, "Pack version unsupported: " + itos(version) + ".");
|
||||
ERR_FAIL_COND_V_MSG(ver_major > VERSION_MAJOR || (ver_major == VERSION_MAJOR && ver_minor > VERSION_MINOR), false, "Pack created with a newer version of the engine: " + itos(ver_major) + "." + itos(ver_minor) + ".");
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
//reserved
|
||||
|
@ -322,10 +320,9 @@ bool FileAccessPack::file_exists(const String &p_name) {
|
|||
FileAccessPack::FileAccessPack(const String &p_path, const PackedData::PackedFile &p_file) :
|
||||
pf(p_file),
|
||||
f(FileAccess::open(pf.pack, FileAccess::READ)) {
|
||||
if (!f) {
|
||||
ERR_EXPLAIN("Can't open pack-referenced file: " + String(pf.pack));
|
||||
ERR_FAIL_COND(!f);
|
||||
}
|
||||
|
||||
ERR_FAIL_COND_MSG(!f, "Can't open pack-referenced file: " + String(pf.pack) + ".");
|
||||
|
||||
f->seek(pf.offset);
|
||||
pos = 0;
|
||||
eof = false;
|
||||
|
@ -463,11 +460,15 @@ String DirAccessPack::get_current_dir() {
|
|||
|
||||
bool DirAccessPack::file_exists(String p_file) {
|
||||
|
||||
p_file = fix_path(p_file);
|
||||
|
||||
return current->files.has(p_file);
|
||||
}
|
||||
|
||||
bool DirAccessPack::dir_exists(String p_dir) {
|
||||
|
||||
p_dir = fix_path(p_dir);
|
||||
|
||||
return current->subdirs.has(p_dir);
|
||||
}
|
||||
|
||||
|
|
|
@ -37,24 +37,8 @@
|
|||
#include "core/os/file_access.h"
|
||||
#include "core/ustring.h"
|
||||
|
||||
/**
|
||||
@author Juan Linietsky <reduzio@gmail.com>
|
||||
*/
|
||||
|
||||
/**
|
||||
* @class ImageScanLineLoader
|
||||
* @author Juan Linietsky <reduzio@gmail.com>
|
||||
*
|
||||
|
||||
*/
|
||||
class ImageLoader;
|
||||
|
||||
/**
|
||||
* @class ImageLoader
|
||||
* Base Class and singleton for loading images from disk
|
||||
* Can load images in one go, or by scanline
|
||||
*/
|
||||
|
||||
class ImageFormatLoader {
|
||||
friend class ImageLoader;
|
||||
friend class ResourceFormatLoaderImage;
|
||||
|
|
|
@ -81,8 +81,7 @@ static void _parse_hex(const String &p_string, int p_start, uint8_t *p_dst) {
|
|||
} else if (c == ':') {
|
||||
break;
|
||||
} else {
|
||||
ERR_EXPLAIN("Invalid character in ipv6 address: " + p_string);
|
||||
ERR_FAIL();
|
||||
ERR_FAIL_MSG("Invalid character in IPv6 address: " + p_string + ".");
|
||||
};
|
||||
ret = ret << 4;
|
||||
ret += n;
|
||||
|
@ -126,9 +125,7 @@ void IP_Address::_parse_ipv6(const String &p_string) {
|
|||
++parts_count;
|
||||
};
|
||||
} else {
|
||||
|
||||
ERR_EXPLAIN("Invalid character in IPv6 address: " + p_string);
|
||||
ERR_FAIL();
|
||||
ERR_FAIL_MSG("Invalid character in IPv6 address: " + p_string + ".");
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -166,10 +163,7 @@ void IP_Address::_parse_ipv4(const String &p_string, int p_start, uint8_t *p_ret
|
|||
};
|
||||
|
||||
int slices = ip.get_slice_count(".");
|
||||
if (slices != 4) {
|
||||
ERR_EXPLAIN("Invalid IP Address String: " + ip);
|
||||
ERR_FAIL();
|
||||
}
|
||||
ERR_FAIL_COND_MSG(slices != 4, "Invalid IP address string: " + ip + ".");
|
||||
for (int i = 0; i < 4; i++) {
|
||||
p_ret[i] = ip.get_slicec('.', i).to_int();
|
||||
}
|
||||
|
@ -229,7 +223,7 @@ IP_Address::IP_Address(const String &p_string) {
|
|||
valid = true;
|
||||
|
||||
} else {
|
||||
ERR_PRINT("Invalid IP address");
|
||||
ERR_PRINT("Invalid IP address.");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -377,11 +377,6 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
|
|||
}
|
||||
|
||||
} break;
|
||||
/*case Variant::RESOURCE: {
|
||||
|
||||
ERR_EXPLAIN("Can't marshallize resources");
|
||||
ERR_FAIL_V(ERR_INVALID_DATA); //no, i'm sorry, no go
|
||||
} break;*/
|
||||
case Variant::_RID: {
|
||||
|
||||
r_variant = RID();
|
||||
|
@ -1066,11 +1061,6 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
|
|||
r_len += 4 * 4;
|
||||
|
||||
} break;
|
||||
/*case Variant::RESOURCE: {
|
||||
|
||||
ERR_EXPLAIN("Can't marshallize resources");
|
||||
ERR_FAIL_V(ERR_INVALID_DATA); //no, i'm sorry, no go
|
||||
} break;*/
|
||||
case Variant::_RID: {
|
||||
|
||||
} break;
|
||||
|
|
|
@ -33,6 +33,10 @@
|
|||
#include "core/io/marshalls.h"
|
||||
#include "scene/main/node.h"
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
#include "core/os/os.h"
|
||||
#endif
|
||||
|
||||
_FORCE_INLINE_ bool _should_call_local(MultiplayerAPI::RPCMode mode, bool is_master, bool &r_skip_rpc) {
|
||||
|
||||
switch (mode) {
|
||||
|
@ -146,8 +150,7 @@ void MultiplayerAPI::set_network_peer(const Ref<NetworkedMultiplayerPeer> &p_pee
|
|||
|
||||
network_peer = p_peer;
|
||||
|
||||
ERR_EXPLAIN("Supplied NetworkedNetworkPeer must be connecting or connected.");
|
||||
ERR_FAIL_COND(p_peer.is_valid() && p_peer->get_connection_status() == NetworkedMultiplayerPeer::CONNECTION_DISCONNECTED);
|
||||
ERR_FAIL_COND_MSG(p_peer.is_valid() && p_peer->get_connection_status() == NetworkedMultiplayerPeer::CONNECTION_DISCONNECTED, "Supplied NetworkedNetworkPeer must be connecting or connected.");
|
||||
|
||||
if (network_peer.is_valid()) {
|
||||
network_peer->connect("peer_connected", this, "_add_peer");
|
||||
|
@ -164,10 +167,16 @@ Ref<NetworkedMultiplayerPeer> MultiplayerAPI::get_network_peer() const {
|
|||
|
||||
void MultiplayerAPI::_process_packet(int p_from, const uint8_t *p_packet, int p_packet_len) {
|
||||
|
||||
ERR_EXPLAIN("Multiplayer root node was not initialized. If you are using custom multiplayer, remember to set the root node via MultiplayerAPI.set_root_node before using it");
|
||||
ERR_FAIL_COND(root_node == NULL);
|
||||
ERR_EXPLAIN("Invalid packet received. Size too small.");
|
||||
ERR_FAIL_COND(p_packet_len < 1);
|
||||
ERR_FAIL_COND_MSG(root_node == NULL, "Multiplayer root node was not initialized. If you are using custom multiplayer, remember to set the root node via MultiplayerAPI.set_root_node before using it.");
|
||||
ERR_FAIL_COND_MSG(p_packet_len < 1, "Invalid packet received. Size too small.");
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (profiling) {
|
||||
bandwidth_incoming_data.write[bandwidth_incoming_pointer].timestamp = OS::get_singleton()->get_ticks_msec();
|
||||
bandwidth_incoming_data.write[bandwidth_incoming_pointer].packet_size = p_packet_len;
|
||||
bandwidth_incoming_pointer = (bandwidth_incoming_pointer + 1) % bandwidth_incoming_data.size();
|
||||
}
|
||||
#endif
|
||||
|
||||
uint8_t packet_type = p_packet[0];
|
||||
|
||||
|
@ -186,13 +195,11 @@ void MultiplayerAPI::_process_packet(int p_from, const uint8_t *p_packet, int p_
|
|||
case NETWORK_COMMAND_REMOTE_CALL:
|
||||
case NETWORK_COMMAND_REMOTE_SET: {
|
||||
|
||||
ERR_EXPLAIN("Invalid packet received. Size too small.");
|
||||
ERR_FAIL_COND(p_packet_len < 6);
|
||||
ERR_FAIL_COND_MSG(p_packet_len < 6, "Invalid packet received. Size too small.");
|
||||
|
||||
Node *node = _process_get_node(p_from, p_packet, p_packet_len);
|
||||
|
||||
ERR_EXPLAIN("Invalid packet received. Requested node was not found.");
|
||||
ERR_FAIL_COND(node == NULL);
|
||||
ERR_FAIL_COND_MSG(node == NULL, "Invalid packet received. Requested node was not found.");
|
||||
|
||||
// Detect cstring end.
|
||||
int len_end = 5;
|
||||
|
@ -202,8 +209,7 @@ void MultiplayerAPI::_process_packet(int p_from, const uint8_t *p_packet, int p_
|
|||
}
|
||||
}
|
||||
|
||||
ERR_EXPLAIN("Invalid packet received. Size too small.");
|
||||
ERR_FAIL_COND(len_end >= p_packet_len);
|
||||
ERR_FAIL_COND_MSG(len_end >= p_packet_len, "Invalid packet received. Size too small.");
|
||||
|
||||
StringName name = String::utf8((const char *)&p_packet[5]);
|
||||
|
||||
|
@ -235,8 +241,7 @@ Node *MultiplayerAPI::_process_get_node(int p_from, const uint8_t *p_packet, int
|
|||
|
||||
int ofs = target & 0x7FFFFFFF;
|
||||
|
||||
ERR_EXPLAIN("Invalid packet received. Size smaller than declared.");
|
||||
ERR_FAIL_COND_V(ofs >= p_packet_len, NULL);
|
||||
ERR_FAIL_COND_V_MSG(ofs >= p_packet_len, NULL, "Invalid packet received. Size smaller than declared.");
|
||||
|
||||
String paths;
|
||||
paths.parse_utf8((const char *)&p_packet[ofs], p_packet_len - ofs);
|
||||
|
@ -246,33 +251,30 @@ Node *MultiplayerAPI::_process_get_node(int p_from, const uint8_t *p_packet, int
|
|||
node = root_node->get_node(np);
|
||||
|
||||
if (!node)
|
||||
ERR_PRINTS("Failed to get path from RPC: " + String(np));
|
||||
ERR_PRINTS("Failed to get path from RPC: " + String(np) + ".");
|
||||
} else {
|
||||
// Use cached path.
|
||||
int id = target;
|
||||
|
||||
Map<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);
|
||||
ERR_EXPLAIN("Invalid packet received. Unabled to find requested cached node.");
|
||||
ERR_FAIL_COND_V(!F, NULL);
|
||||
ERR_FAIL_COND_V_MSG(!F, NULL, "Invalid packet received. Unabled to find requested cached node.");
|
||||
|
||||
PathGetCache::NodeInfo *ni = &F->get();
|
||||
// Do proper caching later.
|
||||
|
||||
node = root_node->get_node(ni->path);
|
||||
if (!node)
|
||||
ERR_PRINTS("Failed to get cached path from RPC: " + String(ni->path));
|
||||
ERR_PRINTS("Failed to get cached path from RPC: " + String(ni->path) + ".");
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
void MultiplayerAPI::_process_rpc(Node *p_node, const StringName &p_name, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset) {
|
||||
|
||||
ERR_EXPLAIN("Invalid packet received. Size too small.");
|
||||
ERR_FAIL_COND(p_offset >= p_packet_len);
|
||||
ERR_FAIL_COND_MSG(p_offset >= p_packet_len, "Invalid packet received. Size too small.");
|
||||
|
||||
// Check that remote can call the RPC on this node.
|
||||
RPCMode rpc_mode = RPC_MODE_DISABLED;
|
||||
|
@ -284,8 +286,7 @@ void MultiplayerAPI::_process_rpc(Node *p_node, const StringName &p_name, int p_
|
|||
}
|
||||
|
||||
bool can_call = _can_call_mode(p_node, rpc_mode, p_from);
|
||||
ERR_EXPLAIN("RPC '" + String(p_name) + "' is not allowed on node " + p_node->get_path() + " from: " + itos(p_from) + ". Mode is " + itos((int)rpc_mode) + ", master is " + itos(p_node->get_network_master()) + ".");
|
||||
ERR_FAIL_COND(!can_call);
|
||||
ERR_FAIL_COND_MSG(!can_call, "RPC '" + String(p_name) + "' is not allowed on node " + p_node->get_path() + " from: " + itos(p_from) + ". Mode is " + itos((int)rpc_mode) + ", master is " + itos(p_node->get_network_master()) + ".");
|
||||
|
||||
int argc = p_packet[p_offset];
|
||||
Vector<Variant> args;
|
||||
|
@ -295,15 +296,21 @@ void MultiplayerAPI::_process_rpc(Node *p_node, const StringName &p_name, int p_
|
|||
|
||||
p_offset++;
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (profiling) {
|
||||
ObjectID id = p_node->get_instance_id();
|
||||
_init_node_profile(id);
|
||||
profiler_frame_data[id].incoming_rpc += 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
for (int i = 0; i < argc; i++) {
|
||||
|
||||
ERR_EXPLAIN("Invalid packet received. Size too small.");
|
||||
ERR_FAIL_COND(p_offset >= p_packet_len);
|
||||
ERR_FAIL_COND_MSG(p_offset >= p_packet_len, "Invalid packet received. Size too small.");
|
||||
|
||||
int vlen;
|
||||
Error err = decode_variant(args.write[i], &p_packet[p_offset], p_packet_len - p_offset, &vlen, allow_object_decoding || network_peer->is_object_decoding_allowed());
|
||||
ERR_EXPLAIN("Invalid packet received. Unable to decode RPC argument.");
|
||||
ERR_FAIL_COND(err != OK);
|
||||
ERR_FAIL_COND_MSG(err != OK, "Invalid packet received. Unable to decode RPC argument.");
|
||||
|
||||
argp.write[i] = &args[i];
|
||||
p_offset += vlen;
|
||||
|
@ -321,8 +328,7 @@ void MultiplayerAPI::_process_rpc(Node *p_node, const StringName &p_name, int p_
|
|||
|
||||
void MultiplayerAPI::_process_rset(Node *p_node, const StringName &p_name, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset) {
|
||||
|
||||
ERR_EXPLAIN("Invalid packet received. Size too small.");
|
||||
ERR_FAIL_COND(p_offset >= p_packet_len);
|
||||
ERR_FAIL_COND_MSG(p_offset >= p_packet_len, "Invalid packet received. Size too small.");
|
||||
|
||||
// Check that remote can call the RSET on this node.
|
||||
RPCMode rset_mode = RPC_MODE_DISABLED;
|
||||
|
@ -334,28 +340,33 @@ void MultiplayerAPI::_process_rset(Node *p_node, const StringName &p_name, int p
|
|||
}
|
||||
|
||||
bool can_call = _can_call_mode(p_node, rset_mode, p_from);
|
||||
ERR_EXPLAIN("RSET '" + String(p_name) + "' is not allowed on node " + p_node->get_path() + " from: " + itos(p_from) + ". Mode is " + itos((int)rset_mode) + ", master is " + itos(p_node->get_network_master()) + ".");
|
||||
ERR_FAIL_COND(!can_call);
|
||||
ERR_FAIL_COND_MSG(!can_call, "RSET '" + String(p_name) + "' is not allowed on node " + p_node->get_path() + " from: " + itos(p_from) + ". Mode is " + itos((int)rset_mode) + ", master is " + itos(p_node->get_network_master()) + ".");
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (profiling) {
|
||||
ObjectID id = p_node->get_instance_id();
|
||||
_init_node_profile(id);
|
||||
profiler_frame_data[id].incoming_rset += 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
Variant value;
|
||||
Error err = decode_variant(value, &p_packet[p_offset], p_packet_len - p_offset, NULL, allow_object_decoding || network_peer->is_object_decoding_allowed());
|
||||
|
||||
ERR_EXPLAIN("Invalid packet received. Unable to decode RSET value.");
|
||||
ERR_FAIL_COND(err != OK);
|
||||
ERR_FAIL_COND_MSG(err != OK, "Invalid packet received. Unable to decode RSET value.");
|
||||
|
||||
bool valid;
|
||||
|
||||
p_node->set(p_name, value, &valid);
|
||||
if (!valid) {
|
||||
String error = "Error setting remote property '" + String(p_name) + "', not found in object of type " + p_node->get_class();
|
||||
String error = "Error setting remote property '" + String(p_name) + "', not found in object of type " + p_node->get_class() + ".";
|
||||
ERR_PRINTS(error);
|
||||
}
|
||||
}
|
||||
|
||||
void MultiplayerAPI::_process_simplify_path(int p_from, const uint8_t *p_packet, int p_packet_len) {
|
||||
|
||||
ERR_EXPLAIN("Invalid packet received. Size too small.");
|
||||
ERR_FAIL_COND(p_packet_len < 5);
|
||||
ERR_FAIL_COND_MSG(p_packet_len < 5, "Invalid packet received. Size too small.");
|
||||
int id = decode_uint32(&p_packet[1]);
|
||||
|
||||
String paths;
|
||||
|
@ -390,8 +401,7 @@ void MultiplayerAPI::_process_simplify_path(int p_from, const uint8_t *p_packet,
|
|||
|
||||
void MultiplayerAPI::_process_confirm_path(int p_from, const uint8_t *p_packet, int p_packet_len) {
|
||||
|
||||
ERR_EXPLAIN("Invalid packet received. Size too small.");
|
||||
ERR_FAIL_COND(p_packet_len < 2);
|
||||
ERR_FAIL_COND_MSG(p_packet_len < 2, "Invalid packet received. Size too small.");
|
||||
|
||||
String paths;
|
||||
paths.parse_utf8((const char *)&p_packet[1], p_packet_len - 1);
|
||||
|
@ -399,12 +409,10 @@ void MultiplayerAPI::_process_confirm_path(int p_from, const uint8_t *p_packet,
|
|||
NodePath path = paths;
|
||||
|
||||
PathSentCache *psc = path_send_cache.getptr(path);
|
||||
ERR_EXPLAIN("Invalid packet received. Tries to confirm a path which was not found in cache.");
|
||||
ERR_FAIL_COND(!psc);
|
||||
ERR_FAIL_COND_MSG(!psc, "Invalid packet received. Tries to confirm a path which was not found in cache.");
|
||||
|
||||
Map<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;
|
||||
}
|
||||
|
||||
|
@ -460,39 +468,22 @@ bool MultiplayerAPI::_send_confirm_path(NodePath p_path, PathSentCache *psc, int
|
|||
|
||||
void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p_set, const StringName &p_name, const Variant **p_arg, int p_argcount) {
|
||||
|
||||
if (network_peer.is_null()) {
|
||||
ERR_EXPLAIN("Attempt to remote call/set when networking is not active in SceneTree.");
|
||||
ERR_FAIL();
|
||||
}
|
||||
ERR_FAIL_COND_MSG(network_peer.is_null(), "Attempt to remote call/set when networking is not active in SceneTree.");
|
||||
|
||||
if (network_peer->get_connection_status() == NetworkedMultiplayerPeer::CONNECTION_CONNECTING) {
|
||||
ERR_EXPLAIN("Attempt to remote call/set when networking is not connected yet in SceneTree.");
|
||||
ERR_FAIL();
|
||||
}
|
||||
ERR_FAIL_COND_MSG(network_peer->get_connection_status() == NetworkedMultiplayerPeer::CONNECTION_CONNECTING, "Attempt to remote call/set when networking is not connected yet in SceneTree.");
|
||||
|
||||
if (network_peer->get_connection_status() == NetworkedMultiplayerPeer::CONNECTION_DISCONNECTED) {
|
||||
ERR_EXPLAIN("Attempt to remote call/set when networking is disconnected.");
|
||||
ERR_FAIL();
|
||||
}
|
||||
ERR_FAIL_COND_MSG(network_peer->get_connection_status() == NetworkedMultiplayerPeer::CONNECTION_DISCONNECTED, "Attempt to remote call/set when networking is disconnected.");
|
||||
|
||||
if (p_argcount > 255) {
|
||||
ERR_EXPLAIN("Too many arguments >255.");
|
||||
ERR_FAIL();
|
||||
}
|
||||
ERR_FAIL_COND_MSG(p_argcount > 255, "Too many arguments >255.");
|
||||
|
||||
if (p_to != 0 && !connected_peers.has(ABS(p_to))) {
|
||||
if (p_to == network_peer->get_unique_id()) {
|
||||
ERR_EXPLAIN("Attempt to remote call/set yourself! unique ID: " + itos(network_peer->get_unique_id()));
|
||||
} else {
|
||||
ERR_EXPLAIN("Attempt to remote call unexisting ID: " + itos(p_to));
|
||||
}
|
||||
ERR_FAIL_COND_MSG(p_to == network_peer->get_unique_id(), "Attempt to remote call/set yourself! unique ID: " + itos(network_peer->get_unique_id()) + ".");
|
||||
|
||||
ERR_FAIL();
|
||||
ERR_FAIL_MSG("Attempt to remote call unexisting ID: " + itos(p_to) + ".");
|
||||
}
|
||||
|
||||
NodePath from_path = (root_node->get_path()).rel_path_to(p_from->get_path());
|
||||
ERR_EXPLAIN("Unable to send RPC. Relative path is empty. THIS IS LIKELY A BUG IN THE ENGINE!");
|
||||
ERR_FAIL_COND(from_path.is_empty());
|
||||
ERR_FAIL_COND_MSG(from_path.is_empty(), "Unable to send RPC. Relative path is empty. THIS IS LIKELY A BUG IN THE ENGINE!");
|
||||
|
||||
// See if the path is cached.
|
||||
PathSentCache *psc = path_send_cache.getptr(from_path);
|
||||
|
@ -530,8 +521,7 @@ void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p
|
|||
if (p_set) {
|
||||
// Set argument.
|
||||
Error err = encode_variant(*p_arg[0], NULL, len, allow_object_decoding || network_peer->is_object_decoding_allowed());
|
||||
ERR_EXPLAIN("Unable to encode RSET value. THIS IS LIKELY A BUG IN THE ENGINE!");
|
||||
ERR_FAIL_COND(err != OK);
|
||||
ERR_FAIL_COND_MSG(err != OK, "Unable to encode RSET value. THIS IS LIKELY A BUG IN THE ENGINE!");
|
||||
MAKE_ROOM(ofs + len);
|
||||
encode_variant(*p_arg[0], &(packet_cache.write[ofs]), len, allow_object_decoding || network_peer->is_object_decoding_allowed());
|
||||
ofs += len;
|
||||
|
@ -543,14 +533,21 @@ void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p
|
|||
ofs += 1;
|
||||
for (int i = 0; i < p_argcount; i++) {
|
||||
Error err = encode_variant(*p_arg[i], NULL, len, allow_object_decoding || network_peer->is_object_decoding_allowed());
|
||||
ERR_EXPLAIN("Unable to encode RPC argument. THIS IS LIKELY A BUG IN THE ENGINE!");
|
||||
ERR_FAIL_COND(err != OK);
|
||||
ERR_FAIL_COND_MSG(err != OK, "Unable to encode RPC argument. THIS IS LIKELY A BUG IN THE ENGINE!");
|
||||
MAKE_ROOM(ofs + len);
|
||||
encode_variant(*p_arg[i], &(packet_cache.write[ofs]), len, allow_object_decoding || network_peer->is_object_decoding_allowed());
|
||||
ofs += len;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (profiling) {
|
||||
bandwidth_outgoing_data.write[bandwidth_outgoing_pointer].timestamp = OS::get_singleton()->get_ticks_msec();
|
||||
bandwidth_outgoing_data.write[bandwidth_outgoing_pointer].packet_size = ofs;
|
||||
bandwidth_outgoing_pointer = (bandwidth_outgoing_pointer + 1) % bandwidth_outgoing_data.size();
|
||||
}
|
||||
#endif
|
||||
|
||||
// See if all peers have cached path (is so, call can be fast).
|
||||
bool has_all_peers = _send_confirm_path(from_path, psc, p_to);
|
||||
|
||||
|
@ -626,12 +623,9 @@ void MultiplayerAPI::_server_disconnected() {
|
|||
|
||||
void MultiplayerAPI::rpcp(Node *p_node, int p_peer_id, bool p_unreliable, const StringName &p_method, const Variant **p_arg, int p_argcount) {
|
||||
|
||||
ERR_EXPLAIN("Trying to call an RPC while no network peer is active.");
|
||||
ERR_FAIL_COND(!network_peer.is_valid());
|
||||
ERR_EXPLAIN("Trying to call an RPC on a node which is not inside SceneTree.");
|
||||
ERR_FAIL_COND(!p_node->is_inside_tree());
|
||||
ERR_EXPLAIN("Trying to call an RPC via a network peer which is not connected.");
|
||||
ERR_FAIL_COND(network_peer->get_connection_status() != NetworkedMultiplayerPeer::CONNECTION_CONNECTED);
|
||||
ERR_FAIL_COND_MSG(!network_peer.is_valid(), "Trying to call an RPC while no network peer is active.");
|
||||
ERR_FAIL_COND_MSG(!p_node->is_inside_tree(), "Trying to call an RPC on a node which is not inside SceneTree.");
|
||||
ERR_FAIL_COND_MSG(network_peer->get_connection_status() != NetworkedMultiplayerPeer::CONNECTION_CONNECTED, "Trying to call an RPC via a network peer which is not connected.");
|
||||
|
||||
int node_id = network_peer->get_unique_id();
|
||||
bool skip_rpc = node_id == p_peer_id;
|
||||
|
@ -657,6 +651,15 @@ void MultiplayerAPI::rpcp(Node *p_node, int p_peer_id, bool p_unreliable, const
|
|||
}
|
||||
|
||||
if (!skip_rpc) {
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (profiling) {
|
||||
ObjectID id = p_node->get_instance_id();
|
||||
_init_node_profile(id);
|
||||
profiler_frame_data[id].outgoing_rpc += 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
_send_rpc(p_node, p_peer_id, p_unreliable, false, p_method, p_arg, p_argcount);
|
||||
}
|
||||
|
||||
|
@ -668,7 +671,7 @@ void MultiplayerAPI::rpcp(Node *p_node, int p_peer_id, bool p_unreliable, const
|
|||
rpc_sender_id = temp_id;
|
||||
if (ce.error != Variant::CallError::CALL_OK) {
|
||||
String error = Variant::get_call_error_text(p_node, p_method, p_arg, p_argcount, ce);
|
||||
error = "rpc() aborted in local call: - " + error;
|
||||
error = "rpc() aborted in local call: - " + error + ".";
|
||||
ERR_PRINTS(error);
|
||||
return;
|
||||
}
|
||||
|
@ -683,24 +686,20 @@ void MultiplayerAPI::rpcp(Node *p_node, int p_peer_id, bool p_unreliable, const
|
|||
rpc_sender_id = temp_id;
|
||||
if (ce.error != Variant::CallError::CALL_OK) {
|
||||
String error = Variant::get_call_error_text(p_node, p_method, p_arg, p_argcount, ce);
|
||||
error = "rpc() aborted in script local call: - " + error;
|
||||
error = "rpc() aborted in script local call: - " + error + ".";
|
||||
ERR_PRINTS(error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ERR_EXPLAIN("RPC '" + p_method + "' on yourself is not allowed by selected mode");
|
||||
ERR_FAIL_COND(skip_rpc && !(call_local_native || call_local_script));
|
||||
ERR_FAIL_COND_MSG(skip_rpc && !(call_local_native || call_local_script), "RPC '" + p_method + "' on yourself is not allowed by selected mode.");
|
||||
}
|
||||
|
||||
void MultiplayerAPI::rsetp(Node *p_node, int p_peer_id, bool p_unreliable, const StringName &p_property, const Variant &p_value) {
|
||||
|
||||
ERR_EXPLAIN("Trying to RSET while no network peer is active.");
|
||||
ERR_FAIL_COND(!network_peer.is_valid());
|
||||
ERR_EXPLAIN("Trying to RSET on a node which is not inside SceneTree.");
|
||||
ERR_FAIL_COND(!p_node->is_inside_tree());
|
||||
ERR_EXPLAIN("Trying to send an RSET via a network peer which is not connected.");
|
||||
ERR_FAIL_COND(network_peer->get_connection_status() != NetworkedMultiplayerPeer::CONNECTION_CONNECTED);
|
||||
ERR_FAIL_COND_MSG(!network_peer.is_valid(), "Trying to RSET while no network peer is active.");
|
||||
ERR_FAIL_COND_MSG(!p_node->is_inside_tree(), "Trying to RSET on a node which is not inside SceneTree.");
|
||||
ERR_FAIL_COND_MSG(network_peer->get_connection_status() != NetworkedMultiplayerPeer::CONNECTION_CONNECTED, "Trying to send an RSET via a network peer which is not connected.");
|
||||
|
||||
int node_id = network_peer->get_unique_id();
|
||||
bool is_master = p_node->is_network_master();
|
||||
|
@ -724,7 +723,7 @@ void MultiplayerAPI::rsetp(Node *p_node, int p_peer_id, bool p_unreliable, const
|
|||
rpc_sender_id = temp_id;
|
||||
|
||||
if (!valid) {
|
||||
String error = "rset() aborted in local set, property not found: - " + String(p_property);
|
||||
String error = "rset() aborted in local set, property not found: - " + String(p_property) + ".";
|
||||
ERR_PRINTS(error);
|
||||
return;
|
||||
}
|
||||
|
@ -742,7 +741,7 @@ void MultiplayerAPI::rsetp(Node *p_node, int p_peer_id, bool p_unreliable, const
|
|||
rpc_sender_id = temp_id;
|
||||
|
||||
if (!valid) {
|
||||
String error = "rset() aborted in local script set, property not found: - " + String(p_property);
|
||||
String error = "rset() aborted in local script set, property not found: - " + String(p_property) + ".";
|
||||
ERR_PRINTS(error);
|
||||
return;
|
||||
}
|
||||
|
@ -751,11 +750,18 @@ void MultiplayerAPI::rsetp(Node *p_node, int p_peer_id, bool p_unreliable, const
|
|||
}
|
||||
|
||||
if (skip_rset) {
|
||||
ERR_EXPLAIN("RSET for '" + p_property + "' on yourself is not allowed by selected mode");
|
||||
ERR_FAIL_COND(!set_local);
|
||||
ERR_FAIL_COND_MSG(!set_local, "RSET for '" + p_property + "' on yourself is not allowed by selected mode.");
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (profiling) {
|
||||
ObjectID id = p_node->get_instance_id();
|
||||
_init_node_profile(id);
|
||||
profiler_frame_data[id].outgoing_rset += 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
const Variant *vptr = &p_value;
|
||||
|
||||
_send_rpc(p_node, p_peer_id, p_unreliable, true, p_property, &vptr, 1);
|
||||
|
@ -763,12 +769,9 @@ void MultiplayerAPI::rsetp(Node *p_node, int p_peer_id, bool p_unreliable, const
|
|||
|
||||
Error MultiplayerAPI::send_bytes(PoolVector<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);
|
||||
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) {
|
||||
|
||||
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;
|
||||
int len = p_packet_len - 1;
|
||||
|
@ -798,37 +800,32 @@ void MultiplayerAPI::_process_raw(int p_from, const uint8_t *p_packet, int p_pac
|
|||
|
||||
int MultiplayerAPI::get_network_unique_id() const {
|
||||
|
||||
ERR_EXPLAIN("No network peer is assigned. Unable to get unique network ID.");
|
||||
ERR_FAIL_COND_V(!network_peer.is_valid(), 0);
|
||||
ERR_FAIL_COND_V_MSG(!network_peer.is_valid(), 0, "No network peer is assigned. Unable to get unique network ID.");
|
||||
return network_peer->get_unique_id();
|
||||
}
|
||||
|
||||
bool MultiplayerAPI::is_network_server() const {
|
||||
|
||||
// XXX Maybe fail silently? Maybe should actually return true to make development of both local and online multiplayer easier?
|
||||
ERR_EXPLAIN("No network peer is assigned. I can't be a server.");
|
||||
ERR_FAIL_COND_V(!network_peer.is_valid(), false);
|
||||
ERR_FAIL_COND_V_MSG(!network_peer.is_valid(), false, "No network peer is assigned. I can't be a server.");
|
||||
return network_peer->is_server();
|
||||
}
|
||||
|
||||
void MultiplayerAPI::set_refuse_new_network_connections(bool p_refuse) {
|
||||
|
||||
ERR_EXPLAIN("No network peer is assigned. Unable to set 'refuse_new_connections'.");
|
||||
ERR_FAIL_COND(!network_peer.is_valid());
|
||||
ERR_FAIL_COND_MSG(!network_peer.is_valid(), "No network peer is assigned. Unable to set 'refuse_new_connections'.");
|
||||
network_peer->set_refuse_new_connections(p_refuse);
|
||||
}
|
||||
|
||||
bool MultiplayerAPI::is_refusing_new_network_connections() const {
|
||||
|
||||
ERR_EXPLAIN("No network peer is assigned. Unable to get 'refuse_new_connections'.");
|
||||
ERR_FAIL_COND_V(!network_peer.is_valid(), false);
|
||||
ERR_FAIL_COND_V_MSG(!network_peer.is_valid(), false, "No network peer is assigned. Unable to get 'refuse_new_connections'.");
|
||||
return network_peer->is_refusing_new_connections();
|
||||
}
|
||||
|
||||
Vector<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;
|
||||
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;
|
||||
}
|
||||
|
||||
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() {
|
||||
ClassDB::bind_method(D_METHOD("set_root_node", "node"), &MultiplayerAPI::set_root_node);
|
||||
ClassDB::bind_method(D_METHOD("send_bytes", "bytes", "id", "mode"), &MultiplayerAPI::send_bytes, DEFVAL(NetworkedMultiplayerPeer::TARGET_PEER_BROADCAST), DEFVAL(NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE));
|
||||
|
@ -898,6 +985,9 @@ MultiplayerAPI::MultiplayerAPI() :
|
|||
allow_object_decoding(false) {
|
||||
rpc_sender_id = 0;
|
||||
root_node = NULL;
|
||||
#ifdef DEBUG_ENABLED
|
||||
profiling = false;
|
||||
#endif
|
||||
clear();
|
||||
}
|
||||
|
||||
|
|
|
@ -38,6 +38,16 @@ class MultiplayerAPI : public Reference {
|
|||
|
||||
GDCLASS(MultiplayerAPI, Reference);
|
||||
|
||||
public:
|
||||
struct ProfilingInfo {
|
||||
ObjectID node;
|
||||
String node_path;
|
||||
int incoming_rpc;
|
||||
int incoming_rset;
|
||||
int outgoing_rpc;
|
||||
int outgoing_rset;
|
||||
};
|
||||
|
||||
private:
|
||||
//path sent caches
|
||||
struct PathSentCache {
|
||||
|
@ -55,6 +65,23 @@ private:
|
|||
Map<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;
|
||||
int rpc_sender_id;
|
||||
Set<int> connected_peers;
|
||||
|
@ -130,6 +157,13 @@ public:
|
|||
void set_allow_object_decoding(bool p_enable);
|
||||
bool is_object_decoding_allowed() const;
|
||||
|
||||
void profiling_start();
|
||||
void profiling_end();
|
||||
|
||||
int get_profiling_frame(ProfilingInfo *r_info);
|
||||
int get_incoming_bandwidth_usage();
|
||||
int get_outgoing_bandwidth_usage();
|
||||
|
||||
MultiplayerAPI();
|
||||
~MultiplayerAPI();
|
||||
};
|
||||
|
|
|
@ -110,10 +110,11 @@ Error PacketPeer::put_var(const Variant &p_packet, bool p_full_objects) {
|
|||
|
||||
Variant PacketPeer::_bnd_get_var(bool p_allow_objects) {
|
||||
Variant var;
|
||||
get_var(var, p_allow_objects);
|
||||
Error err = get_var(var, p_allow_objects);
|
||||
|
||||
ERR_FAIL_COND_V(err != OK, Variant());
|
||||
return var;
|
||||
};
|
||||
}
|
||||
|
||||
Error PacketPeer::_put_packet(const PoolVector<uint8_t> &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) {
|
||||
|
||||
//warning may lose packets
|
||||
ERR_EXPLAIN("Buffer in use, resizing would cause loss of data");
|
||||
ERR_FAIL_COND(ring_buffer.data_left());
|
||||
ERR_FAIL_COND_MSG(ring_buffer.data_left(), "Buffer in use, resizing would cause loss of data.");
|
||||
ring_buffer.resize(nearest_shift(p_max_size + 4));
|
||||
input_buffer.resize(next_power_of_2(p_max_size + 4));
|
||||
}
|
||||
|
|
|
@ -64,10 +64,7 @@ Error PCKPacker::pck_start(const String &p_file, int p_alignment) {
|
|||
|
||||
file = FileAccess::open(p_file, FileAccess::WRITE);
|
||||
|
||||
if (!file) {
|
||||
ERR_EXPLAIN("Can't open file to write: " + String(p_file));
|
||||
ERR_FAIL_V(ERR_CANT_CREATE);
|
||||
}
|
||||
ERR_FAIL_COND_V_MSG(!file, ERR_CANT_CREATE, "Can't open file to write: " + String(p_file) + ".");
|
||||
|
||||
alignment = p_alignment;
|
||||
|
||||
|
|
|
@ -490,8 +490,7 @@ Error ResourceInteractiveLoaderBinary::parse_variant(Variant &r_v) {
|
|||
#endif
|
||||
|
||||
} else {
|
||||
ERR_EXPLAIN("Vector2 size is NOT 8!");
|
||||
ERR_FAIL_V(ERR_UNAVAILABLE);
|
||||
ERR_FAIL_V_MSG(ERR_UNAVAILABLE, "Vector2 size is NOT 8!");
|
||||
}
|
||||
w.release();
|
||||
r_v = array;
|
||||
|
@ -518,8 +517,7 @@ Error ResourceInteractiveLoaderBinary::parse_variant(Variant &r_v) {
|
|||
#endif
|
||||
|
||||
} else {
|
||||
ERR_EXPLAIN("Vector3 size is NOT 12!");
|
||||
ERR_FAIL_V(ERR_UNAVAILABLE);
|
||||
ERR_FAIL_V_MSG(ERR_UNAVAILABLE, "Vector3 size is NOT 12!");
|
||||
}
|
||||
w.release();
|
||||
r_v = array;
|
||||
|
@ -546,8 +544,7 @@ Error ResourceInteractiveLoaderBinary::parse_variant(Variant &r_v) {
|
|||
#endif
|
||||
|
||||
} else {
|
||||
ERR_EXPLAIN("Color size is NOT 16!");
|
||||
ERR_FAIL_V(ERR_UNAVAILABLE);
|
||||
ERR_FAIL_V_MSG(ERR_UNAVAILABLE, "Color size is NOT 16!");
|
||||
}
|
||||
w.release();
|
||||
r_v = array;
|
||||
|
@ -571,7 +568,7 @@ Error ResourceInteractiveLoaderBinary::parse_variant(Variant &r_v) {
|
|||
const uint32_t current_version = 0;
|
||||
if (format_version > current_version) {
|
||||
|
||||
ERR_PRINT("Format version for encoded binary image is too new");
|
||||
ERR_PRINT("Format version for encoded binary image is too new.");
|
||||
return ERR_PARSE_ERROR;
|
||||
}
|
||||
|
||||
|
@ -655,8 +652,7 @@ Error ResourceInteractiveLoaderBinary::poll() {
|
|||
} else {
|
||||
|
||||
error = ERR_FILE_MISSING_DEPENDENCIES;
|
||||
ERR_EXPLAIN("Can't load dependency: " + path);
|
||||
ERR_FAIL_V(error);
|
||||
ERR_FAIL_V_MSG(error, "Can't load dependency: " + path + ".");
|
||||
}
|
||||
|
||||
} else {
|
||||
|
@ -711,16 +707,15 @@ Error ResourceInteractiveLoaderBinary::poll() {
|
|||
Object *obj = ClassDB::instance(t);
|
||||
if (!obj) {
|
||||
error = ERR_FILE_CORRUPT;
|
||||
ERR_EXPLAIN(local_path + ":Resource of unrecognized type in file: " + t);
|
||||
ERR_FAIL_V(ERR_FILE_CORRUPT);
|
||||
ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, local_path + ":Resource of unrecognized type in file: " + t + ".");
|
||||
}
|
||||
|
||||
Resource *r = Object::cast_to<Resource>(obj);
|
||||
if (!r) {
|
||||
String obj_class = obj->get_class();
|
||||
error = ERR_FILE_CORRUPT;
|
||||
memdelete(obj); //bye
|
||||
ERR_EXPLAIN(local_path + ":Resource type in resource field not a resource, type is: " + obj->get_class());
|
||||
ERR_FAIL_V(ERR_FILE_CORRUPT);
|
||||
ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, local_path + ":Resource type in resource field not a resource, type is: " + obj_class + ".");
|
||||
}
|
||||
|
||||
RES res = RES(r);
|
||||
|
@ -850,8 +845,7 @@ void ResourceInteractiveLoaderBinary::open(FileAccess *p_f) {
|
|||
//not normal
|
||||
|
||||
error = ERR_FILE_UNRECOGNIZED;
|
||||
ERR_EXPLAIN("Unrecognized binary resource file: " + local_path);
|
||||
ERR_FAIL();
|
||||
ERR_FAIL_MSG("Unrecognized binary resource file: " + local_path + ".");
|
||||
}
|
||||
|
||||
bool big_endian = f->get_32();
|
||||
|
@ -877,8 +871,7 @@ void ResourceInteractiveLoaderBinary::open(FileAccess *p_f) {
|
|||
if (ver_format > FORMAT_VERSION || ver_major > VERSION_MAJOR) {
|
||||
|
||||
f->close();
|
||||
ERR_EXPLAIN("File Format '" + itos(FORMAT_VERSION) + "." + itos(ver_major) + "." + itos(ver_minor) + "' is too new! Please upgrade to a new engine version: " + local_path);
|
||||
ERR_FAIL();
|
||||
ERR_FAIL_MSG("File format '" + itos(FORMAT_VERSION) + "." + itos(ver_major) + "." + itos(ver_minor) + "' is too new! Please upgrade to a new engine version: " + local_path + ".");
|
||||
}
|
||||
|
||||
type = get_unicode_string();
|
||||
|
@ -926,8 +919,7 @@ void ResourceInteractiveLoaderBinary::open(FileAccess *p_f) {
|
|||
if (f->eof_reached()) {
|
||||
|
||||
error = ERR_FILE_CORRUPT;
|
||||
ERR_EXPLAIN("Premature End Of File: " + local_path);
|
||||
ERR_FAIL();
|
||||
ERR_FAIL_MSG("Premature end of file (EOF): " + local_path + ".");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1084,8 +1076,7 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons
|
|||
|
||||
//error=ERR_FILE_UNRECOGNIZED;
|
||||
memdelete(f);
|
||||
ERR_EXPLAIN("Unrecognized binary resource file: " + local_path);
|
||||
ERR_FAIL_V(ERR_FILE_UNRECOGNIZED);
|
||||
ERR_FAIL_V_MSG(ERR_FILE_UNRECOGNIZED, "Unrecognized binary resource file: " + local_path + ".");
|
||||
} else {
|
||||
fw = FileAccess::open(p_path + ".depren", FileAccess::WRITE);
|
||||
if (!fw) {
|
||||
|
@ -1122,7 +1113,7 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons
|
|||
memdelete(da);
|
||||
//use the old approach
|
||||
|
||||
WARN_PRINT(("This file is old, so it can't refactor dependencies, opening and resaving: " + p_path).utf8().get_data());
|
||||
WARN_PRINTS("This file is old, so it can't refactor dependencies, opening and resaving: " + p_path + ".");
|
||||
|
||||
Error err;
|
||||
f = FileAccess::open(p_path, FileAccess::READ, &err);
|
||||
|
@ -1153,8 +1144,7 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons
|
|||
|
||||
memdelete(f);
|
||||
memdelete(fw);
|
||||
ERR_EXPLAIN("File Format '" + itos(FORMAT_VERSION) + "." + itos(ver_major) + "." + itos(ver_minor) + "' is too new! Please upgrade to a new engine version: " + local_path);
|
||||
ERR_FAIL_V(ERR_FILE_UNRECOGNIZED);
|
||||
ERR_FAIL_V_MSG(ERR_FILE_UNRECOGNIZED, "File format '" + itos(FORMAT_VERSION) + "." + itos(ver_major) + "." + itos(ver_minor) + "' is too new! Please upgrade to a new engine version: " + local_path + ".");
|
||||
}
|
||||
|
||||
// Since we're not actually converting the file contents, leave the version
|
||||
|
@ -1477,7 +1467,7 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia
|
|||
case Variant::_RID: {
|
||||
|
||||
f->store_32(VARIANT_RID);
|
||||
WARN_PRINT("Can't save RIDs");
|
||||
WARN_PRINT("Can't save RIDs.");
|
||||
RID val = p_property;
|
||||
f->store_32(val.get_id());
|
||||
} break;
|
||||
|
@ -1497,8 +1487,7 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia
|
|||
|
||||
if (!resource_set.has(res)) {
|
||||
f->store_32(OBJECT_EMPTY);
|
||||
ERR_EXPLAIN("Resource was not pre cached for the resource section, most likely due to circular refedence.");
|
||||
ERR_FAIL();
|
||||
ERR_FAIL_MSG("Resource was not pre cached for the resource section, most likely due to circular reference.");
|
||||
}
|
||||
|
||||
f->store_32(OBJECT_INTERNAL_RESOURCE);
|
||||
|
@ -1629,8 +1618,7 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia
|
|||
} break;
|
||||
default: {
|
||||
|
||||
ERR_EXPLAIN("Invalid variant");
|
||||
ERR_FAIL();
|
||||
ERR_FAIL_MSG("Invalid variant.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1798,6 +1786,7 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const RES &p
|
|||
|
||||
if (f->get_error() != OK && f->get_error() != ERR_FILE_EOF) {
|
||||
f->close();
|
||||
memdelete(f);
|
||||
return ERR_CANT_CREATE;
|
||||
}
|
||||
|
||||
|
@ -1950,10 +1939,12 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const RES &p
|
|||
|
||||
if (f->get_error() != OK && f->get_error() != ERR_FILE_EOF) {
|
||||
f->close();
|
||||
memdelete(f);
|
||||
return ERR_CANT_CREATE;
|
||||
}
|
||||
|
||||
f->close();
|
||||
memdelete(f);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
|
|
@ -275,12 +275,9 @@ RES ResourceLoader::_load(const String &p_path, const String &p_original_path, c
|
|||
return res;
|
||||
}
|
||||
|
||||
if (found) {
|
||||
ERR_EXPLAIN("Failed loading resource: " + p_path);
|
||||
} else {
|
||||
ERR_EXPLAIN("No loader found for resource: " + p_path);
|
||||
}
|
||||
ERR_FAIL_V(RES());
|
||||
ERR_FAIL_COND_V_MSG(found, RES(), "Failed loading resource: " + p_path + ".");
|
||||
|
||||
ERR_FAIL_V_MSG(RES(), "No loader found for resource: " + p_path + ".");
|
||||
}
|
||||
|
||||
bool ResourceLoader::_add_to_loading_map(const String &p_path) {
|
||||
|
@ -355,10 +352,7 @@ RES ResourceLoader::load(const String &p_path, const String &p_type_hint, bool p
|
|||
|
||||
{
|
||||
bool success = _add_to_loading_map(local_path);
|
||||
if (!success) {
|
||||
ERR_EXPLAIN("Resource: '" + local_path + "' is already being loaded. Cyclic reference?");
|
||||
ERR_FAIL_V(RES());
|
||||
}
|
||||
ERR_FAIL_COND_V_MSG(!success, RES(), "Resource: '" + local_path + "' is already being loaded. Cyclic reference?");
|
||||
}
|
||||
|
||||
//lock first if possible
|
||||
|
@ -395,8 +389,7 @@ RES ResourceLoader::load(const String &p_path, const String &p_type_hint, bool p
|
|||
if (!p_no_cache) {
|
||||
_remove_from_loading_map(local_path);
|
||||
}
|
||||
ERR_EXPLAIN("Remapping '" + local_path + "'failed.");
|
||||
ERR_FAIL_V(RES());
|
||||
ERR_FAIL_V_MSG(RES(), "Remapping '" + local_path + "' failed.");
|
||||
}
|
||||
|
||||
print_verbose("Loading resource: " + path);
|
||||
|
@ -479,10 +472,7 @@ Ref<ResourceInteractiveLoader> ResourceLoader::load_interactive(const String &p_
|
|||
if (!p_no_cache) {
|
||||
|
||||
bool success = _add_to_loading_map(local_path);
|
||||
if (!success) {
|
||||
ERR_EXPLAIN("Resource: '" + local_path + "' is already being loaded. Cyclic reference?");
|
||||
ERR_FAIL_V(RES());
|
||||
}
|
||||
ERR_FAIL_COND_V_MSG(!success, RES(), "Resource: '" + local_path + "' is already being loaded. Cyclic reference?");
|
||||
|
||||
if (ResourceCache::has(local_path)) {
|
||||
|
||||
|
@ -503,8 +493,7 @@ Ref<ResourceInteractiveLoader> ResourceLoader::load_interactive(const String &p_
|
|||
if (!p_no_cache) {
|
||||
_remove_from_loading_map(local_path);
|
||||
}
|
||||
ERR_EXPLAIN("Remapping '" + local_path + "'failed.");
|
||||
ERR_FAIL_V(RES());
|
||||
ERR_FAIL_V_MSG(RES(), "Remapping '" + local_path + "' failed.");
|
||||
}
|
||||
|
||||
print_verbose("Loading resource: " + path);
|
||||
|
@ -534,12 +523,9 @@ Ref<ResourceInteractiveLoader> ResourceLoader::load_interactive(const String &p_
|
|||
_remove_from_loading_map(local_path);
|
||||
}
|
||||
|
||||
if (found) {
|
||||
ERR_EXPLAIN("Failed loading resource: " + path);
|
||||
} else {
|
||||
ERR_EXPLAIN("No loader found for resource: " + path);
|
||||
}
|
||||
ERR_FAIL_V(Ref<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) {
|
||||
|
@ -801,7 +787,7 @@ String ResourceLoader::_path_remap(const String &p_path, bool *r_translation_rem
|
|||
if (err == ERR_FILE_EOF) {
|
||||
break;
|
||||
} else if (err != OK) {
|
||||
ERR_PRINTS("Parse error: " + p_path + ".remap:" + itos(lines) + " error: " + error_text);
|
||||
ERR_PRINTS("Parse error: " + p_path + ".remap:" + itos(lines) + " error: " + error_text + ".");
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -932,16 +918,13 @@ bool ResourceLoader::add_custom_resource_format_loader(String script_path) {
|
|||
Ref<Script> s = res;
|
||||
StringName ibt = s->get_instance_base_type();
|
||||
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);
|
||||
|
||||
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());
|
||||
ResourceLoader::add_resource_format_loader(crl);
|
||||
|
||||
|
|
|
@ -33,9 +33,6 @@
|
|||
|
||||
#include "core/os/thread.h"
|
||||
#include "core/resource.h"
|
||||
/**
|
||||
@author Juan Linietsky <reduzio@gmail.com>
|
||||
*/
|
||||
|
||||
class ResourceInteractiveLoader : public Reference {
|
||||
|
||||
|
|
|
@ -214,16 +214,13 @@ bool ResourceSaver::add_custom_resource_format_saver(String script_path) {
|
|||
Ref<Script> s = res;
|
||||
StringName ibt = s->get_instance_base_type();
|
||||
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);
|
||||
|
||||
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());
|
||||
ResourceSaver::add_resource_format_saver(crl);
|
||||
|
||||
|
|
|
@ -33,10 +33,6 @@
|
|||
|
||||
#include "core/resource.h"
|
||||
|
||||
/**
|
||||
@author Juan Linietsky <reduzio@gmail.com>
|
||||
*/
|
||||
|
||||
class ResourceFormatSaver : public Reference {
|
||||
GDCLASS(ResourceFormatSaver, Reference);
|
||||
|
||||
|
|
|
@ -30,10 +30,7 @@
|
|||
|
||||
#include "stream_peer_ssl.h"
|
||||
|
||||
#include "core/io/certs_compressed.gen.h"
|
||||
#include "core/io/compression.h"
|
||||
#include "core/os/file_access.h"
|
||||
#include "core/project_settings.h"
|
||||
#include "core/engine.h"
|
||||
|
||||
StreamPeerSSL *(*StreamPeerSSL::_create)() = NULL;
|
||||
|
||||
|
@ -44,22 +41,8 @@ StreamPeerSSL *StreamPeerSSL::create() {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
StreamPeerSSL::LoadCertsFromMemory StreamPeerSSL::load_certs_func = NULL;
|
||||
bool StreamPeerSSL::available = false;
|
||||
|
||||
void StreamPeerSSL::load_certs_from_memory(const PoolByteArray &p_memory) {
|
||||
if (load_certs_func)
|
||||
load_certs_func(p_memory);
|
||||
}
|
||||
|
||||
void StreamPeerSSL::load_certs_from_file(String p_path) {
|
||||
if (p_path != "") {
|
||||
PoolByteArray certs = get_cert_file_as_array(p_path);
|
||||
if (certs.size() > 0)
|
||||
load_certs_func(certs);
|
||||
}
|
||||
}
|
||||
|
||||
bool StreamPeerSSL::is_available() {
|
||||
return available;
|
||||
}
|
||||
|
@ -72,56 +55,11 @@ bool StreamPeerSSL::is_blocking_handshake_enabled() const {
|
|||
return blocking_handshake;
|
||||
}
|
||||
|
||||
PoolByteArray StreamPeerSSL::get_cert_file_as_array(String p_path) {
|
||||
|
||||
PoolByteArray out;
|
||||
FileAccess *f = FileAccess::open(p_path, FileAccess::READ);
|
||||
if (f) {
|
||||
int flen = f->get_len();
|
||||
out.resize(flen + 1);
|
||||
PoolByteArray::Write w = out.write();
|
||||
f->get_buffer(w.ptr(), flen);
|
||||
w[flen] = 0; // Make sure it ends with string terminator
|
||||
memdelete(f);
|
||||
#ifdef DEBUG_ENABLED
|
||||
print_verbose(vformat("Loaded certs from '%s'.", p_path));
|
||||
#endif
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
PoolByteArray StreamPeerSSL::get_project_cert_array() {
|
||||
|
||||
PoolByteArray out;
|
||||
String certs_path = GLOBAL_DEF("network/ssl/certificates", "");
|
||||
ProjectSettings::get_singleton()->set_custom_property_info("network/ssl/certificates", PropertyInfo(Variant::STRING, "network/ssl/certificates", PROPERTY_HINT_FILE, "*.crt"));
|
||||
|
||||
if (certs_path != "") {
|
||||
// Use certs defined in project settings.
|
||||
return get_cert_file_as_array(certs_path);
|
||||
}
|
||||
#ifdef BUILTIN_CERTS_ENABLED
|
||||
else {
|
||||
// Use builtin certs only if user did not override it in project settings.
|
||||
out.resize(_certs_uncompressed_size + 1);
|
||||
PoolByteArray::Write w = out.write();
|
||||
Compression::decompress(w.ptr(), _certs_uncompressed_size, _certs_compressed, _certs_compressed_size, Compression::MODE_DEFLATE);
|
||||
w[_certs_uncompressed_size] = 0; // Make sure it ends with string terminator
|
||||
#ifdef DEBUG_ENABLED
|
||||
print_verbose("Loaded builtin certs");
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
void StreamPeerSSL::_bind_methods() {
|
||||
|
||||
ClassDB::bind_method(D_METHOD("poll"), &StreamPeerSSL::poll);
|
||||
ClassDB::bind_method(D_METHOD("accept_stream", "base"), &StreamPeerSSL::accept_stream);
|
||||
ClassDB::bind_method(D_METHOD("connect_to_stream", "stream", "validate_certs", "for_hostname"), &StreamPeerSSL::connect_to_stream, DEFVAL(false), DEFVAL(String()));
|
||||
ClassDB::bind_method(D_METHOD("accept_stream", "stream", "private_key", "certificate", "chain"), &StreamPeerSSL::accept_stream, DEFVAL(Ref<X509Certificate>()));
|
||||
ClassDB::bind_method(D_METHOD("connect_to_stream", "stream", "validate_certs", "for_hostname", "valid_certificate"), &StreamPeerSSL::connect_to_stream, DEFVAL(false), DEFVAL(String()), DEFVAL(Ref<X509Certificate>()));
|
||||
ClassDB::bind_method(D_METHOD("get_status"), &StreamPeerSSL::get_status);
|
||||
ClassDB::bind_method(D_METHOD("disconnect_from_stream"), &StreamPeerSSL::disconnect_from_stream);
|
||||
ClassDB::bind_method(D_METHOD("set_blocking_handshake_enabled", "enabled"), &StreamPeerSSL::set_blocking_handshake_enabled);
|
||||
|
|
|
@ -31,19 +31,16 @@
|
|||
#ifndef STREAM_PEER_SSL_H
|
||||
#define STREAM_PEER_SSL_H
|
||||
|
||||
#include "core/crypto/crypto.h"
|
||||
#include "core/io/stream_peer.h"
|
||||
|
||||
class StreamPeerSSL : public StreamPeer {
|
||||
GDCLASS(StreamPeerSSL, StreamPeer);
|
||||
|
||||
public:
|
||||
typedef void (*LoadCertsFromMemory)(const PoolByteArray &p_certs);
|
||||
|
||||
protected:
|
||||
static StreamPeerSSL *(*_create)();
|
||||
static void _bind_methods();
|
||||
|
||||
static LoadCertsFromMemory load_certs_func;
|
||||
static bool available;
|
||||
|
||||
bool blocking_handshake;
|
||||
|
@ -61,18 +58,14 @@ public:
|
|||
bool is_blocking_handshake_enabled() const;
|
||||
|
||||
virtual void poll() = 0;
|
||||
virtual Error accept_stream(Ref<StreamPeer> p_base) = 0;
|
||||
virtual Error connect_to_stream(Ref<StreamPeer> p_base, bool p_validate_certs = false, const String &p_for_hostname = String()) = 0;
|
||||
virtual Error accept_stream(Ref<StreamPeer> p_base, Ref<CryptoKey> p_key, Ref<X509Certificate> p_cert, Ref<X509Certificate> p_ca_chain = Ref<X509Certificate>()) = 0;
|
||||
virtual Error connect_to_stream(Ref<StreamPeer> p_base, bool p_validate_certs = false, const String &p_for_hostname = String(), Ref<X509Certificate> p_valid_cert = Ref<X509Certificate>()) = 0;
|
||||
virtual Status get_status() const = 0;
|
||||
|
||||
virtual void disconnect_from_stream() = 0;
|
||||
|
||||
static StreamPeerSSL *create();
|
||||
|
||||
static PoolByteArray get_cert_file_as_array(String p_path);
|
||||
static PoolByteArray get_project_cert_array();
|
||||
static void load_certs_from_file(String p_path);
|
||||
static void load_certs_from_memory(const PoolByteArray &p_memory);
|
||||
static bool is_available();
|
||||
|
||||
StreamPeerSSL();
|
||||
|
|
|
@ -67,8 +67,7 @@ RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error, const S
|
|||
|
||||
if (status == STATUS_READING_ID) {
|
||||
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 {
|
||||
break;
|
||||
}
|
||||
|
@ -79,8 +78,7 @@ RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error, const S
|
|||
if (status == STATUS_READING_ID) {
|
||||
|
||||
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 != "") {
|
||||
|
@ -102,8 +100,7 @@ RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error, const S
|
|||
if (status != STATUS_READING_ID) {
|
||||
|
||||
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();
|
||||
|
@ -118,11 +115,7 @@ RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error, const S
|
|||
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());
|
||||
//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.c_unescape();
|
||||
|
@ -163,10 +153,7 @@ RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error, const S
|
|||
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");
|
||||
for (int i = 0; i < configs.size(); i++) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -334,9 +334,6 @@ def make_version(template, nargs, argmax, const, ret):
|
|||
elif (cmd == "noarg"):
|
||||
for i in range(nargs + 1, argmax + 1):
|
||||
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
|
||||
|
||||
|
|
|
@ -33,12 +33,8 @@
|
|||
|
||||
#include "core/set.h"
|
||||
|
||||
/**
|
||||
@author Juan Linietsky <reduzio@gmail.com>
|
||||
*/
|
||||
|
||||
// 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>
|
||||
class Map {
|
||||
|
|
|
@ -2,37 +2,6 @@
|
|||
|
||||
Import('env')
|
||||
|
||||
env_math = env.Clone() # Maybe make one specific for crypto?
|
||||
|
||||
is_builtin = env["builtin_mbedtls"]
|
||||
has_module = env["module_mbedtls_enabled"]
|
||||
|
||||
if is_builtin or not has_module:
|
||||
# Use our headers for builtin or if the module is not going to be compiled.
|
||||
# We decided not to depend on system mbedtls just for these few files that can
|
||||
# be easily extracted.
|
||||
env_math.Prepend(CPPPATH=["#thirdparty/mbedtls/include"])
|
||||
|
||||
# MbedTLS core functions (for CryptoCore).
|
||||
# If the mbedtls module is compiled we don't need to add the .c files with our
|
||||
# custom config since they will be built by the module itself.
|
||||
# Only if the module is not enabled, we must compile here the required sources
|
||||
# to make a "light" build with only the necessary mbedtls files.
|
||||
if not has_module:
|
||||
env_thirdparty = env_math.Clone()
|
||||
env_thirdparty.disable_warnings()
|
||||
# Custom config file
|
||||
env_thirdparty.Append(CPPDEFINES=[('MBEDTLS_CONFIG_FILE', '\\"thirdparty/mbedtls/include/godot_core_mbedtls_config.h\\"')])
|
||||
thirdparty_mbedtls_dir = "#thirdparty/mbedtls/library/"
|
||||
thirdparty_mbedtls_sources = [
|
||||
"aes.c",
|
||||
"base64.c",
|
||||
"md5.c",
|
||||
"sha1.c",
|
||||
"sha256.c",
|
||||
"godot_core_mbedtls_platform.c"
|
||||
]
|
||||
thirdparty_mbedtls_sources = [thirdparty_mbedtls_dir + file for file in thirdparty_mbedtls_sources]
|
||||
env_thirdparty.add_source_files(env.core_sources, thirdparty_mbedtls_sources)
|
||||
env_math = env.Clone()
|
||||
|
||||
env_math.add_source_files(env.core_sources, "*.cpp")
|
||||
|
|
|
@ -40,7 +40,17 @@ int AStar::get_available_point_id() const {
|
|||
return 1;
|
||||
}
|
||||
|
||||
return points.back()->key() + 1;
|
||||
// calculate our new next available point id if bigger than before or next id already contained in set of points.
|
||||
if (points.has(last_free_id)) {
|
||||
int cur_new_id = last_free_id;
|
||||
while (points.has(cur_new_id)) {
|
||||
cur_new_id++;
|
||||
}
|
||||
int &non_const = const_cast<int &>(last_free_id);
|
||||
non_const = cur_new_id;
|
||||
}
|
||||
|
||||
return last_free_id;
|
||||
}
|
||||
|
||||
void AStar::add_point(int p_id, const Vector3 &p_pos, real_t p_weight_scale) {
|
||||
|
@ -48,7 +58,10 @@ void AStar::add_point(int p_id, const Vector3 &p_pos, real_t p_weight_scale) {
|
|||
ERR_FAIL_COND(p_id < 0);
|
||||
ERR_FAIL_COND(p_weight_scale < 1);
|
||||
|
||||
if (!points.has(p_id)) {
|
||||
Point *found_pt;
|
||||
bool p_exists = points.lookup(p_id, found_pt);
|
||||
|
||||
if (!p_exists) {
|
||||
Point *pt = memnew(Point);
|
||||
pt->id = p_id;
|
||||
pt->pos = p_pos;
|
||||
|
@ -57,84 +70,98 @@ void AStar::add_point(int p_id, const Vector3 &p_pos, real_t p_weight_scale) {
|
|||
pt->open_pass = 0;
|
||||
pt->closed_pass = 0;
|
||||
pt->enabled = true;
|
||||
points[p_id] = pt;
|
||||
points.set(p_id, pt);
|
||||
} else {
|
||||
points[p_id]->pos = p_pos;
|
||||
points[p_id]->weight_scale = p_weight_scale;
|
||||
found_pt->pos = p_pos;
|
||||
found_pt->weight_scale = p_weight_scale;
|
||||
}
|
||||
}
|
||||
|
||||
Vector3 AStar::get_point_position(int p_id) const {
|
||||
|
||||
ERR_FAIL_COND_V(!points.has(p_id), Vector3());
|
||||
Point *p;
|
||||
bool p_exists = points.lookup(p_id, p);
|
||||
ERR_FAIL_COND_V(!p_exists, Vector3());
|
||||
|
||||
return points[p_id]->pos;
|
||||
return p->pos;
|
||||
}
|
||||
|
||||
void AStar::set_point_position(int p_id, const Vector3 &p_pos) {
|
||||
|
||||
ERR_FAIL_COND(!points.has(p_id));
|
||||
Point *p;
|
||||
bool p_exists = points.lookup(p_id, p);
|
||||
ERR_FAIL_COND(!p_exists);
|
||||
|
||||
points[p_id]->pos = p_pos;
|
||||
p->pos = p_pos;
|
||||
}
|
||||
|
||||
real_t AStar::get_point_weight_scale(int p_id) const {
|
||||
|
||||
ERR_FAIL_COND_V(!points.has(p_id), 0);
|
||||
Point *p;
|
||||
bool p_exists = points.lookup(p_id, p);
|
||||
ERR_FAIL_COND_V(!p_exists, 0);
|
||||
|
||||
return points[p_id]->weight_scale;
|
||||
return p->weight_scale;
|
||||
}
|
||||
|
||||
void AStar::set_point_weight_scale(int p_id, real_t p_weight_scale) {
|
||||
|
||||
ERR_FAIL_COND(!points.has(p_id));
|
||||
Point *p;
|
||||
bool p_exists = points.lookup(p_id, p);
|
||||
ERR_FAIL_COND(!p_exists);
|
||||
ERR_FAIL_COND(p_weight_scale < 1);
|
||||
|
||||
points[p_id]->weight_scale = p_weight_scale;
|
||||
p->weight_scale = p_weight_scale;
|
||||
}
|
||||
|
||||
void AStar::remove_point(int p_id) {
|
||||
|
||||
ERR_FAIL_COND(!points.has(p_id));
|
||||
Point *p;
|
||||
bool p_exists = points.lookup(p_id, p);
|
||||
ERR_FAIL_COND(!p_exists);
|
||||
|
||||
Point *p = points[p_id];
|
||||
for (OAHashMap<int, Point *>::Iterator it = p->neighbours.iter(); it.valid; it = p->neighbours.next_iter(it)) {
|
||||
|
||||
for (Set<Point *>::Element *E = p->neighbours.front(); E; E = E->next()) {
|
||||
|
||||
Segment s(p_id, E->get()->id);
|
||||
Segment s(p_id, (*it.key));
|
||||
segments.erase(s);
|
||||
|
||||
E->get()->neighbours.erase(p);
|
||||
E->get()->unlinked_neighbours.erase(p);
|
||||
(*it.value)->neighbours.remove(p->id);
|
||||
(*it.value)->unlinked_neighbours.remove(p->id);
|
||||
}
|
||||
|
||||
for (Set<Point *>::Element *E = p->unlinked_neighbours.front(); E; E = E->next()) {
|
||||
for (OAHashMap<int, Point *>::Iterator it = p->unlinked_neighbours.iter(); it.valid; it = p->unlinked_neighbours.next_iter(it)) {
|
||||
|
||||
Segment s(p_id, E->get()->id);
|
||||
Segment s(p_id, (*it.key));
|
||||
segments.erase(s);
|
||||
|
||||
E->get()->neighbours.erase(p);
|
||||
E->get()->unlinked_neighbours.erase(p);
|
||||
(*it.value)->neighbours.remove(p->id);
|
||||
(*it.value)->unlinked_neighbours.remove(p->id);
|
||||
}
|
||||
|
||||
memdelete(p);
|
||||
points.erase(p_id);
|
||||
points.remove(p_id);
|
||||
last_free_id = p_id;
|
||||
}
|
||||
|
||||
void AStar::connect_points(int p_id, int p_with_id, bool bidirectional) {
|
||||
|
||||
ERR_FAIL_COND(!points.has(p_id));
|
||||
ERR_FAIL_COND(!points.has(p_with_id));
|
||||
ERR_FAIL_COND(p_id == p_with_id);
|
||||
|
||||
Point *a = points[p_id];
|
||||
Point *b = points[p_with_id];
|
||||
a->neighbours.insert(b);
|
||||
Point *a;
|
||||
bool from_exists = points.lookup(p_id, a);
|
||||
ERR_FAIL_COND(!from_exists);
|
||||
|
||||
if (bidirectional)
|
||||
b->neighbours.insert(a);
|
||||
else
|
||||
b->unlinked_neighbours.insert(a);
|
||||
Point *b;
|
||||
bool to_exists = points.lookup(p_with_id, b);
|
||||
ERR_FAIL_COND(!to_exists);
|
||||
|
||||
a->neighbours.set(b->id, b);
|
||||
|
||||
if (bidirectional) {
|
||||
b->neighbours.set(a->id, a);
|
||||
} else {
|
||||
b->unlinked_neighbours.set(a->id, a);
|
||||
}
|
||||
|
||||
Segment s(p_id, p_with_id);
|
||||
if (s.from == p_id) {
|
||||
|
@ -147,6 +174,7 @@ void AStar::connect_points(int p_id, int p_with_id, bool bidirectional) {
|
|||
|
||||
segments.insert(s);
|
||||
}
|
||||
|
||||
void AStar::disconnect_points(int p_id, int p_with_id) {
|
||||
|
||||
Segment s(p_id, p_with_id);
|
||||
|
@ -154,12 +182,18 @@ void AStar::disconnect_points(int p_id, int p_with_id) {
|
|||
|
||||
segments.erase(s);
|
||||
|
||||
Point *a = points[p_id];
|
||||
Point *b = points[p_with_id];
|
||||
a->neighbours.erase(b);
|
||||
a->unlinked_neighbours.erase(b);
|
||||
b->neighbours.erase(a);
|
||||
b->unlinked_neighbours.erase(a);
|
||||
Point *a;
|
||||
bool a_exists = points.lookup(p_id, a);
|
||||
CRASH_COND(!a_exists);
|
||||
|
||||
Point *b;
|
||||
bool b_exists = points.lookup(p_with_id, b);
|
||||
CRASH_COND(!b_exists);
|
||||
|
||||
a->neighbours.remove(b->id);
|
||||
a->unlinked_neighbours.remove(b->id);
|
||||
b->neighbours.remove(a->id);
|
||||
b->unlinked_neighbours.remove(a->id);
|
||||
}
|
||||
|
||||
bool AStar::has_point(int p_id) const {
|
||||
|
@ -171,8 +205,8 @@ Array AStar::get_points() {
|
|||
|
||||
Array point_list;
|
||||
|
||||
for (const Map<int, Point *>::Element *E = points.front(); E; E = E->next()) {
|
||||
point_list.push_back(E->key());
|
||||
for (OAHashMap<int, Point *>::Iterator it = points.iter(); it.valid; it = points.next_iter(it)) {
|
||||
point_list.push_back(*(it.key));
|
||||
}
|
||||
|
||||
return point_list;
|
||||
|
@ -180,14 +214,14 @@ Array AStar::get_points() {
|
|||
|
||||
PoolVector<int> AStar::get_point_connections(int p_id) {
|
||||
|
||||
ERR_FAIL_COND_V(!points.has(p_id), PoolVector<int>());
|
||||
Point *p;
|
||||
bool p_exists = points.lookup(p_id, p);
|
||||
ERR_FAIL_COND_V(!p_exists, PoolVector<int>());
|
||||
|
||||
PoolVector<int> point_list;
|
||||
|
||||
Point *p = points[p_id];
|
||||
|
||||
for (Set<Point *>::Element *E = p->neighbours.front(); E; E = E->next()) {
|
||||
point_list.push_back(E->get()->id);
|
||||
for (OAHashMap<int, Point *>::Iterator it = p->neighbours.iter(); it.valid; it = p->neighbours.next_iter(it)) {
|
||||
point_list.push_back((*it.key));
|
||||
}
|
||||
|
||||
return point_list;
|
||||
|
@ -201,27 +235,41 @@ bool AStar::are_points_connected(int p_id, int p_with_id) const {
|
|||
|
||||
void AStar::clear() {
|
||||
|
||||
for (const Map<int, Point *>::Element *E = points.front(); E; E = E->next()) {
|
||||
|
||||
memdelete(E->get());
|
||||
last_free_id = 0;
|
||||
for (OAHashMap<int, Point *>::Iterator it = points.iter(); it.valid; it = points.next_iter(it)) {
|
||||
memdelete(*(it.value));
|
||||
}
|
||||
segments.clear();
|
||||
points.clear();
|
||||
}
|
||||
|
||||
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 closest_id = -1;
|
||||
real_t closest_dist = 1e20;
|
||||
|
||||
for (const Map<int, Point *>::Element *E = points.front(); E; E = E->next()) {
|
||||
for (OAHashMap<int, Point *>::Iterator it = points.iter(); it.valid; it = points.next_iter(it)) {
|
||||
|
||||
if (!E->get()->enabled)
|
||||
continue; //Disabled points should not be considered
|
||||
real_t d = p_point.distance_squared_to(E->get()->pos);
|
||||
if (!(*it.value)->enabled) continue; // Disabled points should not be considered.
|
||||
|
||||
real_t d = p_point.distance_squared_to((*it.value)->pos);
|
||||
if (closest_id < 0 || d < closest_dist) {
|
||||
closest_dist = d;
|
||||
closest_id = E->key();
|
||||
closest_id = *(it.key);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -230,8 +278,8 @@ int AStar::get_closest_point(const Vector3 &p_point) const {
|
|||
|
||||
Vector3 AStar::get_closest_position_in_segment(const Vector3 &p_point) const {
|
||||
|
||||
real_t closest_dist = 1e20;
|
||||
bool found = false;
|
||||
real_t closest_dist = 1e20;
|
||||
Vector3 closest_point;
|
||||
|
||||
for (const Set<Segment>::Element *E = segments.front(); E; E = E->next()) {
|
||||
|
@ -262,8 +310,7 @@ bool AStar::_solve(Point *begin_point, Point *end_point) {
|
|||
|
||||
pass++;
|
||||
|
||||
if (!end_point->enabled)
|
||||
return false;
|
||||
if (!end_point->enabled) return false;
|
||||
|
||||
bool found_route = false;
|
||||
|
||||
|
@ -272,13 +319,9 @@ bool AStar::_solve(Point *begin_point, Point *end_point) {
|
|||
|
||||
begin_point->g_score = 0;
|
||||
begin_point->f_score = _estimate_cost(begin_point->id, end_point->id);
|
||||
|
||||
open_list.push_back(begin_point);
|
||||
|
||||
while (true) {
|
||||
|
||||
if (open_list.size() == 0) // No path found
|
||||
break;
|
||||
while (!open_list.empty()) {
|
||||
|
||||
Point *p = open_list[0]; // The currently processed point
|
||||
|
||||
|
@ -291,24 +334,23 @@ bool AStar::_solve(Point *begin_point, Point *end_point) {
|
|||
open_list.remove(open_list.size() - 1);
|
||||
p->closed_pass = pass; // Mark the point as closed
|
||||
|
||||
for (Set<Point *>::Element *E = p->neighbours.front(); E; E = E->next()) {
|
||||
for (OAHashMap<int, Point *>::Iterator it = p->neighbours.iter(); it.valid; it = p->neighbours.next_iter(it)) {
|
||||
|
||||
Point *e = E->get(); // The neighbour point
|
||||
Point *e = *(it.value); // The neighbour point
|
||||
|
||||
if (!e->enabled || e->closed_pass == pass)
|
||||
if (!e->enabled || e->closed_pass == pass) {
|
||||
continue;
|
||||
}
|
||||
|
||||
real_t tentative_g_score = p->g_score + _compute_cost(p->id, e->id) * e->weight_scale;
|
||||
|
||||
bool new_point = false;
|
||||
|
||||
if (e->open_pass != pass) { // The point wasn't inside the open list
|
||||
|
||||
if (e->open_pass != pass) { // The point wasn't inside the open list.
|
||||
e->open_pass = pass;
|
||||
open_list.push_back(e);
|
||||
new_point = true;
|
||||
} else if (tentative_g_score >= e->g_score) { // The new path is worse than the previous
|
||||
|
||||
} else if (tentative_g_score >= e->g_score) { // The new path is worse than the previous.
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -316,10 +358,11 @@ bool AStar::_solve(Point *begin_point, Point *end_point) {
|
|||
e->g_score = tentative_g_score;
|
||||
e->f_score = e->g_score + _estimate_cost(e->id, end_point->id);
|
||||
|
||||
if (new_point) // The position of the new points is already known
|
||||
if (new_point) { // The position of the new points is already known.
|
||||
sorter.push_heap(0, open_list.size() - 1, 0, e, open_list.ptrw());
|
||||
else
|
||||
} else {
|
||||
sorter.push_heap(0, open_list.find(e), 0, e, open_list.ptrw());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -331,7 +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))
|
||||
return get_script_instance()->call(SceneStringNames::get_singleton()->_estimate_cost, p_from_id, p_to_id);
|
||||
|
||||
return points[p_from_id]->pos.distance_to(points[p_to_id]->pos);
|
||||
Point *from_point;
|
||||
bool from_exists = points.lookup(p_from_id, from_point);
|
||||
CRASH_COND(!from_exists);
|
||||
|
||||
Point *to_point;
|
||||
bool to_exists = points.lookup(p_to_id, to_point);
|
||||
CRASH_COND(!to_exists);
|
||||
|
||||
return from_point->pos.distance_to(to_point->pos);
|
||||
}
|
||||
|
||||
float AStar::_compute_cost(int p_from_id, int p_to_id) {
|
||||
|
@ -339,16 +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))
|
||||
return get_script_instance()->call(SceneStringNames::get_singleton()->_compute_cost, p_from_id, p_to_id);
|
||||
|
||||
return points[p_from_id]->pos.distance_to(points[p_to_id]->pos);
|
||||
Point *from_point;
|
||||
bool from_exists = points.lookup(p_from_id, from_point);
|
||||
CRASH_COND(!from_exists);
|
||||
|
||||
Point *to_point;
|
||||
bool to_exists = points.lookup(p_to_id, to_point);
|
||||
CRASH_COND(!to_exists);
|
||||
|
||||
return from_point->pos.distance_to(to_point->pos);
|
||||
}
|
||||
|
||||
PoolVector<Vector3> AStar::get_point_path(int p_from_id, int p_to_id) {
|
||||
|
||||
ERR_FAIL_COND_V(!points.has(p_from_id), PoolVector<Vector3>());
|
||||
ERR_FAIL_COND_V(!points.has(p_to_id), PoolVector<Vector3>());
|
||||
Point *a;
|
||||
bool from_exists = points.lookup(p_from_id, a);
|
||||
ERR_FAIL_COND_V(!from_exists, PoolVector<Vector3>());
|
||||
|
||||
Point *a = points[p_from_id];
|
||||
Point *b = points[p_to_id];
|
||||
Point *b;
|
||||
bool to_exists = points.lookup(p_to_id, b);
|
||||
ERR_FAIL_COND_V(!to_exists, PoolVector<Vector3>());
|
||||
|
||||
if (a == b) {
|
||||
PoolVector<Vector3> ret;
|
||||
|
@ -360,11 +421,8 @@ PoolVector<Vector3> AStar::get_point_path(int p_from_id, int p_to_id) {
|
|||
Point *end_point = b;
|
||||
|
||||
bool found_route = _solve(begin_point, end_point);
|
||||
if (!found_route) return PoolVector<Vector3>();
|
||||
|
||||
if (!found_route)
|
||||
return PoolVector<Vector3>();
|
||||
|
||||
// Midpoints
|
||||
Point *p = end_point;
|
||||
int pc = 1; // Begin point
|
||||
while (p != begin_point) {
|
||||
|
@ -393,11 +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) {
|
||||
|
||||
ERR_FAIL_COND_V(!points.has(p_from_id), PoolVector<int>());
|
||||
ERR_FAIL_COND_V(!points.has(p_to_id), PoolVector<int>());
|
||||
Point *a;
|
||||
bool from_exists = points.lookup(p_from_id, a);
|
||||
ERR_FAIL_COND_V(!from_exists, PoolVector<int>());
|
||||
|
||||
Point *a = points[p_from_id];
|
||||
Point *b = points[p_to_id];
|
||||
Point *b;
|
||||
bool to_exists = points.lookup(p_to_id, b);
|
||||
ERR_FAIL_COND_V(!to_exists, PoolVector<int>());
|
||||
|
||||
if (a == b) {
|
||||
PoolVector<int> ret;
|
||||
|
@ -409,11 +469,8 @@ PoolVector<int> AStar::get_id_path(int p_from_id, int p_to_id) {
|
|||
Point *end_point = b;
|
||||
|
||||
bool found_route = _solve(begin_point, end_point);
|
||||
if (!found_route) return PoolVector<int>();
|
||||
|
||||
if (!found_route)
|
||||
return PoolVector<int>();
|
||||
|
||||
// Midpoints
|
||||
Point *p = end_point;
|
||||
int pc = 1; // Begin point
|
||||
while (p != begin_point) {
|
||||
|
@ -442,16 +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) {
|
||||
|
||||
ERR_FAIL_COND(!points.has(p_id));
|
||||
Point *p;
|
||||
bool p_exists = points.lookup(p_id, p);
|
||||
ERR_FAIL_COND(!p_exists);
|
||||
|
||||
points[p_id]->enabled = !p_disabled;
|
||||
p->enabled = !p_disabled;
|
||||
}
|
||||
|
||||
bool AStar::is_point_disabled(int p_id) const {
|
||||
|
||||
ERR_FAIL_COND_V(!points.has(p_id), false);
|
||||
Point *p;
|
||||
bool p_exists = points.lookup(p_id, p);
|
||||
ERR_FAIL_COND_V(!p_exists, false);
|
||||
|
||||
return !points[p_id]->enabled;
|
||||
return !p->enabled;
|
||||
}
|
||||
|
||||
void AStar::_bind_methods() {
|
||||
|
@ -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("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("get_closest_point", "to_position"), &AStar::get_closest_point);
|
||||
|
@ -487,13 +551,11 @@ void AStar::_bind_methods() {
|
|||
}
|
||||
|
||||
AStar::AStar() {
|
||||
|
||||
last_free_id = 0;
|
||||
pass = 1;
|
||||
}
|
||||
|
||||
AStar::~AStar() {
|
||||
|
||||
pass = 1;
|
||||
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);
|
||||
}
|
||||
|
||||
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() {
|
||||
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 {
|
||||
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("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("get_closest_point", "to_position"), &AStar2D::get_closest_point);
|
||||
|
|
|
@ -31,8 +31,8 @@
|
|||
#ifndef ASTAR_H
|
||||
#define ASTAR_H
|
||||
|
||||
#include "core/oa_hash_map.h"
|
||||
#include "core/reference.h"
|
||||
#include "core/self_list.h"
|
||||
|
||||
/**
|
||||
A* pathfinding algorithm
|
||||
|
@ -44,19 +44,21 @@ class AStar : public Reference {
|
|||
|
||||
GDCLASS(AStar, Reference);
|
||||
|
||||
uint64_t pass;
|
||||
|
||||
struct Point {
|
||||
|
||||
Point() :
|
||||
neighbours(4u),
|
||||
unlinked_neighbours(4u) {}
|
||||
|
||||
int id;
|
||||
Vector3 pos;
|
||||
real_t weight_scale;
|
||||
bool enabled;
|
||||
|
||||
Set<Point *> neighbours;
|
||||
Set<Point *> unlinked_neighbours;
|
||||
OAHashMap<int, Point *> neighbours;
|
||||
OAHashMap<int, Point *> unlinked_neighbours;
|
||||
|
||||
// Used for pathfinding
|
||||
// Used for pathfinding.
|
||||
Point *prev_point;
|
||||
real_t g_score;
|
||||
real_t f_score;
|
||||
|
@ -64,16 +66,15 @@ class AStar : public Reference {
|
|||
uint64_t closed_pass;
|
||||
};
|
||||
|
||||
Map<int, Point *> points;
|
||||
|
||||
struct SortPoints {
|
||||
_FORCE_INLINE_ bool operator()(const Point *A, const Point *B) const { // Returns true when the Point A is worse than Point B
|
||||
if (A->f_score > B->f_score)
|
||||
_FORCE_INLINE_ bool operator()(const Point *A, const Point *B) const { // Returns true when the Point A is worse than Point B.
|
||||
if (A->f_score > B->f_score) {
|
||||
return true;
|
||||
else if (A->f_score < B->f_score)
|
||||
} else if (A->f_score < B->f_score) {
|
||||
return false;
|
||||
else
|
||||
return A->g_score < B->g_score; // If the f_costs are the same then prioritize the points that are further away from the start
|
||||
} else {
|
||||
return A->g_score < B->g_score; // If the f_costs are the same then prioritize the points that are further away from the start.
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -101,6 +102,10 @@ class AStar : public Reference {
|
|||
}
|
||||
};
|
||||
|
||||
int last_free_id;
|
||||
uint64_t pass;
|
||||
|
||||
OAHashMap<int, Point *> points;
|
||||
Set<Segment> segments;
|
||||
|
||||
bool _solve(Point *begin_point, Point *end_point);
|
||||
|
@ -131,6 +136,9 @@ public:
|
|||
void disconnect_points(int p_id, int p_with_id);
|
||||
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();
|
||||
|
||||
int get_closest_point(const Vector3 &p_point) const;
|
||||
|
@ -170,6 +178,9 @@ public:
|
|||
void disconnect_points(int p_id, int p_with_id);
|
||||
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();
|
||||
|
||||
int get_closest_point(const Vector2 &p_point) const;
|
||||
|
|
|
@ -618,10 +618,7 @@ Basis::operator String() const {
|
|||
Quat Basis::get_quat() const {
|
||||
|
||||
#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
|
||||
/* Allow getting a quaternion from an unnormalized transform */
|
||||
Basis m = *this;
|
||||
|
|
|
@ -36,10 +36,6 @@
|
|||
|
||||
#include "core/math/quat.h"
|
||||
|
||||
/**
|
||||
@author Juan Linietsky <reduzio@gmail.com>
|
||||
*/
|
||||
|
||||
class Basis {
|
||||
public:
|
||||
Vector3 elements[3];
|
||||
|
|
|
@ -38,9 +38,7 @@
|
|||
#include "core/pool_vector.h"
|
||||
#include "core/variant.h"
|
||||
#include "core/vector.h"
|
||||
/**
|
||||
@author Juan Linietsky <reduzio@gmail.com>
|
||||
*/
|
||||
|
||||
class BSP_Tree {
|
||||
public:
|
||||
enum {
|
||||
|
|
|
@ -302,8 +302,8 @@ Vector<Plane> CameraMatrix::get_projection_planes(const Transform &p_transform)
|
|||
|
||||
/** Fast Plane Extraction from combined modelview/projection matrices.
|
||||
* 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;
|
||||
|
|
|
@ -34,10 +34,6 @@
|
|||
#include "core/math/rect2.h"
|
||||
#include "core/math/transform.h"
|
||||
|
||||
/**
|
||||
@author Juan Linietsky <reduzio@gmail.com>
|
||||
*/
|
||||
|
||||
struct CameraMatrix {
|
||||
|
||||
enum Planes {
|
||||
|
|
|
@ -80,11 +80,11 @@ public:
|
|||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
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;
|
||||
Variant output;
|
||||
|
@ -2173,10 +2171,7 @@ Variant Expression::execute(Array p_inputs, Object *p_base, bool p_show_error) {
|
|||
if (err) {
|
||||
execution_error = true;
|
||||
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;
|
||||
|
|
|
@ -34,9 +34,10 @@
|
|||
#include "thirdparty/misc/clipper.hpp"
|
||||
#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) {
|
||||
|
||||
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) {
|
||||
/* 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;
|
||||
|
||||
|
@ -195,13 +196,6 @@ static bool _connect_faces(_FaceClassify *p_faces, int len, int p_group) {
|
|||
if (p_faces[i].links[j].face == -1)
|
||||
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;
|
||||
}
|
||||
|
@ -249,10 +243,10 @@ PoolVector<PoolVector<Face3> > Geometry::separate_objects(PoolVector<Face3> p_ar
|
|||
|
||||
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;
|
||||
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++) {
|
||||
|
||||
|
@ -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) {
|
||||
|
||||
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;
|
||||
|
||||
|
@ -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];
|
||||
|
||||
//printf("at %i,%i,%i\n",x,y,z);
|
||||
|
||||
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;
|
||||
//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 not done, increase step */
|
||||
// If not done, increase step.
|
||||
c += 1 << 2;
|
||||
//printf("incrementing cell step\n");
|
||||
}
|
||||
|
||||
if ((c & _CELL_STEP_MASK) == _CELL_STEP_DONE) {
|
||||
/* Go back */
|
||||
//printf("done, going back a cell\n");
|
||||
|
||||
// Go back.
|
||||
switch (c & _CELL_PREV_MASK) {
|
||||
case _CELL_PREV_FIRST: {
|
||||
//printf("at end, finished marking\n");
|
||||
return;
|
||||
} break;
|
||||
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;
|
||||
}
|
||||
|
||||
//printf("attempting new cell!\n");
|
||||
|
||||
int next_x = x, next_y = y, next_z = z;
|
||||
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();
|
||||
}
|
||||
|
||||
//printf("testing if new cell will be ok...!\n");
|
||||
|
||||
if (next_x < 0 || next_x >= len_x)
|
||||
continue;
|
||||
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)
|
||||
continue;
|
||||
|
||||
//printf("testing if new cell is traversable\n");
|
||||
|
||||
if (p_cell_status[next_x][next_y][next_z] & 3)
|
||||
continue;
|
||||
|
||||
//printf("move to it\n");
|
||||
|
||||
x = next_x;
|
||||
y = next_y;
|
||||
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)
|
||||
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)
|
||||
|
||||
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,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++) {
|
||||
|
||||
|
@ -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;
|
||||
|
||||
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.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);
|
||||
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++) {
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
// 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++) {
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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();
|
||||
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);
|
||||
in_poly.push_back(inp);
|
||||
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!");
|
||||
return decomp;
|
||||
}
|
||||
|
@ -781,7 +731,7 @@ Geometry::MeshData Geometry::build_convex_mesh(const PoolVector<Plane> &p_planes
|
|||
|
||||
#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++) {
|
||||
|
||||
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);
|
||||
|
||||
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 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 dist1 = clip.distance_to(edge1_A);
|
||||
|
||||
if (dist0 <= 0) { // behind plane
|
||||
if (dist0 <= 0) { // Behind plane.
|
||||
|
||||
new_vertices.push_back(vertices[k]);
|
||||
}
|
||||
|
||||
// check for different sides and non coplanar
|
||||
// Check for different sides and non coplanar.
|
||||
if ((dist0 * dist1) < 0) {
|
||||
|
||||
// calculate intersection
|
||||
// Calculate intersection.
|
||||
Vector3 rel = edge1_A - edge0_A;
|
||||
|
||||
real_t den = clip.normal.dot(rel);
|
||||
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;
|
||||
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)
|
||||
continue;
|
||||
|
||||
//result is a clockwise face
|
||||
// Result is a clockwise face.
|
||||
|
||||
MeshData::Face face;
|
||||
|
||||
// add face indices
|
||||
// Add face indices.
|
||||
for (int j = 0; j < vertices.size(); j++) {
|
||||
|
||||
int idx = -1;
|
||||
|
@ -882,7 +832,7 @@ Geometry::MeshData Geometry::build_convex_mesh(const PoolVector<Plane> &p_planes
|
|||
face.plane = p;
|
||||
mesh.faces.push_back(face);
|
||||
|
||||
//add edge
|
||||
// Add edge.
|
||||
|
||||
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++) {
|
||||
|
||||
//todo this is stupid, fix
|
||||
// FIXME: This is stupid.
|
||||
Vector3 angle = normal.linear_interpolate(axis, j / (real_t)p_lats).normalized();
|
||||
Vector3 pos = angle * p_radius;
|
||||
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) {
|
||||
|
||||
//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).
|
||||
|
||||
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++)
|
||||
hmax.write[j] = 0;
|
||||
|
||||
//place them
|
||||
// Place them.
|
||||
int ofs = 0;
|
||||
int limit_h = 0;
|
||||
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)
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -1112,7 +1062,7 @@ void Geometry::make_atlas(const Vector<Size2i> &p_rects, Vector<Point2i> &r_resu
|
|||
results.push_back(result);
|
||||
}
|
||||
|
||||
//find the result with the best aspect ratio
|
||||
// Find the result with the best aspect ratio.
|
||||
|
||||
int best = -1;
|
||||
real_t best_aspect = 1e20;
|
||||
|
@ -1152,7 +1102,7 @@ Vector<Vector<Point2> > Geometry::_polypaths_do_operation(PolyBooleanOperation p
|
|||
}
|
||||
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) {
|
||||
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);
|
||||
}
|
||||
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;
|
||||
|
||||
if (is_a_open) {
|
||||
PolyTree tree; // needed to populate polylines
|
||||
PolyTree tree; // Needed to populate polylines.
|
||||
clp.Execute(op, tree);
|
||||
OpenPathsFromPolyTree(tree, paths);
|
||||
} 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;
|
||||
|
||||
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;
|
||||
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) {
|
||||
path << IntPoint(p_polypath[i].x * SCALE_FACTOR, p_polypath[i].y * SCALE_FACTOR);
|
||||
}
|
||||
co.AddPath(path, jt, et);
|
||||
|
||||
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;
|
||||
|
||||
for (Paths::size_type i = 0; i < paths.size(); ++i) {
|
||||
|
|
|
@ -41,47 +41,43 @@
|
|||
#include "core/print_string.h"
|
||||
#include "core/vector.h"
|
||||
|
||||
/**
|
||||
@author Juan Linietsky <reduzio@gmail.com>
|
||||
*/
|
||||
|
||||
class Geometry {
|
||||
Geometry();
|
||||
|
||||
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) {
|
||||
|
||||
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;
|
||||
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 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) {
|
||||
// Both segments degenerate into points
|
||||
// Both segments degenerate into points.
|
||||
c1 = p1;
|
||||
c2 = p2;
|
||||
return Math::sqrt((c1 - c2).dot(c1 - c2));
|
||||
}
|
||||
if (a <= CMP_EPSILON) {
|
||||
// First segment degenerates into a point
|
||||
// First segment degenerates into a point.
|
||||
s = 0.0;
|
||||
t = f / e; // s = 0 => t = (b*s + f) / e = f / e
|
||||
t = CLAMP(t, 0.0, 1.0);
|
||||
} else {
|
||||
real_t c = d1.dot(r);
|
||||
if (e <= CMP_EPSILON) {
|
||||
// Second segment degenerates into a point
|
||||
// Second segment degenerates into a point.
|
||||
t = 0.0;
|
||||
s = CLAMP(-c / a, 0.0, 1.0); // t = 0 => s = (b*t - c) / a = -c / a
|
||||
} else {
|
||||
// The general nondegenerate case starts here
|
||||
// The general nondegenerate case starts here.
|
||||
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
|
||||
// clamp to segment S1. Else pick arbitrary s (here 0)
|
||||
// clamp to segment S1. Else pick arbitrary s (here 0).
|
||||
if (denom != 0.0) {
|
||||
s = CLAMP((b * f - c * e) / denom, 0.0, 1.0);
|
||||
} else
|
||||
|
@ -92,7 +88,7 @@ public:
|
|||
|
||||
//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
|
||||
// and clamp s to [0, 1]
|
||||
// and clamp s to [0, 1].
|
||||
if (t < 0.0) {
|
||||
t = 0.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) {
|
||||
|
||||
//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))
|
||||
|
||||
//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 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 (mub < 0) mub = 0;
|
||||
if (mua > 1) mua = 1;
|
||||
|
@ -129,38 +125,38 @@ public:
|
|||
Vector3 u = p_to_a - p_from_a;
|
||||
Vector3 v = p_to_b - p_from_b;
|
||||
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 c = v.dot(v); // always >= 0
|
||||
real_t c = v.dot(v); // Always >= 0
|
||||
real_t d = u.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 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;
|
||||
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);
|
||||
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;
|
||||
tN = e;
|
||||
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;
|
||||
tN = e + b;
|
||||
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;
|
||||
// recompute sc for this edge
|
||||
// Recompute sc for this edge.
|
||||
if (-d < 0.0)
|
||||
sN = 0.0;
|
||||
else if (-d > a)
|
||||
|
@ -169,9 +165,9 @@ public:
|
|||
sN = -d;
|
||||
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;
|
||||
// recompute sc for this edge
|
||||
// Recompute sc for this edge.
|
||||
if ((-d + b) < 0.0)
|
||||
sN = 0;
|
||||
else if ((-d + b) > a)
|
||||
|
@ -181,14 +177,14 @@ public:
|
|||
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);
|
||||
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)
|
||||
|
||||
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) {
|
||||
|
@ -196,7 +192,7 @@ public:
|
|||
Vector3 e2 = p_v2 - p_v0;
|
||||
Vector3 h = p_dir.cross(e2);
|
||||
real_t a = e1.dot(h);
|
||||
if (Math::is_zero_approx(a)) // parallel test
|
||||
if (Math::is_zero_approx(a)) // Parallel test.
|
||||
return false;
|
||||
|
||||
real_t f = 1.0 / a;
|
||||
|
@ -214,16 +210,15 @@ public:
|
|||
if (v < 0.0 || u + v > 1.0)
|
||||
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);
|
||||
|
||||
if (t > 0.00001) { // ray intersection
|
||||
if (r_res)
|
||||
*r_res = p_from + p_dir * t;
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -234,7 +229,7 @@ public:
|
|||
Vector3 e2 = p_v2 - p_v0;
|
||||
Vector3 h = rel.cross(e2);
|
||||
real_t a = e1.dot(h);
|
||||
if (Math::is_zero_approx(a)) // parallel test
|
||||
if (Math::is_zero_approx(a)) // Parallel test.
|
||||
return false;
|
||||
|
||||
real_t f = 1.0 / a;
|
||||
|
@ -252,16 +247,15 @@ public:
|
|||
if (v < 0.0 || u + v > 1.0)
|
||||
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);
|
||||
|
||||
if (t > CMP_EPSILON && t <= 1.0) { // ray intersection
|
||||
if (t > CMP_EPSILON && t <= 1.0) { // Ray intersection.
|
||||
if (r_res)
|
||||
*r_res = p_from + rel * t;
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -271,13 +265,11 @@ public:
|
|||
Vector3 rel = (p_to - p_from);
|
||||
real_t rel_l = rel.length();
|
||||
if (rel_l < CMP_EPSILON)
|
||||
return false; // both points are the same
|
||||
return false; // Both points are the same.
|
||||
Vector3 normal = rel / rel_l;
|
||||
|
||||
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);
|
||||
|
||||
if (ray_distance >= p_sphere_radius)
|
||||
|
@ -289,7 +281,7 @@ public:
|
|||
if (inters_d2 >= CMP_EPSILON)
|
||||
inters_d -= Math::sqrt(inters_d2);
|
||||
|
||||
// check in segment
|
||||
// Check in segment.
|
||||
if (inters_d < 0 || inters_d > rel_l)
|
||||
return false;
|
||||
|
||||
|
@ -308,9 +300,9 @@ public:
|
|||
Vector3 rel = (p_to - p_from);
|
||||
real_t rel_l = rel.length();
|
||||
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 crs = normal.cross(Vector3(0, 0, 1));
|
||||
real_t crs_l = crs.length();
|
||||
|
@ -318,8 +310,7 @@ public:
|
|||
Vector3 z_dir;
|
||||
|
||||
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 {
|
||||
z_dir = crs / crs_l;
|
||||
}
|
||||
|
@ -327,12 +318,12 @@ public:
|
|||
real_t dist = z_dir.dot(p_from);
|
||||
|
||||
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;
|
||||
if (w2 < CMP_EPSILON)
|
||||
return false; //avoid numerical error
|
||||
return false; // Avoid numerical error.
|
||||
Size2 size(Math::sqrt(w2), p_height * 0.5);
|
||||
|
||||
Vector3 x_dir = z_dir.cross(Vector3(0, 0, 1)).normalized();
|
||||
|
@ -379,7 +370,7 @@ public:
|
|||
return false;
|
||||
}
|
||||
|
||||
// convert to 3D again
|
||||
// Convert to 3D again.
|
||||
Vector3 result = p_from + (rel * min);
|
||||
Vector3 res_normal = result;
|
||||
|
||||
|
@ -420,19 +411,18 @@ public:
|
|||
|
||||
real_t den = p.normal.dot(dir);
|
||||
|
||||
//printf("den is %i\n",den);
|
||||
if (Math::abs(den) <= CMP_EPSILON)
|
||||
continue; // ignore parallel plane
|
||||
continue; // Ignore parallel plane.
|
||||
|
||||
real_t dist = -p.distance_to(p_from) / den;
|
||||
|
||||
if (den > 0) {
|
||||
//backwards facing plane
|
||||
// Backwards facing plane.
|
||||
if (dist < max)
|
||||
max = dist;
|
||||
} else {
|
||||
|
||||
//front facing plane
|
||||
// Front facing plane.
|
||||
if (dist > min) {
|
||||
min = dist;
|
||||
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)
|
||||
*p_res = p_from + dir * min;
|
||||
|
@ -457,16 +447,16 @@ public:
|
|||
Vector3 n = p_segment[1] - p_segment[0];
|
||||
real_t l2 = n.length_squared();
|
||||
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;
|
||||
|
||||
if (d <= 0.0)
|
||||
return p_segment[0]; // before first point
|
||||
return p_segment[0]; // Before first point.
|
||||
else if (d >= 1.0)
|
||||
return p_segment[1]; // after first point
|
||||
return p_segment[1]; // After first point.
|
||||
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) {
|
||||
|
@ -475,11 +465,11 @@ public:
|
|||
Vector3 n = p_segment[1] - p_segment[0];
|
||||
real_t l2 = n.length_squared();
|
||||
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;
|
||||
|
||||
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) {
|
||||
|
@ -488,16 +478,16 @@ public:
|
|||
Vector2 n = p_segment[1] - p_segment[0];
|
||||
real_t l2 = n.length_squared();
|
||||
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;
|
||||
|
||||
if (d <= 0.0)
|
||||
return p_segment[0]; // before first point
|
||||
return p_segment[0]; // Before first point.
|
||||
else if (d >= 1.0)
|
||||
return p_segment[1]; // after first point
|
||||
return p_segment[1]; // After first point.
|
||||
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) {
|
||||
|
@ -512,27 +502,25 @@ public:
|
|||
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) {
|
||||
|
||||
Vector2 p = p_point - p_segment[0];
|
||||
Vector2 n = p_segment[1] - p_segment[0];
|
||||
real_t l2 = n.length_squared();
|
||||
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;
|
||||
|
||||
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) {
|
||||
|
||||
// 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;
|
||||
if (Math::is_zero_approx(denom)) { // parallel?
|
||||
if (Math::is_zero_approx(denom)) { // Parallel?
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -560,11 +548,11 @@ public:
|
|||
|
||||
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)
|
||||
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)
|
||||
*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]);
|
||||
|
||||
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;
|
||||
|
||||
Vector3 contact = p_sphere_pos - (p_normal * d);
|
||||
|
@ -617,25 +605,25 @@ public:
|
|||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
|
||||
// check edge cylinder
|
||||
// Check edge cylinder.
|
||||
|
||||
Vector3 n1 = verts[i] - 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);
|
||||
axis.normalize(); // ugh
|
||||
axis.normalize();
|
||||
|
||||
real_t ad = axis.dot(n2);
|
||||
|
||||
if (ABS(ad) > p_sphere_radius) {
|
||||
// no chance with this edge, too far away
|
||||
// No chance with this edge, too far away.
|
||||
continue;
|
||||
}
|
||||
|
||||
// check point within edge capsule cylinder
|
||||
// Check point within edge capsule cylinder.
|
||||
/** 4th TEST INSIDE EDGE POINTS **/
|
||||
|
||||
real_t sphere_at = n1.dot(n2);
|
||||
|
@ -644,8 +632,7 @@ public:
|
|||
|
||||
r_triangle_contact = p_sphere_pos - axis * (axis.dot(n2));
|
||||
r_sphere_contact = p_sphere_pos - axis * p_sphere_radius;
|
||||
// point inside here
|
||||
//printf("solved inside edge\n");
|
||||
// Point inside here.
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -655,48 +642,51 @@ public:
|
|||
|
||||
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_sphere_contact = p_sphere_pos - n * p_sphere_radius;
|
||||
//printf("solved inside point segment 1\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (n2.distance_squared_to(n1) < r2) {
|
||||
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_sphere_contact = p_sphere_pos - n * p_sphere_radius;
|
||||
//printf("solved inside point segment 1\n");
|
||||
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;
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
Vector2 line_vec = p_to - p_from;
|
||||
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;
|
||||
|
||||
a = line_vec.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;
|
||||
|
||||
/* solve for t */
|
||||
// Solve for t.
|
||||
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 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);
|
||||
real_t res1 = (-b - sqrtterm) / (2 * a);
|
||||
real_t res2 = (-b + sqrtterm) / (2 * a);
|
||||
|
@ -722,7 +712,6 @@ public:
|
|||
int outside_count = 0;
|
||||
|
||||
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]);
|
||||
if (dist < -CMP_POINT_IN_PLANE_EPSILON) {
|
||||
location_cache[a] = LOC_INSIDE;
|
||||
|
@ -739,11 +728,11 @@ public:
|
|||
|
||||
if (outside_count == 0) {
|
||||
|
||||
return polygon; // no changes
|
||||
return polygon; // No changes.
|
||||
|
||||
} else if (inside_count == 0) {
|
||||
|
||||
return Vector<Vector3>(); //empty
|
||||
return Vector<Vector3>(); // Empty.
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
Vector<Delaunay2D::Triangle> tr = Delaunay2D::triangulate(p_points);
|
||||
|
@ -899,7 +877,7 @@ public:
|
|||
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) {
|
||||
int c = p_polygon.size();
|
||||
if (c < 3)
|
||||
|
@ -915,7 +893,8 @@ public:
|
|||
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;
|
||||
for (int i = 0; i < c; i++) {
|
||||
|
@ -931,7 +910,8 @@ public:
|
|||
|
||||
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 {
|
||||
|
||||
|
@ -1013,17 +993,17 @@ public:
|
|||
Vector<Point2> H;
|
||||
H.resize(2 * n);
|
||||
|
||||
// Sort points lexicographically
|
||||
// Sort points lexicographically.
|
||||
P.sort();
|
||||
|
||||
// Build lower hull
|
||||
// Build lower hull.
|
||||
for (int i = 0; i < n; ++i) {
|
||||
while (k >= 2 && vec2_cross(H[k - 2], H[k - 1], P[i]) <= 0)
|
||||
k--;
|
||||
H.write[k++] = P[i];
|
||||
}
|
||||
|
||||
// Build upper hull
|
||||
// Build upper hull.
|
||||
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)
|
||||
k--;
|
||||
|
|
|
@ -79,6 +79,15 @@ int Math::step_decimals(double p_step) {
|
|||
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 sgn = p_value < 0 ? -1.0 : 1.0;
|
||||
double val = Math::abs(p_value);
|
||||
|
|
|
@ -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_ 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) {
|
||||
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) {
|
||||
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,
|
||||
static double ease(double p_x, double p_c);
|
||||
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 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) {
|
||||
// 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);
|
||||
if (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) {
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
|
|
@ -38,10 +38,6 @@
|
|||
#include "core/print_string.h"
|
||||
#include "core/variant.h"
|
||||
|
||||
/**
|
||||
@author Juan Linietsky <reduzio@gmail.com>
|
||||
*/
|
||||
|
||||
typedef uint32_t OctreeElementID;
|
||||
|
||||
#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)) {
|
||||
|
||||
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_count++;
|
||||
|
|
|
@ -38,10 +38,6 @@
|
|||
#include "core/math/math_funcs.h"
|
||||
#include "core/ustring.h"
|
||||
|
||||
/**
|
||||
@author Juan Linietsky <reduzio@gmail.com>
|
||||
*/
|
||||
|
||||
class Quat {
|
||||
public:
|
||||
real_t x, y, z, w;
|
||||
|
|
|
@ -213,3 +213,8 @@ Transform::Transform(const Basis &p_basis, const Vector3 &p_origin) :
|
|||
basis(p_basis),
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -34,10 +34,7 @@
|
|||
#include "core/math/aabb.h"
|
||||
#include "core/math/basis.h"
|
||||
#include "core/math/plane.h"
|
||||
|
||||
/**
|
||||
@author Juan Linietsky <reduzio@gmail.com>
|
||||
*/
|
||||
#include "core/pool_vector.h"
|
||||
|
||||
class Transform {
|
||||
public:
|
||||
|
@ -86,6 +83,9 @@ public:
|
|||
_FORCE_INLINE_ AABB xform(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);
|
||||
Transform operator*(const Transform &p_transform) const;
|
||||
|
||||
|
@ -108,6 +108,7 @@ public:
|
|||
|
||||
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() {}
|
||||
};
|
||||
|
@ -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 {
|
||||
/* 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 {
|
||||
|
@ -201,4 +209,32 @@ _FORCE_INLINE_ AABB Transform::xform_inv(const AABB &p_aabb) const {
|
|||
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
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#define TRANSFORM_2D_H
|
||||
|
||||
#include "core/math/rect2.h" // also includes vector2, math_funcs, and ustring
|
||||
#include "core/pool_vector.h"
|
||||
|
||||
struct Transform2D {
|
||||
// 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_ Rect2 xform(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;
|
||||
|
||||
|
@ -199,4 +202,32 @@ Rect2 Transform2D::xform_inv(const Rect2 &p_rect) const {
|
|||
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
|
||||
|
|
|
@ -98,6 +98,11 @@ real_t Vector2::cross(const Vector2 &p_other) const {
|
|||
return x * p_other.y - y * p_other.x;
|
||||
}
|
||||
|
||||
Vector2 Vector2::sign() const {
|
||||
|
||||
return Vector2(SGN(x), SGN(y));
|
||||
}
|
||||
|
||||
Vector2 Vector2::floor() const {
|
||||
|
||||
return Vector2(Math::floor(x), Math::floor(y));
|
||||
|
@ -121,6 +126,14 @@ Vector2 Vector2::rotated(real_t p_by) const {
|
|||
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 {
|
||||
return p_b * (dot(p_b) / p_b.length_squared());
|
||||
}
|
||||
|
|
|
@ -38,6 +38,11 @@ struct Vector2i;
|
|||
|
||||
struct Vector2 {
|
||||
|
||||
enum Axis {
|
||||
AXIS_X,
|
||||
AXIS_Y,
|
||||
};
|
||||
|
||||
union {
|
||||
real_t x;
|
||||
real_t width;
|
||||
|
@ -69,6 +74,8 @@ struct Vector2 {
|
|||
|
||||
real_t dot(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 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 { 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;
|
||||
|
||||
|
@ -129,6 +138,7 @@ struct Vector2 {
|
|||
return Vector2(y, -x);
|
||||
}
|
||||
|
||||
Vector2 sign() const;
|
||||
Vector2 floor() const;
|
||||
Vector2 ceil() const;
|
||||
Vector2 round() const;
|
||||
|
@ -141,10 +151,7 @@ struct Vector2 {
|
|||
x = p_x;
|
||||
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 {
|
||||
|
@ -262,6 +269,11 @@ typedef Vector2 Point2;
|
|||
|
||||
struct Vector2i {
|
||||
|
||||
enum Axis {
|
||||
AXIS_X,
|
||||
AXIS_Y,
|
||||
};
|
||||
|
||||
union {
|
||||
int x;
|
||||
int width;
|
||||
|
|
|
@ -31,9 +31,7 @@
|
|||
#ifndef VECTOR3_H
|
||||
#define VECTOR3_H
|
||||
|
||||
#include "core/math/math_defs.h"
|
||||
#include "core/math/math_funcs.h"
|
||||
#include "core/typedefs.h"
|
||||
#include "core/ustring.h"
|
||||
|
||||
class Basis;
|
||||
|
@ -110,6 +108,8 @@ struct Vector3 {
|
|||
_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_ 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_ 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;
|
||||
|
||||
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) {
|
||||
x = p_x;
|
||||
y = p_y;
|
||||
z = p_z;
|
||||
}
|
||||
_FORCE_INLINE_ Vector3() { x = y = z = 0; }
|
||||
};
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
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 {
|
||||
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 {
|
||||
|
||||
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) {
|
||||
|
||||
return p_a.cross(p_b);
|
||||
|
|
|
@ -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();
|
||||
print_line("Failed method: " + type + ":" + p_method + " target ID: " + itos(p_id));
|
||||
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);
|
||||
|
@ -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();
|
||||
print_line("Failed set: " + type + ":" + p_prop + " target ID: " + itos(p_id));
|
||||
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);
|
||||
|
@ -136,8 +134,7 @@ Error MessageQueue::push_notification(ObjectID p_id, int p_notification) {
|
|||
type = ObjectDB::get_instance(p_id)->get_class();
|
||||
print_line("Failed notification: " + itos(p_notification) + " target ID: " + itos(p_id));
|
||||
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);
|
||||
|
@ -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);
|
||||
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) + ".");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -38,10 +38,6 @@
|
|||
|
||||
#include <stdio.h>
|
||||
|
||||
/**
|
||||
@author Juan Linietsky <reduzio@gmail.com>
|
||||
*/
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
#define DEBUG_METHODS_ENABLED
|
||||
#endif
|
||||
|
|
|
@ -375,8 +375,7 @@ NodePath::NodePath(const String &p_path) {
|
|||
if (str == "") {
|
||||
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);
|
||||
|
||||
|
|
|
@ -34,10 +34,6 @@
|
|||
#include "core/string_name.h"
|
||||
#include "core/ustring.h"
|
||||
|
||||
/**
|
||||
@author Juan Linietsky <reduzio@gmail.com>
|
||||
*/
|
||||
|
||||
class NodePath {
|
||||
|
||||
struct Data {
|
||||
|
|
|
@ -62,7 +62,7 @@ private:
|
|||
static const uint32_t EMPTY_HASH = 0;
|
||||
static const uint32_t DELETED_HASH_BIT = 1 << 31;
|
||||
|
||||
_FORCE_INLINE_ uint32_t _hash(const TKey &p_key) {
|
||||
_FORCE_INLINE_ uint32_t _hash(const TKey &p_key) const {
|
||||
uint32_t hash = Hasher::hash(p_key);
|
||||
|
||||
if (hash == EMPTY_HASH) {
|
||||
|
@ -74,12 +74,11 @@ private:
|
|||
return hash;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ uint32_t _get_probe_length(uint32_t p_pos, uint32_t p_hash) {
|
||||
_FORCE_INLINE_ uint32_t _get_probe_length(uint32_t p_pos, uint32_t p_hash) const {
|
||||
p_hash = p_hash & ~DELETED_HASH_BIT; // we don't care if it was deleted or not
|
||||
|
||||
uint32_t original_pos = p_hash % capacity;
|
||||
|
||||
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) {
|
||||
|
@ -90,7 +89,7 @@ private:
|
|||
num_elements++;
|
||||
}
|
||||
|
||||
bool _lookup_pos(const TKey &p_key, uint32_t &r_pos) {
|
||||
bool _lookup_pos(const TKey &p_key, uint32_t &r_pos) const {
|
||||
uint32_t hash = _hash(p_key);
|
||||
uint32_t pos = hash % capacity;
|
||||
uint32_t distance = 0;
|
||||
|
@ -151,17 +150,17 @@ private:
|
|||
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;
|
||||
TValue *old_values = values;
|
||||
uint32_t *old_hashes = hashes;
|
||||
|
||||
uint32_t old_capacity = capacity;
|
||||
|
||||
capacity = old_capacity * 2;
|
||||
num_elements = 0;
|
||||
|
||||
keys = memnew_arr(TKey, capacity);
|
||||
values = memnew_arr(TValue, capacity);
|
||||
hashes = memnew_arr(uint32_t, capacity);
|
||||
|
@ -186,10 +185,38 @@ private:
|
|||
memdelete_arr(old_hashes);
|
||||
}
|
||||
|
||||
void _resize_and_rehash() {
|
||||
_resize_and_rehash(capacity * 2);
|
||||
}
|
||||
|
||||
public:
|
||||
_FORCE_INLINE_ uint32_t get_capacity() const { return capacity; }
|
||||
_FORCE_INLINE_ uint32_t get_num_elements() const { return num_elements; }
|
||||
|
||||
bool empty() const {
|
||||
return num_elements == 0;
|
||||
}
|
||||
|
||||
void clear() {
|
||||
|
||||
for (uint32_t i = 0; i < capacity; i++) {
|
||||
|
||||
if (hashes[i] == 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) {
|
||||
|
||||
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
|
||||
* it points to.
|
||||
*/
|
||||
bool lookup(const TKey &p_key, TValue &r_data) {
|
||||
bool lookup(const TKey &p_key, TValue &r_data) const {
|
||||
uint32_t pos = 0;
|
||||
bool exists = _lookup_pos(p_key, pos);
|
||||
|
||||
|
@ -232,7 +259,7 @@ public:
|
|||
return false;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ bool has(const TKey &p_key) {
|
||||
_FORCE_INLINE_ bool has(const TKey &p_key) const {
|
||||
uint32_t _pos = 0;
|
||||
return _lookup_pos(p_key, _pos);
|
||||
}
|
||||
|
@ -251,6 +278,16 @@ public:
|
|||
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 {
|
||||
bool valid;
|
||||
|
||||
|
@ -302,6 +339,9 @@ public:
|
|||
return it;
|
||||
}
|
||||
|
||||
OAHashMap(const OAHashMap &) = delete; // Delete the copy constructor so we don't get unexpected copies and dangling pointers.
|
||||
OAHashMap &operator=(const OAHashMap &) = delete; // Same for assignment operator.
|
||||
|
||||
OAHashMap(uint32_t p_initial_capacity = 64) {
|
||||
|
||||
capacity = p_initial_capacity;
|
||||
|
@ -312,7 +352,7 @@ public:
|
|||
hashes = memnew_arr(uint32_t, p_initial_capacity);
|
||||
|
||||
for (uint32_t i = 0; i < p_initial_capacity; i++) {
|
||||
hashes[i] = 0;
|
||||
hashes[i] = EMPTY_HASH;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -635,12 +635,9 @@ void Object::get_property_list(List<PropertyInfo> *p_list, bool p_reversed) cons
|
|||
#endif
|
||||
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) {
|
||||
p_list->push_back(PropertyInfo(Variant::NIL, "Script Variables", PROPERTY_HINT_NONE, String(), PROPERTY_USAGE_CATEGORY));
|
||||
script_instance->get_property_list(p_list);
|
||||
|
@ -712,20 +709,17 @@ static void _test_call_error(const StringName &p_func, const Variant::CallError
|
|||
break;
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
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) {
|
||||
#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
|
||||
|
||||
//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 ret = call(p_method, argptrs, p_args.size(), ce);
|
||||
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;
|
||||
}
|
||||
|
@ -891,15 +878,13 @@ Variant Object::call(const StringName &p_method, const Variant **p_args, int p_a
|
|||
if (Object::cast_to<Reference>(this)) {
|
||||
r_error.argument = 0;
|
||||
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) {
|
||||
r_error.argument = 0;
|
||||
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
|
||||
|
@ -1175,10 +1160,7 @@ Error Object::emit_signal(const StringName &p_name, const Variant **p_args, int
|
|||
#ifdef DEBUG_ENABLED
|
||||
bool signal_is_valid = ClassDB::has_signal(get_class_name(), p_name);
|
||||
//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
|
||||
//not connected? just return
|
||||
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())) {
|
||||
//most likely object is not initialized yet, do not throw error.
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
bool Object::has_persistent_signal_connections() const {
|
||||
int Object::get_persistent_signal_connection_count() const {
|
||||
|
||||
int count = 0;
|
||||
const StringName *S = NULL;
|
||||
|
||||
while ((S = signal_map.next(S))) {
|
||||
|
@ -1427,13 +1410,13 @@ bool Object::has_persistent_signal_connections() const {
|
|||
const Signal *s = &signal_map[*S];
|
||||
|
||||
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 {
|
||||
|
@ -1466,10 +1449,8 @@ Error Object::connect(const StringName &p_signal, Object *p_to_object, const Str
|
|||
#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();
|
||||
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++;
|
||||
return OK;
|
||||
} 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))
|
||||
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);
|
||||
|
@ -1536,21 +1515,13 @@ void Object::_disconnect(const StringName &p_signal, Object *p_to_object, const
|
|||
|
||||
ERR_FAIL_NULL(p_to_object);
|
||||
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);
|
||||
|
||||
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];
|
||||
|
||||
|
@ -1977,10 +1948,7 @@ Object::~Object() {
|
|||
|
||||
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
|
||||
int slot_count = s->slot_map.size();
|
||||
|
|
|
@ -58,7 +58,7 @@ enum PropertyHint {
|
|||
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_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_FLAGS, ///< hint_text= "flag1,flag2,etc" (as bit flags)
|
||||
PROPERTY_HINT_LAYERS_2D_RENDER,
|
||||
|
@ -104,7 +104,8 @@ enum PropertyUsageFlags {
|
|||
PROPERTY_USAGE_INTERNATIONALIZED = 64, //hint for internationalized strings
|
||||
PROPERTY_USAGE_GROUP = 128, //used for grouping props in the editor
|
||||
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_NONONE = 1024, //only store if false
|
||||
PROPERTY_USAGE_NO_INSTANCE_STATE = 2048,
|
||||
|
@ -121,6 +122,7 @@ enum PropertyUsageFlags {
|
|||
PROPERTY_USAGE_HIGH_END_GFX = 1 << 22,
|
||||
PROPERTY_USAGE_NODE_PATH_FROM_SCENE_ROOT = 1 << 23,
|
||||
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_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_connection_list(const StringName &p_signal, 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;
|
||||
|
||||
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();
|
||||
|
||||
_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;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -179,14 +179,6 @@ Error DirAccess::make_dir_recursive(String p_dir) {
|
|||
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 {
|
||||
|
||||
switch (_access_type) {
|
||||
|
|
|
@ -34,10 +34,6 @@
|
|||
#include "core/typedefs.h"
|
||||
#include "core/ustring.h"
|
||||
|
||||
/**
|
||||
@author Juan Linietsky <reduzio@gmail.com>
|
||||
*/
|
||||
|
||||
//@ TODO, excellent candidate for THREAD_SAFE MACRO, should go through all these and add THREAD_SAFE where it applies
|
||||
class DirAccess {
|
||||
public:
|
||||
|
@ -71,7 +67,6 @@ protected:
|
|||
|
||||
public:
|
||||
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 bool current_is_dir() 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 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;
|
||||
static String get_full_path(const String &p_path, AccessType p_access);
|
||||
static DirAccess *create_for_path(const String &p_path);
|
||||
|
|
|
@ -30,9 +30,9 @@
|
|||
|
||||
#include "file_access.h"
|
||||
|
||||
#include "core/crypto/crypto_core.h"
|
||||
#include "core/io/file_access_pack.h"
|
||||
#include "core/io/marshalls.h"
|
||||
#include "core/math/crypto_core.h"
|
||||
#include "core/os/os.h"
|
||||
#include "core/project_settings.h"
|
||||
|
||||
|
@ -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
|
||||
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;
|
||||
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) {
|
||||
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;
|
||||
|
|
|
@ -80,6 +80,7 @@ void Input::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("get_joy_axis_index_from_string", "axis"), &Input::get_joy_axis_index_from_string);
|
||||
ClassDB::bind_method(D_METHOD("start_joy_vibration", "device", "weak_magnitude", "strong_magnitude", "duration"), &Input::start_joy_vibration, DEFVAL(0));
|
||||
ClassDB::bind_method(D_METHOD("stop_joy_vibration", "device"), &Input::stop_joy_vibration);
|
||||
ClassDB::bind_method(D_METHOD("vibrate_handheld", "duration_ms"), &Input::vibrate_handheld, DEFVAL(500));
|
||||
ClassDB::bind_method(D_METHOD("get_gravity"), &Input::get_gravity);
|
||||
ClassDB::bind_method(D_METHOD("get_accelerometer"), &Input::get_accelerometer);
|
||||
ClassDB::bind_method(D_METHOD("get_magnetometer"), &Input::get_magnetometer);
|
||||
|
|
|
@ -100,6 +100,7 @@ public:
|
|||
virtual uint64_t get_joy_vibration_timestamp(int p_device) = 0;
|
||||
virtual void start_joy_vibration(int p_device, float p_weak_magnitude, float p_strong_magnitude, float p_duration = 0) = 0;
|
||||
virtual void stop_joy_vibration(int p_device) = 0;
|
||||
virtual void vibrate_handheld(int p_duration_ms = 500) = 0;
|
||||
|
||||
virtual Point2 get_mouse_position() const = 0;
|
||||
virtual Point2 get_last_mouse_speed() const = 0;
|
||||
|
|
|
@ -450,7 +450,7 @@ bool InputEventMouseButton::is_doubleclick() 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);
|
||||
|
||||
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 {
|
||||
|
||||
Vector2 g = p_xform.xform(get_global_position());
|
||||
Vector2 g = get_global_position();
|
||||
Vector2 l = p_xform.xform(get_position() + p_local_ofs);
|
||||
Vector2 r = p_xform.basis_xform(get_relative());
|
||||
Vector2 s = p_xform.basis_xform(get_speed());
|
||||
|
|
|
@ -37,10 +37,6 @@
|
|||
#include "core/typedefs.h"
|
||||
#include "core/ustring.h"
|
||||
|
||||
/**
|
||||
@author Juan Linietsky <reduzio@gmail.com>
|
||||
*/
|
||||
|
||||
/**
|
||||
* Input Event classes. These are used in the main loop.
|
||||
* The events are pretty obvious.
|
||||
|
|
|
@ -33,10 +33,6 @@
|
|||
|
||||
#include "core/ustring.h"
|
||||
|
||||
/**
|
||||
@author Juan Linietsky <reduzio@gmail.com>
|
||||
*/
|
||||
|
||||
/*
|
||||
Special Key:
|
||||
|
||||
|
|
|
@ -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("_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_EXIT);
|
||||
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);
|
||||
}
|
||||
|
||||
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() {
|
||||
|
||||
if (get_script_instance()) {
|
||||
|
|
|
@ -35,10 +35,6 @@
|
|||
#include "core/reference.h"
|
||||
#include "core/script_language.h"
|
||||
|
||||
/**
|
||||
@author Juan Linietsky <reduzio@gmail.com>
|
||||
*/
|
||||
|
||||
class MainLoop : public Object {
|
||||
|
||||
GDCLASS(MainLoop, Object);
|
||||
|
@ -75,6 +71,7 @@ public:
|
|||
virtual void finish();
|
||||
|
||||
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);
|
||||
|
||||
|
|
|
@ -35,10 +35,6 @@
|
|||
|
||||
#include <stddef.h>
|
||||
|
||||
/**
|
||||
@author Juan Linietsky <reduzio@gmail.com>
|
||||
*/
|
||||
|
||||
#ifndef PAD_ALIGN
|
||||
#define PAD_ALIGN 16 //must always be greater than this at much
|
||||
#endif
|
||||
|
|
|
@ -186,6 +186,11 @@ int OS::get_process_id() const {
|
|||
return -1;
|
||||
};
|
||||
|
||||
void OS::vibrate_handheld(int p_duration_ms) {
|
||||
|
||||
WARN_PRINTS("vibrate_handheld() only works with Android and iOS");
|
||||
}
|
||||
|
||||
bool OS::is_stdout_verbose() const {
|
||||
|
||||
return _verbose_stdout;
|
||||
|
@ -268,8 +273,7 @@ void OS::print_all_resources(String p_to_file) {
|
|||
_OSPRF = FileAccess::open(p_to_file, FileAccess::WRITE, &err);
|
||||
if (err != OK) {
|
||||
_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);
|
||||
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);
|
||||
}
|
||||
|
|
10
core/os/os.h
10
core/os/os.h
|
@ -41,10 +41,6 @@
|
|||
|
||||
#include <stdarg.h>
|
||||
|
||||
/**
|
||||
@author Juan Linietsky <reduzio@gmail.com>
|
||||
*/
|
||||
|
||||
class Mutex;
|
||||
|
||||
class OS {
|
||||
|
@ -147,6 +143,11 @@ public:
|
|||
|
||||
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(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 kill(const ProcessID &p_pid) = 0;
|
||||
virtual int get_process_id() const;
|
||||
virtual void vibrate_handheld(int p_duration_ms = 500);
|
||||
|
||||
virtual Error shell_open(String p_uri);
|
||||
virtual Error set_cwd(const String &p_cwd);
|
||||
|
|
|
@ -33,10 +33,6 @@
|
|||
|
||||
#include "core/error_list.h"
|
||||
|
||||
/**
|
||||
@author Juan Linietsky <reduzio@gmail.com>
|
||||
*/
|
||||
|
||||
class Semaphore {
|
||||
protected:
|
||||
static Semaphore *(*create_func)();
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue