Compare commits
	
		
			No commits in common. "development" and "feature/engine-template-rework" have entirely different histories.
		
	
	
		
			developmen
			...
			feature/en
		
	
		
							
								
								
									
										8
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							|  | @ -12,11 +12,3 @@ build/PROJECT.pck | ||||||
| build/PROJECT.x86_64 | build/PROJECT.x86_64 | ||||||
| build/PROJECT.exe | build/PROJECT.exe | ||||||
| build.zip | build.zip | ||||||
| 
 |  | ||||||
| *.o |  | ||||||
| compile_commands.json |  | ||||||
| .tree.hash |  | ||||||
| .cache |  | ||||||
| .kdev4 |  | ||||||
| *.kdev4 |  | ||||||
| *__pycache__ |  | ||||||
|  |  | ||||||
							
								
								
									
										1
									
								
								engine
									
									
									
									
									
								
							
							
								
								
								
								
								
								
							
						
						
									
										1
									
								
								engine
									
									
									
									
									
								
							|  | @ -1 +0,0 @@ | ||||||
| Subproject commit 215acd52e82f4c575abb715e25e54558deeef998 |  | ||||||
							
								
								
									
										19
									
								
								engine/.clang-tidy
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								engine/.clang-tidy
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,19 @@ | ||||||
|  | Checks: | ||||||
|  |   - -* | ||||||
|  |   - cppcoreguidelines-pro-type-member-init | ||||||
|  |   - modernize-redundant-void-arg | ||||||
|  |   - modernize-use-bool-literals | ||||||
|  |   - modernize-use-default-member-init | ||||||
|  |   - modernize-use-nullptr | ||||||
|  |   - readability-braces-around-statements | ||||||
|  |   - readability-redundant-member-init | ||||||
|  | HeaderFileExtensions: ["", h, hh, hpp, hxx, inc, glsl] | ||||||
|  | ImplementationFileExtensions: [c, cc, cpp, cxx, m, mm, java] | ||||||
|  | HeaderFilterRegex: (core|doc|drivers|editor|main|modules|platform|scene|servers|tests)/ | ||||||
|  | FormatStyle: file | ||||||
|  | CheckOptions: | ||||||
|  |   cppcoreguidelines-pro-type-member-init.IgnoreArrays: true | ||||||
|  |   cppcoreguidelines-pro-type-member-init.UseAssignment: true | ||||||
|  |   modernize-use-bool-literals.IgnoreMacros: false | ||||||
|  |   modernize-use-default-member-init.IgnoreMacros: false | ||||||
|  |   modernize-use-default-member-init.UseAssignment: true | ||||||
							
								
								
									
										30
									
								
								engine/.clangd
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								engine/.clangd
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,30 @@ | ||||||
|  | # https://clangd.llvm.org/config | ||||||
|  | --- | ||||||
|  | # Default conditions, apply everywhere. | ||||||
|  | 
 | ||||||
|  | Diagnostics: | ||||||
|  |   Includes: | ||||||
|  |     IgnoreHeader: | ||||||
|  |       - \.compat\.inc | ||||||
|  | --- | ||||||
|  | # Header-specific conditions. | ||||||
|  | 
 | ||||||
|  | If: | ||||||
|  |   PathMatch: .*\.(h|hh|hpp|hxx|inc) | ||||||
|  | 
 | ||||||
|  | # Exclude certain, noisy warnings that lack full context. Replace with lowered severity if/when | ||||||
|  | # clangd gets diagnostic severity support. (See: https://github.com/clangd/clangd/issues/1937) | ||||||
|  | CompileFlags: | ||||||
|  |   Add: | ||||||
|  |     - -Wno-unneeded-internal-declaration | ||||||
|  |     - -Wno-unused-const-variable | ||||||
|  |     - -Wno-unused-function | ||||||
|  |     - -Wno-unused-variable | ||||||
|  | --- | ||||||
|  | # Suppress all third-party warnings. | ||||||
|  | 
 | ||||||
|  | If: | ||||||
|  |   PathMatch: thirdparty/.* | ||||||
|  | 
 | ||||||
|  | Diagnostics: | ||||||
|  |   Suppress: "*" | ||||||
							
								
								
									
										21
									
								
								engine/.editorconfig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								engine/.editorconfig
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,21 @@ | ||||||
|  | root = true | ||||||
|  | 
 | ||||||
|  | [*] | ||||||
|  | charset = utf-8 | ||||||
|  | end_of_line = lf | ||||||
|  | indent_size = 4 | ||||||
|  | indent_style = tab | ||||||
|  | insert_final_newline = true | ||||||
|  | max_line_length = 120 | ||||||
|  | trim_trailing_whitespace = true | ||||||
|  | 
 | ||||||
|  | [{*.py,SConstruct,SCsub}] | ||||||
|  | indent_style = space | ||||||
|  | 
 | ||||||
|  | [{*.{yml,yaml},.clang{-format,-tidy,d}}] | ||||||
|  | indent_size = 2 | ||||||
|  | indent_style = space | ||||||
|  | 
 | ||||||
|  | [{*.props,*.vcxproj}] | ||||||
|  | indent_size = 2 | ||||||
|  | indent_style = space | ||||||
							
								
								
									
										74
									
								
								engine/.git-blame-ignore-revs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								engine/.git-blame-ignore-revs
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,74 @@ | ||||||
|  | # This file contains a list of Git commit hashes that should be hidden from the | ||||||
|  | # regular Git history. Typically, this includes commits involving mass auto-formatting | ||||||
|  | # or other normalizations. Commit hashes *must* use the full 40-character notation. | ||||||
|  | # To apply the ignore list in your local Git client, you must run: | ||||||
|  | # | ||||||
|  | #   git config blame.ignoreRevsFile .git-blame-ignore-revs | ||||||
|  | # | ||||||
|  | # This file is automatically used by GitHub.com's blame view. | ||||||
|  | 
 | ||||||
|  | # A Whole New World (clang-format edition) | ||||||
|  | 5dbf1809c6e3e905b94b8764e99491e608122261 | ||||||
|  | 
 | ||||||
|  | # Style: clang-format: Disable KeepEmptyLinesAtTheStartOfBlocks | ||||||
|  | 0be6d925dc3c6413bce7a3ccb49631b8e4a6e67a | ||||||
|  | 
 | ||||||
|  | # Style: clang-format: Disable AllowShortIfStatementsOnASingleLine | ||||||
|  | e956e80c1fa1cc8aefcb1533e5acf5cf3c8ffdd9 | ||||||
|  | 
 | ||||||
|  | # One Copyright Update to rule them all | ||||||
|  | d95794ec8a7c362b06a9cf080e2554ef77adb667 | ||||||
|  | 
 | ||||||
|  | # Update copyright statements to 2022 | ||||||
|  | fe52458154c64fb1b741df4f7bd10106395f7cbd | ||||||
|  | 
 | ||||||
|  | # Update copyright statements to 2021 | ||||||
|  | b5334d14f7a471f94bcbd64d5bae2ad853d0b7f1 | ||||||
|  | 
 | ||||||
|  | # Update copyright statements to 2020 | ||||||
|  | a7f49ac9a107820a62677ee3fb49d38982a25165 | ||||||
|  | 
 | ||||||
|  | # Update copyright statements to 2019 | ||||||
|  | b16c309f82c77d606472c3c721a1857e323a09e7 | ||||||
|  | 
 | ||||||
|  | # Update copyright statements to 2018 | ||||||
|  | b50a9114b105dafafdda8248a38653bca314a6f3 | ||||||
|  | 
 | ||||||
|  | # Welcome in 2017, dear changelog reader! | ||||||
|  | c7bc44d5ad9aae4902280012f7654e2318cd910e | ||||||
|  | 
 | ||||||
|  | # Update copyright to 2016 in headers | ||||||
|  | 5be9ff7b6715a661e85f99b108f96340de7ef435 | ||||||
|  | 
 | ||||||
|  | # Updated copyright year in all headers | ||||||
|  | fdaa2920eb21fff3320a17e9239e04dfadecdb00 | ||||||
|  | 
 | ||||||
|  | # Add missing copyright headers and fix formatting | ||||||
|  | e4213e66b2dd8f5a87d8cf5015ac83ba3143279d | ||||||
|  | 
 | ||||||
|  | # Use HTTPS URL for Godot's website in the headers | ||||||
|  | bd282ff43f23fe845f29a3e25c8efc01bd65ffb0 | ||||||
|  | 
 | ||||||
|  | # Add "Godot Engine contributors" copyright line | ||||||
|  | df61dc4b2bd54a5a40c515493c76f5a458e5b541 | ||||||
|  | 
 | ||||||
|  | # Enforce template syntax `typename` over `class` | ||||||
|  | 9903e6779b70fc03aae70a37b9cf053f4f355b91 | ||||||
|  | 
 | ||||||
|  | # Style: Apply new `clang-format` fixes | ||||||
|  | b37fc1014abf7adda70dc30b0822d775b3a4433f | ||||||
|  | 
 | ||||||
|  | # Set clang-format `RemoveSemicolon` rule to `true` | ||||||
|  | 0d350e71086fffce0553811739aae9f6ad66136c | ||||||
|  | 
 | ||||||
|  | # Style: Apply clang-tidy fixes (superficial) | ||||||
|  | bb5f390fb9b466be35a5df7651323d7e66afca31 | ||||||
|  | 
 | ||||||
|  | # Style: Enforce `AllowShortFunctionsOnASingleLine` | ||||||
|  | e06d83860d798b6766b23d6eae48557387a7db85 | ||||||
|  | 
 | ||||||
|  | # Style: Enforce trailing newlines on svgs | ||||||
|  | 7e5baa042639ffa835271703c720e2595e90afb8 | ||||||
|  | 
 | ||||||
|  | # Style: Replace header guards with `#pragma once` | ||||||
|  | 324512e11c1b7663c3cf47bec6ddbe65c6b8db2b | ||||||
							
								
								
									
										23
									
								
								engine/.gitattributes
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								engine/.gitattributes
									
									
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,23 @@ | ||||||
|  | # Properly detect languages on Github | ||||||
|  | *.h linguist-language=C++ | ||||||
|  | *.inc linguist-language=C++ | ||||||
|  | thirdparty/* linguist-vendored | ||||||
|  | 
 | ||||||
|  | # Normalize EOL for all files that Git considers text files | ||||||
|  | * text=auto eol=lf | ||||||
|  | # Except for Windows-only / Visual Studio files | ||||||
|  | *.bat eol=crlf | ||||||
|  | *.sln eol=crlf | ||||||
|  | *.csproj eol=crlf | ||||||
|  | misc/msvs/*.template eol=crlf | ||||||
|  | # And some test files where the EOL matters | ||||||
|  | *.test.txt -text | ||||||
|  | 
 | ||||||
|  | # The above only works properly for Git 2.10+, so for older versions | ||||||
|  | # we need to manually list the binary files we don't want modified. | ||||||
|  | *.icns binary | ||||||
|  | *.ico binary | ||||||
|  | *.jar binary | ||||||
|  | *.png binary | ||||||
|  | *.ttf binary | ||||||
|  | *.tza binary | ||||||
							
								
								
									
										388
									
								
								engine/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										388
									
								
								engine/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,388 @@ | ||||||
|  | # Godot .gitignore config | ||||||
|  | # | ||||||
|  | # Aims to encompass the most commonly found files that we don't want committed | ||||||
|  | # to Git, such as compilation output, IDE specific files, etc. | ||||||
|  | # | ||||||
|  | # It doesn't cover *all* thirdparty IDE extensions under the sun so if you have | ||||||
|  | # specific needs covered here, you can add them to: | ||||||
|  | #   .git/info/exclude | ||||||
|  | # | ||||||
|  | # Or contribute them to this file if they're common enough that a good number of | ||||||
|  | # users would benefit from the shared rules. | ||||||
|  | # | ||||||
|  | # This file is organized by sections, with subsections ordered alphabetically. | ||||||
|  | # - Build configuration | ||||||
|  | # - Godot generated files | ||||||
|  | # - General build output | ||||||
|  | # - IDE and tool specific | ||||||
|  | # - Visual Studio specific | ||||||
|  | # - OS specific | ||||||
|  | 
 | ||||||
|  | ########################### | ||||||
|  | ### Build configuration ### | ||||||
|  | ########################### | ||||||
|  | 
 | ||||||
|  | /custom.py | ||||||
|  | misc/hooks/pre-commit-custom-* | ||||||
|  | 
 | ||||||
|  | ############################# | ||||||
|  | ### Godot generated files ### | ||||||
|  | ############################# | ||||||
|  | 
 | ||||||
|  | # Buildsystem | ||||||
|  | bin | ||||||
|  | *.gen.* | ||||||
|  | compile_commands.json | ||||||
|  | platform/windows/godot_res.res | ||||||
|  | 
 | ||||||
|  | # Ninja build files | ||||||
|  | *.ninja | ||||||
|  | .ninja/ | ||||||
|  | run_ninja_env.bat | ||||||
|  | 
 | ||||||
|  | # Generated by Godot binary | ||||||
|  | .import/ | ||||||
|  | /gdextension_interface.h | ||||||
|  | extension_api.json | ||||||
|  | logs/ | ||||||
|  | 
 | ||||||
|  | # Generated by unit tests | ||||||
|  | tests/data/*.translation | ||||||
|  | tests/data/crypto/out* | ||||||
|  | 
 | ||||||
|  | ############################ | ||||||
|  | ### General build output ### | ||||||
|  | ############################ | ||||||
|  | 
 | ||||||
|  | # C/C++ generated | ||||||
|  | *.a | ||||||
|  | *.ax | ||||||
|  | *.d | ||||||
|  | *.dll | ||||||
|  | *.lib | ||||||
|  | *.lo | ||||||
|  | *.o | ||||||
|  | *.os | ||||||
|  | *.ox | ||||||
|  | *.Plo | ||||||
|  | *.so | ||||||
|  | # Binutils tmp linker output of the form "stXXXXXX" where "X" is alphanumeric | ||||||
|  | st[A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9][A-Za-z0-9] | ||||||
|  | 
 | ||||||
|  | # Python development | ||||||
|  | .venv | ||||||
|  | venv | ||||||
|  | 
 | ||||||
|  | # Python generated | ||||||
|  | __pycache__/ | ||||||
|  | *.pyc | ||||||
|  | 
 | ||||||
|  | # Python modules | ||||||
|  | .*_cache/ | ||||||
|  | 
 | ||||||
|  | # Documentation | ||||||
|  | doc/_build/ | ||||||
|  | 
 | ||||||
|  | # Android | ||||||
|  | .gradle/ | ||||||
|  | local.properties | ||||||
|  | *.iml | ||||||
|  | .gradletasknamecache | ||||||
|  | project.properties | ||||||
|  | platform/android/java/*/.cxx/ | ||||||
|  | platform/android/java/*/build/ | ||||||
|  | platform/android/java/*/libs/ | ||||||
|  | 
 | ||||||
|  | # iOS | ||||||
|  | *.dSYM | ||||||
|  | 
 | ||||||
|  | # Web platform | ||||||
|  | *.bc | ||||||
|  | platform/web/node_modules/ | ||||||
|  | 
 | ||||||
|  | # Misc | ||||||
|  | *.debug | ||||||
|  | 
 | ||||||
|  | ############################# | ||||||
|  | ### IDE and tool specific ### | ||||||
|  | ############################# | ||||||
|  | 
 | ||||||
|  | # Automake | ||||||
|  | .deps/* | ||||||
|  | .dirstamp | ||||||
|  | 
 | ||||||
|  | # ccls | ||||||
|  | .ccls-cache/ | ||||||
|  | 
 | ||||||
|  | # clangd | ||||||
|  | .clangd/ | ||||||
|  | .cache/ | ||||||
|  | 
 | ||||||
|  | # CLion | ||||||
|  | cmake-build-debug | ||||||
|  | 
 | ||||||
|  | # Code::Blocks | ||||||
|  | *.cbp | ||||||
|  | *.layout | ||||||
|  | *.depend | ||||||
|  | 
 | ||||||
|  | # CodeLite | ||||||
|  | *.project | ||||||
|  | *.workspace | ||||||
|  | .codelite/ | ||||||
|  | 
 | ||||||
|  | # Cppcheck | ||||||
|  | *.cppcheck | ||||||
|  | cppcheck-cppcheck-build-dir/ | ||||||
|  | 
 | ||||||
|  | # Eclipse CDT | ||||||
|  | .cproject | ||||||
|  | .settings/ | ||||||
|  | *.pydevproject | ||||||
|  | *.launch | ||||||
|  | 
 | ||||||
|  | # Emacs | ||||||
|  | \#*\# | ||||||
|  | .\#* | ||||||
|  | 
 | ||||||
|  | # GCOV code coverage | ||||||
|  | *.gcda | ||||||
|  | *.gcno | ||||||
|  | 
 | ||||||
|  | # Geany | ||||||
|  | *.geany | ||||||
|  | .geanyprj | ||||||
|  | 
 | ||||||
|  | # Gprof | ||||||
|  | gmon.out | ||||||
|  | 
 | ||||||
|  | # Jetbrains IDEs | ||||||
|  | .idea/ | ||||||
|  | .fleet/ | ||||||
|  | 
 | ||||||
|  | # Kate | ||||||
|  | *.kate-swp | ||||||
|  | .kateproject.build | ||||||
|  | 
 | ||||||
|  | # Kdevelop | ||||||
|  | *.kdev4 | ||||||
|  | 
 | ||||||
|  | # Qt Creator | ||||||
|  | *.config | ||||||
|  | *.creator | ||||||
|  | *.creator.* | ||||||
|  | *.files | ||||||
|  | *.includes | ||||||
|  | *.cflags | ||||||
|  | *.cxxflags | ||||||
|  | 
 | ||||||
|  | # SCons | ||||||
|  | .sconf_temp | ||||||
|  | .sconsign*.dblite | ||||||
|  | .scons_env.json | ||||||
|  | .scons_node_count | ||||||
|  | 
 | ||||||
|  | # Sourcetrail | ||||||
|  | *.srctrl* | ||||||
|  | 
 | ||||||
|  | # Tags | ||||||
|  | # https://github.com/github/gitignore/blob/master/Global/Tags.gitignore | ||||||
|  | # Ignore tags created by etags, ctags, gtags (GNU global) and cscope | ||||||
|  | TAGS | ||||||
|  | !TAGS/ | ||||||
|  | tags | ||||||
|  | *.tags | ||||||
|  | !tags/ | ||||||
|  | gtags.files | ||||||
|  | GTAGS | ||||||
|  | GRTAGS | ||||||
|  | GPATH | ||||||
|  | cscope.files | ||||||
|  | cscope.out | ||||||
|  | cscope.in.out | ||||||
|  | cscope.po.out | ||||||
|  | 
 | ||||||
|  | # Vim | ||||||
|  | *.swo | ||||||
|  | *.swp | ||||||
|  | 
 | ||||||
|  | # Visual Studio Code | ||||||
|  | .vscode/ | ||||||
|  | *.code-workspace | ||||||
|  | .history/ | ||||||
|  | 
 | ||||||
|  | # Xcode | ||||||
|  | xcuserdata/ | ||||||
|  | *.xcscmblueprint | ||||||
|  | *.xccheckout | ||||||
|  | *.xcodeproj/* | ||||||
|  | !misc/misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj | ||||||
|  | 
 | ||||||
|  | ############################## | ||||||
|  | ### Visual Studio specific ### | ||||||
|  | ############################## | ||||||
|  | 
 | ||||||
|  | # https://github.com/github/gitignore/blob/master/VisualStudio.gitignore | ||||||
|  | # Ignore Visual Studio temporary files, build results, and | ||||||
|  | # files generated by popular Visual Studio add-ons. | ||||||
|  | 
 | ||||||
|  | # Actual VS project files we don't use | ||||||
|  | *.sln | ||||||
|  | *.vcxproj* | ||||||
|  | 
 | ||||||
|  | # User-specific files | ||||||
|  | *.rsuser | ||||||
|  | *.suo | ||||||
|  | *.user | ||||||
|  | *.userosscache | ||||||
|  | *.sln.docstates | ||||||
|  | 
 | ||||||
|  | # User-specific files (MonoDevelop/Xamarin Studio) | ||||||
|  | *.userprefs | ||||||
|  | 
 | ||||||
|  | # Build results | ||||||
|  | [Dd]ebug/ | ||||||
|  | [Dd]ebugPublic/ | ||||||
|  | [Rr]elease/ | ||||||
|  | [Rr]eleases/ | ||||||
|  | x64/ | ||||||
|  | x86/ | ||||||
|  | 
 | ||||||
|  | [Ww][Ii][Nn]32/ | ||||||
|  | [Aa][Rr][Mm]/ | ||||||
|  | [Aa][Rr][Mm]64/ | ||||||
|  | bld/ | ||||||
|  | [Bb]in/ | ||||||
|  | [Oo]bj/ | ||||||
|  | [Ll]og/ | ||||||
|  | [Ll]ogs/ | ||||||
|  | 
 | ||||||
|  | # Do not ignore arch-specific folders anywhere under thirdparty libraries | ||||||
|  | !thirdparty/**/x64/ | ||||||
|  | !thirdparty/**/x86/ | ||||||
|  | !thirdparty/**/arm/ | ||||||
|  | !thirdparty/**/arm64/ | ||||||
|  | 
 | ||||||
|  | thirdparty/swappy-frame-pacing/arm64-v8a/abi.json | ||||||
|  | thirdparty/swappy-frame-pacing/armeabi-v7a/abi.json | ||||||
|  | thirdparty/swappy-frame-pacing/x86/abi.json | ||||||
|  | thirdparty/swappy-frame-pacing/x86_64/abi.json | ||||||
|  | 
 | ||||||
|  | # Visual Studio 2015/2017 cache/options directory | ||||||
|  | .vs/ | ||||||
|  | 
 | ||||||
|  | # Visual Studio 2017 auto generated files | ||||||
|  | Generated\ Files/ | ||||||
|  | 
 | ||||||
|  | # Files built by Visual Studio | ||||||
|  | *_i.c | ||||||
|  | *_p.c | ||||||
|  | *_h.h | ||||||
|  | *.ilk | ||||||
|  | *.meta | ||||||
|  | *.obj | ||||||
|  | *.iobj | ||||||
|  | *.pch | ||||||
|  | *.pdb | ||||||
|  | *.ipdb | ||||||
|  | *.pgc | ||||||
|  | *.pgd | ||||||
|  | *.rsp | ||||||
|  | *.sbr | ||||||
|  | *.tlb | ||||||
|  | *.tli | ||||||
|  | *.tlh | ||||||
|  | *.tmp | ||||||
|  | *.tmp_proj | ||||||
|  | *_wpftmp.csproj | ||||||
|  | *.log | ||||||
|  | *.tlog | ||||||
|  | *.vspscc | ||||||
|  | *.vssscc | ||||||
|  | .builds | ||||||
|  | *.pidb | ||||||
|  | *.svclog | ||||||
|  | *.scc | ||||||
|  | 
 | ||||||
|  | # Visual C++ cache files | ||||||
|  | ipch/ | ||||||
|  | *.aps | ||||||
|  | *.ncb | ||||||
|  | *.opendb | ||||||
|  | *.opensdf | ||||||
|  | *.sdf | ||||||
|  | *.cachefile | ||||||
|  | *.VC.db | ||||||
|  | *.VC.VC.opendb | ||||||
|  | 
 | ||||||
|  | # Visual Studio profiler | ||||||
|  | *.psess | ||||||
|  | *.vsp | ||||||
|  | *.vspx | ||||||
|  | *.sap | ||||||
|  | 
 | ||||||
|  | # Visual Studio Trace Files | ||||||
|  | *.e2e | ||||||
|  | 
 | ||||||
|  | # ReSharper is a .NET coding add-in | ||||||
|  | _ReSharper*/ | ||||||
|  | *.[Rr]e[Ss]harper | ||||||
|  | *.DotSettings.user | ||||||
|  | 
 | ||||||
|  | # Visual Studio cache files | ||||||
|  | # files ending in .cache can be ignored | ||||||
|  | *.[Cc]ache | ||||||
|  | 
 | ||||||
|  | # Others | ||||||
|  | ClientBin/ | ||||||
|  | enc_temp_folder/ | ||||||
|  | ~$* | ||||||
|  | *.dbmdl | ||||||
|  | *.dbproj.schemaview | ||||||
|  | *.jfm | ||||||
|  | *.pfx | ||||||
|  | *.publishsettings | ||||||
|  | orleans.codegen.cs | ||||||
|  | 
 | ||||||
|  | # Backup & report files from converting an old project file | ||||||
|  | # to a newer Visual Studio version. Backup files are not needed, | ||||||
|  | # because we have git ;-) | ||||||
|  | _UpgradeReport_Files/ | ||||||
|  | Backup*/ | ||||||
|  | UpgradeLog*.XML | ||||||
|  | UpgradeLog*.htm | ||||||
|  | ServiceFabricBackup/ | ||||||
|  | *.rptproj.bak | ||||||
|  | 
 | ||||||
|  | # Hint file for IntelliSense | ||||||
|  | cpp.hint | ||||||
|  | 
 | ||||||
|  | ################### | ||||||
|  | ### OS specific ### | ||||||
|  | ################### | ||||||
|  | 
 | ||||||
|  | # Linux | ||||||
|  | *~ | ||||||
|  | .directory | ||||||
|  | 
 | ||||||
|  | # macOS | ||||||
|  | .DS_Store | ||||||
|  | __MACOSX | ||||||
|  | Godot.app | ||||||
|  | 
 | ||||||
|  | # Windows | ||||||
|  | # https://github.com/github/gitignore/blob/main/Global/Windows.gitignore | ||||||
|  | [Tt]humbs.db | ||||||
|  | [Tt]humbs.db:encryptable | ||||||
|  | ehthumbs.db | ||||||
|  | ehthumbs_vista.db | ||||||
|  | *.stackdump | ||||||
|  | [Dd]esktop.ini | ||||||
|  | $RECYCLE.BIN/ | ||||||
|  | *.cab | ||||||
|  | *.msi | ||||||
|  | *.msix | ||||||
|  | *.msm | ||||||
|  | *.msp | ||||||
|  | *.lnk | ||||||
|  | *.generated.props | ||||||
							
								
								
									
										195
									
								
								engine/.mailmap
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										195
									
								
								engine/.mailmap
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,195 @@ | ||||||
|  | Aaron Record <aaronjrecord@gmail.com> | ||||||
|  | ajreckof <66184050+ajreckof@users.noreply.github.com> <tbonhoure@ymail.com> | ||||||
|  | Alexander Hartmann <alex.hart.278@gmail.com> | ||||||
|  | Alexander Holland <alexander.holland@live.de> | ||||||
|  | Alexander Holland <alexander.holland@live.de> <alexander.holland@haw-hamburg.de> | ||||||
|  | Alexander Holland <alexander.holland@live.de> <AlexHolly> | ||||||
|  | Alfred Reinold Baudisch <alfred@alfredbaudisch.com> | ||||||
|  | Andrea Catania <info@andreacatania.com> | ||||||
|  | Anish Bhobe <anishbhobe@hotmail.com> | ||||||
|  | Anutrix <numaanzaheerahmed@yahoo.com> | ||||||
|  | Aren Villanueva <arenvillanueva@yomogi-soft.com> <aren@displaysweet.com> | ||||||
|  | Ariel Manzur <ariel@godotengine.org> | ||||||
|  | Ariel Manzur <ariel@godotengine.org> <puntob@gmail.com> | ||||||
|  | Ariel Manzur <ariel@godotengine.org> <punto@godotengine.org> | ||||||
|  | Ariel Manzur <ariel@godotengine.org> <ariel@okamstudio.com> | ||||||
|  | Ariel Manzur <ariel@godotengine.org> <punto@Ariels-Mac-mini.local> | ||||||
|  | Ariel Manzur <ariel@godotengine.org> <punto@Ariels-Mac-mini-2.local> | ||||||
|  | Arman Elgudzhyan <48544263+puchik@users.noreply.github.com> | ||||||
|  | A Thousand Ships <96648715+AThousandShips@users.noreply.github.com> | ||||||
|  | A Thousand Ships <96648715+AThousandShips@users.noreply.github.com> <over999ships@gmail.com> | ||||||
|  | Bastiaan Olij <mux213@gmail.com> | ||||||
|  | Benjamin <mafortion.benjamin@gmail.com> | ||||||
|  | Bernhard Liebl <Bernhard.Liebl@gmx.org> <poke1024@gmx.de> | ||||||
|  | Bernhard Liebl <Bernhard.Liebl@gmx.org> <poke1024@gmx.org> | ||||||
|  | Bruno Lourenço <madequa@users.noreply.github.com> <bmlourenco@gmail.com> | ||||||
|  | Chaosus <chaosus89@gmail.com> | ||||||
|  | ChibiDenDen <pdaniq@gmail.com> | ||||||
|  | Chris Bradfield <chris@kidscancode.org> <cb@scribe.net> | ||||||
|  | Clay John <claynjohn@gmail.com> | ||||||
|  | Clay John <claynjohn@gmail.com> <clayjohn@shaw.ca> | ||||||
|  | CookieBadger <emil.dobetsberger@gmail.com> | ||||||
|  | Dana Olson <dana@shineuponthee.com> <adolson@gmail.com> | ||||||
|  | dankan1890 <mewuidev2@gmail.com> | ||||||
|  | Daniel J. Ramirez <djrmuv@gmail.com> | ||||||
|  | Dario <dariosamo@gmail.com> | ||||||
|  | David Cambré <david.cambre@gmail.com> <David.Cambre@gmail.com> | ||||||
|  | DmitriySalnikov <salnikov.mine@yandex.ru> | ||||||
|  | DmitriySalnikov <salnikov.mine@yandex.ru> <dimokgamer@gmail.com> | ||||||
|  | Dominik 'dreamsComeTrue' Jasiński <dominikjasinski@o2.pl> | ||||||
|  | DeeJayLSP <djlsplays@gmail.com> <60024671+DeeJayLSP@users.noreply.github.com> | ||||||
|  | Emmanuel Barroga <emmanuelbarroga@gmail.com> | ||||||
|  | Eric M <itsjusteza@gmail.com> | ||||||
|  | Eric Rybicki <info@ericrybicki.com> <stratos695@googlemail.com> | ||||||
|  | Erik Selecký <35656626+rxlecky@users.noreply.github.com> | ||||||
|  | Erik Selecký <35656626+rxlecky@users.noreply.github.com> <35656626+SeleckyErik@users.noreply.github.com> | ||||||
|  | Eveline Jarosz <marqin.pl@gmail.com> | ||||||
|  | Eveline Jarosz <marqin.pl@gmail.com> <marqin.pl+git@gmail.com> | ||||||
|  | Fabian <supagu@gmail.com> | ||||||
|  | Ferenc Arn <tagcup@yahoo.com> | ||||||
|  | Ferenc Arn <tagcup@yahoo.com> <tagcup@users.noreply.github.com> | ||||||
|  | FireForge <67974470+fire-forge@users.noreply.github.com> <isaacr.7.2005@gmail.com> | ||||||
|  | Florian Kothmeier <floriankothmeier@web.de> | ||||||
|  | foxydevloper <12120644+foxydevloper@users.noreply.github.com> | ||||||
|  | Fredia Huya-Kouadio <fhuyakou@gmail.com> | ||||||
|  | Fredia Huya-Kouadio <fhuyakou@gmail.com> <fhuya@google.com> | ||||||
|  | Fredia Huya-Kouadio <fhuyakou@gmail.com> <fhuya@fb.com> | ||||||
|  | Fredia Huya-Kouadio <fhuyakou@gmail.com> <fhuya@meta.com> | ||||||
|  | Geequlim <geequlim@gmail.com> | ||||||
|  | Gilles Roudiere <gilles.roudiere@gmail.com> | ||||||
|  | Gilles Roudiere <gilles.roudiere@gmail.com> <gilles.roudiere@laas.fr> | ||||||
|  | Gordon MacPherson <gordon@gordonite.tech> | ||||||
|  | Guilherme Felipe <guilhermefelipecgs@gmail.com> | ||||||
|  | Hakim <hakim.rouatbi@gmail.com> | ||||||
|  | Hanif Bin Ariffin <hanif.ariffin.4326@gmail.com> | ||||||
|  | HaSa1002 <johawitt@outlook.de> | ||||||
|  | Hein-Pieter van Braam-Stewart <hp@tmm.cx> | ||||||
|  | Hugo Locurcio <hugo.locurcio@hugo.pro> <hugo.l@openmailbox.org> | ||||||
|  | Hugo Locurcio <hugo.locurcio@hugo.pro> <Calinou@users.noreply.github.com> | ||||||
|  | Hugo Locurcio <hugo.locurcio@hugo.pro> Calinou <calinou@opmbx.org> | ||||||
|  | Ian Bishop <ianb96@gmail.com> | ||||||
|  | Ignacio Etcheverry <ignalfonsore@gmail.com> | ||||||
|  | Ignacio Etcheverry <ignalfonsore@gmail.com> <neikeq@users.noreply.github.com> | ||||||
|  | Ilaria Cislaghi <cislaghi.ilaria@gmail.com> | ||||||
|  | Ilaria Cislaghi <cislaghi.ilaria@gmail.com> <ilaria.cislaghi@simedis.com> | ||||||
|  | Indah Sylvia <ISylvox@yahoo.com> | ||||||
|  | Ivan Shakhov <ivan.shakhov@jetbrains.com> <Ivan.Shakhov@jetbrains.com> | ||||||
|  | Ivan Shakhov <ivan.shakhov@jetbrains.com> <van800@gmail.com> | ||||||
|  | iwek <miwanczuk7@gmail.com> | ||||||
|  | J08nY <johny@neuromancer.sk> <jancar.jj@gmail.com> | ||||||
|  | J08nY <johny@neuromancer.sk> <J08nY@users.noreply.github.com> | ||||||
|  | Jake Young <young9003@gmail.com> | ||||||
|  | Jakub Grzesik <kubecz3k@gmail.com> | ||||||
|  | Jakub Marcowski <chubercikbattle@gmail.com> <01158831@pw.edu.pl> | ||||||
|  | Jakub Marcowski <chubercikbattle@gmail.com> <37378746+Chubercik@users.noreply.github.com> | ||||||
|  | janglee <merupatel123@gmail.com> | ||||||
|  | Jason Knight <00jknight@gmail.com> <jason@winterpixel.com> | ||||||
|  | Jean-Michel Bernard <jmb462@gmail.com> | ||||||
|  | Jérôme Gully <jerome.gully0@gmail.com> | ||||||
|  | JFonS <joan.fonssanchez@gmail.com> | ||||||
|  | jitspoe <jitspoe@yahoo.com> <jitspoeAyahoooDcom> | ||||||
|  | Johan Aires Rastén <johan@oljud.se> | ||||||
|  | Juan Linietsky <reduzio@gmail.com> | ||||||
|  | Juan Linietsky <reduzio@gmail.com> <juan@godotengine.org> | ||||||
|  | Juan Linietsky <reduzio@gmail.com> <juan@okamstudio.com> | ||||||
|  | Juan Linietsky <reduzio@gmail.com> <reduz@Juans-MBP.fibertel.com.ar> | ||||||
|  | Juan Linietsky <reduzio@gmail.com> <red@kyoko> | ||||||
|  | Julian Murgia <the.straton@gmail.com> | ||||||
|  | Kanabenki <lucien.menassol@gmail.com> <18357657+Kanabenki@users.noreply.github.com> | ||||||
|  | karroffel <therzog@mail.de> | ||||||
|  | karroffel <therzog@mail.de> <thomas.herzog@mail.com> | ||||||
|  | karroffel <therzog@mail.de> <thomas.herzog@simedis.com> | ||||||
|  | Kasper Arnklit Frandsen <kasper.arnklit@gmail.com> | ||||||
|  | Kelly Thomas <kelly.thomas@hotmail.com.au> | ||||||
|  | Kongfa Waroros <gongpha@hotmail.com> | ||||||
|  | K. S. Ernest (iFire) Lee <ernest.lee@chibifire.com> | ||||||
|  | K. S. Ernest (iFire) Lee <ernest.lee@chibifire.com> <fire@users.noreply.github.com> | ||||||
|  | kleonc <9283098+kleonc@users.noreply.github.com> <kleonc@users.noreply.github.com> | ||||||
|  | Leon Krause <lk@leonkrause.com> <eska@eska.me> | ||||||
|  | Leon Krause <lk@leonkrause.com> <eska014@users.noreply.github.com> | ||||||
|  | Liz Haas <27thLiz@gmail.com> | ||||||
|  | Liz Haas <27thLiz@gmail.com> <liu.gam3@gmail.com> | ||||||
|  | Liz Haas <27thLiz@gmail.com> <hinsbart@gmail.com> | ||||||
|  | Liz Haas <27thLiz@gmail.com> <hinsbart@users.noreply.github.com> | ||||||
|  | Liz Haas <27thLiz@gmail.com> <entenflugstuhl@gmail.com> | ||||||
|  | Logan Lang <devloglogan@gmail.com> | ||||||
|  | Manuele Finocchiaro <m4nu3lf@gmail.com> | ||||||
|  | Manuel Strey <manuel.strey@gmx.de> | ||||||
|  | Marcel Admiraal <madmiraal@users.noreply.github.com> | ||||||
|  | Marcelo Fernandez <marcelofg55@gmail.com> | ||||||
|  | Marcin Zawiejski <dragmz@gmail.com> | ||||||
|  | Marcus Elg <marcusaccounts@yahoo.se> | ||||||
|  | Mariano Javier Suligoy <marianognu.easyrpg@gmail.com> | ||||||
|  | Mario Schlack <m4r10.5ch14ck@gmail.com> | ||||||
|  | Mark DiBarry <markdibarry@protonmail.com> | ||||||
|  | marxin <mliska@suse.cz> | ||||||
|  | marynate <mary.w.nate@gmail.com> <marynate@github.com> | ||||||
|  | Mateo Kuruk Miccino <mateomiccino@gmail.com> | ||||||
|  | Max Hilbrunner <m.hilbrunner@gmail.com> | ||||||
|  | Max Hilbrunner <m.hilbrunner@gmail.com> <mhilbrunner@users.noreply.github.com> | ||||||
|  | MewPurPur <mew.pur.pur@abv.bg> | ||||||
|  | Michael Alexsander <michaelalexsander@protonmail.com> | ||||||
|  | Micky <micheledevita2@gmail.com> <66727710+Mickeon@users.noreply.github.com> | ||||||
|  | Nathan Franke <natfra@pm.me> <nathanwfranke@gmail.com> | ||||||
|  | Nathan Lovato <nathan@gdquest.com> | ||||||
|  | Nathan Warden <nathan@nathanwarden.com> <nathanwardenlee@icloud.com> | ||||||
|  | Nazarii <nazarii.yablonskyi.pp.2022@lpnu.ua> | ||||||
|  | Nicholas Huelin <62965063+SirQuartz@users.noreply.github.com> | ||||||
|  | Nils ANDRÉ-CHANG <nils@nilsand.re> | ||||||
|  | Nils ANDRÉ-CHANG <nils@nilsand.re> <nils.andre.chang@gmail.com> | ||||||
|  | Nông Văn Tình <vannongtinh@gmail.com> | ||||||
|  | Nuno Donato <nunodonato@gmail.com> <n.donato@estrelasustentavel.pt> | ||||||
|  | ocean (they/them) <anvilfolk@gmail.com> | ||||||
|  | Pawel Kowal <pkowal1982@gmail.com> | ||||||
|  | Pedro J. Estébanez <pedrojrulez@gmail.com> <RandomShaper@users.noreply.github.com> | ||||||
|  | Patrick Exner <patrick.exner1@web.de> | ||||||
|  | Patrick <firefly2442@gmail.com> | ||||||
|  | Paul Batty <p_batty@hotmail.co.uk> | ||||||
|  | Paul Batty <p_batty@hotmail.co.uk> <Paulb23@users.noreply.github.com> | ||||||
|  | Pāvels Nadtočajevs <7645683+bruvzg@users.noreply.github.com> | ||||||
|  | Pawel Kowal <pkowal1982@gmail.com> <pawel.kowal@javart.eu> | ||||||
|  | Pieter-Jan Briers <pieterjan.briers+git@gmail.com> | ||||||
|  | Pieter-Jan Briers <pieterjan.briers+git@gmail.com> <pieterjan.briers@gmail.com> | ||||||
|  | Poommetee Ketson <poommetee@protonmail.com> | ||||||
|  | Przemysław Gołąb (n-pigeon) <golab.przemyslaw@gmail.com> | ||||||
|  | Radiant <69520693+RadiantUwU@users.noreply.github.com> <i.like.using.discord@gmail.com> | ||||||
|  | Rafał Mikrut <mikrutrafal@protonmail.com> | ||||||
|  | Rafał Mikrut <mikrutrafal@protonmail.com> <mikrutrafal54@gmail.com> | ||||||
|  | Ralf Hölzemer <r.hoelzemer@posteo.de> <rollenrolm@posteo.de> | ||||||
|  | Ralf Hölzemer <r.hoelzemer@posteo.de> <rollenrolm@users.noreply.github.com> | ||||||
|  | Ramesh Ravone <ramesh.maran443@gmail.com> | ||||||
|  | RaphaelHunter <raphael10241024@gmail.com> | ||||||
|  | RaphaelHunter <raphael10241024@gmail.com> <Raphael10241024@gmail.com> | ||||||
|  | RaphaelHunter <raphael10241024@gmail.com> <raphael20141024@gmail.com> | ||||||
|  | Rémi Verschelde <rverschelde@gmail.com> <remi@verschelde.fr> | ||||||
|  | Rhody Lugo <rhodylugo@gmail.com> <rhodylugo@me.com> | ||||||
|  | Ricardo Subtil <ricasubtil@gmail.com> | ||||||
|  | Rindbee <idleman@yeah.net> | ||||||
|  | Riteo Siuga <riteo@posteo.net> | ||||||
|  | Robin Hübner <profan@prfn.se> <robinhubner@gmail.com> | ||||||
|  | romulox_x <romulox_x@yahoo.com> | ||||||
|  | Rudolph Bester <Rudolph.f.Bester@gmail.com> <Rudolph.f.bester@gmail.com> | ||||||
|  | rune-scape <allie.smith.epic@gmail.com> | ||||||
|  | rune-scape <allie.smith.epic@gmail.com> <spartacrafter@gmail.com> | ||||||
|  | Ruslan Mustakov <r.mustakov@gmail.com> <ruslan.mustakov@xored.com> | ||||||
|  | Saracen <SaracenOne@gmail.com> | ||||||
|  | sheepandshepherd <sheepandshepherd@hotmail.com> <sheepandshepherd@users.noreply.github.com> | ||||||
|  | Silc 'Tokage' Renew <tokage.it.lab@gmail.com> | ||||||
|  | Silc 'Tokage' Renew <tokage.it.lab@gmail.com> <61938263+TokageItLab@users.noreply.github.com> | ||||||
|  | Swarnim Arun <swarnimarun11@gmail.com> | ||||||
|  | TechnoPorg <jonah.janzen@gmail.com> <69441745+TechnoPorg@users.noreply.github.com> | ||||||
|  | tetrapod00 <145553014+tetrapod00@users.noreply.github.com> | ||||||
|  | Theo Hallenius <redsymbzone@hotmail.com> | ||||||
|  | Tomasz Chabora <kobewi4e@gmail.com> | ||||||
|  | Twarit <wtwarit@gmail.com> | ||||||
|  | Vitika9 <vitika.program@gmail.com> | ||||||
|  | V.VamsiKrishna <vk@bsb.in> <vamsikrishna.v@gmail.com> | ||||||
|  | Wilhem Barbier <nounoursheureux@openmailbox.org> <wilhem.b@free.fr> | ||||||
|  | Wilhem Barbier <nounoursheureux@openmailbox.org> <schtroumps31@gmail.com> | ||||||
|  | Will Nations <willnationsdev@gmail.com> | ||||||
|  | yg2f <yoann@terminajones.com> | ||||||
|  | Yuri Sizov <yuris@humnom.net> <pycbouh@users.noreply.github.com> | ||||||
|  | Yuri Sizov <yuris@humnom.net> <yaschik4ilicha@gmail.com> | ||||||
|  | Zae <zaevi@live.com> | ||||||
|  | Zak Stam <zakscomputers@hotmail.com> | ||||||
|  | Zher Huei Lee <lee.zh.92@gmail.com> | ||||||
							
								
								
									
										195
									
								
								engine/.pre-commit-config.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										195
									
								
								engine/.pre-commit-config.yaml
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,195 @@ | ||||||
|  | default_language_version: | ||||||
|  |   python: python3 | ||||||
|  | 
 | ||||||
|  | exclude: | | ||||||
|  |   (?x)^( | ||||||
|  |     .*thirdparty/.*| | ||||||
|  |     .*-(dll|dylib|so)_wrap\.[ch]| | ||||||
|  |     platform/android/java/editor/src/main/java/com/android/.*| | ||||||
|  |     platform/android/java/lib/src/com/google/.* | ||||||
|  |   )$ | ||||||
|  | 
 | ||||||
|  | repos: | ||||||
|  |   - repo: https://github.com/pre-commit/mirrors-clang-format | ||||||
|  |     rev: v20.1.0 | ||||||
|  |     hooks: | ||||||
|  |       - id: clang-format | ||||||
|  |         files: \.(c|h|cpp|hpp|cc|hh|cxx|hxx|m|mm|inc|java)$ | ||||||
|  |         types_or: [text] | ||||||
|  |       - id: clang-format | ||||||
|  |         name: clang-format-glsl | ||||||
|  |         files: \.glsl$ | ||||||
|  |         types_or: [text] | ||||||
|  |         args: [-style=file:misc/utility/clang_format_glsl.yml] | ||||||
|  | 
 | ||||||
|  |   - repo: https://github.com/pocc/pre-commit-hooks | ||||||
|  |     rev: v1.3.5 | ||||||
|  |     hooks: | ||||||
|  |       - id: clang-tidy | ||||||
|  |         files: \.(c|h|cpp|hpp|cc|hh|cxx|hxx|m|mm|inc|java|glsl)$ | ||||||
|  |         args: [--fix, --quiet, --use-color] | ||||||
|  |         types_or: [text] | ||||||
|  |         additional_dependencies: [clang-tidy==20.1.0] | ||||||
|  |         require_serial: true | ||||||
|  |         stages: [manual] # Not automatically triggered, invoked via `pre-commit run --hook-stage manual clang-tidy` | ||||||
|  | 
 | ||||||
|  |   - repo: https://github.com/astral-sh/ruff-pre-commit | ||||||
|  |     rev: v0.11.4 | ||||||
|  |     hooks: | ||||||
|  |       - id: ruff | ||||||
|  |         args: [--fix] | ||||||
|  |         files: (\.py|SConstruct|SCsub)$ | ||||||
|  |         types_or: [text] | ||||||
|  |       - id: ruff-format | ||||||
|  |         files: (\.py|SConstruct|SCsub)$ | ||||||
|  |         types_or: [text] | ||||||
|  | 
 | ||||||
|  |   - repo: https://github.com/pre-commit/mirrors-mypy | ||||||
|  |     rev: v1.14.1 # Latest version that supports Python 3.8 | ||||||
|  |     hooks: | ||||||
|  |       - id: mypy | ||||||
|  |         files: \.py$ | ||||||
|  |         types_or: [text] | ||||||
|  | 
 | ||||||
|  |   - repo: https://github.com/codespell-project/codespell | ||||||
|  |     rev: v2.4.1 | ||||||
|  |     hooks: | ||||||
|  |       - id: codespell | ||||||
|  |         additional_dependencies: [tomli] | ||||||
|  | 
 | ||||||
|  |   ### Requires Docker; look into alternative implementation. | ||||||
|  |   # - repo: https://github.com/comkieffer/pre-commit-xmllint.git | ||||||
|  |   #   rev: 1.0.0 | ||||||
|  |   #   hooks: | ||||||
|  |   #     - id: xmllint | ||||||
|  |   #       language: docker | ||||||
|  |   #       types_or: [text] | ||||||
|  |   #       files: ^(doc/classes|.*/doc_classes)/.*\.xml$ | ||||||
|  |   #       args: [--schema, doc/class.xsd] | ||||||
|  | 
 | ||||||
|  |   - repo: local | ||||||
|  |     hooks: | ||||||
|  |       - id: make-rst | ||||||
|  |         name: make-rst | ||||||
|  |         language: python | ||||||
|  |         entry: python doc/tools/make_rst.py | ||||||
|  |         args: [doc/classes, modules, platform, --dry-run, --color] | ||||||
|  |         pass_filenames: false | ||||||
|  |         files: ^(doc/classes|.*/doc_classes)/.*\.xml$ | ||||||
|  | 
 | ||||||
|  |       - id: doc-status | ||||||
|  |         name: doc-status | ||||||
|  |         language: python | ||||||
|  |         entry: python doc/tools/doc_status.py | ||||||
|  |         args: [doc/classes, modules/*/doc_classes, platform/*/doc_classes, -c] | ||||||
|  |         pass_filenames: false | ||||||
|  |         files: ^(doc/classes|.*/doc_classes)/.*\.xml$ | ||||||
|  | 
 | ||||||
|  |       - id: validate-builders | ||||||
|  |         name: validate-builders | ||||||
|  |         language: python | ||||||
|  |         entry: python tests/python_build/validate_builders.py | ||||||
|  |         pass_filenames: false | ||||||
|  |         files: ^(gles3|glsl)_builders\.py$ | ||||||
|  | 
 | ||||||
|  |       - id: eslint | ||||||
|  |         name: eslint | ||||||
|  |         language: node | ||||||
|  |         entry: eslint | ||||||
|  |         files: ^(platform/web/js/|modules/|misc/dist/html/).*\.(js|html)$ | ||||||
|  |         args: | ||||||
|  |           - --fix | ||||||
|  |           - --no-warn-ignored | ||||||
|  |           - --no-config-lookup | ||||||
|  |           - --config | ||||||
|  |           - platform/web/eslint.config.cjs | ||||||
|  |         additional_dependencies: | ||||||
|  |           - "@eslint/js@^9.3.0" | ||||||
|  |           - "@html-eslint/eslint-plugin@^0.24.1" | ||||||
|  |           - "@html-eslint/parser@^0.24.1" | ||||||
|  |           - "@stylistic/eslint-plugin@^2.1.0" | ||||||
|  |           - eslint@^9.3.0 | ||||||
|  |           - eslint-plugin-html@^8.1.1 | ||||||
|  |           - globals@^15.3.0 | ||||||
|  |           - espree@^10.0.1 | ||||||
|  | 
 | ||||||
|  |       - id: jsdoc | ||||||
|  |         name: jsdoc | ||||||
|  |         language: node | ||||||
|  |         entry: jsdoc | ||||||
|  |         files: ^platform/web/js/engine/(engine|config|features)\.js$ | ||||||
|  |         args: | ||||||
|  |           - --template | ||||||
|  |           - platform/web/js/jsdoc2rst/ | ||||||
|  |           - platform/web/js/engine/engine.js | ||||||
|  |           - platform/web/js/engine/config.js | ||||||
|  |           - platform/web/js/engine/features.js | ||||||
|  |           - --destination | ||||||
|  |           - "" | ||||||
|  |           - -d | ||||||
|  |           - dry-run | ||||||
|  |         pass_filenames: false | ||||||
|  |         additional_dependencies: [jsdoc@^4.0.3] | ||||||
|  | 
 | ||||||
|  |       - id: svgo | ||||||
|  |         name: svgo | ||||||
|  |         language: node | ||||||
|  |         entry: svgo | ||||||
|  |         files: \.svg$ | ||||||
|  |         args: [--quiet, --config, misc/utility/svgo.config.mjs] | ||||||
|  |         additional_dependencies: [svgo@3.3.2] | ||||||
|  | 
 | ||||||
|  |       - id: copyright-headers | ||||||
|  |         name: copyright-headers | ||||||
|  |         language: python | ||||||
|  |         entry: python misc/scripts/copyright_headers.py | ||||||
|  |         files: \.(c|h|cpp|hpp|cc|hh|cxx|hxx|m|mm|inc|java)$ | ||||||
|  |         exclude: | | ||||||
|  |           (?x)^( | ||||||
|  |             core/math/bvh_.*\.inc| | ||||||
|  |             platform/(?!android|ios|linuxbsd|macos|web|windows)\w+/.*| | ||||||
|  |             platform/android/java/lib/src/org/godotengine/godot/gl/GLSurfaceView\.java| | ||||||
|  |             platform/android/java/lib/src/org/godotengine/godot/gl/EGLLogWrapper\.java| | ||||||
|  |             platform/android/java/lib/src/org/godotengine/godot/utils/ProcessPhoenix\.java | ||||||
|  |           )$ | ||||||
|  | 
 | ||||||
|  |       - id: header-guards | ||||||
|  |         name: header-guards | ||||||
|  |         language: python | ||||||
|  |         entry: python misc/scripts/header_guards.py | ||||||
|  |         files: \.(h|hpp|hh|hxx)$ | ||||||
|  | 
 | ||||||
|  |       - id: file-format | ||||||
|  |         name: file-format | ||||||
|  |         language: python | ||||||
|  |         entry: python misc/scripts/file_format.py | ||||||
|  |         types_or: [text] | ||||||
|  |         exclude: | | ||||||
|  |           (?x)^( | ||||||
|  |             .*\.test\.txt| | ||||||
|  |             .*\.svg| | ||||||
|  |             .*\.patch| | ||||||
|  |             .*\.out| | ||||||
|  |             modules/gdscript/tests/scripts/parser/features/mixed_indentation_on_blank_lines\.gd| | ||||||
|  |             modules/gdscript/tests/scripts/parser/warnings/empty_file_newline_comment\.norun\.gd| | ||||||
|  |             modules/gdscript/tests/scripts/parser/warnings/empty_file_newline\.norun\.gd| | ||||||
|  |             tests/data/.*\.bin | ||||||
|  |           )$ | ||||||
|  | 
 | ||||||
|  |       - id: dotnet-format | ||||||
|  |         name: dotnet-format | ||||||
|  |         language: python | ||||||
|  |         entry: python misc/scripts/dotnet_format.py | ||||||
|  |         types_or: [c#] | ||||||
|  | # | ||||||
|  | # End of upstream Godot pre-commit hooks. | ||||||
|  | # | ||||||
|  | # Keep this separation to let downstream forks add their own hooks to this file, | ||||||
|  | # without running into merge conflicts when rebasing on latest upstream. | ||||||
|  | # | ||||||
|  | # Start of downstream pre-commit hooks. | ||||||
|  | # | ||||||
|  | # This is still the "repo: local" scope, so new local hooks can be defined directly at this indentation: | ||||||
|  | #     - id: new-local-hook | ||||||
|  | # To add external repo hooks, bring the indentation back to: | ||||||
|  | # - repo: my-remote-hook | ||||||
							
								
								
									
										359
									
								
								engine/AUTHORS.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										359
									
								
								engine/AUTHORS.md
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,359 @@ | ||||||
|  | # Godot Engine authors | ||||||
|  | 
 | ||||||
|  | Godot Engine is developed by a community of voluntary contributors who | ||||||
|  | contribute code, bug reports, documentation, translations, support, etc., | ||||||
|  | across multiple repositories. | ||||||
|  | 
 | ||||||
|  | It is impossible to list them all; nevertheless, this file aims at listing | ||||||
|  | the developers who contributed significant improvements to the engine code. | ||||||
|  | 
 | ||||||
|  | To keep the list curated, we use a threshold of a minimum of 11 commits. | ||||||
|  | 
 | ||||||
|  | This file does not strictly reflect copyright ownership for the engine | ||||||
|  | source code. For this, refer to the Git history to know which contributor | ||||||
|  | wrote which part of the codebase. | ||||||
|  | 
 | ||||||
|  | GitHub usernames are indicated in parentheses, or as sole entry when no other | ||||||
|  | name is available. | ||||||
|  | 
 | ||||||
|  | ## Project Founders | ||||||
|  | 
 | ||||||
|  |     Juan Linietsky (reduz) | ||||||
|  |     Ariel Manzur (punto-) | ||||||
|  | 
 | ||||||
|  | ## Lead Developer | ||||||
|  | 
 | ||||||
|  |     Juan Linietsky (reduz) | ||||||
|  | 
 | ||||||
|  | ## Project Manager | ||||||
|  | 
 | ||||||
|  |     Rémi Verschelde (akien-mga) | ||||||
|  | 
 | ||||||
|  | ## Developers | ||||||
|  | 
 | ||||||
|  |     Aaron Franke (aaronfranke) | ||||||
|  |     Aaron Pagano (aaronp64) | ||||||
|  |     Aaron Record (LightningAA) | ||||||
|  |     Adam Scott (adamscott) | ||||||
|  |     Alexander Hartmann (Alex2782) | ||||||
|  |     Alexander Holland (AlexHolly) | ||||||
|  |     Alex Drozd (brno32) | ||||||
|  |     Alexey Khoroshavin (allkhor) | ||||||
|  |     Allen Pestaluky (allenwp) | ||||||
|  |     Alfred Reinold Baudisch (alfredbaudisch) | ||||||
|  |     Alistair Leslie-Hughes (alesliehughes) | ||||||
|  |     Alket Rexhepi (alketii) | ||||||
|  |     Alvin Wong (alvinhochun) | ||||||
|  |     Andrea Catania (AndreaCatania) | ||||||
|  |     Andreia Gaita (shana) | ||||||
|  |     Andrés Botero (0xafbf) | ||||||
|  |     Andrii Doroshenko (Xrayez) | ||||||
|  |     Andy Maloney (asmaloney) | ||||||
|  |     Andy Moss (MillionOstrich) | ||||||
|  |     Angad Kambli (angad-k) | ||||||
|  |     Anilforextra (AnilBK) | ||||||
|  |     Anish Bhobe (KidRigger) | ||||||
|  |     Anish Mishra (syntaxerror247) | ||||||
|  |     Anni Ryynänen (anniryynanen) | ||||||
|  |     Anton Yabchinskiy (a12n) | ||||||
|  |     Anutrix | ||||||
|  |     Aren Villanueva (kurikaesu) | ||||||
|  |     Ariel Manzur (punto-) | ||||||
|  |     arkology | ||||||
|  |     Arman Elgudzhyan (puchik) | ||||||
|  |     Arseny Kapoulkine (zeux) | ||||||
|  |     AThousandShips | ||||||
|  |     aXu-AP | ||||||
|  |     Bartłomiej T. Listwon (Listwon) | ||||||
|  |     Bastiaan Olij (BastiaanOlij) | ||||||
|  |     Ben Brookshire (sheepandshepherd) | ||||||
|  |     Benjamin Larsson (Nallebeorn) | ||||||
|  |     Bernhard Liebl (poke1024) | ||||||
|  |     Bhuvan Vemula (Bhu1-V) | ||||||
|  |     bitsawer | ||||||
|  |     Błażej Szczygieł (zaps166) | ||||||
|  |     BlueCube3310 | ||||||
|  |     Bojidar Marinov (bojidar-bg) | ||||||
|  |     Brian Semrau (briansemrau) | ||||||
|  |     Bruno Lourenço (MadEqua) | ||||||
|  |     Cameron Reikes (creikey) | ||||||
|  |     Camille Mohr-Daurat (pouleyKetchoupp) | ||||||
|  |     Caner Demirer (cdemirer) | ||||||
|  |     Carl Olsson (not-surt) | ||||||
|  |     Carter Anderson (cart) | ||||||
|  |     ChibiDenDen | ||||||
|  |     Chris Bradfield (cbscribe) | ||||||
|  |     Chris Cranford (Naros) | ||||||
|  |     Christian Kaiser (ckaiser) | ||||||
|  |     Clay John (clayjohn) | ||||||
|  |     ConteZero | ||||||
|  |     CookieBadger | ||||||
|  |     Dana Olson (adolson) | ||||||
|  |     Daniel J. Ramirez (djrm) | ||||||
|  |     Daniel Rakos (aqnuep) | ||||||
|  |     Daniel Zilberleyb (dzil123) | ||||||
|  |     Danil Alexeev (dalexeev) | ||||||
|  |     dankan1890 | ||||||
|  |     Darío Banini (DarioSamo) | ||||||
|  |     David Cambré (Gallilus) | ||||||
|  |     David Sichma (DavidSichma) | ||||||
|  |     David Snopek (dsnopek) | ||||||
|  |     derammo | ||||||
|  |     Dharkael (lupoDharkael) | ||||||
|  |     Dirk Steinmetz (rsjtdrjgfuzkfg) | ||||||
|  |     Dmitry Koteroff (Krakean) | ||||||
|  |     Dmitry Maganov (vonagam) | ||||||
|  |     Dominik Jasiński (dreamsComeTrue) | ||||||
|  |     Douglas Leão (DeeJayLSP) | ||||||
|  |     DualMatrix | ||||||
|  |     Ellen Poe (ellenhp) | ||||||
|  |     Emilio Coppola (coppolaemilio) | ||||||
|  |     Emmanuel Barroga (codecustard) | ||||||
|  |     Emmanuel Leblond (touilleMan) | ||||||
|  |     Eoin O'Neill (Eoin-ONeill-Yokai) | ||||||
|  |     Eric Lasota (elasota) | ||||||
|  |     Eric M (EricEzaM) | ||||||
|  |     Eric Rybicki (ericrybick) | ||||||
|  |     Erik Selecký (rxlecky) | ||||||
|  |     est31 | ||||||
|  |     Eveline Jarosz (Marqin) | ||||||
|  |     Fabian Mathews (supagu) | ||||||
|  |     Fabio Alessandrelli (Faless) | ||||||
|  |     Fabrice Cipolla (fabriceci) | ||||||
|  |     Ferenc Arn (tagcup) | ||||||
|  |     FireForge (fire-forge) | ||||||
|  |     Florent Guiocheau (Flarkk) | ||||||
|  |     Florian Kothmeier (Dragoncraft89) | ||||||
|  |     follower | ||||||
|  |     foxydevloper | ||||||
|  |     François Belair (Razoric480) | ||||||
|  |     Franklin Sobrinho (TheHX) | ||||||
|  |     Fredia Huya-Kouadio (m4gr3d) | ||||||
|  |     Geequlim | ||||||
|  |     George Marques (vnen) | ||||||
|  |     Gerrit Großkopf (Grosskopf) | ||||||
|  |     Giganzo | ||||||
|  |     Gilles Roudiere (groud) | ||||||
|  |     Gordon MacPherson (RevoluPowered) | ||||||
|  |     Guilherme Felipe de C. G. da Silva (guilhermefelipecgs) | ||||||
|  |     Guillaume Mouton (kiroxas) | ||||||
|  |     Hakim Rouatbi (hakro) | ||||||
|  |     Hanif Bin Ariffin (hbina) | ||||||
|  |     Haoyu Qiu (timothyqiu) | ||||||
|  |     Hein-Pieter van Braam-Stewart (hpvb) | ||||||
|  |     Hendrik Brucker (Geometror) | ||||||
|  |     Hilderin | ||||||
|  |     Hiroshi Ogawa (hi-ogawa) | ||||||
|  |     HolonProduction | ||||||
|  |     homer666 | ||||||
|  |     hoontee | ||||||
|  |     Hugo Locurcio (Calinou) | ||||||
|  |     Ian Bishop (ianb96) | ||||||
|  |     Ibrahn Sahir (ibrahn) | ||||||
|  |     Ignacio Roldán Etcheverry (neikeq) | ||||||
|  |     Igor Kordiukiewicz (IgorKordiukiewicz) | ||||||
|  |     Ilaria Cislaghi (QbieShay) | ||||||
|  |     Indah Sylvia (ISylvox) | ||||||
|  |     Ivan Šachov (van800) | ||||||
|  |     J08nY | ||||||
|  |     Jake Young (Duroxxigar) | ||||||
|  |     Jakub Grzesik (kubecz3k) | ||||||
|  |     Jakub Marcowski (Chubercik) | ||||||
|  |     James Buck (jbuck3) | ||||||
|  |     Jan Haller (Bromeon) | ||||||
|  |     Jason Knight (jasonwinterpixel) | ||||||
|  |     Jean-Michel Bernard (jmb462) | ||||||
|  |     Jérôme Gully (Nutriz) | ||||||
|  |     Jia Jun Chai (SkyLucilfer) | ||||||
|  |     jitspoe | ||||||
|  |     Joan Fons Sanchez (JFonS) | ||||||
|  |     Johan Aires Rastén (JohanAR) | ||||||
|  |     Johan Manuel (29jm) | ||||||
|  |     Johannes Witt (HaSa1002) | ||||||
|  |     Jonathan Nicholl (jtnicholl) | ||||||
|  |     Jordan Schidlowsky (winterpixelgames) | ||||||
|  |     Josh Jones (DarkKilauea) | ||||||
|  |     Joshua Grams (JoshuaGrams) | ||||||
|  |     Juan Linietsky (reduz) | ||||||
|  |     Julian Murgia (StraToN) | ||||||
|  |     Julien Nguyen (Blackiris) | ||||||
|  |     Jummit | ||||||
|  |     Justo Delgado (mrcdk) | ||||||
|  |     karroffel | ||||||
|  |     Kassandra Pucher (PucklaJ) | ||||||
|  |     Kelly Thomas (KellyThomas) | ||||||
|  |     kleonc | ||||||
|  |     Kongfa Waroros (gongpha) | ||||||
|  |     Kostadin Damyanov (Max-Might) | ||||||
|  |     K. S. Ernest (iFire) Lee (fire) | ||||||
|  |     Kyle Eichlin (likeich) | ||||||
|  |     Lars Pettersson (larspet) | ||||||
|  |     lawnjelly | ||||||
|  |     Leon Krause (leonkrause) | ||||||
|  |     Liz Haas (27thLiz) | ||||||
|  |     Logan Lang (devloglogan) | ||||||
|  |     Lucien Menassol (Kanabenki) | ||||||
|  |     Lukas Tenbrink (Ivorforce) | ||||||
|  |     Lyuma | ||||||
|  |     Maganty Rushyendra (mrushyendra) | ||||||
|  |     Magian (magian1127) | ||||||
|  |     Mai Lavelle (maiself) | ||||||
|  |     Malcolm Anderson (Meorge) | ||||||
|  |     Malcolm Nixon (Malcolmnixon) | ||||||
|  |     Manuele Finocchiaro (m4nu3lf) | ||||||
|  |     Marcel Admiraal (madmiraal) | ||||||
|  |     Marcelo Fernandez (marcelofg55) | ||||||
|  |     Marc Gilleron (Zylann) | ||||||
|  |     Marcin Zawiejski (dragmz) | ||||||
|  |     Marcus Brummer (mbrlabs) | ||||||
|  |     Marcus Elg (MCrafterzz) | ||||||
|  |     Mariano Javier Suligoy (MarianoGnu) | ||||||
|  |     Mario Liebisch (MarioLiebisch) | ||||||
|  |     Mario Schlack (hurikhan) | ||||||
|  |     Marios Staikopoulos (marstaik) | ||||||
|  |     Marius Hanl (Maran23) | ||||||
|  |     Mark DiBarry (markdibarry) | ||||||
|  |     Mark Riedesel (klowner) | ||||||
|  |     Markus Sauermann (Sauermann) | ||||||
|  |     Martin Capitanio (capnm) | ||||||
|  |     Martin Liška (marxin) | ||||||
|  |     Martin Sjursen (binbitten) | ||||||
|  |     marynate | ||||||
|  |     Masoud BH (masoudbh3) | ||||||
|  |     Mateo Kuruk Miccino (kuruk-mm) | ||||||
|  |     Matias N. Goldberg (darksylinc) | ||||||
|  |     Matthew Murphy (mashumafi) | ||||||
|  |     Matthew (skyace65) | ||||||
|  |     Matthias Hölzl (hoelzl) | ||||||
|  |     Max Hilbrunner (mhilbrunner) | ||||||
|  |     merumelu | ||||||
|  |     Meru Patel (Janglee123) | ||||||
|  |     MewPurPur | ||||||
|  |     Michael Alexsander (YeldhamDev) | ||||||
|  |     Michał Iwańczuk (iwek7) | ||||||
|  |     MichiRecRoom (LikeLakers2) | ||||||
|  |     Micky (Mickeon) | ||||||
|  |     Mikael Hermansson (mihe) | ||||||
|  |     Mika Viskari (miv391) | ||||||
|  |     MinusKube | ||||||
|  |     MJacred | ||||||
|  |     Mounir Tohami (WhalesState) | ||||||
|  |     mrezai | ||||||
|  |     Muhammad Huri (CakHuri) | ||||||
|  |     muiroc | ||||||
|  |     myaaaaaaaaa | ||||||
|  |     Nathalie Galla (MurderVeggie) | ||||||
|  |     Nathan Franke (nathanfranke) | ||||||
|  |     Nathan Lovato (NathanLovato) | ||||||
|  |     Nathan Warden (NathanWarden) | ||||||
|  |     Nazarii Yablonskyi (Nazarwadim) | ||||||
|  |     Nicholas Huelin (SirQuartz) | ||||||
|  |     Nikita Lita (nikitalita) | ||||||
|  |     Nils André-Chang (NilsIrl) | ||||||
|  |     Noah Beard (TwistedTwigleg) | ||||||
|  |     Nông Văn Tình (nongvantinh) | ||||||
|  |     Nuno Donato (nunodonato) | ||||||
|  |     ocean (they/them) (anvilfolk) | ||||||
|  |     Omar El Sheikh (The-O-King) | ||||||
|  |     Ovnuniarchos | ||||||
|  |     Pablo Andres Fuente (pafuent) | ||||||
|  |     Pascal Richter (ShyRed) | ||||||
|  |     passivestar | ||||||
|  |     Patrick Dawson (pkdawson) | ||||||
|  |     Patrick Exner (FlameLizard) | ||||||
|  |     Patrick (firefly2442) | ||||||
|  |     patwork | ||||||
|  |     Paul Batty (Paulb23) | ||||||
|  |     Paul Joannon (paulloz) | ||||||
|  |     Paul Trojahn (ptrojahn) | ||||||
|  |     Pāvels Nadtočajevs (bruvzg) | ||||||
|  |     Paweł Fertyk (pfertyk) | ||||||
|  |     Pawel Kowal (pkowal1982) | ||||||
|  |     Pawel Lampe (Scony) | ||||||
|  |     Pedro J. Estébanez (RandomShaper) | ||||||
|  |     Pieter-Jan Briers (PJB3005) | ||||||
|  |     Poommetee Ketson (Noshyaar) | ||||||
|  |     Przemysław Gołąb (n-pigeon) | ||||||
|  |     Radiant (radiantgurl) | ||||||
|  |     Rafael M. G. (rafallus) | ||||||
|  |     Rafał Mikrut (qarmin) | ||||||
|  |     Raffaele Picca (RPicster) | ||||||
|  |     Ralf Hölzemer (rollenrolm) | ||||||
|  |     Ramesh Ravone (RameshRavone) | ||||||
|  |     Raphael2048 | ||||||
|  |     Raul Santos (raulsntos) | ||||||
|  |     Ray Koopa (RayKoopa) | ||||||
|  |     RedMser | ||||||
|  |     RedworkDE | ||||||
|  |     Rémi Verschelde (akien-mga) | ||||||
|  |     Rhody Lugo (rraallvv) | ||||||
|  |     Ricardo Buring (rburing) | ||||||
|  |     Ricardo Subtil (Ev1lbl0w) | ||||||
|  |     Riteo Siuga (Riteo) | ||||||
|  |     Roberto F. Arroyo (robfram) | ||||||
|  |     Robert Yevdokimov (ryevdokimov) | ||||||
|  |     Robin Hübner (profan) | ||||||
|  |     romulox-x | ||||||
|  |     Rudolph Bester (Rudolph-B) | ||||||
|  |     Rune Smith (rune-scape) | ||||||
|  |     Ruslan Mustakov (endragor) | ||||||
|  |     Ryan Roden-Corrent (rrcore) | ||||||
|  |     Saniko (sanikoyes) | ||||||
|  |     santouits | ||||||
|  |     SaracenOne | ||||||
|  |     Septian Ganendra S. K. (sepTN) | ||||||
|  |     Sergey Minakov (naithar) | ||||||
|  |     sersoong | ||||||
|  |     Shiqing (kawa-yoiko) | ||||||
|  |     Silc 'Tokage' Renew (TokageItLab) | ||||||
|  |     Simon Wenner (swenner) | ||||||
|  |     smix8 | ||||||
|  |     snailrhymer | ||||||
|  |     Sofox (TheSofox) | ||||||
|  |     Stanislav Labzyuk (DarkMessiah) | ||||||
|  |     Stijn Hinlopen (hinlopen) | ||||||
|  |     stmSi | ||||||
|  |     Stuart Carnie (stuartcarnie) | ||||||
|  |     Swarnim Arun (minraws) | ||||||
|  |     TC (floppyhammer) | ||||||
|  |     TechnoPorg | ||||||
|  |     tetrapod00 | ||||||
|  |     Thaddeus Crews (Repiteo) | ||||||
|  |     Thakee Nathees (ThakeeNathees) | ||||||
|  |     thebestnom | ||||||
|  |     Theo Hallenius (TheoXD) | ||||||
|  |     Thomas ten Cate (ttencate) | ||||||
|  |     Timo Schwarzer (timoschwarzer) | ||||||
|  |     Timothé Bonhoure (ajreckof) | ||||||
|  |     Timo (toger5) | ||||||
|  |     Tomasz Chabora (KoBeWi) | ||||||
|  |     Travis Lange (TCROC) | ||||||
|  |     trollodel | ||||||
|  |     Twarit Waikar (IronicallySerious) | ||||||
|  |     Umang Kalra (theoway) | ||||||
|  |     Victor Hampel (havi05) | ||||||
|  |     Vinzenz Feenstra (vinzenz) | ||||||
|  |     Vitika Soni (Vitika9) | ||||||
|  |     박한얼 (volzhs) | ||||||
|  |     V. Vamsi Krishna (vkbsb) | ||||||
|  |     Wilhem Barbier (nounoursheureux) | ||||||
|  |     William Deurwaarder (williamd67) | ||||||
|  |     Will Nations (willnationsdev) | ||||||
|  |     Wilson E. Alvarez (Rubonnek) | ||||||
|  |     Xavier Cho (mysticfall) | ||||||
|  |     Yaohua Xiong (xiongyaohua) | ||||||
|  |     Yevhen Babiichuk (dustdfg) | ||||||
|  |     yg2f (SuperUserNameMan) | ||||||
|  |     Yordan Dolchinkov (Jordyfel) | ||||||
|  |     Yufeng Ying (YYF233333) | ||||||
|  |     Yuri Rubinsky (Chaosus) | ||||||
|  |     Yuri Sizov (YuriSizov) | ||||||
|  |     Zae Chao (zaevi) | ||||||
|  |     Zak Stam (zaksnet) | ||||||
|  |     Zher Huei Lee (leezh) | ||||||
|  |     Zi Ye (MajorMcDoom) | ||||||
|  |     ZuBsPaCe | ||||||
|  |     Дмитрий Сальников (DmitriySalnikov) | ||||||
|  |     忘忧の (Daylily-Zeleen) | ||||||
|  |     谢天 (jsjtxietian) | ||||||
|  |     风青山 (Rindbee) | ||||||
							
								
								
									
										2953
									
								
								engine/CHANGELOG.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2953
									
								
								engine/CHANGELOG.md
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										213
									
								
								engine/CONTRIBUTING.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										213
									
								
								engine/CONTRIBUTING.md
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,213 @@ | ||||||
|  | # Contributors guidelines | ||||||
|  | 
 | ||||||
|  | This document summarizes the most important points for people interested in | ||||||
|  | contributing to Godot, especially via bug reports or pull requests. | ||||||
|  | 
 | ||||||
|  | The Godot documentation has a dedicated [Contributing section](https://docs.godotengine.org/en/latest/contributing/how_to_contribute.html) | ||||||
|  | which details these points and more, and is a recommended read. | ||||||
|  | 
 | ||||||
|  | ## Table of contents | ||||||
|  | 
 | ||||||
|  | - [Reporting bugs](#reporting-bugs) | ||||||
|  | - [Proposing features or improvements](#proposing-features-or-improvements) | ||||||
|  | - [Contributing pull requests](#contributing-pull-requests) | ||||||
|  | - [Contributing to Godot translations](#contributing-to-godot-translations) | ||||||
|  | - [Communicating with developers](#communicating-with-developers) | ||||||
|  | 
 | ||||||
|  | ## Reporting bugs | ||||||
|  | 
 | ||||||
|  | Report bugs [here](https://github.com/godotengine/godot/issues/new?assignees=&labels=&template=bug_report.yml). | ||||||
|  | Please follow the instructions in the template when you do. | ||||||
|  | 
 | ||||||
|  | Notably, please include a Minimal Reproduction Project (MRP), which is a small | ||||||
|  | Godot project which reproduces the issue, with no unnecessary files included. | ||||||
|  | Be sure to not include the `.godot` folder in the archive to save space. | ||||||
|  | 
 | ||||||
|  | Make sure that the bug you are experiencing is reproducible in the latest Godot | ||||||
|  | releases. You can find an overview of all Godot releases [on the website](https://godotengine.org/download/archive/) | ||||||
|  | to confirm whether your current version is the latest one. It's worth testing | ||||||
|  | against both the latest stable release and the latest dev snapshot for the next | ||||||
|  | Godot release. | ||||||
|  | 
 | ||||||
|  | If you run into a bug which wasn't present in an earlier Godot version (what we | ||||||
|  | call a _regression_), please mention it and clarify which versions you tested | ||||||
|  | (both the one(s) working and the one(s) exhibiting the bug). | ||||||
|  | 
 | ||||||
|  | ## Proposing features or improvements | ||||||
|  | 
 | ||||||
|  | **The main issue tracker is for bug reports and does not accept feature proposals.** | ||||||
|  | 
 | ||||||
|  | Instead, head to the [Godot Proposals repository](https://github.com/godotengine/godot-proposals) | ||||||
|  | and follow the instructions in the README file and issue template. | ||||||
|  | 
 | ||||||
|  | ## Contributing pull requests | ||||||
|  | 
 | ||||||
|  | If you want to add new engine features, please make sure that: | ||||||
|  | 
 | ||||||
|  | - This functionality is desired, which means that it solves a common use case | ||||||
|  |   that several users will need in their real-life projects. | ||||||
|  | - You talked to other developers on how to implement it best. See also | ||||||
|  |   [Proposing features or improvements](#proposing-features-or-improvements). | ||||||
|  | - Even if it doesn't get merged, your PR is useful for future work by another | ||||||
|  |   developer. | ||||||
|  | 
 | ||||||
|  | Similar rules can be applied when contributing bug fixes - it's always best to | ||||||
|  | discuss the implementation in the bug report first if you are not 100% about | ||||||
|  | what would be the best fix. | ||||||
|  | 
 | ||||||
|  | You can refer to the [Pull request review process](https://docs.godotengine.org/en/latest/contributing/workflow/pr_review_guidelines.html) | ||||||
|  | for insights into the intended lifecycle of pull requests. This should help you | ||||||
|  | ensure that your pull request fulfills the requirements. | ||||||
|  | 
 | ||||||
|  | In addition to the following tips, also take a look at the | ||||||
|  | [Engine development guide](https://docs.godotengine.org/en/latest/contributing/development/index.html) | ||||||
|  | for an introduction to developing on Godot. | ||||||
|  | 
 | ||||||
|  | The [Contributing docs](https://docs.godotengine.org/en/latest/contributing/how_to_contribute.html) | ||||||
|  | also have important information on the [PR workflow](https://docs.godotengine.org/en/latest/contributing/workflow/pr_workflow.html) | ||||||
|  | (with a helpful guide for Git usage), and our [Code style guidelines](https://docs.godotengine.org/en/latest/contributing/development/code_style_guidelines.html) | ||||||
|  | which all contributions need to follow. | ||||||
|  | 
 | ||||||
|  | ### Be mindful of your commits | ||||||
|  | 
 | ||||||
|  | Try to make simple PRs that handle one specific topic. Just like for reporting | ||||||
|  | issues, it's better to open 3 different PRs that each address a different issue | ||||||
|  | than one big PR with three commits. This makes it easier to review, approve, and | ||||||
|  | merge the changes independently. | ||||||
|  | 
 | ||||||
|  | When updating your fork with upstream changes, please use ``git pull --rebase`` | ||||||
|  | to avoid creating "merge commits". Those commits unnecessarily pollute the git | ||||||
|  | history when coming from PRs. | ||||||
|  | 
 | ||||||
|  | Also try to make commits that bring the engine from one stable state to another | ||||||
|  | stable state, i.e. if your first commit has a bug that you fixed in the second | ||||||
|  | commit, try to merge them together before making your pull request. This | ||||||
|  | includes fixing build issues or typos, adding documentation, etc. | ||||||
|  | 
 | ||||||
|  | See our [PR workflow](https://docs.godotengine.org/en/latest/contributing/workflow/pr_workflow.html) | ||||||
|  | documentation for tips on using Git, amending commits and rebasing branches. | ||||||
|  | 
 | ||||||
|  | This [Git style guide](https://github.com/agis-/git-style-guide) also has some | ||||||
|  | good practices to have in mind. | ||||||
|  | 
 | ||||||
|  | ### Format your commit messages with readability in mind | ||||||
|  | 
 | ||||||
|  | The way you format your commit messages is quite important to ensure that the | ||||||
|  | commit history and changelog will be easy to read and understand. A Git commit | ||||||
|  | message is formatted as a short title (first line) and an extended description | ||||||
|  | (everything after the first line and an empty separation line). | ||||||
|  | 
 | ||||||
|  | The short title is the most important part, as it is what will appear in the | ||||||
|  | changelog or in the GitHub interface unless you click the "expand" button. | ||||||
|  | Try to keep that first line under 72 characters, but you can go slightly above | ||||||
|  | if necessary to keep the sentence clear. | ||||||
|  | 
 | ||||||
|  | It should be written in English, starting with a capital letter, and usually | ||||||
|  | with a verb in imperative form. A typical bugfix would start with "Fix", while | ||||||
|  | the addition of a new feature would start with "Add". A prefix can be added to | ||||||
|  | specify the engine area affected by the commit. Some examples: | ||||||
|  | 
 | ||||||
|  | - Add C# iOS support | ||||||
|  | - Show doc tooltips when hovering properties in the theme editor | ||||||
|  | - Fix GLES3 instanced rendering color and custom data defaults | ||||||
|  | - Core: Fix `Object::has_method()` for script static methods | ||||||
|  | 
 | ||||||
|  | If your commit fixes a reported issue, please include it in the _description_ | ||||||
|  | of the PR (not in the title, or the commit message) using one of the | ||||||
|  | [GitHub closing keywords](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue) | ||||||
|  | such as "Fixes #1234". This will cause the issue to be closed automatically if | ||||||
|  | the PR is merged. Adding it to the commit message is easier, but adds a lot of | ||||||
|  | unnecessary updates in the issue distracting from the thread. | ||||||
|  | 
 | ||||||
|  | Here's an example of a well-formatted commit message (note how the extended | ||||||
|  | description is also manually wrapped at 80 chars for readability): | ||||||
|  | 
 | ||||||
|  | ```text | ||||||
|  | Prevent French fries carbonization by fixing heat regulation | ||||||
|  | 
 | ||||||
|  | When using the French fries frying module, Godot would not regulate the heat | ||||||
|  | and thus bring the oil bath to supercritical liquid conditions, thus causing | ||||||
|  | unwanted side effects in the physics engine. | ||||||
|  | 
 | ||||||
|  | By fixing the regulation system via an added binding to the internal feature, | ||||||
|  | this commit now ensures that Godot will not go past the ebullition temperature | ||||||
|  | of cooking oil under normal atmospheric conditions. | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | **Note:** When using the GitHub online editor or its drag-and-drop | ||||||
|  | feature, *please* edit the commit title to something meaningful. Commits named | ||||||
|  | "Update my_file.cpp" won't be accepted. | ||||||
|  | 
 | ||||||
|  | ### Document your changes | ||||||
|  | 
 | ||||||
|  | If your pull request adds methods, properties or signals that are exposed to | ||||||
|  | scripting APIs, you **must** update the class reference to document those. | ||||||
|  | This is to ensure the documentation coverage doesn't decrease as contributions | ||||||
|  | are merged. | ||||||
|  | 
 | ||||||
|  | [Update documentation XML files](https://docs.godotengine.org/en/latest/contributing/documentation/updating_the_class_reference.html) | ||||||
|  | using your compiled binary, then fill in the descriptions. | ||||||
|  | Follow the style guide described in the | ||||||
|  | [Documentation writing guidelines](https://docs.godotengine.org/en/latest/contributing/documentation/docs_writing_guidelines.html). | ||||||
|  | 
 | ||||||
|  | If your pull request modifies parts of the code in a non-obvious way, make sure | ||||||
|  | to add comments in the code as well. This helps other people understand the | ||||||
|  | change without having to dive into the Git history. | ||||||
|  | 
 | ||||||
|  | ### Write unit tests | ||||||
|  | 
 | ||||||
|  | When fixing a bug or contributing a new feature, we recommend including unit | ||||||
|  | tests in the same commit as the rest of the pull request. Unit tests are pieces | ||||||
|  | of code that compare the output to a predetermined *expected result* to detect | ||||||
|  | regressions. Tests are compiled and run on GitHub Actions for every commit and | ||||||
|  | pull request. | ||||||
|  | 
 | ||||||
|  | Pull requests that include tests are more likely to be merged, since we can have | ||||||
|  | greater confidence in them not being the target of regressions in the future. | ||||||
|  | 
 | ||||||
|  | For bugs, the unit tests should cover the functionality that was previously | ||||||
|  | broken. If done well, this ensures regressions won't appear in the future | ||||||
|  | again. For new features, the unit tests should cover the newly added | ||||||
|  | functionality, testing both the "success" and "expected failure" cases if | ||||||
|  | applicable. | ||||||
|  | 
 | ||||||
|  | Feel free to contribute standalone pull requests to add new tests or improve | ||||||
|  | existing tests as well. | ||||||
|  | 
 | ||||||
|  | See [Unit testing](https://docs.godotengine.org/en/latest/contributing/development/core_and_modules/unit_testing.html) | ||||||
|  | for information on writing tests in Godot's C++ codebase. | ||||||
|  | 
 | ||||||
|  | ## Contributing to Godot translations | ||||||
|  | 
 | ||||||
|  | You can contribute to Godot translations on [Hosted Weblate](https://hosted.weblate.org/projects/godot-engine/), | ||||||
|  | an open source and web-based translation platform. | ||||||
|  | 
 | ||||||
|  | Please refer to our [editor and documentation localization guidelines](https://docs.godotengine.org/en/latest/contributing/documentation/editor_and_docs_localization.html) | ||||||
|  | for an overview of the translation resources and what they correspond to. | ||||||
|  | 
 | ||||||
|  | ## Communicating with developers | ||||||
|  | 
 | ||||||
|  | The Godot Engine community has [many communication | ||||||
|  | channels](https://godotengine.org/community), some used more for user-level | ||||||
|  | discussions and support, others more for development discussions. | ||||||
|  | 
 | ||||||
|  | To communicate with developers (e.g. to discuss a feature you want to implement | ||||||
|  | or a bug you want to fix), the following channels can be used: | ||||||
|  | 
 | ||||||
|  | - [Godot Contributors Chat](https://chat.godotengine.org): You will | ||||||
|  |   find most core developers there, so it's the go-to platform for direct chat | ||||||
|  |   about Godot Engine development. Browse the [Directory](https://chat.godotengine.org/directory/channels) | ||||||
|  |   for an overview of public channels focusing on various engine areas which you | ||||||
|  |   might be interested in. | ||||||
|  | - [Bug tracker](https://github.com/godotengine/godot/issues): If there is an | ||||||
|  |   existing issue about a topic you want to discuss, you can participate directly. | ||||||
|  |   If not, you can open a new issue. Please mind the guidelines outlined above | ||||||
|  |   for bug reporting. | ||||||
|  | - [Feature proposals](https://github.com/godotengine/godot-proposals/issues): | ||||||
|  |   To propose a new feature, we have a dedicated issue tracker for that. Don't | ||||||
|  |   hesitate to start by talking about your idea on the Godot Contributors Chat | ||||||
|  |   to make sure that it makes sense in Godot's context. | ||||||
|  | 
 | ||||||
|  | Thanks for your interest in contributing! | ||||||
|  | 
 | ||||||
|  | —The Godot development team | ||||||
							
								
								
									
										2214
									
								
								engine/COPYRIGHT.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2214
									
								
								engine/COPYRIGHT.txt
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										293
									
								
								engine/DONORS.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										293
									
								
								engine/DONORS.md
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,293 @@ | ||||||
|  | # Donors to the Godot Engine project | ||||||
|  | 
 | ||||||
|  | Godot Engine is a non-profit project developed by a community of voluntary | ||||||
|  | contributors, as well as occasional paid contributors thanks to the financial | ||||||
|  | support of generous donors. | ||||||
|  | 
 | ||||||
|  | The ways to donate to the project, as well as details on how the funds are | ||||||
|  | used, are described on [Godot's website](https://godotengine.org/donate). | ||||||
|  | 
 | ||||||
|  | The following is a list of the current monthly donors, who will have their | ||||||
|  | generous deed immortalized in the next stable release of Godot Engine. | ||||||
|  | 
 | ||||||
|  | ## Patrons | ||||||
|  | 
 | ||||||
|  |     Khronos® Group <https://www.khronos.org/> | ||||||
|  |     OSS Capital <https://oss.capital/> | ||||||
|  | 
 | ||||||
|  | ## Platinum sponsors | ||||||
|  | 
 | ||||||
|  |     Scorewarrior <https://scwr.gg/godot> | ||||||
|  |     V-Sekai <https://github.com/V-Sekai> | ||||||
|  |     W4 Games <https://w4games.com/> | ||||||
|  | 
 | ||||||
|  | ## Gold sponsors | ||||||
|  | 
 | ||||||
|  |     Mega Crit <https://www.megacrit.com/> | ||||||
|  |     Prehensile Tales <https://prehensile-tales.com> | ||||||
|  |     Robot Gentleman <http://robotgentleman.com/> | ||||||
|  | 
 | ||||||
|  | ## Silver sponsors | ||||||
|  | 
 | ||||||
|  |     Bippinbits <https://bippinbits.com/> | ||||||
|  |     Broken Rules <https://brokenrul.es> | ||||||
|  |     Chasing Carrots <https://www.chasing-carrots.com> | ||||||
|  |     Copia Wealth Studios <https://copiawealthstudios.com/> | ||||||
|  |     LoadComplete <https://loadcomplete.com/> | ||||||
|  |     Null <https://null.com/> | ||||||
|  |     Orbital Knight <https://www.orbitalknight.com/> | ||||||
|  |     Playful Studios <https://playfulstudios.com/> | ||||||
|  |     Re-Logic <https://re-logic.com/> | ||||||
|  | 
 | ||||||
|  | ## Diamond members | ||||||
|  | 
 | ||||||
|  |     ASIFA-Hollywood <https://www.asifa-hollywood.org/> | ||||||
|  |     Christina Coffin <https://bsky.app/profile/christinacoffin.bsky.social> | ||||||
|  |     Dominic Harris <https://wayfarer-games.com/> | ||||||
|  |     Kiri "ExpiredPopsicle" Artemis <https://expiredpopsicle.com/> | ||||||
|  |     Petr Kharitonov <https://petrkharitonov.com/> | ||||||
|  |     Seats.aero <https://seats.aero/> | ||||||
|  |     Sylv <https://rankith.itch.io/unnamed-space-idle-prototype> | ||||||
|  |     And 5 anonymous donors | ||||||
|  | 
 | ||||||
|  | ## Titanium members | ||||||
|  | 
 | ||||||
|  |     Adriaan de Jongh <https://adriaan.games> | ||||||
|  |     Basically Games | ||||||
|  |     Draknek & Friends <https://www.draknek.org/> | ||||||
|  |     Garry Newman | ||||||
|  |     Gigabrain AI Reddit Search Engine <https://thegigabrain.com/?utm_source=godot> | ||||||
|  |     Jettelly <https://jettelly.com/> | ||||||
|  |     Justo Delgado Baudí <https://portfolio.mrcdk.com/> | ||||||
|  |     Kenney <https://kenney.nl/> | ||||||
|  |     Lucid Silence Games | ||||||
|  |     Matthew Hall <https://crossyroad.com/> | ||||||
|  |     PolyMars <https://polymars.dev/> | ||||||
|  |     Purple Moss Collectors <https://purplemosscollectors.com/> | ||||||
|  |     RPG in a Box <https://www.rpginabox.com> | ||||||
|  |     Starkandco <https://github.com/Starkandco> | ||||||
|  |     Studio Sunshower <https://www.studiosunshower.com/> | ||||||
|  |     TrampolineTales <https://TrampolineTales.com/> | ||||||
|  |     粟二华 (Su Erhua) | ||||||
|  |     And 4 anonymous donors | ||||||
|  | 
 | ||||||
|  | ## Platinum members | ||||||
|  | 
 | ||||||
|  |     Andy Touch | ||||||
|  |     BlockImperiumGames (BIG) | ||||||
|  |     Bytten Studio | ||||||
|  |     Christopher Shifflett | ||||||
|  |     Cody Bentley | ||||||
|  |     Darrin Massena | ||||||
|  |     Fabio Alessandrelli | ||||||
|  |     HP van Braam | ||||||
|  |     iCommitGames | ||||||
|  |     Jason Hamilton | ||||||
|  |     Jonah Stich | ||||||
|  |     Josh Anthony | ||||||
|  |     Ludvig Temperli Risan | ||||||
|  |     Matthew Ekenstedt | ||||||
|  |     Memories in 8Bit | ||||||
|  |     Michael Martin | ||||||
|  |     Mike King | ||||||
|  |     Neal Gompa (Conan Kudo) | ||||||
|  |     Nico Ulriksen | ||||||
|  |     Nikita Blizniuk | ||||||
|  |     Raptor85 | ||||||
|  |     Rémi Verschelde | ||||||
|  |     Ronnie Cheng | ||||||
|  |     Ryan Heath | ||||||
|  |     ShikadiGum | ||||||
|  |     Silver Creek Entertainment | ||||||
|  |     Stephan Kessler | ||||||
|  |     Stephen Rice | ||||||
|  |     And 14 anonymous donors | ||||||
|  | 
 | ||||||
|  | ## Gold members | ||||||
|  | 
 | ||||||
|  |     2 Nerdy Nerds | ||||||
|  |     73unny | ||||||
|  |     80px | ||||||
|  |     Abigail F. | ||||||
|  |     Admiral Potato | ||||||
|  |     afreytes | ||||||
|  |     AinaVT | ||||||
|  |     Ajat BlackSun | ||||||
|  |     Alex177Alex | ||||||
|  |     alMoo Games | ||||||
|  |     Alva Majo | ||||||
|  |     Andrew Eiche | ||||||
|  |     Antti Vesanen | ||||||
|  |     Arediss | ||||||
|  |     Asher Glick | ||||||
|  |     Axthelm | ||||||
|  |     BangTheWall | ||||||
|  |     Benito | ||||||
|  |     Benjamin Bridges | ||||||
|  |     Ben Rog-Wilhelm | ||||||
|  |     BetaTester704 | ||||||
|  |     Brut | ||||||
|  |     Bryce Dixon | ||||||
|  |     Carlo Cabanilla | ||||||
|  |     Carlo del Mundo | ||||||
|  |     Carl van der Geest | ||||||
|  |     Chocolate Software | ||||||
|  |     Chris Backas | ||||||
|  |     Chris Lambson | ||||||
|  |     Christine Elisabeth Koppel | ||||||
|  |     Cindy Trieu | ||||||
|  |     ClarkThyLord | ||||||
|  |     Codex404 | ||||||
|  |     cora | ||||||
|  |     Daniel Eichler | ||||||
|  |     Daniel Krafft | ||||||
|  |     Datzju | ||||||
|  |     David Chen Zhen | ||||||
|  |     David Coles | ||||||
|  |     David Hubber | ||||||
|  |     David Snopek | ||||||
|  |     Deakcor | ||||||
|  |     Delton Ding | ||||||
|  |     dgehrig | ||||||
|  |     Disco Cat | ||||||
|  |     Distorted Realities | ||||||
|  |     DitherDream | ||||||
|  |     Dominik Frizel | ||||||
|  |     Don't You Know Who I Am? Inc. | ||||||
|  |     Dustuu | ||||||
|  |     Dylan Dromard | ||||||
|  |     Edelweiss | ||||||
|  |     eelSkillz | ||||||
|  |     Emily Flion | ||||||
|  |     Ends | ||||||
|  |     Eren Ogrul | ||||||
|  |     Eric Brand | ||||||
|  |     Eric Phy | ||||||
|  |     Faisal Al-Kubaisi (QatariGameDev) | ||||||
|  |     Fanny Pack Studios | ||||||
|  |     Felix Adam | ||||||
|  |     FeralBytes | ||||||
|  |     Francisco Aliaga | ||||||
|  |     Francis Jasmin | ||||||
|  |     Frozen Fractal | ||||||
|  |     Gaudipern | ||||||
|  |     GetIntoGameDev | ||||||
|  |     GlassBrick | ||||||
|  |     Grau | ||||||
|  |     Grzegorz Wereszko | ||||||
|  |     Guangzhou Lingchan | ||||||
|  |     Guilherme Cattani | ||||||
|  |     Hayden Oliver | ||||||
|  |     hiulit | ||||||
|  |     https://domi.zip | ||||||
|  |     Huedeane | ||||||
|  |     Ikuti | ||||||
|  |     Illyan | ||||||
|  |     I.M.I.Self | ||||||
|  |     Immaculate Lift Studio | ||||||
|  |     Intrepid Marmot LLC | ||||||
|  |     Isaac Marovitz | ||||||
|  |     Ivan Tabashki | ||||||
|  |     jakemiki | ||||||
|  |     Jam | ||||||
|  |     Jason Cawood | ||||||
|  |     Jeff Hungerford | ||||||
|  |     Jesús Chicharro | ||||||
|  |     Johannes Wuensch | ||||||
|  |     John Gabriel | ||||||
|  |     Jonas Yamazaki | ||||||
|  |     Jonathan | ||||||
|  |     José Canepa | ||||||
|  |     Julian Todd | ||||||
|  |     Justin Laster | ||||||
|  |     Justin Sasso | ||||||
|  |     Kalydi Balázs | ||||||
|  |     KAR Games | ||||||
|  |     Kiryonn | ||||||
|  |     kodebold | ||||||
|  |     KOGA Mitsuhiro (@shiena) | ||||||
|  |     korinVR | ||||||
|  |     Kristian Kriehl | ||||||
|  |     KyletheDab | ||||||
|  |     Lars Thießen | ||||||
|  |     Lisandro Lorea (Red Mage Games) | ||||||
|  |     Logan Apple | ||||||
|  |     Luca Junge | ||||||
|  |     LyaaaaaGames | ||||||
|  |     m1n1ster | ||||||
|  |     Madison Nicole Videogames | ||||||
|  |     Mara Huldra | ||||||
|  |     Marek Belski | ||||||
|  |     Martin Šenkeřík | ||||||
|  |     Matthias B. | ||||||
|  |     Michael Alexsander | ||||||
|  |     Michael Gooch | ||||||
|  |     Michael Harrington | ||||||
|  |     Modus Ponens | ||||||
|  |     Moshe Harris | ||||||
|  |     Moth Soup | ||||||
|  |     Mr. Byte | ||||||
|  |     Muscarian Softworks | ||||||
|  |     Nassor Paulino da Silva | ||||||
|  |     Neuroticfly Games | ||||||
|  |     nezticle | ||||||
|  |     nikkehtine | ||||||
|  |     Niklas Wahrman | ||||||
|  |     Nitzan Bueno | ||||||
|  |     NotNet | ||||||
|  |     Oathbringer | ||||||
|  |     Officine Pixel | ||||||
|  |     ohanaya3 | ||||||
|  |     Okatima AB | ||||||
|  |     Oscar Robin | ||||||
|  |     ovym | ||||||
|  |     Patrick Traynor | ||||||
|  |     Péter Horváth-Lázár | ||||||
|  |     Petr Maláč | ||||||
|  |     pirey | ||||||
|  |     protogames | ||||||
|  |     Rafa Laguna | ||||||
|  |     Reid Hannaford | ||||||
|  |     @reilaos | ||||||
|  |     Request | ||||||
|  |     re:thinc | ||||||
|  |     Richard Ivánek | ||||||
|  |     Robin Ward | ||||||
|  |     Ronny Mühle | ||||||
|  |     Rubén Rüger | ||||||
|  |     Samuel Judd | ||||||
|  |     Santi_FC | ||||||
|  |     Seref Karahan | ||||||
|  |     shazzner | ||||||
|  |     Shiny Shinken | ||||||
|  |     Silverclad Studios | ||||||
|  |     Skie Radscale | ||||||
|  |     Snow Diamond | ||||||
|  |     Sofox | ||||||
|  |     Space Kraken Studios | ||||||
|  |     Spoony Panda | ||||||
|  |     tenuki | ||||||
|  |     ThatGamer | ||||||
|  |     Thomas Lobig | ||||||
|  |     Tobias Bocanegra | ||||||
|  |     Tom Langwaldt | ||||||
|  |     Trevor Slocum | ||||||
|  |     tukon | ||||||
|  |     twitch.tv/RobitussinMD | ||||||
|  |     Tyler C | ||||||
|  |     Urban Protagonist | ||||||
|  |     Vincent Foulon | ||||||
|  |     Vojtech Lacina | ||||||
|  |     Voxel Floof | ||||||
|  |     Watchinofoye | ||||||
|  |     Yannick | ||||||
|  |     zikes | ||||||
|  |     Zoey Smith | ||||||
|  |     嗯大爷 | ||||||
|  | 
 | ||||||
|  |     And 143 anonymous donors | ||||||
|  | 
 | ||||||
|  | ## Silver and bronze donors | ||||||
|  | 
 | ||||||
|  | There are even more donors that support the project with a small monthly donation. | ||||||
|  | Every bit counts and we thank every one of them for their amazing support! | ||||||
							
								
								
									
										20
									
								
								engine/LICENSE.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								engine/LICENSE.txt
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,20 @@ | ||||||
|  | Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). | ||||||
|  | Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. | ||||||
|  | 
 | ||||||
|  | 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. | ||||||
							
								
								
									
										5
									
								
								engine/LOGO_LICENSE.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								engine/LOGO_LICENSE.txt
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,5 @@ | ||||||
|  | Godot Engine Logo | ||||||
|  | Copyright (c) 2017 Andrea Calabró | ||||||
|  | 
 | ||||||
|  | This work is licensed under the Creative Commons Attribution 4.0 International | ||||||
|  | license (CC BY 4.0 International): https://creativecommons.org/licenses/by/4.0/ | ||||||
							
								
								
									
										78
									
								
								engine/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								engine/README.md
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,78 @@ | ||||||
|  | # Godot Engine | ||||||
|  | 
 | ||||||
|  | <p align="center"> | ||||||
|  |   <a href="https://godotengine.org"> | ||||||
|  |     <img src="logo_outlined.svg" width="400" alt="Godot Engine logo"> | ||||||
|  |   </a> | ||||||
|  | </p> | ||||||
|  | 
 | ||||||
|  | ## 2D and 3D cross-platform game engine | ||||||
|  | 
 | ||||||
|  | **[Godot Engine](https://godotengine.org) is a feature-packed, cross-platform | ||||||
|  | game engine to create 2D and 3D games from a unified interface.** It provides a | ||||||
|  | comprehensive set of [common tools](https://godotengine.org/features), so that | ||||||
|  | users can focus on making games without having to reinvent the wheel. Games can | ||||||
|  | be exported with one click to a number of platforms, including the major desktop | ||||||
|  | platforms (Linux, macOS, Windows), mobile platforms (Android, iOS), as well as | ||||||
|  | Web-based platforms and [consoles](https://docs.godotengine.org/en/latest/tutorials/platform/consoles.html). | ||||||
|  | 
 | ||||||
|  | ## Free, open source and community-driven | ||||||
|  | 
 | ||||||
|  | Godot is completely free and open source under the very permissive [MIT license](https://godotengine.org/license). | ||||||
|  | No strings attached, no royalties, nothing. The users' games are theirs, down | ||||||
|  | to the last line of engine code. Godot's development is fully independent and | ||||||
|  | community-driven, empowering users to help shape their engine to match their | ||||||
|  | expectations. It is supported by the [Godot Foundation](https://godot.foundation/) | ||||||
|  | not-for-profit. | ||||||
|  | 
 | ||||||
|  | Before being open sourced in [February 2014](https://github.com/godotengine/godot/commit/0b806ee0fc9097fa7bda7ac0109191c9c5e0a1ac), | ||||||
|  | Godot had been developed by [Juan Linietsky](https://github.com/reduz) and | ||||||
|  | [Ariel Manzur](https://github.com/punto-) (both still maintaining the project) | ||||||
|  | for several years as an in-house engine, used to publish several work-for-hire | ||||||
|  | titles. | ||||||
|  | 
 | ||||||
|  |  | ||||||
|  | 
 | ||||||
|  | ## Getting the engine | ||||||
|  | 
 | ||||||
|  | ### Binary downloads | ||||||
|  | 
 | ||||||
|  | Official binaries for the Godot editor and the export templates can be found | ||||||
|  | [on the Godot website](https://godotengine.org/download). | ||||||
|  | 
 | ||||||
|  | ### Compiling from source | ||||||
|  | 
 | ||||||
|  | [See the official docs](https://docs.godotengine.org/en/latest/contributing/development/compiling) | ||||||
|  | for compilation instructions for every supported platform. | ||||||
|  | 
 | ||||||
|  | ## Community and contributing | ||||||
|  | 
 | ||||||
|  | Godot is not only an engine but an ever-growing community of users and engine | ||||||
|  | developers. The main community channels are listed [on the homepage](https://godotengine.org/community). | ||||||
|  | 
 | ||||||
|  | The best way to get in touch with the core engine developers is to join the | ||||||
|  | [Godot Contributors Chat](https://chat.godotengine.org). | ||||||
|  | 
 | ||||||
|  | To get started contributing to the project, see the [contributing guide](CONTRIBUTING.md). | ||||||
|  | This document also includes guidelines for reporting bugs. | ||||||
|  | 
 | ||||||
|  | ## Documentation and demos | ||||||
|  | 
 | ||||||
|  | The official documentation is hosted on [Read the Docs](https://docs.godotengine.org). | ||||||
|  | It is maintained by the Godot community in its own [GitHub repository](https://github.com/godotengine/godot-docs). | ||||||
|  | 
 | ||||||
|  | The [class reference](https://docs.godotengine.org/en/latest/classes/) | ||||||
|  | is also accessible from the Godot editor. | ||||||
|  | 
 | ||||||
|  | We also maintain official demos in their own [GitHub repository](https://github.com/godotengine/godot-demo-projects) | ||||||
|  | as well as a list of [awesome Godot community resources](https://github.com/godotengine/awesome-godot). | ||||||
|  | 
 | ||||||
|  | There are also a number of other | ||||||
|  | [learning resources](https://docs.godotengine.org/en/latest/community/tutorials.html) | ||||||
|  | provided by the community, such as text and video tutorials, demos, etc. | ||||||
|  | Consult the [community channels](https://godotengine.org/community) | ||||||
|  | for more information. | ||||||
|  | 
 | ||||||
|  | [](https://www.codetriage.com/godotengine/godot) | ||||||
|  | [](https://hosted.weblate.org/engage/godot-engine/?utm_source=widget) | ||||||
|  | [](https://www.tickgit.com/browse?repo=github.com/godotengine/godot) | ||||||
							
								
								
									
										1163
									
								
								engine/SConstruct
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1163
									
								
								engine/SConstruct
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										293
									
								
								engine/core/SCsub
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										293
									
								
								engine/core/SCsub
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,293 @@ | ||||||
|  | #!/usr/bin/env python | ||||||
|  | from misc.utility.scons_hints import * | ||||||
|  | 
 | ||||||
|  | Import("env") | ||||||
|  | 
 | ||||||
|  | import os | ||||||
|  | 
 | ||||||
|  | import core_builders | ||||||
|  | 
 | ||||||
|  | import methods | ||||||
|  | 
 | ||||||
|  | env.core_sources = [] | ||||||
|  | 
 | ||||||
|  | # Add required thirdparty code. | ||||||
|  | 
 | ||||||
|  | thirdparty_obj = [] | ||||||
|  | 
 | ||||||
|  | env_thirdparty = env.Clone() | ||||||
|  | env_thirdparty.disable_warnings() | ||||||
|  | 
 | ||||||
|  | # Misc thirdparty code: header paths are hardcoded, we don't need to append | ||||||
|  | # to the include path (saves a few chars on the compiler invocation for touchy MSVC...) | ||||||
|  | thirdparty_misc_dir = "#thirdparty/misc/" | ||||||
|  | thirdparty_misc_sources = [ | ||||||
|  |     # C sources | ||||||
|  |     "fastlz.c", | ||||||
|  |     "r128.c", | ||||||
|  |     "smaz.c", | ||||||
|  |     # C++ sources | ||||||
|  |     "pcg.cpp", | ||||||
|  |     "polypartition.cpp", | ||||||
|  |     "smolv.cpp", | ||||||
|  | ] | ||||||
|  | thirdparty_misc_sources = [thirdparty_misc_dir + file for file in thirdparty_misc_sources] | ||||||
|  | env_thirdparty.add_source_files(thirdparty_obj, thirdparty_misc_sources) | ||||||
|  | 
 | ||||||
|  | # Brotli | ||||||
|  | if env["brotli"] and env["builtin_brotli"]: | ||||||
|  |     thirdparty_brotli_dir = "#thirdparty/brotli/" | ||||||
|  |     thirdparty_brotli_sources = [ | ||||||
|  |         "common/constants.c", | ||||||
|  |         "common/context.c", | ||||||
|  |         "common/dictionary.c", | ||||||
|  |         "common/platform.c", | ||||||
|  |         "common/shared_dictionary.c", | ||||||
|  |         "common/transform.c", | ||||||
|  |         "dec/bit_reader.c", | ||||||
|  |         "dec/decode.c", | ||||||
|  |         "dec/huffman.c", | ||||||
|  |         "dec/state.c", | ||||||
|  |     ] | ||||||
|  |     thirdparty_brotli_sources = [thirdparty_brotli_dir + file for file in thirdparty_brotli_sources] | ||||||
|  | 
 | ||||||
|  |     env_thirdparty.Prepend(CPPEXTPATH=[thirdparty_brotli_dir + "include"]) | ||||||
|  |     env.Prepend(CPPEXTPATH=[thirdparty_brotli_dir + "include"]) | ||||||
|  | 
 | ||||||
|  |     if env.get("use_ubsan") or env.get("use_asan") or env.get("use_tsan") or env.get("use_lsan") or env.get("use_msan"): | ||||||
|  |         env_thirdparty.Append(CPPDEFINES=["BROTLI_BUILD_PORTABLE"]) | ||||||
|  | 
 | ||||||
|  |     env_thirdparty.add_source_files(thirdparty_obj, thirdparty_brotli_sources) | ||||||
|  | 
 | ||||||
|  | # Clipper2 Thirdparty source files used for polygon and polyline boolean operations. | ||||||
|  | if env["builtin_clipper2"]: | ||||||
|  |     thirdparty_clipper_dir = "#thirdparty/clipper2/" | ||||||
|  |     thirdparty_clipper_sources = [ | ||||||
|  |         "src/clipper.engine.cpp", | ||||||
|  |         "src/clipper.offset.cpp", | ||||||
|  |         "src/clipper.rectclip.cpp", | ||||||
|  |     ] | ||||||
|  |     thirdparty_clipper_sources = [thirdparty_clipper_dir + file for file in thirdparty_clipper_sources] | ||||||
|  | 
 | ||||||
|  |     env_thirdparty.Prepend(CPPEXTPATH=[thirdparty_clipper_dir + "include"]) | ||||||
|  |     env.Prepend(CPPEXTPATH=[thirdparty_clipper_dir + "include"]) | ||||||
|  | 
 | ||||||
|  |     env_thirdparty.Append(CPPDEFINES=["CLIPPER2_ENABLED"]) | ||||||
|  |     env.Append(CPPDEFINES=["CLIPPER2_ENABLED"]) | ||||||
|  | 
 | ||||||
|  |     env_thirdparty.add_source_files(thirdparty_obj, thirdparty_clipper_sources) | ||||||
|  | 
 | ||||||
|  | # Zlib library, can be unbundled | ||||||
|  | if env["builtin_zlib"]: | ||||||
|  |     thirdparty_zlib_dir = "#thirdparty/zlib/" | ||||||
|  |     thirdparty_zlib_sources = [ | ||||||
|  |         "adler32.c", | ||||||
|  |         "compress.c", | ||||||
|  |         "crc32.c", | ||||||
|  |         "deflate.c", | ||||||
|  |         "inffast.c", | ||||||
|  |         "inflate.c", | ||||||
|  |         "inftrees.c", | ||||||
|  |         "trees.c", | ||||||
|  |         "uncompr.c", | ||||||
|  |         "zutil.c", | ||||||
|  |     ] | ||||||
|  |     thirdparty_zlib_sources = [thirdparty_zlib_dir + file for file in thirdparty_zlib_sources] | ||||||
|  | 
 | ||||||
|  |     env_thirdparty.Prepend(CPPEXTPATH=[thirdparty_zlib_dir]) | ||||||
|  |     # Needs to be available in main env too | ||||||
|  |     env.Prepend(CPPEXTPATH=[thirdparty_zlib_dir]) | ||||||
|  |     if env.dev_build: | ||||||
|  |         env_thirdparty.Append(CPPDEFINES=["ZLIB_DEBUG"]) | ||||||
|  |         # Affects headers so it should also be defined for Godot code | ||||||
|  |         env.Append(CPPDEFINES=["ZLIB_DEBUG"]) | ||||||
|  | 
 | ||||||
|  |     env_thirdparty.add_source_files(thirdparty_obj, thirdparty_zlib_sources) | ||||||
|  | 
 | ||||||
|  | # Minizip library, could be unbundled in theory | ||||||
|  | # However, our version has some custom modifications, so it won't compile with the system one | ||||||
|  | thirdparty_minizip_dir = "#thirdparty/minizip/" | ||||||
|  | thirdparty_minizip_sources = ["ioapi.c", "unzip.c", "zip.c"] | ||||||
|  | thirdparty_minizip_sources = [thirdparty_minizip_dir + file for file in thirdparty_minizip_sources] | ||||||
|  | env_thirdparty.add_source_files(thirdparty_obj, thirdparty_minizip_sources) | ||||||
|  | 
 | ||||||
|  | # Zstd library, can be unbundled in theory | ||||||
|  | # though we currently use some private symbols | ||||||
|  | # https://github.com/godotengine/godot/issues/17374 | ||||||
|  | if env["builtin_zstd"]: | ||||||
|  |     thirdparty_zstd_dir = "#thirdparty/zstd/" | ||||||
|  |     thirdparty_zstd_sources = [ | ||||||
|  |         "common/debug.c", | ||||||
|  |         "common/entropy_common.c", | ||||||
|  |         "common/error_private.c", | ||||||
|  |         "common/fse_decompress.c", | ||||||
|  |         "common/pool.c", | ||||||
|  |         "common/threading.c", | ||||||
|  |         "common/xxhash.c", | ||||||
|  |         "common/zstd_common.c", | ||||||
|  |         "compress/fse_compress.c", | ||||||
|  |         "compress/hist.c", | ||||||
|  |         "compress/huf_compress.c", | ||||||
|  |         "compress/zstd_compress.c", | ||||||
|  |         "compress/zstd_double_fast.c", | ||||||
|  |         "compress/zstd_fast.c", | ||||||
|  |         "compress/zstd_lazy.c", | ||||||
|  |         "compress/zstd_ldm.c", | ||||||
|  |         "compress/zstd_opt.c", | ||||||
|  |         "compress/zstdmt_compress.c", | ||||||
|  |         "compress/zstd_compress_literals.c", | ||||||
|  |         "compress/zstd_compress_sequences.c", | ||||||
|  |         "compress/zstd_compress_superblock.c", | ||||||
|  |         "decompress/huf_decompress.c", | ||||||
|  |         "decompress/zstd_ddict.c", | ||||||
|  |         "decompress/zstd_decompress_block.c", | ||||||
|  |         "decompress/zstd_decompress.c", | ||||||
|  |     ] | ||||||
|  |     if env["platform"] in ["android", "ios", "linuxbsd", "macos"] and env["arch"] == "x86_64": | ||||||
|  |         # Match platforms with ZSTD_ASM_SUPPORTED in common/portability_macros.h | ||||||
|  |         thirdparty_zstd_sources.append("decompress/huf_decompress_amd64.S") | ||||||
|  |     thirdparty_zstd_sources = [thirdparty_zstd_dir + file for file in thirdparty_zstd_sources] | ||||||
|  | 
 | ||||||
|  |     env_thirdparty.Prepend(CPPEXTPATH=[thirdparty_zstd_dir, thirdparty_zstd_dir + "common"]) | ||||||
|  |     env_thirdparty.Append(CPPDEFINES=["ZSTD_STATIC_LINKING_ONLY"]) | ||||||
|  |     env.Prepend(CPPEXTPATH=thirdparty_zstd_dir) | ||||||
|  |     # Also needed in main env includes will trigger warnings | ||||||
|  |     env.Append(CPPDEFINES=["ZSTD_STATIC_LINKING_ONLY"]) | ||||||
|  | 
 | ||||||
|  |     env_thirdparty.add_source_files(thirdparty_obj, thirdparty_zstd_sources) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | env.core_sources += thirdparty_obj | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # Godot source files | ||||||
|  | 
 | ||||||
|  | env.add_source_files(env.core_sources, "*.cpp") | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # Generate disabled classes | ||||||
|  | def disabled_class_builder(target, source, env): | ||||||
|  |     with methods.generated_wrapper(str(target[0])) as file: | ||||||
|  |         for c in source[0].read(): | ||||||
|  |             if cs := c.strip(): | ||||||
|  |                 file.write(f"#define ClassDB_Disable_{cs} 1\n") | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | env.CommandNoCache("disabled_classes.gen.h", env.Value(env.disabled_classes), env.Run(disabled_class_builder)) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # Generate version info | ||||||
|  | def version_info_builder(target, source, env): | ||||||
|  |     with methods.generated_wrapper(str(target[0])) as file: | ||||||
|  |         file.write( | ||||||
|  |             """\ | ||||||
|  | #define GODOT_VERSION_SHORT_NAME "{short_name}" | ||||||
|  | #define GODOT_VERSION_NAME "{name}" | ||||||
|  | #define GODOT_VERSION_MAJOR {major} | ||||||
|  | #define GODOT_VERSION_MINOR {minor} | ||||||
|  | #define GODOT_VERSION_PATCH {patch} | ||||||
|  | #define GODOT_VERSION_STATUS "{status}" | ||||||
|  | #define GODOT_VERSION_BUILD "{build}" | ||||||
|  | #define GODOT_VERSION_MODULE_CONFIG "{module_config}" | ||||||
|  | #define GODOT_VERSION_WEBSITE "{website}" | ||||||
|  | #define GODOT_VERSION_DOCS_BRANCH "{docs_branch}" | ||||||
|  | #define GODOT_VERSION_DOCS_URL "https://docs.godotengine.org/en/" GODOT_VERSION_DOCS_BRANCH | ||||||
|  | """.format(**source[0].read()) | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | env.CommandNoCache( | ||||||
|  |     "version_generated.gen.h", | ||||||
|  |     env.Value(methods.get_version_info(env.module_version_string)), | ||||||
|  |     env.Run(version_info_builder), | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # Generate version hash | ||||||
|  | def version_hash_builder(target, source, env): | ||||||
|  |     with methods.generated_wrapper(str(target[0])) as file: | ||||||
|  |         file.write( | ||||||
|  |             """\ | ||||||
|  | #include "core/version.h" | ||||||
|  | 
 | ||||||
|  | const char *const GODOT_VERSION_HASH = "{git_hash}"; | ||||||
|  | const uint64_t GODOT_VERSION_TIMESTAMP = {git_timestamp}; | ||||||
|  | """.format(**source[0].read()) | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | gen_hash = env.CommandNoCache("version_hash.gen.cpp", env.Value(methods.get_git_info()), env.Run(version_hash_builder)) | ||||||
|  | env.add_source_files(env.core_sources, gen_hash) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # Generate AES256 script encryption key | ||||||
|  | def encryption_key_builder(target, source, env): | ||||||
|  |     with methods.generated_wrapper(str(target[0])) as file: | ||||||
|  |         file.write( | ||||||
|  |             f"""\ | ||||||
|  | #include "core/config/project_settings.h" | ||||||
|  | 
 | ||||||
|  | uint8_t script_encryption_key[32] = {{ | ||||||
|  | 	{source[0]} | ||||||
|  | }};""" | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | gdkey = os.environ.get("SCRIPT_AES256_ENCRYPTION_KEY", "0" * 64) | ||||||
|  | ec_valid = len(gdkey) == 64 | ||||||
|  | if ec_valid: | ||||||
|  |     try: | ||||||
|  |         gdkey = ", ".join([str(int(f"{a}{b}", 16)) for a, b in zip(gdkey[0::2], gdkey[1::2])]) | ||||||
|  |     except Exception: | ||||||
|  |         ec_valid = False | ||||||
|  | if not ec_valid: | ||||||
|  |     methods.print_error( | ||||||
|  |         f'Invalid AES256 encryption key, not 64 hexadecimal characters: "{gdkey}".\n' | ||||||
|  |         "Unset `SCRIPT_AES256_ENCRYPTION_KEY` in your environment " | ||||||
|  |         "or make sure that it contains exactly 64 hexadecimal characters." | ||||||
|  |     ) | ||||||
|  |     Exit(255) | ||||||
|  | gen_encrypt = env.CommandNoCache("script_encryption_key.gen.cpp", env.Value(gdkey), env.Run(encryption_key_builder)) | ||||||
|  | env.add_source_files(env.core_sources, gen_encrypt) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # Certificates | ||||||
|  | env.CommandNoCache( | ||||||
|  |     "#core/io/certs_compressed.gen.h", | ||||||
|  |     ["#thirdparty/certs/ca-certificates.crt", env.Value(env["builtin_certs"]), env.Value(env["system_certs_path"])], | ||||||
|  |     env.Run(core_builders.make_certs_header), | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | # Authors | ||||||
|  | env.CommandNoCache("#core/authors.gen.h", "#AUTHORS.md", env.Run(core_builders.make_authors_header)) | ||||||
|  | 
 | ||||||
|  | # Donors | ||||||
|  | env.CommandNoCache("#core/donors.gen.h", "#DONORS.md", env.Run(core_builders.make_donors_header)) | ||||||
|  | 
 | ||||||
|  | # License | ||||||
|  | env.CommandNoCache( | ||||||
|  |     "#core/license.gen.h", ["#COPYRIGHT.txt", "#LICENSE.txt"], env.Run(core_builders.make_license_header) | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | # Chain load SCsubs | ||||||
|  | SConscript("os/SCsub") | ||||||
|  | SConscript("math/SCsub") | ||||||
|  | SConscript("crypto/SCsub") | ||||||
|  | SConscript("io/SCsub") | ||||||
|  | SConscript("debugger/SCsub") | ||||||
|  | SConscript("input/SCsub") | ||||||
|  | SConscript("variant/SCsub") | ||||||
|  | SConscript("extension/SCsub") | ||||||
|  | SConscript("object/SCsub") | ||||||
|  | SConscript("templates/SCsub") | ||||||
|  | SConscript("string/SCsub") | ||||||
|  | SConscript("config/SCsub") | ||||||
|  | SConscript("error/SCsub") | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # Build it all as a library | ||||||
|  | lib = env.add_library("core", env.core_sources) | ||||||
|  | env.Prepend(LIBS=[lib]) | ||||||
|  | 
 | ||||||
|  | # Needed to force rebuilding the core files when the thirdparty code is updated. | ||||||
|  | env.Depends(lib, thirdparty_obj) | ||||||
							
								
								
									
										8
									
								
								engine/core/config/SCsub
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								engine/core/config/SCsub
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,8 @@ | ||||||
|  | #!/usr/bin/env python | ||||||
|  | from misc.utility.scons_hints import * | ||||||
|  | 
 | ||||||
|  | Import("env") | ||||||
|  | 
 | ||||||
|  | env_config = env.Clone() | ||||||
|  | 
 | ||||||
|  | env_config.add_source_files(env.core_sources, "*.cpp") | ||||||
							
								
								
									
										446
									
								
								engine/core/config/engine.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										446
									
								
								engine/core/config/engine.cpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,446 @@ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*  engine.cpp                                                            */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*                         This file is part of:                          */ | ||||||
|  | /*                             GODOT ENGINE                               */ | ||||||
|  | /*                        https://godotengine.org                         */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ | ||||||
|  | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */ | ||||||
|  | /*                                                                        */ | ||||||
|  | /* 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 "engine.h" | ||||||
|  | 
 | ||||||
|  | #include "core/authors.gen.h" | ||||||
|  | #include "core/config/project_settings.h" | ||||||
|  | #include "core/donors.gen.h" | ||||||
|  | #include "core/license.gen.h" | ||||||
|  | #include "core/variant/typed_array.h" | ||||||
|  | #include "core/version.h" | ||||||
|  | #include "servers/rendering/rendering_device.h" | ||||||
|  | 
 | ||||||
|  | void Engine::set_physics_ticks_per_second(int p_ips) { | ||||||
|  | 	ERR_FAIL_COND_MSG(p_ips <= 0, "Engine iterations per second must be greater than 0."); | ||||||
|  | 	ips = p_ips; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int Engine::get_physics_ticks_per_second() const { | ||||||
|  | 	return ips; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Engine::set_max_physics_steps_per_frame(int p_max_physics_steps) { | ||||||
|  | 	ERR_FAIL_COND_MSG(p_max_physics_steps <= 0, "Maximum number of physics steps per frame must be greater than 0."); | ||||||
|  | 	max_physics_steps_per_frame = p_max_physics_steps; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int Engine::get_max_physics_steps_per_frame() const { | ||||||
|  | 	return max_physics_steps_per_frame; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Engine::set_physics_jitter_fix(double p_threshold) { | ||||||
|  | 	if (p_threshold < 0) { | ||||||
|  | 		p_threshold = 0; | ||||||
|  | 	} | ||||||
|  | 	physics_jitter_fix = p_threshold; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | double Engine::get_physics_jitter_fix() const { | ||||||
|  | 	return physics_jitter_fix; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Engine::set_max_fps(int p_fps) { | ||||||
|  | 	_max_fps = p_fps > 0 ? p_fps : 0; | ||||||
|  | 
 | ||||||
|  | 	RenderingDevice *rd = RenderingDevice::get_singleton(); | ||||||
|  | 	if (rd) { | ||||||
|  | 		rd->_set_max_fps(_max_fps); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int Engine::get_max_fps() const { | ||||||
|  | 	return _max_fps; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Engine::set_audio_output_latency(int p_msec) { | ||||||
|  | 	_audio_output_latency = p_msec > 1 ? p_msec : 1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int Engine::get_audio_output_latency() const { | ||||||
|  | 	return _audio_output_latency; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Engine::increment_frames_drawn() { | ||||||
|  | 	if (frame_server_synced) { | ||||||
|  | 		server_syncs++; | ||||||
|  | 	} else { | ||||||
|  | 		server_syncs = 0; | ||||||
|  | 	} | ||||||
|  | 	frame_server_synced = false; | ||||||
|  | 
 | ||||||
|  | 	frames_drawn++; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint64_t Engine::get_frames_drawn() { | ||||||
|  | 	return frames_drawn; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Engine::set_frame_delay(uint32_t p_msec) { | ||||||
|  | 	_frame_delay = p_msec; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint32_t Engine::get_frame_delay() const { | ||||||
|  | 	return _frame_delay; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Engine::set_time_scale(double p_scale) { | ||||||
|  | 	_time_scale = p_scale; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | double Engine::get_time_scale() const { | ||||||
|  | 	return freeze_time_scale ? 0 : _time_scale; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | double Engine::get_unfrozen_time_scale() const { | ||||||
|  | 	return _time_scale; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Dictionary Engine::get_version_info() const { | ||||||
|  | 	Dictionary dict; | ||||||
|  | 	dict["major"] = GODOT_VERSION_MAJOR; | ||||||
|  | 	dict["minor"] = GODOT_VERSION_MINOR; | ||||||
|  | 	dict["patch"] = GODOT_VERSION_PATCH; | ||||||
|  | 	dict["hex"] = GODOT_VERSION_HEX; | ||||||
|  | 	dict["status"] = GODOT_VERSION_STATUS; | ||||||
|  | 	dict["build"] = GODOT_VERSION_BUILD; | ||||||
|  | 
 | ||||||
|  | 	String hash = String(GODOT_VERSION_HASH); | ||||||
|  | 	dict["hash"] = hash.is_empty() ? String("unknown") : hash; | ||||||
|  | 
 | ||||||
|  | 	dict["timestamp"] = GODOT_VERSION_TIMESTAMP; | ||||||
|  | 
 | ||||||
|  | 	String stringver = String(dict["major"]) + "." + String(dict["minor"]); | ||||||
|  | 	if ((int)dict["patch"] != 0) { | ||||||
|  | 		stringver += "." + String(dict["patch"]); | ||||||
|  | 	} | ||||||
|  | 	stringver += "-" + String(dict["status"]) + " (" + String(dict["build"]) + ")"; | ||||||
|  | 	dict["string"] = stringver; | ||||||
|  | 
 | ||||||
|  | 	return dict; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static Array array_from_info(const char *const *info_list) { | ||||||
|  | 	Array arr; | ||||||
|  | 	for (int i = 0; info_list[i] != nullptr; i++) { | ||||||
|  | 		arr.push_back(String::utf8(info_list[i])); | ||||||
|  | 	} | ||||||
|  | 	return arr; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static Array array_from_info_count(const char *const *info_list, int info_count) { | ||||||
|  | 	Array arr; | ||||||
|  | 	for (int i = 0; i < info_count; i++) { | ||||||
|  | 		arr.push_back(String::utf8(info_list[i])); | ||||||
|  | 	} | ||||||
|  | 	return arr; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Dictionary Engine::get_author_info() const { | ||||||
|  | 	Dictionary dict; | ||||||
|  | 
 | ||||||
|  | 	dict["lead_developers"] = array_from_info(AUTHORS_LEAD_DEVELOPERS); | ||||||
|  | 	dict["project_managers"] = array_from_info(AUTHORS_PROJECT_MANAGERS); | ||||||
|  | 	dict["founders"] = array_from_info(AUTHORS_FOUNDERS); | ||||||
|  | 	dict["developers"] = array_from_info(AUTHORS_DEVELOPERS); | ||||||
|  | 
 | ||||||
|  | 	return dict; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TypedArray<Dictionary> Engine::get_copyright_info() const { | ||||||
|  | 	TypedArray<Dictionary> components; | ||||||
|  | 	for (int component_index = 0; component_index < COPYRIGHT_INFO_COUNT; component_index++) { | ||||||
|  | 		const ComponentCopyright &cp_info = COPYRIGHT_INFO[component_index]; | ||||||
|  | 		Dictionary component_dict; | ||||||
|  | 		component_dict["name"] = String::utf8(cp_info.name); | ||||||
|  | 		Array parts; | ||||||
|  | 		for (int i = 0; i < cp_info.part_count; i++) { | ||||||
|  | 			const ComponentCopyrightPart &cp_part = cp_info.parts[i]; | ||||||
|  | 			Dictionary part_dict; | ||||||
|  | 			part_dict["files"] = array_from_info_count(cp_part.files, cp_part.file_count); | ||||||
|  | 			part_dict["copyright"] = array_from_info_count(cp_part.copyright_statements, cp_part.copyright_count); | ||||||
|  | 			part_dict["license"] = String::utf8(cp_part.license); | ||||||
|  | 			parts.push_back(part_dict); | ||||||
|  | 		} | ||||||
|  | 		component_dict["parts"] = parts; | ||||||
|  | 
 | ||||||
|  | 		components.push_back(component_dict); | ||||||
|  | 	} | ||||||
|  | 	return components; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Dictionary Engine::get_donor_info() const { | ||||||
|  | 	Dictionary donors; | ||||||
|  | 	donors["patrons"] = array_from_info(DONORS_PATRONS); | ||||||
|  | 	donors["platinum_sponsors"] = array_from_info(DONORS_SPONSORS_PLATINUM); | ||||||
|  | 	donors["gold_sponsors"] = array_from_info(DONORS_SPONSORS_GOLD); | ||||||
|  | 	donors["silver_sponsors"] = array_from_info(DONORS_SPONSORS_SILVER); | ||||||
|  | 	donors["diamond_members"] = array_from_info(DONORS_MEMBERS_DIAMOND); | ||||||
|  | 	donors["titanium_members"] = array_from_info(DONORS_MEMBERS_TITANIUM); | ||||||
|  | 	donors["platinum_members"] = array_from_info(DONORS_MEMBERS_PLATINUM); | ||||||
|  | 	donors["gold_members"] = array_from_info(DONORS_MEMBERS_GOLD); | ||||||
|  | 	return donors; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Dictionary Engine::get_license_info() const { | ||||||
|  | 	Dictionary licenses; | ||||||
|  | 	for (int i = 0; i < LICENSE_COUNT; i++) { | ||||||
|  | 		licenses[LICENSE_NAMES[i]] = LICENSE_BODIES[i]; | ||||||
|  | 	} | ||||||
|  | 	return licenses; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | String Engine::get_license_text() const { | ||||||
|  | 	return String(GODOT_LICENSE_TEXT); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | String Engine::get_architecture_name() const { | ||||||
|  | #if defined(__x86_64) || defined(__x86_64__) || defined(__amd64__) || defined(_M_X64) | ||||||
|  | 	return "x86_64"; | ||||||
|  | 
 | ||||||
|  | #elif defined(__i386) || defined(__i386__) || defined(_M_IX86) | ||||||
|  | 	return "x86_32"; | ||||||
|  | 
 | ||||||
|  | #elif defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC) | ||||||
|  | 	return "arm64"; | ||||||
|  | 
 | ||||||
|  | #elif defined(__arm__) || defined(_M_ARM) | ||||||
|  | 	return "arm32"; | ||||||
|  | 
 | ||||||
|  | #elif defined(__riscv) | ||||||
|  | #if __riscv_xlen == 8 | ||||||
|  | 	return "rv64"; | ||||||
|  | #else | ||||||
|  | 	return "riscv"; | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #elif defined(__powerpc__) | ||||||
|  | #if defined(__powerpc64__) | ||||||
|  | 	return "ppc64"; | ||||||
|  | #else | ||||||
|  | 	return "ppc"; | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #elif defined(__loongarch64) | ||||||
|  | 	return "loongarch64"; | ||||||
|  | 
 | ||||||
|  | #elif defined(__wasm__) | ||||||
|  | #if defined(__wasm64__) | ||||||
|  | 	return "wasm64"; | ||||||
|  | #elif defined(__wasm32__) | ||||||
|  | 	return "wasm32"; | ||||||
|  | #endif | ||||||
|  | #endif | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool Engine::is_abort_on_gpu_errors_enabled() const { | ||||||
|  | 	return abort_on_gpu_errors; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int32_t Engine::get_gpu_index() const { | ||||||
|  | 	return gpu_idx; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool Engine::is_validation_layers_enabled() const { | ||||||
|  | 	return use_validation_layers; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool Engine::is_generate_spirv_debug_info_enabled() const { | ||||||
|  | 	return generate_spirv_debug_info; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool Engine::is_extra_gpu_memory_tracking_enabled() const { | ||||||
|  | 	return extra_gpu_memory_tracking; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #if defined(DEBUG_ENABLED) || defined(DEV_ENABLED) | ||||||
|  | bool Engine::is_accurate_breadcrumbs_enabled() const { | ||||||
|  | 	return accurate_breadcrumbs; | ||||||
|  | } | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | void Engine::set_print_to_stdout(bool p_enabled) { | ||||||
|  | 	CoreGlobals::print_line_enabled = p_enabled; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool Engine::is_printing_to_stdout() const { | ||||||
|  | 	return CoreGlobals::print_line_enabled; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Engine::set_print_error_messages(bool p_enabled) { | ||||||
|  | 	CoreGlobals::print_error_enabled = p_enabled; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool Engine::is_printing_error_messages() const { | ||||||
|  | 	return CoreGlobals::print_error_enabled; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Engine::print_header(const String &p_string) const { | ||||||
|  | 	if (_print_header) { | ||||||
|  | 		print_line(p_string); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Engine::print_header_rich(const String &p_string) const { | ||||||
|  | 	if (_print_header) { | ||||||
|  | 		print_line_rich(p_string); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Engine::add_singleton(const Singleton &p_singleton) { | ||||||
|  | 	ERR_FAIL_COND_MSG(singleton_ptrs.has(p_singleton.name), vformat("Can't register singleton '%s' because it already exists.", p_singleton.name)); | ||||||
|  | 	singletons.push_back(p_singleton); | ||||||
|  | 	singleton_ptrs[p_singleton.name] = p_singleton.ptr; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Object *Engine::get_singleton_object(const StringName &p_name) const { | ||||||
|  | 	HashMap<StringName, Object *>::ConstIterator E = singleton_ptrs.find(p_name); | ||||||
|  | 	ERR_FAIL_COND_V_MSG(!E, nullptr, vformat("Failed to retrieve non-existent singleton '%s'.", p_name)); | ||||||
|  | 
 | ||||||
|  | #ifdef TOOLS_ENABLED | ||||||
|  | 	if (!is_editor_hint() && is_singleton_editor_only(p_name)) { | ||||||
|  | 		ERR_FAIL_V_MSG(nullptr, vformat("Can't retrieve singleton '%s' outside of editor.", p_name)); | ||||||
|  | 	} | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 	return E->value; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool Engine::is_singleton_user_created(const StringName &p_name) const { | ||||||
|  | 	ERR_FAIL_COND_V(!singleton_ptrs.has(p_name), false); | ||||||
|  | 
 | ||||||
|  | 	for (const Singleton &E : singletons) { | ||||||
|  | 		if (E.name == p_name && E.user_created) { | ||||||
|  | 			return true; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool Engine::is_singleton_editor_only(const StringName &p_name) const { | ||||||
|  | 	ERR_FAIL_COND_V(!singleton_ptrs.has(p_name), false); | ||||||
|  | 
 | ||||||
|  | 	for (const Singleton &E : singletons) { | ||||||
|  | 		if (E.name == p_name && E.editor_only) { | ||||||
|  | 			return true; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Engine::remove_singleton(const StringName &p_name) { | ||||||
|  | 	ERR_FAIL_COND(!singleton_ptrs.has(p_name)); | ||||||
|  | 
 | ||||||
|  | 	for (List<Singleton>::Element *E = singletons.front(); E; E = E->next()) { | ||||||
|  | 		if (E->get().name == p_name) { | ||||||
|  | 			singletons.erase(E); | ||||||
|  | 			singleton_ptrs.erase(p_name); | ||||||
|  | 			return; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool Engine::has_singleton(const StringName &p_name) const { | ||||||
|  | 	return singleton_ptrs.has(p_name); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Engine::get_singletons(List<Singleton> *p_singletons) { | ||||||
|  | 	for (const Singleton &E : singletons) { | ||||||
|  | #ifdef TOOLS_ENABLED | ||||||
|  | 		if (!is_editor_hint() && E.editor_only) { | ||||||
|  | 			continue; | ||||||
|  | 		} | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 		p_singletons->push_back(E); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | String Engine::get_write_movie_path() const { | ||||||
|  | 	return write_movie_path; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Engine::set_write_movie_path(const String &p_path) { | ||||||
|  | 	write_movie_path = p_path; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Engine::set_shader_cache_path(const String &p_path) { | ||||||
|  | 	shader_cache_path = p_path; | ||||||
|  | } | ||||||
|  | String Engine::get_shader_cache_path() const { | ||||||
|  | 	return shader_cache_path; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Engine *Engine::singleton = nullptr; | ||||||
|  | 
 | ||||||
|  | Engine *Engine::get_singleton() { | ||||||
|  | 	return singleton; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool Engine::notify_frame_server_synced() { | ||||||
|  | 	frame_server_synced = true; | ||||||
|  | 	return server_syncs > SERVER_SYNC_FRAME_COUNT_WARNING; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Engine::set_freeze_time_scale(bool p_frozen) { | ||||||
|  | 	freeze_time_scale = p_frozen; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Engine::set_embedded_in_editor(bool p_enabled) { | ||||||
|  | 	embedded_in_editor = p_enabled; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool Engine::is_embedded_in_editor() const { | ||||||
|  | 	return embedded_in_editor; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Engine::Engine() { | ||||||
|  | 	singleton = this; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Engine::~Engine() { | ||||||
|  | 	if (singleton == this) { | ||||||
|  | 		singleton = nullptr; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Engine::Singleton::Singleton(const StringName &p_name, Object *p_ptr, const StringName &p_class_name) : | ||||||
|  | 		name(p_name), | ||||||
|  | 		ptr(p_ptr), | ||||||
|  | 		class_name(p_class_name) { | ||||||
|  | #ifdef DEBUG_ENABLED | ||||||
|  | 	RefCounted *rc = Object::cast_to<RefCounted>(p_ptr); | ||||||
|  | 	if (rc && !rc->is_referenced()) { | ||||||
|  | 		WARN_PRINT("You must use Ref<> to ensure the lifetime of a RefCounted object intended to be used as a singleton."); | ||||||
|  | 	} | ||||||
|  | #endif | ||||||
|  | } | ||||||
							
								
								
									
										215
									
								
								engine/core/config/engine.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										215
									
								
								engine/core/config/engine.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,215 @@ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*  engine.h                                                              */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*                         This file is part of:                          */ | ||||||
|  | /*                             GODOT ENGINE                               */ | ||||||
|  | /*                        https://godotengine.org                         */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ | ||||||
|  | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */ | ||||||
|  | /*                                                                        */ | ||||||
|  | /* 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.                 */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include "core/os/main_loop.h" | ||||||
|  | #include "core/string/ustring.h" | ||||||
|  | #include "core/templates/list.h" | ||||||
|  | 
 | ||||||
|  | template <typename T> | ||||||
|  | class TypedArray; | ||||||
|  | 
 | ||||||
|  | class Engine { | ||||||
|  | public: | ||||||
|  | 	struct Singleton { | ||||||
|  | 		StringName name; | ||||||
|  | 		Object *ptr = nullptr; | ||||||
|  | 		StringName class_name; // Used for binding generation hinting.
 | ||||||
|  | 		// Singleton scope flags.
 | ||||||
|  | 		bool user_created = false; | ||||||
|  | 		bool editor_only = false; | ||||||
|  | 
 | ||||||
|  | 		Singleton(const StringName &p_name = StringName(), Object *p_ptr = nullptr, const StringName &p_class_name = StringName()); | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  | 	friend class Main; | ||||||
|  | 
 | ||||||
|  | 	uint64_t frames_drawn = 0; | ||||||
|  | 	uint32_t _frame_delay = 0; | ||||||
|  | 	uint64_t _frame_ticks = 0; | ||||||
|  | 	double _process_step = 0; | ||||||
|  | 
 | ||||||
|  | 	int ips = 60; | ||||||
|  | 	double physics_jitter_fix = 0.5; | ||||||
|  | 	double _fps = 1; | ||||||
|  | 	int _max_fps = 0; | ||||||
|  | 	int _audio_output_latency = 0; | ||||||
|  | 	double _time_scale = 1.0; | ||||||
|  | 	uint64_t _physics_frames = 0; | ||||||
|  | 	int max_physics_steps_per_frame = 8; | ||||||
|  | 	double _physics_interpolation_fraction = 0.0f; | ||||||
|  | 	bool abort_on_gpu_errors = false; | ||||||
|  | 	bool use_validation_layers = false; | ||||||
|  | 	bool generate_spirv_debug_info = false; | ||||||
|  | 	bool extra_gpu_memory_tracking = false; | ||||||
|  | #if defined(DEBUG_ENABLED) || defined(DEV_ENABLED) | ||||||
|  | 	bool accurate_breadcrumbs = false; | ||||||
|  | #endif | ||||||
|  | 	int32_t gpu_idx = -1; | ||||||
|  | 
 | ||||||
|  | 	uint64_t _process_frames = 0; | ||||||
|  | 	bool _in_physics = false; | ||||||
|  | 
 | ||||||
|  | 	List<Singleton> singletons; | ||||||
|  | 	HashMap<StringName, Object *> singleton_ptrs; | ||||||
|  | 
 | ||||||
|  | 	bool editor_hint = false; | ||||||
|  | 	bool project_manager_hint = false; | ||||||
|  | 	bool extension_reloading = false; | ||||||
|  | 	bool embedded_in_editor = false; | ||||||
|  | 	bool recovery_mode_hint = false; | ||||||
|  | 
 | ||||||
|  | 	bool _print_header = true; | ||||||
|  | 
 | ||||||
|  | 	static Engine *singleton; | ||||||
|  | 
 | ||||||
|  | 	String write_movie_path; | ||||||
|  | 	String shader_cache_path; | ||||||
|  | 
 | ||||||
|  | 	static constexpr int SERVER_SYNC_FRAME_COUNT_WARNING = 5; | ||||||
|  | 	int server_syncs = 0; | ||||||
|  | 	bool frame_server_synced = false; | ||||||
|  | 
 | ||||||
|  | 	bool freeze_time_scale = false; | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  | 	static Engine *get_singleton(); | ||||||
|  | 
 | ||||||
|  | 	virtual void set_physics_ticks_per_second(int p_ips); | ||||||
|  | 	virtual int get_physics_ticks_per_second() const; | ||||||
|  | 
 | ||||||
|  | 	virtual void set_max_physics_steps_per_frame(int p_max_physics_steps); | ||||||
|  | 	virtual int get_max_physics_steps_per_frame() const; | ||||||
|  | 
 | ||||||
|  | 	void set_physics_jitter_fix(double p_threshold); | ||||||
|  | 	double get_physics_jitter_fix() const; | ||||||
|  | 
 | ||||||
|  | 	virtual void set_max_fps(int p_fps); | ||||||
|  | 	virtual int get_max_fps() const; | ||||||
|  | 
 | ||||||
|  | 	virtual void set_audio_output_latency(int p_msec); | ||||||
|  | 	virtual int get_audio_output_latency() const; | ||||||
|  | 
 | ||||||
|  | 	virtual double get_frames_per_second() const { return _fps; } | ||||||
|  | 
 | ||||||
|  | 	uint64_t get_frames_drawn(); | ||||||
|  | 
 | ||||||
|  | 	uint64_t get_physics_frames() const { return _physics_frames; } | ||||||
|  | 	uint64_t get_process_frames() const { return _process_frames; } | ||||||
|  | 	bool is_in_physics_frame() const { return _in_physics; } | ||||||
|  | 	uint64_t get_frame_ticks() const { return _frame_ticks; } | ||||||
|  | 	double get_process_step() const { return _process_step; } | ||||||
|  | 	double get_physics_interpolation_fraction() const { return _physics_interpolation_fraction; } | ||||||
|  | 
 | ||||||
|  | 	void set_time_scale(double p_scale); | ||||||
|  | 	double get_time_scale() const; | ||||||
|  | 	double get_unfrozen_time_scale() const; | ||||||
|  | 
 | ||||||
|  | 	void set_print_to_stdout(bool p_enabled); | ||||||
|  | 	bool is_printing_to_stdout() const; | ||||||
|  | 
 | ||||||
|  | 	void set_print_error_messages(bool p_enabled); | ||||||
|  | 	bool is_printing_error_messages() const; | ||||||
|  | 	void print_header(const String &p_string) const; | ||||||
|  | 	void print_header_rich(const String &p_string) const; | ||||||
|  | 
 | ||||||
|  | 	void set_frame_delay(uint32_t p_msec); | ||||||
|  | 	uint32_t get_frame_delay() const; | ||||||
|  | 
 | ||||||
|  | 	void add_singleton(const Singleton &p_singleton); | ||||||
|  | 	void get_singletons(List<Singleton> *p_singletons); | ||||||
|  | 	bool has_singleton(const StringName &p_name) const; | ||||||
|  | 	Object *get_singleton_object(const StringName &p_name) const; | ||||||
|  | 	void remove_singleton(const StringName &p_name); | ||||||
|  | 	bool is_singleton_user_created(const StringName &p_name) const; | ||||||
|  | 	bool is_singleton_editor_only(const StringName &p_name) const; | ||||||
|  | 
 | ||||||
|  | #ifdef TOOLS_ENABLED | ||||||
|  | 	_FORCE_INLINE_ void set_editor_hint(bool p_enabled) { editor_hint = p_enabled; } | ||||||
|  | 	_FORCE_INLINE_ bool is_editor_hint() const { return editor_hint; } | ||||||
|  | 
 | ||||||
|  | 	_FORCE_INLINE_ void set_project_manager_hint(bool p_enabled) { project_manager_hint = p_enabled; } | ||||||
|  | 	_FORCE_INLINE_ bool is_project_manager_hint() const { return project_manager_hint; } | ||||||
|  | 
 | ||||||
|  | 	_FORCE_INLINE_ void set_extension_reloading_enabled(bool p_enabled) { extension_reloading = p_enabled; } | ||||||
|  | 	_FORCE_INLINE_ bool is_extension_reloading_enabled() const { return extension_reloading; } | ||||||
|  | 
 | ||||||
|  | 	_FORCE_INLINE_ void set_recovery_mode_hint(bool p_enabled) { recovery_mode_hint = p_enabled; } | ||||||
|  | 	_FORCE_INLINE_ bool is_recovery_mode_hint() const { return recovery_mode_hint; } | ||||||
|  | #else | ||||||
|  | 	_FORCE_INLINE_ void set_editor_hint(bool p_enabled) {} | ||||||
|  | 	_FORCE_INLINE_ bool is_editor_hint() const { return false; } | ||||||
|  | 
 | ||||||
|  | 	_FORCE_INLINE_ void set_project_manager_hint(bool p_enabled) {} | ||||||
|  | 	_FORCE_INLINE_ bool is_project_manager_hint() const { return false; } | ||||||
|  | 
 | ||||||
|  | 	_FORCE_INLINE_ void set_extension_reloading_enabled(bool p_enabled) {} | ||||||
|  | 	_FORCE_INLINE_ bool is_extension_reloading_enabled() const { return false; } | ||||||
|  | 
 | ||||||
|  | 	_FORCE_INLINE_ void set_recovery_mode_hint(bool p_enabled) {} | ||||||
|  | 	_FORCE_INLINE_ bool is_recovery_mode_hint() const { return false; } | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 	Dictionary get_version_info() const; | ||||||
|  | 	Dictionary get_author_info() const; | ||||||
|  | 	TypedArray<Dictionary> get_copyright_info() const; | ||||||
|  | 	Dictionary get_donor_info() const; | ||||||
|  | 	Dictionary get_license_info() const; | ||||||
|  | 	String get_license_text() const; | ||||||
|  | 
 | ||||||
|  | 	void set_write_movie_path(const String &p_path); | ||||||
|  | 	String get_write_movie_path() const; | ||||||
|  | 
 | ||||||
|  | 	String get_architecture_name() const; | ||||||
|  | 
 | ||||||
|  | 	void set_shader_cache_path(const String &p_path); | ||||||
|  | 	String get_shader_cache_path() const; | ||||||
|  | 
 | ||||||
|  | 	bool is_abort_on_gpu_errors_enabled() const; | ||||||
|  | 	bool is_validation_layers_enabled() const; | ||||||
|  | 	bool is_generate_spirv_debug_info_enabled() const; | ||||||
|  | 	bool is_extra_gpu_memory_tracking_enabled() const; | ||||||
|  | #if defined(DEBUG_ENABLED) || defined(DEV_ENABLED) | ||||||
|  | 	bool is_accurate_breadcrumbs_enabled() const; | ||||||
|  | #endif | ||||||
|  | 	int32_t get_gpu_index() const; | ||||||
|  | 
 | ||||||
|  | 	void increment_frames_drawn(); | ||||||
|  | 	bool notify_frame_server_synced(); | ||||||
|  | 
 | ||||||
|  | 	void set_freeze_time_scale(bool p_frozen); | ||||||
|  | 	void set_embedded_in_editor(bool p_enabled); | ||||||
|  | 	bool is_embedded_in_editor() const; | ||||||
|  | 
 | ||||||
|  | 	Engine(); | ||||||
|  | 	virtual ~Engine(); | ||||||
|  | }; | ||||||
							
								
								
									
										1690
									
								
								engine/core/config/project_settings.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1690
									
								
								engine/core/config/project_settings.cpp
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										245
									
								
								engine/core/config/project_settings.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										245
									
								
								engine/core/config/project_settings.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,245 @@ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*  project_settings.h                                                    */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*                         This file is part of:                          */ | ||||||
|  | /*                             GODOT ENGINE                               */ | ||||||
|  | /*                        https://godotengine.org                         */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ | ||||||
|  | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */ | ||||||
|  | /*                                                                        */ | ||||||
|  | /* 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.                 */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include "core/object/class_db.h" | ||||||
|  | 
 | ||||||
|  | template <typename T> | ||||||
|  | class TypedArray; | ||||||
|  | 
 | ||||||
|  | class ProjectSettings : public Object { | ||||||
|  | 	GDCLASS(ProjectSettings, Object); | ||||||
|  | 	_THREAD_SAFE_CLASS_ | ||||||
|  | 	friend class TestProjectSettingsInternalsAccessor; | ||||||
|  | 
 | ||||||
|  | 	bool is_changed = false; | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  | 	typedef HashMap<String, Variant> CustomMap; | ||||||
|  | 	static const String PROJECT_DATA_DIR_NAME_SUFFIX; | ||||||
|  | 
 | ||||||
|  | 	// Properties that are not for built in values begin from this value, so builtin ones are displayed first.
 | ||||||
|  | 	constexpr static const int32_t NO_BUILTIN_ORDER_BASE = 1 << 16; | ||||||
|  | 
 | ||||||
|  | #ifdef TOOLS_ENABLED | ||||||
|  | 	const static PackedStringArray get_required_features(); | ||||||
|  | 	const static PackedStringArray get_unsupported_features(const PackedStringArray &p_project_features); | ||||||
|  | #endif // TOOLS_ENABLED
 | ||||||
|  | 
 | ||||||
|  | 	struct AutoloadInfo { | ||||||
|  | 		StringName name; | ||||||
|  | 		String path; | ||||||
|  | 		bool is_singleton = false; | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | protected: | ||||||
|  | 	struct VariantContainer { | ||||||
|  | 		int order = 0; | ||||||
|  | 		bool persist = false; | ||||||
|  | 		bool basic = false; | ||||||
|  | 		bool internal = false; | ||||||
|  | 		Variant variant; | ||||||
|  | 		Variant initial; | ||||||
|  | 		bool hide_from_editor = false; | ||||||
|  | 		bool restart_if_changed = false; | ||||||
|  | #ifdef DEBUG_METHODS_ENABLED | ||||||
|  | 		bool ignore_value_in_docs = false; | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 		VariantContainer() {} | ||||||
|  | 
 | ||||||
|  | 		VariantContainer(const Variant &p_variant, int p_order, bool p_persist = false) : | ||||||
|  | 				order(p_order), | ||||||
|  | 				persist(p_persist), | ||||||
|  | 				variant(p_variant) { | ||||||
|  | 		} | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	int last_order = NO_BUILTIN_ORDER_BASE; | ||||||
|  | 	int last_builtin_order = 0; | ||||||
|  | 	uint64_t last_save_time = 0; | ||||||
|  | 
 | ||||||
|  | 	RBMap<StringName, VariantContainer> props; // NOTE: Key order is used e.g. in the save_custom method.
 | ||||||
|  | 	String resource_path; | ||||||
|  | 	HashMap<StringName, PropertyInfo> custom_prop_info; | ||||||
|  | 	bool using_datapack = false; | ||||||
|  | 	bool project_loaded = false; | ||||||
|  | 	List<String> input_presets; | ||||||
|  | 
 | ||||||
|  | 	HashSet<String> custom_features; | ||||||
|  | 	HashMap<StringName, LocalVector<Pair<StringName, StringName>>> feature_overrides; | ||||||
|  | 
 | ||||||
|  | 	LocalVector<String> hidden_prefixes; | ||||||
|  | 	HashMap<StringName, AutoloadInfo> autoloads; | ||||||
|  | 	HashMap<StringName, String> global_groups; | ||||||
|  | 	HashMap<StringName, HashSet<StringName>> scene_groups_cache; | ||||||
|  | 
 | ||||||
|  | 	Array global_class_list; | ||||||
|  | 	bool is_global_class_list_loaded = false; | ||||||
|  | 
 | ||||||
|  | 	String project_data_dir_name; | ||||||
|  | 
 | ||||||
|  | 	bool _set(const StringName &p_name, const Variant &p_value); | ||||||
|  | 	bool _get(const StringName &p_name, Variant &r_ret) const; | ||||||
|  | 	void _get_property_list(List<PropertyInfo> *p_list) const; | ||||||
|  | 	bool _property_can_revert(const StringName &p_name) const; | ||||||
|  | 	bool _property_get_revert(const StringName &p_name, Variant &r_property) const; | ||||||
|  | 
 | ||||||
|  | 	void _queue_changed(); | ||||||
|  | 	void _emit_changed(); | ||||||
|  | 
 | ||||||
|  | 	static ProjectSettings *singleton; | ||||||
|  | 
 | ||||||
|  | 	Error _load_settings_text(const String &p_path); | ||||||
|  | 	Error _load_settings_binary(const String &p_path); | ||||||
|  | 	Error _load_settings_text_or_binary(const String &p_text_path, const String &p_bin_path); | ||||||
|  | 
 | ||||||
|  | 	Error _save_settings_text(const String &p_file, const RBMap<String, List<String>> &props, const CustomMap &p_custom = CustomMap(), const String &p_custom_features = String()); | ||||||
|  | 	Error _save_settings_binary(const String &p_file, const RBMap<String, List<String>> &props, const CustomMap &p_custom = CustomMap(), const String &p_custom_features = String()); | ||||||
|  | 
 | ||||||
|  | 	Error _save_custom_bnd(const String &p_file); | ||||||
|  | 
 | ||||||
|  | #ifdef TOOLS_ENABLED | ||||||
|  | 	const static PackedStringArray _get_supported_features(); | ||||||
|  | 	const static PackedStringArray _trim_to_supported_features(const PackedStringArray &p_project_features); | ||||||
|  | #endif // TOOLS_ENABLED
 | ||||||
|  | 
 | ||||||
|  | 	void _convert_to_last_version(int p_from_version); | ||||||
|  | 
 | ||||||
|  | 	bool load_resource_pack(const String &p_pack, bool p_replace_files, int p_offset); | ||||||
|  | 	bool _load_resource_pack(const String &p_pack, bool p_replace_files = true, int p_offset = 0, bool p_main_pack = false); | ||||||
|  | 
 | ||||||
|  | 	void _add_property_info_bind(const Dictionary &p_info); | ||||||
|  | 
 | ||||||
|  | 	Error _setup(const String &p_path, const String &p_main_pack, bool p_upwards = false, bool p_ignore_override = false); | ||||||
|  | 
 | ||||||
|  | 	void _add_builtin_input_map(); | ||||||
|  | 
 | ||||||
|  | protected: | ||||||
|  | 	static void _bind_methods(); | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  | 	static const int CONFIG_VERSION = 5; | ||||||
|  | 
 | ||||||
|  | 	void set_setting(const String &p_setting, const Variant &p_value); | ||||||
|  | 	Variant get_setting(const String &p_setting, const Variant &p_default_value = Variant()) const; | ||||||
|  | 	TypedArray<Dictionary> get_global_class_list(); | ||||||
|  | 	void refresh_global_class_list(); | ||||||
|  | 	void store_global_class_list(const Array &p_classes); | ||||||
|  | 	String get_global_class_list_path() const; | ||||||
|  | 
 | ||||||
|  | 	bool has_setting(const String &p_var) const; | ||||||
|  | 	String localize_path(const String &p_path) const; | ||||||
|  | 	String globalize_path(const String &p_path) const; | ||||||
|  | 
 | ||||||
|  | 	void set_initial_value(const String &p_name, const Variant &p_value); | ||||||
|  | 	void set_as_basic(const String &p_name, bool p_basic); | ||||||
|  | 	void set_as_internal(const String &p_name, bool p_internal); | ||||||
|  | 	void set_restart_if_changed(const String &p_name, bool p_restart); | ||||||
|  | 	void set_ignore_value_in_docs(const String &p_name, bool p_ignore); | ||||||
|  | 	bool get_ignore_value_in_docs(const String &p_name) const; | ||||||
|  | 	void add_hidden_prefix(const String &p_prefix); | ||||||
|  | 
 | ||||||
|  | 	String get_project_data_dir_name() const; | ||||||
|  | 	String get_project_data_path() const; | ||||||
|  | 	String get_resource_path() const; | ||||||
|  | 	String get_imported_files_path() const; | ||||||
|  | 
 | ||||||
|  | 	static ProjectSettings *get_singleton(); | ||||||
|  | 
 | ||||||
|  | 	void clear(const String &p_name); | ||||||
|  | 	int get_order(const String &p_name) const; | ||||||
|  | 	void set_order(const String &p_name, int p_order); | ||||||
|  | 	void set_builtin_order(const String &p_name); | ||||||
|  | 	bool is_builtin_setting(const String &p_name) const; | ||||||
|  | 
 | ||||||
|  | 	Error setup(const String &p_path, const String &p_main_pack, bool p_upwards = false, bool p_ignore_override = false); | ||||||
|  | 
 | ||||||
|  | 	Error load_custom(const String &p_path); | ||||||
|  | 	Error save_custom(const String &p_path = "", const CustomMap &p_custom = CustomMap(), const Vector<String> &p_custom_features = Vector<String>(), bool p_merge_with_current = true); | ||||||
|  | 	Error save(); | ||||||
|  | 	void set_custom_property_info(const PropertyInfo &p_info); | ||||||
|  | 	const HashMap<StringName, PropertyInfo> &get_custom_property_info() const; | ||||||
|  | 	uint64_t get_last_saved_time() { return last_save_time; } | ||||||
|  | 
 | ||||||
|  | 	List<String> get_input_presets() const { return input_presets; } | ||||||
|  | 
 | ||||||
|  | 	Variant get_setting_with_override(const StringName &p_name) const; | ||||||
|  | 	Variant get_setting_with_override_and_custom_features(const StringName &p_name, const Vector<String> &p_features) const; | ||||||
|  | 
 | ||||||
|  | 	bool is_using_datapack() const; | ||||||
|  | 	bool is_project_loaded() const; | ||||||
|  | 
 | ||||||
|  | 	bool has_custom_feature(const String &p_feature) const; | ||||||
|  | 
 | ||||||
|  | 	const HashMap<StringName, AutoloadInfo> &get_autoload_list() const; | ||||||
|  | 	void add_autoload(const AutoloadInfo &p_autoload); | ||||||
|  | 	void remove_autoload(const StringName &p_autoload); | ||||||
|  | 	bool has_autoload(const StringName &p_autoload) const; | ||||||
|  | 	AutoloadInfo get_autoload(const StringName &p_name) const; | ||||||
|  | 
 | ||||||
|  | 	const HashMap<StringName, String> &get_global_groups_list() const; | ||||||
|  | 	void add_global_group(const StringName &p_name, const String &p_description); | ||||||
|  | 	void remove_global_group(const StringName &p_name); | ||||||
|  | 	bool has_global_group(const StringName &p_name) const; | ||||||
|  | 
 | ||||||
|  | 	const HashMap<StringName, HashSet<StringName>> &get_scene_groups_cache() const; | ||||||
|  | 	void add_scene_groups_cache(const StringName &p_path, const HashSet<StringName> &p_cache); | ||||||
|  | 	void remove_scene_groups_cache(const StringName &p_path); | ||||||
|  | 	void save_scene_groups_cache(); | ||||||
|  | 	String get_scene_groups_cache_path() const; | ||||||
|  | 	void load_scene_groups_cache(); | ||||||
|  | 
 | ||||||
|  | #ifdef TOOLS_ENABLED | ||||||
|  | 	virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override; | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 	ProjectSettings(); | ||||||
|  | 	ProjectSettings(const String &p_path); | ||||||
|  | 	~ProjectSettings(); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // Not a macro any longer.
 | ||||||
|  | Variant _GLOBAL_DEF(const String &p_var, const Variant &p_default, bool p_restart_if_changed = false, bool p_ignore_value_in_docs = false, bool p_basic = false, bool p_internal = false); | ||||||
|  | Variant _GLOBAL_DEF(const PropertyInfo &p_info, const Variant &p_default, bool p_restart_if_changed = false, bool p_ignore_value_in_docs = false, bool p_basic = false, bool p_internal = false); | ||||||
|  | 
 | ||||||
|  | #define GLOBAL_DEF(m_var, m_value) _GLOBAL_DEF(m_var, m_value) | ||||||
|  | #define GLOBAL_DEF_RST(m_var, m_value) _GLOBAL_DEF(m_var, m_value, true) | ||||||
|  | #define GLOBAL_DEF_NOVAL(m_var, m_value) _GLOBAL_DEF(m_var, m_value, false, true) | ||||||
|  | #define GLOBAL_DEF_RST_NOVAL(m_var, m_value) _GLOBAL_DEF(m_var, m_value, true, true) | ||||||
|  | #define GLOBAL_GET(m_var) ProjectSettings::get_singleton()->get_setting_with_override(m_var) | ||||||
|  | 
 | ||||||
|  | #define GLOBAL_DEF_BASIC(m_var, m_value) _GLOBAL_DEF(m_var, m_value, false, false, true) | ||||||
|  | #define GLOBAL_DEF_RST_BASIC(m_var, m_value) _GLOBAL_DEF(m_var, m_value, true, false, true) | ||||||
|  | #define GLOBAL_DEF_NOVAL_BASIC(m_var, m_value) _GLOBAL_DEF(m_var, m_value, false, true, true) | ||||||
|  | #define GLOBAL_DEF_RST_NOVAL_BASIC(m_var, m_value) _GLOBAL_DEF(m_var, m_value, true, true, true) | ||||||
|  | 
 | ||||||
|  | #define GLOBAL_DEF_INTERNAL(m_var, m_value) _GLOBAL_DEF(m_var, m_value, false, false, false, true) | ||||||
							
								
								
									
										64
									
								
								engine/core/core_bind.compat.inc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								engine/core/core_bind.compat.inc
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,64 @@ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*  core_bind.compat.inc                                                  */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*                         This file is part of:                          */ | ||||||
|  | /*                             GODOT ENGINE                               */ | ||||||
|  | /*                        https://godotengine.org                         */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ | ||||||
|  | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */ | ||||||
|  | /*                                                                        */ | ||||||
|  | /* 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 DISABLE_DEPRECATED | ||||||
|  | 
 | ||||||
|  | namespace CoreBind { | ||||||
|  | 
 | ||||||
|  | // Semaphore
 | ||||||
|  | 
 | ||||||
|  | void Semaphore::_post_bind_compat_93605() { | ||||||
|  | 	post(1); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Semaphore::_bind_compatibility_methods() { | ||||||
|  | 	ClassDB::bind_compatibility_method(D_METHOD("post"), &Semaphore::_post_bind_compat_93605); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // OS
 | ||||||
|  | 
 | ||||||
|  | String OS::_read_string_from_stdin_bind_compat_91201() { | ||||||
|  | 	return read_string_from_stdin(1024); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Dictionary OS::_execute_with_pipe_bind_compat_94434(const String &p_path, const Vector<String> &p_arguments) { | ||||||
|  | 	return execute_with_pipe(p_path, p_arguments, true); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void OS::_bind_compatibility_methods() { | ||||||
|  | 	ClassDB::bind_compatibility_method(D_METHOD("read_string_from_stdin", "buffer_size"), &OS::read_string_from_stdin); | ||||||
|  | 	ClassDB::bind_compatibility_method(D_METHOD("read_buffer_from_stdin", "buffer_size"), &OS::read_buffer_from_stdin); | ||||||
|  | 	ClassDB::bind_compatibility_method(D_METHOD("read_string_from_stdin"), &OS::_read_string_from_stdin_bind_compat_91201); | ||||||
|  | 	ClassDB::bind_compatibility_method(D_METHOD("execute_with_pipe", "path", "arguments"), &OS::_execute_with_pipe_bind_compat_94434); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } // namespace CoreBind
 | ||||||
|  | 
 | ||||||
|  | #endif // DISABLE_DEPRECATED
 | ||||||
							
								
								
									
										2193
									
								
								engine/core/core_bind.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2193
									
								
								engine/core/core_bind.cpp
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										671
									
								
								engine/core/core_bind.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										671
									
								
								engine/core/core_bind.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,671 @@ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*  core_bind.h                                                           */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*                         This file is part of:                          */ | ||||||
|  | /*                             GODOT ENGINE                               */ | ||||||
|  | /*                        https://godotengine.org                         */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ | ||||||
|  | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */ | ||||||
|  | /*                                                                        */ | ||||||
|  | /* 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.                 */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include "core/debugger/engine_profiler.h" | ||||||
|  | #include "core/io/resource_loader.h" | ||||||
|  | #include "core/io/resource_saver.h" | ||||||
|  | #include "core/os/semaphore.h" | ||||||
|  | #include "core/os/thread.h" | ||||||
|  | #include "core/templates/safe_refcount.h" | ||||||
|  | 
 | ||||||
|  | class MainLoop; | ||||||
|  | template <typename T> | ||||||
|  | class TypedArray; | ||||||
|  | 
 | ||||||
|  | namespace CoreBind { | ||||||
|  | 
 | ||||||
|  | class ResourceLoader : public Object { | ||||||
|  | 	GDCLASS(ResourceLoader, Object); | ||||||
|  | 
 | ||||||
|  | protected: | ||||||
|  | 	static void _bind_methods(); | ||||||
|  | 	static ResourceLoader *singleton; | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  | 	enum ThreadLoadStatus { | ||||||
|  | 		THREAD_LOAD_INVALID_RESOURCE, | ||||||
|  | 		THREAD_LOAD_IN_PROGRESS, | ||||||
|  | 		THREAD_LOAD_FAILED, | ||||||
|  | 		THREAD_LOAD_LOADED | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	enum CacheMode { | ||||||
|  | 		CACHE_MODE_IGNORE, | ||||||
|  | 		CACHE_MODE_REUSE, | ||||||
|  | 		CACHE_MODE_REPLACE, | ||||||
|  | 		CACHE_MODE_IGNORE_DEEP, | ||||||
|  | 		CACHE_MODE_REPLACE_DEEP, | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	static ResourceLoader *get_singleton() { return singleton; } | ||||||
|  | 
 | ||||||
|  | 	Error load_threaded_request(const String &p_path, const String &p_type_hint = "", bool p_use_sub_threads = false, CacheMode p_cache_mode = CACHE_MODE_REUSE); | ||||||
|  | 	ThreadLoadStatus load_threaded_get_status(const String &p_path, Array r_progress = ClassDB::default_array_arg); | ||||||
|  | 	Ref<Resource> load_threaded_get(const String &p_path); | ||||||
|  | 
 | ||||||
|  | 	Ref<Resource> load(const String &p_path, const String &p_type_hint = "", CacheMode p_cache_mode = CACHE_MODE_REUSE); | ||||||
|  | 	Vector<String> get_recognized_extensions_for_type(const String &p_type); | ||||||
|  | 	void add_resource_format_loader(Ref<ResourceFormatLoader> p_format_loader, bool p_at_front); | ||||||
|  | 	void remove_resource_format_loader(Ref<ResourceFormatLoader> p_format_loader); | ||||||
|  | 	void set_abort_on_missing_resources(bool p_abort); | ||||||
|  | 	PackedStringArray get_dependencies(const String &p_path); | ||||||
|  | 	bool has_cached(const String &p_path); | ||||||
|  | 	Ref<Resource> get_cached_ref(const String &p_path); | ||||||
|  | 	bool exists(const String &p_path, const String &p_type_hint = ""); | ||||||
|  | 	ResourceUID::ID get_resource_uid(const String &p_path); | ||||||
|  | 
 | ||||||
|  | 	Vector<String> list_directory(const String &p_directory); | ||||||
|  | 
 | ||||||
|  | 	ResourceLoader() { singleton = this; } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class ResourceSaver : public Object { | ||||||
|  | 	GDCLASS(ResourceSaver, Object); | ||||||
|  | 
 | ||||||
|  | protected: | ||||||
|  | 	static void _bind_methods(); | ||||||
|  | 	static ResourceSaver *singleton; | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  | 	enum SaverFlags { | ||||||
|  | 		FLAG_NONE = 0, | ||||||
|  | 		FLAG_RELATIVE_PATHS = 1, | ||||||
|  | 		FLAG_BUNDLE_RESOURCES = 2, | ||||||
|  | 		FLAG_CHANGE_PATH = 4, | ||||||
|  | 		FLAG_OMIT_EDITOR_PROPERTIES = 8, | ||||||
|  | 		FLAG_SAVE_BIG_ENDIAN = 16, | ||||||
|  | 		FLAG_COMPRESS = 32, | ||||||
|  | 		FLAG_REPLACE_SUBRESOURCE_PATHS = 64, | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	static ResourceSaver *get_singleton() { return singleton; } | ||||||
|  | 
 | ||||||
|  | 	Error save(const Ref<Resource> &p_resource, const String &p_path, BitField<SaverFlags> p_flags); | ||||||
|  | 	Vector<String> get_recognized_extensions(const Ref<Resource> &p_resource); | ||||||
|  | 	void add_resource_format_saver(Ref<ResourceFormatSaver> p_format_saver, bool p_at_front); | ||||||
|  | 	void remove_resource_format_saver(Ref<ResourceFormatSaver> p_format_saver); | ||||||
|  | 
 | ||||||
|  | 	ResourceUID::ID get_resource_id_for_path(const String &p_path, bool p_generate = false); | ||||||
|  | 
 | ||||||
|  | 	ResourceSaver() { singleton = this; } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class OS : public Object { | ||||||
|  | 	GDCLASS(OS, Object); | ||||||
|  | 
 | ||||||
|  | 	mutable HashMap<String, bool> feature_cache; | ||||||
|  | 
 | ||||||
|  | protected: | ||||||
|  | 	static void _bind_methods(); | ||||||
|  | 	static OS *singleton; | ||||||
|  | 
 | ||||||
|  | #ifndef DISABLE_DEPRECATED | ||||||
|  | 	Dictionary _execute_with_pipe_bind_compat_94434(const String &p_path, const Vector<String> &p_arguments); | ||||||
|  | 
 | ||||||
|  | 	String _read_string_from_stdin_bind_compat_91201(); | ||||||
|  | 	static void _bind_compatibility_methods(); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  | 	enum RenderingDriver { | ||||||
|  | 		RENDERING_DRIVER_VULKAN, | ||||||
|  | 		RENDERING_DRIVER_OPENGL3, | ||||||
|  | 		RENDERING_DRIVER_D3D12, | ||||||
|  | 		RENDERING_DRIVER_METAL, | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	PackedByteArray get_entropy(int p_bytes); | ||||||
|  | 	String get_system_ca_certificates(); | ||||||
|  | 
 | ||||||
|  | 	enum StdHandleType { | ||||||
|  | 		STD_HANDLE_INVALID, | ||||||
|  | 		STD_HANDLE_CONSOLE, | ||||||
|  | 		STD_HANDLE_FILE, | ||||||
|  | 		STD_HANDLE_PIPE, | ||||||
|  | 		STD_HANDLE_UNKNOWN, | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	virtual PackedStringArray get_connected_midi_inputs(); | ||||||
|  | 	virtual void open_midi_inputs(); | ||||||
|  | 	virtual void close_midi_inputs(); | ||||||
|  | 
 | ||||||
|  | 	void set_low_processor_usage_mode(bool p_enabled); | ||||||
|  | 	bool is_in_low_processor_usage_mode() const; | ||||||
|  | 
 | ||||||
|  | 	void set_low_processor_usage_mode_sleep_usec(int p_usec); | ||||||
|  | 	int get_low_processor_usage_mode_sleep_usec() const; | ||||||
|  | 
 | ||||||
|  | 	void set_delta_smoothing(bool p_enabled); | ||||||
|  | 	bool is_delta_smoothing_enabled() const; | ||||||
|  | 
 | ||||||
|  | 	void alert(const String &p_alert, const String &p_title = "ALERT!"); | ||||||
|  | 	void crash(const String &p_message); | ||||||
|  | 
 | ||||||
|  | 	Vector<String> get_system_fonts() const; | ||||||
|  | 	String get_system_font_path(const String &p_font_name, int p_weight = 400, int p_stretch = 100, bool p_italic = false) const; | ||||||
|  | 	Vector<String> get_system_font_path_for_text(const String &p_font_name, const String &p_text, const String &p_locale = String(), const String &p_script = String(), int p_weight = 400, int p_stretch = 100, bool p_italic = false) const; | ||||||
|  | 	String get_executable_path() const; | ||||||
|  | 
 | ||||||
|  | 	String read_string_from_stdin(int64_t p_buffer_size = 1024); | ||||||
|  | 	PackedByteArray read_buffer_from_stdin(int64_t p_buffer_size = 1024); | ||||||
|  | 	StdHandleType get_stdin_type() const; | ||||||
|  | 	StdHandleType get_stdout_type() const; | ||||||
|  | 	StdHandleType get_stderr_type() const; | ||||||
|  | 
 | ||||||
|  | 	int execute(const String &p_path, const Vector<String> &p_arguments, Array r_output = ClassDB::default_array_arg, bool p_read_stderr = false, bool p_open_console = false); | ||||||
|  | 	Dictionary execute_with_pipe(const String &p_path, const Vector<String> &p_arguments, bool p_blocking = true); | ||||||
|  | 	int create_process(const String &p_path, const Vector<String> &p_arguments, bool p_open_console = false); | ||||||
|  | 	int create_instance(const Vector<String> &p_arguments); | ||||||
|  | 	Error kill(int p_pid); | ||||||
|  | 	Error shell_open(const String &p_uri); | ||||||
|  | 	Error shell_show_in_file_manager(const String &p_path, bool p_open_folder = true); | ||||||
|  | 
 | ||||||
|  | 	bool is_process_running(int p_pid) const; | ||||||
|  | 	int get_process_exit_code(int p_pid) const; | ||||||
|  | 	int get_process_id() const; | ||||||
|  | 
 | ||||||
|  | 	void set_restart_on_exit(bool p_restart, const Vector<String> &p_restart_arguments = Vector<String>()); | ||||||
|  | 	bool is_restart_on_exit_set() const; | ||||||
|  | 	Vector<String> get_restart_on_exit_arguments() const; | ||||||
|  | 
 | ||||||
|  | 	bool has_environment(const String &p_var) const; | ||||||
|  | 	String get_environment(const String &p_var) const; | ||||||
|  | 	void set_environment(const String &p_var, const String &p_value) const; | ||||||
|  | 	void unset_environment(const String &p_var) const; | ||||||
|  | 
 | ||||||
|  | 	String get_name() const; | ||||||
|  | 	String get_distribution_name() const; | ||||||
|  | 	String get_version() const; | ||||||
|  | 	String get_version_alias() const; | ||||||
|  | 	Vector<String> get_cmdline_args(); | ||||||
|  | 	Vector<String> get_cmdline_user_args(); | ||||||
|  | 
 | ||||||
|  | 	Vector<String> get_video_adapter_driver_info() const; | ||||||
|  | 
 | ||||||
|  | 	String get_locale() const; | ||||||
|  | 	String get_locale_language() const; | ||||||
|  | 
 | ||||||
|  | 	String get_model_name() const; | ||||||
|  | 
 | ||||||
|  | 	bool is_debug_build() const; | ||||||
|  | 
 | ||||||
|  | 	String get_unique_id() const; | ||||||
|  | 
 | ||||||
|  | 	String get_keycode_string(Key p_code) const; | ||||||
|  | 	bool is_keycode_unicode(char32_t p_unicode) const; | ||||||
|  | 	Key find_keycode_from_string(const String &p_code) const; | ||||||
|  | 
 | ||||||
|  | 	void set_use_file_access_save_and_swap(bool p_enable); | ||||||
|  | 
 | ||||||
|  | 	uint64_t get_static_memory_usage() const; | ||||||
|  | 	uint64_t get_static_memory_peak_usage() const; | ||||||
|  | 	Dictionary get_memory_info() const; | ||||||
|  | 
 | ||||||
|  | 	void delay_usec(int p_usec) const; | ||||||
|  | 	void delay_msec(int p_msec) const; | ||||||
|  | 	uint64_t get_ticks_msec() const; | ||||||
|  | 	uint64_t get_ticks_usec() const; | ||||||
|  | 
 | ||||||
|  | 	bool is_userfs_persistent() const; | ||||||
|  | 
 | ||||||
|  | 	bool is_stdout_verbose() const; | ||||||
|  | 
 | ||||||
|  | 	int get_processor_count() const; | ||||||
|  | 	String get_processor_name() const; | ||||||
|  | 
 | ||||||
|  | 	enum SystemDir { | ||||||
|  | 		SYSTEM_DIR_DESKTOP, | ||||||
|  | 		SYSTEM_DIR_DCIM, | ||||||
|  | 		SYSTEM_DIR_DOCUMENTS, | ||||||
|  | 		SYSTEM_DIR_DOWNLOADS, | ||||||
|  | 		SYSTEM_DIR_MOVIES, | ||||||
|  | 		SYSTEM_DIR_MUSIC, | ||||||
|  | 		SYSTEM_DIR_PICTURES, | ||||||
|  | 		SYSTEM_DIR_RINGTONES, | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	String get_system_dir(SystemDir p_dir, bool p_shared_storage = true) const; | ||||||
|  | 
 | ||||||
|  | 	Error move_to_trash(const String &p_path) const; | ||||||
|  | 	String get_user_data_dir() const; | ||||||
|  | 	String get_config_dir() const; | ||||||
|  | 	String get_data_dir() const; | ||||||
|  | 	String get_cache_dir() const; | ||||||
|  | 	String get_temp_dir() const; | ||||||
|  | 
 | ||||||
|  | 	Error set_thread_name(const String &p_name); | ||||||
|  | 	::Thread::ID get_thread_caller_id() const; | ||||||
|  | 	::Thread::ID get_main_thread_id() const; | ||||||
|  | 
 | ||||||
|  | 	bool has_feature(const String &p_feature) const; | ||||||
|  | 	bool is_sandboxed() const; | ||||||
|  | 
 | ||||||
|  | 	bool request_permission(const String &p_name); | ||||||
|  | 	bool request_permissions(); | ||||||
|  | 	Vector<String> get_granted_permissions() const; | ||||||
|  | 	void revoke_granted_permissions(); | ||||||
|  | 
 | ||||||
|  | 	static OS *get_singleton() { return singleton; } | ||||||
|  | 
 | ||||||
|  | 	OS() { singleton = this; } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class Geometry2D : public Object { | ||||||
|  | 	GDCLASS(Geometry2D, Object); | ||||||
|  | 
 | ||||||
|  | 	static Geometry2D *singleton; | ||||||
|  | 
 | ||||||
|  | protected: | ||||||
|  | 	static void _bind_methods(); | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  | 	static Geometry2D *get_singleton(); | ||||||
|  | 	Variant segment_intersects_segment(const Vector2 &p_from_a, const Vector2 &p_to_a, const Vector2 &p_from_b, const Vector2 &p_to_b); | ||||||
|  | 	Variant line_intersects_line(const Vector2 &p_from_a, const Vector2 &p_dir_a, const Vector2 &p_from_b, const Vector2 &p_dir_b); | ||||||
|  | 	Vector<Vector2> get_closest_points_between_segments(const Vector2 &p1, const Vector2 &q1, const Vector2 &p2, const Vector2 &q2); | ||||||
|  | 	Vector2 get_closest_point_to_segment(const Vector2 &p_point, const Vector2 &p_a, const Vector2 &p_b); | ||||||
|  | 	Vector2 get_closest_point_to_segment_uncapped(const Vector2 &p_point, const Vector2 &p_a, const Vector2 &p_b); | ||||||
|  | 	bool point_is_inside_triangle(const Vector2 &s, const Vector2 &a, const Vector2 &b, const Vector2 &c) const; | ||||||
|  | 
 | ||||||
|  | 	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); | ||||||
|  | 
 | ||||||
|  | 	bool is_polygon_clockwise(const Vector<Vector2> &p_polygon); | ||||||
|  | 	bool is_point_in_polygon(const Point2 &p_point, const Vector<Vector2> &p_polygon); | ||||||
|  | 	Vector<int> triangulate_polygon(const Vector<Vector2> &p_polygon); | ||||||
|  | 	Vector<int> triangulate_delaunay(const Vector<Vector2> &p_points); | ||||||
|  | 	Vector<Point2> convex_hull(const Vector<Point2> &p_points); | ||||||
|  | 	TypedArray<PackedVector2Array> decompose_polygon_in_convex(const Vector<Vector2> &p_polygon); | ||||||
|  | 
 | ||||||
|  | 	enum PolyBooleanOperation { | ||||||
|  | 		OPERATION_UNION, | ||||||
|  | 		OPERATION_DIFFERENCE, | ||||||
|  | 		OPERATION_INTERSECTION, | ||||||
|  | 		OPERATION_XOR | ||||||
|  | 	}; | ||||||
|  | 	// 2D polygon boolean operations.
 | ||||||
|  | 	TypedArray<PackedVector2Array> merge_polygons(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b); // Union (add).
 | ||||||
|  | 	TypedArray<PackedVector2Array> clip_polygons(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b); // Difference (subtract).
 | ||||||
|  | 	TypedArray<PackedVector2Array> intersect_polygons(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b); // Common area (multiply).
 | ||||||
|  | 	TypedArray<PackedVector2Array> exclude_polygons(const Vector<Vector2> &p_polygon_a, const Vector<Vector2> &p_polygon_b); // All but common area (xor).
 | ||||||
|  | 
 | ||||||
|  | 	// 2D polyline vs polygon operations.
 | ||||||
|  | 	TypedArray<PackedVector2Array> clip_polyline_with_polygon(const Vector<Vector2> &p_polyline, const Vector<Vector2> &p_polygon); // Cut.
 | ||||||
|  | 	TypedArray<PackedVector2Array> intersect_polyline_with_polygon(const Vector<Vector2> &p_polyline, const Vector<Vector2> &p_polygon); // Chop.
 | ||||||
|  | 
 | ||||||
|  | 	// 2D offset polygons/polylines.
 | ||||||
|  | 	enum PolyJoinType { | ||||||
|  | 		JOIN_SQUARE, | ||||||
|  | 		JOIN_ROUND, | ||||||
|  | 		JOIN_MITER | ||||||
|  | 	}; | ||||||
|  | 	enum PolyEndType { | ||||||
|  | 		END_POLYGON, | ||||||
|  | 		END_JOINED, | ||||||
|  | 		END_BUTT, | ||||||
|  | 		END_SQUARE, | ||||||
|  | 		END_ROUND | ||||||
|  | 	}; | ||||||
|  | 	TypedArray<PackedVector2Array> offset_polygon(const Vector<Vector2> &p_polygon, real_t p_delta, PolyJoinType p_join_type = JOIN_SQUARE); | ||||||
|  | 	TypedArray<PackedVector2Array> offset_polyline(const Vector<Vector2> &p_polygon, real_t p_delta, PolyJoinType p_join_type = JOIN_SQUARE, PolyEndType p_end_type = END_SQUARE); | ||||||
|  | 
 | ||||||
|  | 	Dictionary make_atlas(const Vector<Size2> &p_rects); | ||||||
|  | 
 | ||||||
|  | 	TypedArray<Point2i> bresenham_line(const Point2i &p_from, const Point2i &p_to); | ||||||
|  | 
 | ||||||
|  | 	Geometry2D() { singleton = this; } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class Geometry3D : public Object { | ||||||
|  | 	GDCLASS(Geometry3D, Object); | ||||||
|  | 
 | ||||||
|  | 	static Geometry3D *singleton; | ||||||
|  | 
 | ||||||
|  | protected: | ||||||
|  | 	static void _bind_methods(); | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  | 	static Geometry3D *get_singleton(); | ||||||
|  | 	Vector<Vector3> compute_convex_mesh_points(const TypedArray<Plane> &p_planes); | ||||||
|  | 	TypedArray<Plane> build_box_planes(const Vector3 &p_extents); | ||||||
|  | 	TypedArray<Plane> build_cylinder_planes(float p_radius, float p_height, int p_sides, Vector3::Axis p_axis = Vector3::AXIS_Z); | ||||||
|  | 	TypedArray<Plane> build_capsule_planes(float p_radius, float p_height, int p_sides, int p_lats, Vector3::Axis p_axis = Vector3::AXIS_Z); | ||||||
|  | 	Vector<Vector3> get_closest_points_between_segments(const Vector3 &p1, const Vector3 &p2, const Vector3 &q1, const Vector3 &q2); | ||||||
|  | 	Vector3 get_closest_point_to_segment(const Vector3 &p_point, const Vector3 &p_a, const Vector3 &p_b); | ||||||
|  | 	Vector3 get_closest_point_to_segment_uncapped(const Vector3 &p_point, const Vector3 &p_a, const Vector3 &p_b); | ||||||
|  | 	Vector3 get_triangle_barycentric_coords(const Vector3 &p_point, const Vector3 &p_v0, const Vector3 &p_v1, const Vector3 &p_v2); | ||||||
|  | 	Variant ray_intersects_triangle(const Vector3 &p_from, const Vector3 &p_dir, const Vector3 &p_v0, const Vector3 &p_v1, const Vector3 &p_v2); | ||||||
|  | 	Variant segment_intersects_triangle(const Vector3 &p_from, const Vector3 &p_to, const Vector3 &p_v0, const Vector3 &p_v1, const Vector3 &p_v2); | ||||||
|  | 
 | ||||||
|  | 	Vector<Vector3> segment_intersects_sphere(const Vector3 &p_from, const Vector3 &p_to, const Vector3 &p_sphere_pos, real_t p_sphere_radius); | ||||||
|  | 	Vector<Vector3> segment_intersects_cylinder(const Vector3 &p_from, const Vector3 &p_to, float p_height, float p_radius); | ||||||
|  | 	Vector<Vector3> segment_intersects_convex(const Vector3 &p_from, const Vector3 &p_to, const TypedArray<Plane> &p_planes); | ||||||
|  | 
 | ||||||
|  | 	Vector<Vector3> clip_polygon(const Vector<Vector3> &p_points, const Plane &p_plane); | ||||||
|  | 	Vector<int32_t> tetrahedralize_delaunay(const Vector<Vector3> &p_points); | ||||||
|  | 
 | ||||||
|  | 	Geometry3D() { singleton = this; } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class Marshalls : public Object { | ||||||
|  | 	GDCLASS(Marshalls, Object); | ||||||
|  | 
 | ||||||
|  | 	static Marshalls *singleton; | ||||||
|  | 
 | ||||||
|  | protected: | ||||||
|  | 	static void _bind_methods(); | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  | 	static Marshalls *get_singleton(); | ||||||
|  | 
 | ||||||
|  | 	String variant_to_base64(const Variant &p_var, bool p_full_objects = false); | ||||||
|  | 	Variant base64_to_variant(const String &p_str, bool p_allow_objects = false); | ||||||
|  | 
 | ||||||
|  | 	String raw_to_base64(const Vector<uint8_t> &p_arr); | ||||||
|  | 	Vector<uint8_t> base64_to_raw(const String &p_str); | ||||||
|  | 
 | ||||||
|  | 	String utf8_to_base64(const String &p_str); | ||||||
|  | 	String base64_to_utf8(const String &p_str); | ||||||
|  | 
 | ||||||
|  | 	Marshalls() { singleton = this; } | ||||||
|  | 	~Marshalls() { singleton = nullptr; } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class Mutex : public RefCounted { | ||||||
|  | 	GDCLASS(Mutex, RefCounted); | ||||||
|  | 	::Mutex mutex; | ||||||
|  | 
 | ||||||
|  | 	static void _bind_methods(); | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  | 	void lock(); | ||||||
|  | 	bool try_lock(); | ||||||
|  | 	void unlock(); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class Semaphore : public RefCounted { | ||||||
|  | 	GDCLASS(Semaphore, RefCounted); | ||||||
|  | 	::Semaphore semaphore; | ||||||
|  | 
 | ||||||
|  | protected: | ||||||
|  | 	static void _bind_methods(); | ||||||
|  | #ifndef DISABLE_DEPRECATED | ||||||
|  | 	void _post_bind_compat_93605(); | ||||||
|  | 	static void _bind_compatibility_methods(); | ||||||
|  | #endif // DISABLE_DEPRECATED
 | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  | 	void wait(); | ||||||
|  | 	bool try_wait(); | ||||||
|  | 	void post(int p_count = 1); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class Thread : public RefCounted { | ||||||
|  | 	GDCLASS(Thread, RefCounted); | ||||||
|  | 
 | ||||||
|  | protected: | ||||||
|  | 	Variant ret; | ||||||
|  | 	SafeFlag running; | ||||||
|  | 	Callable target_callable; | ||||||
|  | 	::Thread thread; | ||||||
|  | 	static void _bind_methods(); | ||||||
|  | 	static void _start_func(void *ud); | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  | 	enum Priority { | ||||||
|  | 		PRIORITY_LOW, | ||||||
|  | 		PRIORITY_NORMAL, | ||||||
|  | 		PRIORITY_HIGH, | ||||||
|  | 		PRIORITY_MAX | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	Error start(const Callable &p_callable, Priority p_priority = PRIORITY_NORMAL); | ||||||
|  | 	String get_id() const; | ||||||
|  | 	bool is_started() const; | ||||||
|  | 	bool is_alive() const; | ||||||
|  | 	Variant wait_to_finish(); | ||||||
|  | 
 | ||||||
|  | 	static void set_thread_safety_checks_enabled(bool p_enabled); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | namespace Special { | ||||||
|  | 
 | ||||||
|  | class ClassDB : public Object { | ||||||
|  | 	GDCLASS(ClassDB, Object); | ||||||
|  | 
 | ||||||
|  | protected: | ||||||
|  | 	static void _bind_methods(); | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  | 	enum APIType { | ||||||
|  | 		API_CORE, | ||||||
|  | 		API_EDITOR, | ||||||
|  | 		API_EXTENSION, | ||||||
|  | 		API_EDITOR_EXTENSION, | ||||||
|  | 		API_NONE, | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	PackedStringArray get_class_list() const; | ||||||
|  | 	PackedStringArray get_inheriters_from_class(const StringName &p_class) const; | ||||||
|  | 	StringName get_parent_class(const StringName &p_class) const; | ||||||
|  | 	bool class_exists(const StringName &p_class) const; | ||||||
|  | 	bool is_parent_class(const StringName &p_class, const StringName &p_inherits) const; | ||||||
|  | 	bool can_instantiate(const StringName &p_class) const; | ||||||
|  | 	Variant instantiate(const StringName &p_class) const; | ||||||
|  | 
 | ||||||
|  | 	APIType class_get_api_type(const StringName &p_class) const; | ||||||
|  | 	bool class_has_signal(const StringName &p_class, const StringName &p_signal) const; | ||||||
|  | 	Dictionary class_get_signal(const StringName &p_class, const StringName &p_signal) const; | ||||||
|  | 	TypedArray<Dictionary> class_get_signal_list(const StringName &p_class, bool p_no_inheritance = false) const; | ||||||
|  | 
 | ||||||
|  | 	TypedArray<Dictionary> class_get_property_list(const StringName &p_class, bool p_no_inheritance = false) const; | ||||||
|  | 	StringName class_get_property_getter(const StringName &p_class, const StringName &p_property); | ||||||
|  | 	StringName class_get_property_setter(const StringName &p_class, const StringName &p_property); | ||||||
|  | 	Variant class_get_property(Object *p_object, const StringName &p_property) const; | ||||||
|  | 	Error class_set_property(Object *p_object, const StringName &p_property, const Variant &p_value) const; | ||||||
|  | 
 | ||||||
|  | 	Variant class_get_property_default_value(const StringName &p_class, const StringName &p_property) const; | ||||||
|  | 
 | ||||||
|  | 	bool class_has_method(const StringName &p_class, const StringName &p_method, bool p_no_inheritance = false) const; | ||||||
|  | 
 | ||||||
|  | 	int class_get_method_argument_count(const StringName &p_class, const StringName &p_method, bool p_no_inheritance = false) const; | ||||||
|  | 
 | ||||||
|  | 	TypedArray<Dictionary> class_get_method_list(const StringName &p_class, bool p_no_inheritance = false) const; | ||||||
|  | 	Variant class_call_static(const Variant **p_arguments, int p_argcount, Callable::CallError &r_call_error); | ||||||
|  | 
 | ||||||
|  | 	PackedStringArray class_get_integer_constant_list(const StringName &p_class, bool p_no_inheritance = false) const; | ||||||
|  | 	bool class_has_integer_constant(const StringName &p_class, const StringName &p_name) const; | ||||||
|  | 	int64_t class_get_integer_constant(const StringName &p_class, const StringName &p_name) const; | ||||||
|  | 
 | ||||||
|  | 	bool class_has_enum(const StringName &p_class, const StringName &p_name, bool p_no_inheritance = false) const; | ||||||
|  | 	PackedStringArray class_get_enum_list(const StringName &p_class, bool p_no_inheritance = false) const; | ||||||
|  | 	PackedStringArray class_get_enum_constants(const StringName &p_class, const StringName &p_enum, bool p_no_inheritance = false) const; | ||||||
|  | 	StringName class_get_integer_constant_enum(const StringName &p_class, const StringName &p_name, bool p_no_inheritance = false) const; | ||||||
|  | 
 | ||||||
|  | 	bool is_class_enum_bitfield(const StringName &p_class, const StringName &p_enum, bool p_no_inheritance = false) const; | ||||||
|  | 
 | ||||||
|  | 	bool is_class_enabled(const StringName &p_class) const; | ||||||
|  | 
 | ||||||
|  | #ifdef TOOLS_ENABLED | ||||||
|  | 	virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override; | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 	ClassDB() {} | ||||||
|  | 	~ClassDB() {} | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } // namespace Special
 | ||||||
|  | 
 | ||||||
|  | class Engine : public Object { | ||||||
|  | 	GDCLASS(Engine, Object); | ||||||
|  | 
 | ||||||
|  | protected: | ||||||
|  | 	static void _bind_methods(); | ||||||
|  | 	static Engine *singleton; | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  | 	static Engine *get_singleton() { return singleton; } | ||||||
|  | 	void set_physics_ticks_per_second(int p_ips); | ||||||
|  | 	int get_physics_ticks_per_second() const; | ||||||
|  | 
 | ||||||
|  | 	void set_max_physics_steps_per_frame(int p_max_physics_steps); | ||||||
|  | 	int get_max_physics_steps_per_frame() const; | ||||||
|  | 
 | ||||||
|  | 	void set_physics_jitter_fix(double p_threshold); | ||||||
|  | 	double get_physics_jitter_fix() const; | ||||||
|  | 	double get_physics_interpolation_fraction() const; | ||||||
|  | 
 | ||||||
|  | 	void set_max_fps(int p_fps); | ||||||
|  | 	int get_max_fps() const; | ||||||
|  | 
 | ||||||
|  | 	double get_frames_per_second() const; | ||||||
|  | 	uint64_t get_physics_frames() const; | ||||||
|  | 	uint64_t get_process_frames() const; | ||||||
|  | 
 | ||||||
|  | 	int get_frames_drawn(); | ||||||
|  | 
 | ||||||
|  | 	void set_time_scale(double p_scale); | ||||||
|  | 	double get_time_scale(); | ||||||
|  | 
 | ||||||
|  | 	MainLoop *get_main_loop() const; | ||||||
|  | 
 | ||||||
|  | 	Dictionary get_version_info() const; | ||||||
|  | 	Dictionary get_author_info() const; | ||||||
|  | 	TypedArray<Dictionary> get_copyright_info() const; | ||||||
|  | 	Dictionary get_donor_info() const; | ||||||
|  | 	Dictionary get_license_info() const; | ||||||
|  | 	String get_license_text() const; | ||||||
|  | 
 | ||||||
|  | 	String get_architecture_name() const; | ||||||
|  | 
 | ||||||
|  | 	bool is_in_physics_frame() const; | ||||||
|  | 
 | ||||||
|  | 	bool has_singleton(const StringName &p_name) const; | ||||||
|  | 	Object *get_singleton_object(const StringName &p_name) const; | ||||||
|  | 	void register_singleton(const StringName &p_name, Object *p_object); | ||||||
|  | 	void unregister_singleton(const StringName &p_name); | ||||||
|  | 	Vector<String> get_singleton_list() const; | ||||||
|  | 
 | ||||||
|  | 	Error register_script_language(ScriptLanguage *p_language); | ||||||
|  | 	Error unregister_script_language(const ScriptLanguage *p_language); | ||||||
|  | 	int get_script_language_count(); | ||||||
|  | 	ScriptLanguage *get_script_language(int p_index) const; | ||||||
|  | 
 | ||||||
|  | 	void set_editor_hint(bool p_enabled); | ||||||
|  | 	bool is_editor_hint() const; | ||||||
|  | 
 | ||||||
|  | 	bool is_embedded_in_editor() const; | ||||||
|  | 
 | ||||||
|  | 	// `set_write_movie_path()` is not exposed to the scripting API as changing it at run-time has no effect.
 | ||||||
|  | 	String get_write_movie_path() const; | ||||||
|  | 
 | ||||||
|  | 	void set_print_to_stdout(bool p_enabled); | ||||||
|  | 	bool is_printing_to_stdout() const; | ||||||
|  | 
 | ||||||
|  | 	void set_print_error_messages(bool p_enabled); | ||||||
|  | 	bool is_printing_error_messages() const; | ||||||
|  | 
 | ||||||
|  | #ifdef TOOLS_ENABLED | ||||||
|  | 	virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override; | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 	Engine() { singleton = this; } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class EngineDebugger : public Object { | ||||||
|  | 	GDCLASS(EngineDebugger, Object); | ||||||
|  | 
 | ||||||
|  | 	HashMap<StringName, Callable> captures; | ||||||
|  | 	HashMap<StringName, Ref<EngineProfiler>> profilers; | ||||||
|  | 
 | ||||||
|  | protected: | ||||||
|  | 	static void _bind_methods(); | ||||||
|  | 	static EngineDebugger *singleton; | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  | 	static EngineDebugger *get_singleton() { return singleton; } | ||||||
|  | 
 | ||||||
|  | 	bool is_active(); | ||||||
|  | 
 | ||||||
|  | 	void register_profiler(const StringName &p_name, Ref<EngineProfiler> p_profiler); | ||||||
|  | 	void unregister_profiler(const StringName &p_name); | ||||||
|  | 	bool is_profiling(const StringName &p_name); | ||||||
|  | 	bool has_profiler(const StringName &p_name); | ||||||
|  | 	void profiler_add_frame_data(const StringName &p_name, const Array &p_data); | ||||||
|  | 	void profiler_enable(const StringName &p_name, bool p_enabled, const Array &p_opts = Array()); | ||||||
|  | 
 | ||||||
|  | 	void register_message_capture(const StringName &p_name, const Callable &p_callable); | ||||||
|  | 	void unregister_message_capture(const StringName &p_name); | ||||||
|  | 	bool has_capture(const StringName &p_name); | ||||||
|  | 
 | ||||||
|  | 	void send_message(const String &p_msg, const Array &p_data); | ||||||
|  | 	void debug(bool p_can_continue = true, bool p_is_error_breakpoint = false); | ||||||
|  | 	void script_debug(ScriptLanguage *p_lang, bool p_can_continue = true, bool p_is_error_breakpoint = false); | ||||||
|  | 
 | ||||||
|  | 	static Error call_capture(void *p_user, const String &p_cmd, const Array &p_data, bool &r_captured); | ||||||
|  | 
 | ||||||
|  | 	void line_poll(); | ||||||
|  | 
 | ||||||
|  | 	void set_lines_left(int p_lines); | ||||||
|  | 	int get_lines_left() const; | ||||||
|  | 
 | ||||||
|  | 	void set_depth(int p_depth); | ||||||
|  | 	int get_depth() const; | ||||||
|  | 
 | ||||||
|  | 	bool is_breakpoint(int p_line, const StringName &p_source) const; | ||||||
|  | 	bool is_skipping_breakpoints() const; | ||||||
|  | 	void insert_breakpoint(int p_line, const StringName &p_source); | ||||||
|  | 	void remove_breakpoint(int p_line, const StringName &p_source); | ||||||
|  | 	void clear_breakpoints(); | ||||||
|  | 
 | ||||||
|  | 	EngineDebugger() { singleton = this; } | ||||||
|  | 	~EngineDebugger(); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } // namespace CoreBind
 | ||||||
|  | 
 | ||||||
|  | VARIANT_ENUM_CAST(CoreBind::ResourceLoader::ThreadLoadStatus); | ||||||
|  | VARIANT_ENUM_CAST(CoreBind::ResourceLoader::CacheMode); | ||||||
|  | 
 | ||||||
|  | VARIANT_BITFIELD_CAST(CoreBind::ResourceSaver::SaverFlags); | ||||||
|  | 
 | ||||||
|  | VARIANT_ENUM_CAST(CoreBind::OS::RenderingDriver); | ||||||
|  | VARIANT_ENUM_CAST(CoreBind::OS::SystemDir); | ||||||
|  | VARIANT_ENUM_CAST(CoreBind::OS::StdHandleType); | ||||||
|  | 
 | ||||||
|  | VARIANT_ENUM_CAST(CoreBind::Geometry2D::PolyBooleanOperation); | ||||||
|  | VARIANT_ENUM_CAST(CoreBind::Geometry2D::PolyJoinType); | ||||||
|  | VARIANT_ENUM_CAST(CoreBind::Geometry2D::PolyEndType); | ||||||
|  | 
 | ||||||
|  | VARIANT_ENUM_CAST(CoreBind::Thread::Priority); | ||||||
|  | 
 | ||||||
|  | VARIANT_ENUM_CAST(CoreBind::Special::ClassDB::APIType); | ||||||
							
								
								
									
										226
									
								
								engine/core/core_builders.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										226
									
								
								engine/core/core_builders.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,226 @@ | ||||||
|  | """Functions used to generate source files during build time""" | ||||||
|  | 
 | ||||||
|  | from collections import OrderedDict | ||||||
|  | from io import TextIOWrapper | ||||||
|  | 
 | ||||||
|  | import methods | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def make_certs_header(target, source, env): | ||||||
|  |     buffer = methods.get_buffer(str(source[0])) | ||||||
|  |     decomp_size = len(buffer) | ||||||
|  |     buffer = methods.compress_buffer(buffer) | ||||||
|  | 
 | ||||||
|  |     with methods.generated_wrapper(str(target[0])) as file: | ||||||
|  |         # System certs path. Editor will use them if defined. (for package maintainers) | ||||||
|  |         file.write('#define _SYSTEM_CERTS_PATH "{}"\n'.format(env["system_certs_path"])) | ||||||
|  |         if env["builtin_certs"]: | ||||||
|  |             # Defined here and not in env so changing it does not trigger a full rebuild. | ||||||
|  |             file.write(f"""\ | ||||||
|  | #define BUILTIN_CERTS_ENABLED | ||||||
|  | 
 | ||||||
|  | inline constexpr int _certs_compressed_size = {len(buffer)}; | ||||||
|  | inline constexpr int _certs_uncompressed_size = {decomp_size}; | ||||||
|  | inline constexpr unsigned char _certs_compressed[] = {{ | ||||||
|  | 	{methods.format_buffer(buffer, 1)} | ||||||
|  | }}; | ||||||
|  | """) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def make_authors_header(target, source, env): | ||||||
|  |     SECTIONS = { | ||||||
|  |         "Project Founders": "AUTHORS_FOUNDERS", | ||||||
|  |         "Lead Developer": "AUTHORS_LEAD_DEVELOPERS", | ||||||
|  |         "Project Manager": "AUTHORS_PROJECT_MANAGERS", | ||||||
|  |         "Developers": "AUTHORS_DEVELOPERS", | ||||||
|  |     } | ||||||
|  |     buffer = methods.get_buffer(str(source[0])) | ||||||
|  |     reading = False | ||||||
|  | 
 | ||||||
|  |     with methods.generated_wrapper(str(target[0])) as file: | ||||||
|  | 
 | ||||||
|  |         def close_section(): | ||||||
|  |             file.write("\tnullptr,\n};\n\n") | ||||||
|  | 
 | ||||||
|  |         for line in buffer.decode().splitlines(): | ||||||
|  |             if line.startswith("    ") and reading: | ||||||
|  |                 file.write(f'\t"{methods.to_escaped_cstring(line).strip()}",\n') | ||||||
|  |             elif line.startswith("## "): | ||||||
|  |                 if reading: | ||||||
|  |                     close_section() | ||||||
|  |                     reading = False | ||||||
|  |                 section = SECTIONS[line[3:].strip()] | ||||||
|  |                 if section: | ||||||
|  |                     file.write(f"inline constexpr const char *{section}[] = {{\n") | ||||||
|  |                     reading = True | ||||||
|  | 
 | ||||||
|  |         if reading: | ||||||
|  |             close_section() | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def make_donors_header(target, source, env): | ||||||
|  |     SECTIONS = { | ||||||
|  |         "Patrons": "DONORS_PATRONS", | ||||||
|  |         "Platinum sponsors": "DONORS_SPONSORS_PLATINUM", | ||||||
|  |         "Gold sponsors": "DONORS_SPONSORS_GOLD", | ||||||
|  |         "Silver sponsors": "DONORS_SPONSORS_SILVER", | ||||||
|  |         "Diamond members": "DONORS_MEMBERS_DIAMOND", | ||||||
|  |         "Titanium members": "DONORS_MEMBERS_TITANIUM", | ||||||
|  |         "Platinum members": "DONORS_MEMBERS_PLATINUM", | ||||||
|  |         "Gold members": "DONORS_MEMBERS_GOLD", | ||||||
|  |     } | ||||||
|  |     buffer = methods.get_buffer(str(source[0])) | ||||||
|  |     reading = False | ||||||
|  | 
 | ||||||
|  |     with methods.generated_wrapper(str(target[0])) as file: | ||||||
|  | 
 | ||||||
|  |         def close_section(): | ||||||
|  |             file.write("\tnullptr,\n};\n\n") | ||||||
|  | 
 | ||||||
|  |         for line in buffer.decode().splitlines(): | ||||||
|  |             if line.startswith("    ") and reading: | ||||||
|  |                 file.write(f'\t"{methods.to_escaped_cstring(line).strip()}",\n') | ||||||
|  |             elif line.startswith("## "): | ||||||
|  |                 if reading: | ||||||
|  |                     close_section() | ||||||
|  |                     reading = False | ||||||
|  |                 section = SECTIONS.get(line[3:].strip()) | ||||||
|  |                 if section: | ||||||
|  |                     file.write(f"inline constexpr const char *{section}[] = {{\n") | ||||||
|  |                     reading = True | ||||||
|  | 
 | ||||||
|  |         if reading: | ||||||
|  |             close_section() | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def make_license_header(target, source, env): | ||||||
|  |     src_copyright = str(source[0]) | ||||||
|  |     src_license = str(source[1]) | ||||||
|  | 
 | ||||||
|  |     class LicenseReader: | ||||||
|  |         def __init__(self, license_file: TextIOWrapper): | ||||||
|  |             self._license_file = license_file | ||||||
|  |             self.line_num = 0 | ||||||
|  |             self.current = self.next_line() | ||||||
|  | 
 | ||||||
|  |         def next_line(self): | ||||||
|  |             line = self._license_file.readline() | ||||||
|  |             self.line_num += 1 | ||||||
|  |             while line.startswith("#"): | ||||||
|  |                 line = self._license_file.readline() | ||||||
|  |                 self.line_num += 1 | ||||||
|  |             self.current = line | ||||||
|  |             return line | ||||||
|  | 
 | ||||||
|  |         def next_tag(self): | ||||||
|  |             if ":" not in self.current: | ||||||
|  |                 return ("", []) | ||||||
|  |             tag, line = self.current.split(":", 1) | ||||||
|  |             lines = [line.strip()] | ||||||
|  |             while self.next_line() and self.current.startswith(" "): | ||||||
|  |                 lines.append(self.current.strip()) | ||||||
|  |             return (tag, lines) | ||||||
|  | 
 | ||||||
|  |     projects = OrderedDict() | ||||||
|  |     license_list = [] | ||||||
|  | 
 | ||||||
|  |     with open(src_copyright, "r", encoding="utf-8") as copyright_file: | ||||||
|  |         reader = LicenseReader(copyright_file) | ||||||
|  |         part = {} | ||||||
|  |         while reader.current: | ||||||
|  |             tag, content = reader.next_tag() | ||||||
|  |             if tag in ("Files", "Copyright", "License"): | ||||||
|  |                 part[tag] = content[:] | ||||||
|  |             elif tag == "Comment" and part: | ||||||
|  |                 # attach non-empty part to named project | ||||||
|  |                 projects[content[0]] = projects.get(content[0], []) + [part] | ||||||
|  | 
 | ||||||
|  |             if not tag or not reader.current: | ||||||
|  |                 # end of a paragraph start a new part | ||||||
|  |                 if "License" in part and "Files" not in part: | ||||||
|  |                     # no Files tag in this one, so assume standalone license | ||||||
|  |                     license_list.append(part["License"]) | ||||||
|  |                 part = {} | ||||||
|  |                 reader.next_line() | ||||||
|  | 
 | ||||||
|  |     data_list = [] | ||||||
|  |     for project in iter(projects.values()): | ||||||
|  |         for part in project: | ||||||
|  |             part["file_index"] = len(data_list) | ||||||
|  |             data_list += part["Files"] | ||||||
|  |             part["copyright_index"] = len(data_list) | ||||||
|  |             data_list += part["Copyright"] | ||||||
|  | 
 | ||||||
|  |     with open(src_license, "r", encoding="utf-8") as file: | ||||||
|  |         license_text = file.read() | ||||||
|  | 
 | ||||||
|  |     with methods.generated_wrapper(str(target[0])) as file: | ||||||
|  |         file.write(f"""\ | ||||||
|  | inline constexpr const char *GODOT_LICENSE_TEXT = {{ | ||||||
|  | {methods.to_raw_cstring(license_text)} | ||||||
|  | }}; | ||||||
|  | 
 | ||||||
|  | struct ComponentCopyrightPart {{ | ||||||
|  | 	const char *license; | ||||||
|  | 	const char *const *files; | ||||||
|  | 	const char *const *copyright_statements; | ||||||
|  | 	int file_count; | ||||||
|  | 	int copyright_count; | ||||||
|  | }}; | ||||||
|  | 
 | ||||||
|  | struct ComponentCopyright {{ | ||||||
|  | 	const char *name; | ||||||
|  | 	const ComponentCopyrightPart *parts; | ||||||
|  | 	int part_count; | ||||||
|  | }}; | ||||||
|  | 
 | ||||||
|  | """) | ||||||
|  | 
 | ||||||
|  |         file.write("inline constexpr const char *COPYRIGHT_INFO_DATA[] = {\n") | ||||||
|  |         for line in data_list: | ||||||
|  |             file.write(f'\t"{methods.to_escaped_cstring(line)}",\n') | ||||||
|  |         file.write("};\n\n") | ||||||
|  | 
 | ||||||
|  |         file.write("inline constexpr ComponentCopyrightPart COPYRIGHT_PROJECT_PARTS[] = {\n") | ||||||
|  |         part_index = 0 | ||||||
|  |         part_indexes = {} | ||||||
|  |         for project_name, project in iter(projects.items()): | ||||||
|  |             part_indexes[project_name] = part_index | ||||||
|  |             for part in project: | ||||||
|  |                 file.write( | ||||||
|  |                     f'\t{{ "{methods.to_escaped_cstring(part["License"][0])}", ' | ||||||
|  |                     + f"©RIGHT_INFO_DATA[{part['file_index']}], " | ||||||
|  |                     + f"©RIGHT_INFO_DATA[{part['copyright_index']}], " | ||||||
|  |                     + f"{len(part['Files'])}, {len(part['Copyright'])} }},\n" | ||||||
|  |                 ) | ||||||
|  |                 part_index += 1 | ||||||
|  |         file.write("};\n\n") | ||||||
|  | 
 | ||||||
|  |         file.write(f"inline constexpr int COPYRIGHT_INFO_COUNT = {len(projects)};\n") | ||||||
|  | 
 | ||||||
|  |         file.write("inline constexpr ComponentCopyright COPYRIGHT_INFO[] = {\n") | ||||||
|  |         for project_name, project in iter(projects.items()): | ||||||
|  |             file.write( | ||||||
|  |                 f'\t{{ "{methods.to_escaped_cstring(project_name)}", ' | ||||||
|  |                 + f"©RIGHT_PROJECT_PARTS[{part_indexes[project_name]}], " | ||||||
|  |                 + f"{len(project)} }},\n" | ||||||
|  |             ) | ||||||
|  |         file.write("};\n\n") | ||||||
|  | 
 | ||||||
|  |         file.write(f"inline constexpr int LICENSE_COUNT = {len(license_list)};\n") | ||||||
|  | 
 | ||||||
|  |         file.write("inline constexpr const char *LICENSE_NAMES[] = {\n") | ||||||
|  |         for license in license_list: | ||||||
|  |             file.write(f'\t"{methods.to_escaped_cstring(license[0])}",\n') | ||||||
|  |         file.write("};\n\n") | ||||||
|  | 
 | ||||||
|  |         file.write("inline constexpr const char *LICENSE_BODIES[] = {\n\n") | ||||||
|  |         for license in license_list: | ||||||
|  |             to_raw = [] | ||||||
|  |             for line in license[1:]: | ||||||
|  |                 if line == ".": | ||||||
|  |                     to_raw += [""] | ||||||
|  |                 else: | ||||||
|  |                     to_raw += [line] | ||||||
|  |             file.write(f"{methods.to_raw_cstring(to_raw)},\n\n") | ||||||
|  |         file.write("};\n\n") | ||||||
							
								
								
									
										862
									
								
								engine/core/core_constants.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										862
									
								
								engine/core/core_constants.cpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,862 @@ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*  core_constants.cpp                                                    */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*                         This file is part of:                          */ | ||||||
|  | /*                             GODOT ENGINE                               */ | ||||||
|  | /*                        https://godotengine.org                         */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ | ||||||
|  | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */ | ||||||
|  | /*                                                                        */ | ||||||
|  | /* 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 "core_constants.h" | ||||||
|  | 
 | ||||||
|  | #include "core/input/input_event.h" | ||||||
|  | #include "core/object/class_db.h" | ||||||
|  | #include "core/os/keyboard.h" | ||||||
|  | #include "core/variant/variant.h" | ||||||
|  | 
 | ||||||
|  | struct _CoreConstant { | ||||||
|  | #ifdef DEBUG_METHODS_ENABLED | ||||||
|  | 	bool ignore_value_in_docs = false; | ||||||
|  | 	bool is_bitfield = false; | ||||||
|  | #endif | ||||||
|  | 	StringName enum_name; | ||||||
|  | 	const char *name = nullptr; | ||||||
|  | 	int64_t value = 0; | ||||||
|  | 
 | ||||||
|  | 	_CoreConstant() {} | ||||||
|  | 
 | ||||||
|  | #ifdef DEBUG_METHODS_ENABLED | ||||||
|  | 	_CoreConstant(const StringName &p_enum_name, const char *p_name, int64_t p_value, bool p_ignore_value_in_docs = false, bool p_is_bitfield = false) : | ||||||
|  | 			ignore_value_in_docs(p_ignore_value_in_docs), | ||||||
|  | 			is_bitfield(p_is_bitfield), | ||||||
|  | 			enum_name(p_enum_name), | ||||||
|  | 			name(p_name), | ||||||
|  | 			value(p_value) { | ||||||
|  | 	} | ||||||
|  | #else | ||||||
|  | 	_CoreConstant(const StringName &p_enum_name, const char *p_name, int64_t p_value) : | ||||||
|  | 			enum_name(p_enum_name), | ||||||
|  | 			name(p_name), | ||||||
|  | 			value(p_value) { | ||||||
|  | 	} | ||||||
|  | #endif | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static Vector<_CoreConstant> _global_constants; | ||||||
|  | static HashMap<StringName, int> _global_constants_map; | ||||||
|  | static HashMap<StringName, Vector<_CoreConstant>> _global_enums; | ||||||
|  | 
 | ||||||
|  | #ifdef DEBUG_METHODS_ENABLED | ||||||
|  | 
 | ||||||
|  | #define BIND_CORE_CONSTANT(m_constant)                                                 \ | ||||||
|  | 	_global_constants.push_back(_CoreConstant(StringName(), #m_constant, m_constant)); \ | ||||||
|  | 	_global_constants_map[#m_constant] = _global_constants.size() - 1; | ||||||
|  | 
 | ||||||
|  | #define BIND_CORE_ENUM_CONSTANT(m_constant)                                                          \ | ||||||
|  | 	{                                                                                                \ | ||||||
|  | 		StringName enum_name = __constant_get_enum_name(m_constant, #m_constant);                    \ | ||||||
|  | 		_global_constants.push_back(_CoreConstant(enum_name, #m_constant, m_constant));              \ | ||||||
|  | 		_global_constants_map[#m_constant] = _global_constants.size() - 1;                           \ | ||||||
|  | 		_global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \ | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | #define BIND_CORE_BITFIELD_FLAG(m_constant)                                                          \ | ||||||
|  | 	{                                                                                                \ | ||||||
|  | 		StringName enum_name = __constant_get_bitfield_name(m_constant, #m_constant);                \ | ||||||
|  | 		_global_constants.push_back(_CoreConstant(enum_name, #m_constant, m_constant, false, true)); \ | ||||||
|  | 		_global_constants_map[#m_constant] = _global_constants.size() - 1;                           \ | ||||||
|  | 		_global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \ | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | // This just binds enum classes as if they were regular enum constants.
 | ||||||
|  | #define BIND_CORE_ENUM_CLASS_CONSTANT(m_enum, m_prefix, m_member)                                                  \ | ||||||
|  | 	{                                                                                                              \ | ||||||
|  | 		StringName enum_name = __constant_get_enum_name(m_enum::m_member, #m_prefix "_" #m_member);                \ | ||||||
|  | 		_global_constants.push_back(_CoreConstant(enum_name, #m_prefix "_" #m_member, (int64_t)m_enum::m_member)); \ | ||||||
|  | 		_global_constants_map[#m_prefix "_" #m_member] = _global_constants.size() - 1;                             \ | ||||||
|  | 		_global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]);               \ | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | #define BIND_CORE_BITFIELD_CLASS_FLAG(m_enum, m_prefix, m_member)                                                               \ | ||||||
|  | 	{                                                                                                                           \ | ||||||
|  | 		StringName enum_name = __constant_get_bitfield_name(m_enum::m_member, #m_prefix "_" #m_member);                         \ | ||||||
|  | 		_global_constants.push_back(_CoreConstant(enum_name, #m_prefix "_" #m_member, (int64_t)m_enum::m_member, false, true)); \ | ||||||
|  | 		_global_constants_map[#m_prefix "_" #m_member] = _global_constants.size() - 1;                                          \ | ||||||
|  | 		_global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]);                            \ | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | #define BIND_CORE_ENUM_CLASS_CONSTANT_CUSTOM(m_enum, m_name, m_member)                               \ | ||||||
|  | 	{                                                                                                \ | ||||||
|  | 		StringName enum_name = __constant_get_enum_name(m_enum::m_member, #m_name);                  \ | ||||||
|  | 		_global_constants.push_back(_CoreConstant(enum_name, #m_name, (int64_t)m_enum::m_member));   \ | ||||||
|  | 		_global_constants_map[#m_name] = _global_constants.size() - 1;                               \ | ||||||
|  | 		_global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \ | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | #define BIND_CORE_BITFIELD_CLASS_FLAG_CUSTOM(m_enum, m_name, m_member)                                          \ | ||||||
|  | 	{                                                                                                           \ | ||||||
|  | 		StringName enum_name = __constant_get_bitfield_name(m_enum::m_member, #m_name);                         \ | ||||||
|  | 		_global_constants.push_back(_CoreConstant(enum_name, #m_name, (int64_t)m_enum::m_member, false, true)); \ | ||||||
|  | 		_global_constants_map[#m_name] = _global_constants.size() - 1;                                          \ | ||||||
|  | 		_global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]);            \ | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | #define BIND_CORE_ENUM_CLASS_CONSTANT_NO_VAL(m_enum, m_prefix, m_member)                                                 \ | ||||||
|  | 	{                                                                                                                    \ | ||||||
|  | 		StringName enum_name = __constant_get_enum_name(m_enum::m_member, #m_prefix "_" #m_member);                      \ | ||||||
|  | 		_global_constants.push_back(_CoreConstant(enum_name, #m_prefix "_" #m_member, (int64_t)m_enum::m_member, true)); \ | ||||||
|  | 		_global_constants_map[#m_prefix "_" #m_member] = _global_constants.size() - 1;                                   \ | ||||||
|  | 		_global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]);                     \ | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | #define BIND_CORE_ENUM_CONSTANT_CUSTOM(m_custom_name, m_constant)                                    \ | ||||||
|  | 	{                                                                                                \ | ||||||
|  | 		StringName enum_name = __constant_get_enum_name(m_constant, #m_constant);                    \ | ||||||
|  | 		_global_constants.push_back(_CoreConstant(enum_name, m_custom_name, m_constant));            \ | ||||||
|  | 		_global_constants_map[m_custom_name] = _global_constants.size() - 1;                         \ | ||||||
|  | 		_global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \ | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | #define BIND_CORE_CONSTANT_NO_VAL(m_constant)                                                \ | ||||||
|  | 	_global_constants.push_back(_CoreConstant(StringName(), #m_constant, m_constant, true)); \ | ||||||
|  | 	_global_constants_map[#m_constant] = _global_constants.size() - 1; | ||||||
|  | 
 | ||||||
|  | #define BIND_CORE_ENUM_CONSTANT_NO_VAL(m_constant)                                                   \ | ||||||
|  | 	{                                                                                                \ | ||||||
|  | 		StringName enum_name = __constant_get_enum_name(m_constant, #m_constant);                    \ | ||||||
|  | 		_global_constants.push_back(_CoreConstant(enum_name, #m_constant, m_constant, true));        \ | ||||||
|  | 		_global_constants_map[#m_constant] = _global_constants.size() - 1;                           \ | ||||||
|  | 		_global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \ | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | #define BIND_CORE_ENUM_CONSTANT_CUSTOM_NO_VAL(m_custom_name, m_constant)                             \ | ||||||
|  | 	{                                                                                                \ | ||||||
|  | 		StringName enum_name = __constant_get_enum_name(m_constant, #m_constant);                    \ | ||||||
|  | 		_global_constants.push_back(_CoreConstant(enum_name, m_custom_name, m_constant, true));      \ | ||||||
|  | 		_global_constants_map[m_custom_name] = _global_constants.size() - 1;                         \ | ||||||
|  | 		_global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \ | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | #else | ||||||
|  | 
 | ||||||
|  | #define BIND_CORE_CONSTANT(m_constant)                                                 \ | ||||||
|  | 	_global_constants.push_back(_CoreConstant(StringName(), #m_constant, m_constant)); \ | ||||||
|  | 	_global_constants_map[#m_constant] = _global_constants.size() - 1; | ||||||
|  | 
 | ||||||
|  | #define BIND_CORE_ENUM_CONSTANT(m_constant)                                                          \ | ||||||
|  | 	{                                                                                                \ | ||||||
|  | 		StringName enum_name = __constant_get_enum_name(m_constant, #m_constant);                    \ | ||||||
|  | 		_global_constants.push_back(_CoreConstant(enum_name, #m_constant, m_constant));              \ | ||||||
|  | 		_global_constants_map[#m_constant] = _global_constants.size() - 1;                           \ | ||||||
|  | 		_global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \ | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | #define BIND_CORE_BITFIELD_FLAG(m_constant)                                                          \ | ||||||
|  | 	{                                                                                                \ | ||||||
|  | 		StringName enum_name = __constant_get_bitfield_name(m_constant, #m_constant);                \ | ||||||
|  | 		_global_constants.push_back(_CoreConstant(enum_name, #m_constant, m_constant));              \ | ||||||
|  | 		_global_constants_map[#m_constant] = _global_constants.size() - 1;                           \ | ||||||
|  | 		_global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \ | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | // This just binds enum classes as if they were regular enum constants.
 | ||||||
|  | #define BIND_CORE_ENUM_CLASS_CONSTANT(m_enum, m_prefix, m_member)                                                  \ | ||||||
|  | 	{                                                                                                              \ | ||||||
|  | 		StringName enum_name = __constant_get_enum_name(m_enum::m_member, #m_prefix "_" #m_member);                \ | ||||||
|  | 		_global_constants.push_back(_CoreConstant(enum_name, #m_prefix "_" #m_member, (int64_t)m_enum::m_member)); \ | ||||||
|  | 		_global_constants_map[#m_prefix "_" #m_member] = _global_constants.size() - 1;                             \ | ||||||
|  | 		_global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]);               \ | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | #define BIND_CORE_BITFIELD_CLASS_FLAG(m_enum, m_prefix, m_member)                                                  \ | ||||||
|  | 	{                                                                                                              \ | ||||||
|  | 		StringName enum_name = __constant_get_bitfield_name(m_enum::m_member, #m_prefix "_" #m_member);            \ | ||||||
|  | 		_global_constants.push_back(_CoreConstant(enum_name, #m_prefix "_" #m_member, (int64_t)m_enum::m_member)); \ | ||||||
|  | 		_global_constants_map[#m_prefix "_" #m_member] = _global_constants.size() - 1;                             \ | ||||||
|  | 		_global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]);               \ | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | #define BIND_CORE_ENUM_CLASS_CONSTANT_CUSTOM(m_enum, m_name, m_member)                               \ | ||||||
|  | 	{                                                                                                \ | ||||||
|  | 		StringName enum_name = __constant_get_enum_name(m_enum::m_member, #m_name);                  \ | ||||||
|  | 		_global_constants.push_back(_CoreConstant(enum_name, #m_name, (int64_t)m_enum::m_member));   \ | ||||||
|  | 		_global_constants_map[#m_name] = _global_constants.size() - 1;                               \ | ||||||
|  | 		_global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \ | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | #define BIND_CORE_BITFIELD_CLASS_FLAG_CUSTOM(m_enum, m_name, m_member)                               \ | ||||||
|  | 	{                                                                                                \ | ||||||
|  | 		StringName enum_name = __constant_get_bitfield_name(m_enum::m_member, #m_name);              \ | ||||||
|  | 		_global_constants.push_back(_CoreConstant(enum_name, #m_name, (int64_t)m_enum::m_member));   \ | ||||||
|  | 		_global_constants_map[#m_name] = _global_constants.size() - 1;                               \ | ||||||
|  | 		_global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \ | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | #define BIND_CORE_ENUM_CLASS_CONSTANT_NO_VAL(m_enum, m_prefix, m_member)                                           \ | ||||||
|  | 	{                                                                                                              \ | ||||||
|  | 		StringName enum_name = __constant_get_enum_name(m_enum::m_member, #m_prefix "_" #m_member);                \ | ||||||
|  | 		_global_constants.push_back(_CoreConstant(enum_name, #m_prefix "_" #m_member, (int64_t)m_enum::m_member)); \ | ||||||
|  | 		_global_constants_map[#m_prefix "_" #m_member] = _global_constants.size() - 1;                             \ | ||||||
|  | 		_global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]);               \ | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | #define BIND_CORE_ENUM_CONSTANT_CUSTOM(m_custom_name, m_constant)                                    \ | ||||||
|  | 	{                                                                                                \ | ||||||
|  | 		StringName enum_name = __constant_get_enum_name(m_constant, #m_constant);                    \ | ||||||
|  | 		_global_constants.push_back(_CoreConstant(enum_name, m_custom_name, m_constant));            \ | ||||||
|  | 		_global_constants_map[m_custom_name] = _global_constants.size() - 1;                         \ | ||||||
|  | 		_global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \ | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | #define BIND_CORE_CONSTANT_NO_VAL(m_constant)                                          \ | ||||||
|  | 	_global_constants.push_back(_CoreConstant(StringName(), #m_constant, m_constant)); \ | ||||||
|  | 	_global_constants_map[#m_constant] = _global_constants.size() - 1; | ||||||
|  | 
 | ||||||
|  | #define BIND_CORE_ENUM_CONSTANT_NO_VAL(m_constant)                                                   \ | ||||||
|  | 	{                                                                                                \ | ||||||
|  | 		StringName enum_name = __constant_get_enum_name(m_constant, #m_constant);                    \ | ||||||
|  | 		_global_constants.push_back(_CoreConstant(enum_name, #m_constant, m_constant));              \ | ||||||
|  | 		_global_constants_map[#m_constant] = _global_constants.size() - 1;                           \ | ||||||
|  | 		_global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \ | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | #define BIND_CORE_ENUM_CONSTANT_CUSTOM_NO_VAL(m_custom_name, m_constant)                             \ | ||||||
|  | 	{                                                                                                \ | ||||||
|  | 		StringName enum_name = __constant_get_enum_name(m_constant, #m_constant);                    \ | ||||||
|  | 		_global_constants.push_back(_CoreConstant(enum_name, m_custom_name, m_constant));            \ | ||||||
|  | 		_global_constants_map[m_custom_name] = _global_constants.size() - 1;                         \ | ||||||
|  | 		_global_enums[enum_name].push_back((_global_constants.ptr())[_global_constants.size() - 1]); \ | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | void register_global_constants() { | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(SIDE_LEFT); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(SIDE_TOP); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(SIDE_RIGHT); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(SIDE_BOTTOM); | ||||||
|  | 
 | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(CORNER_TOP_LEFT); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(CORNER_TOP_RIGHT); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(CORNER_BOTTOM_RIGHT); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(CORNER_BOTTOM_LEFT); | ||||||
|  | 
 | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(VERTICAL); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(HORIZONTAL); | ||||||
|  | 
 | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(CLOCKWISE); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(COUNTERCLOCKWISE); | ||||||
|  | 
 | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(HORIZONTAL_ALIGNMENT_LEFT); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(HORIZONTAL_ALIGNMENT_CENTER); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(HORIZONTAL_ALIGNMENT_RIGHT); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(HORIZONTAL_ALIGNMENT_FILL); | ||||||
|  | 
 | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(VERTICAL_ALIGNMENT_TOP); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(VERTICAL_ALIGNMENT_CENTER); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(VERTICAL_ALIGNMENT_BOTTOM); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(VERTICAL_ALIGNMENT_FILL); | ||||||
|  | 
 | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(INLINE_ALIGNMENT_TOP_TO); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(INLINE_ALIGNMENT_CENTER_TO); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(INLINE_ALIGNMENT_BASELINE_TO); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(INLINE_ALIGNMENT_BOTTOM_TO); | ||||||
|  | 
 | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(INLINE_ALIGNMENT_TO_TOP); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(INLINE_ALIGNMENT_TO_CENTER); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(INLINE_ALIGNMENT_TO_BASELINE); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(INLINE_ALIGNMENT_TO_BOTTOM); | ||||||
|  | 
 | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(INLINE_ALIGNMENT_TOP); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(INLINE_ALIGNMENT_CENTER); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(INLINE_ALIGNMENT_BOTTOM); | ||||||
|  | 
 | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(INLINE_ALIGNMENT_IMAGE_MASK); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(INLINE_ALIGNMENT_TEXT_MASK); | ||||||
|  | 
 | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(EulerOrder, EULER_ORDER, XYZ); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(EulerOrder, EULER_ORDER, XZY); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(EulerOrder, EULER_ORDER, YXZ); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(EulerOrder, EULER_ORDER, YZX); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(EulerOrder, EULER_ORDER, ZXY); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(EulerOrder, EULER_ORDER, ZYX); | ||||||
|  | 
 | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, NONE); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, SPECIAL); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, ESCAPE); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, TAB); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, BACKTAB); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, BACKSPACE); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, ENTER); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, KP_ENTER); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, INSERT); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT_CUSTOM(Key, KEY_DELETE, KEY_DELETE); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, PAUSE); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, PRINT); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, SYSREQ); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, CLEAR); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, HOME); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, END); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, LEFT); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, UP); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, RIGHT); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, DOWN); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, PAGEUP); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, PAGEDOWN); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, SHIFT); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, CTRL); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, META); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, ALT); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, CAPSLOCK); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, NUMLOCK); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, SCROLLLOCK); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, F1); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, F2); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, F3); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, F4); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, F5); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, F6); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, F7); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, F8); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, F9); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, F10); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, F11); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, F12); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, F13); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, F14); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, F15); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, F16); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, F17); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, F18); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, F19); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, F20); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, F21); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, F22); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, F23); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, F24); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, F25); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, F26); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, F27); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, F28); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, F29); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, F30); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, F31); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, F32); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, F33); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, F34); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, F35); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, KP_MULTIPLY); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, KP_DIVIDE); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, KP_SUBTRACT); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, KP_PERIOD); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, KP_ADD); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, KP_0); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, KP_1); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, KP_2); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, KP_3); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, KP_4); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, KP_5); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, KP_6); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, KP_7); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, KP_8); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, KP_9); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, MENU); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, HYPER); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, HELP); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, BACK); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, FORWARD); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, STOP); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, REFRESH); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, VOLUMEDOWN); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, VOLUMEMUTE); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, VOLUMEUP); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, MEDIAPLAY); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, MEDIASTOP); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, MEDIAPREVIOUS); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, MEDIANEXT); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, MEDIARECORD); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, HOMEPAGE); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, FAVORITES); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, SEARCH); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, STANDBY); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, OPENURL); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, LAUNCHMAIL); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, LAUNCHMEDIA); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, LAUNCH0); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, LAUNCH1); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, LAUNCH2); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, LAUNCH3); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, LAUNCH4); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, LAUNCH5); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, LAUNCH6); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, LAUNCH7); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, LAUNCH8); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, LAUNCH9); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, LAUNCHA); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, LAUNCHB); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, LAUNCHC); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, LAUNCHD); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, LAUNCHE); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, LAUNCHF); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, GLOBE); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, KEYBOARD); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, JIS_EISU); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, JIS_KANA); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, UNKNOWN); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, SPACE); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, EXCLAM); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, QUOTEDBL); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, NUMBERSIGN); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, DOLLAR); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, PERCENT); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, AMPERSAND); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, APOSTROPHE); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, PARENLEFT); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, PARENRIGHT); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, ASTERISK); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, PLUS); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, COMMA); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, MINUS); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, PERIOD); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, SLASH); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT_CUSTOM(Key, KEY_0, KEY_0); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT_CUSTOM(Key, KEY_1, KEY_1); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT_CUSTOM(Key, KEY_2, KEY_2); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT_CUSTOM(Key, KEY_3, KEY_3); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT_CUSTOM(Key, KEY_4, KEY_4); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT_CUSTOM(Key, KEY_5, KEY_5); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT_CUSTOM(Key, KEY_6, KEY_6); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT_CUSTOM(Key, KEY_7, KEY_7); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT_CUSTOM(Key, KEY_8, KEY_8); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT_CUSTOM(Key, KEY_9, KEY_9); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, COLON); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, SEMICOLON); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, LESS); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, EQUAL); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, GREATER); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, QUESTION); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, AT); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, A); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, B); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, C); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, D); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, E); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, F); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, G); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, H); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, I); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, J); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, K); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, L); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, M); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, N); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, O); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, P); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, Q); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, R); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, S); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, T); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, U); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, V); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, W); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, X); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, Y); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, Z); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, BRACKETLEFT); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, BACKSLASH); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, BRACKETRIGHT); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, ASCIICIRCUM); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, UNDERSCORE); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, QUOTELEFT); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, BRACELEFT); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, BAR); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, BRACERIGHT); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, ASCIITILDE); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, YEN); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(Key, KEY, SECTION); | ||||||
|  | 
 | ||||||
|  | 	BIND_CORE_BITFIELD_CLASS_FLAG_CUSTOM(KeyModifierMask, KEY_CODE_MASK, CODE_MASK); | ||||||
|  | 	BIND_CORE_BITFIELD_CLASS_FLAG_CUSTOM(KeyModifierMask, KEY_MODIFIER_MASK, MODIFIER_MASK); | ||||||
|  | 	BIND_CORE_BITFIELD_CLASS_FLAG(KeyModifierMask, KEY_MASK, CMD_OR_CTRL); | ||||||
|  | 	BIND_CORE_BITFIELD_CLASS_FLAG(KeyModifierMask, KEY_MASK, SHIFT); | ||||||
|  | 	BIND_CORE_BITFIELD_CLASS_FLAG(KeyModifierMask, KEY_MASK, ALT); | ||||||
|  | 	BIND_CORE_BITFIELD_CLASS_FLAG(KeyModifierMask, KEY_MASK, META); | ||||||
|  | 	BIND_CORE_BITFIELD_CLASS_FLAG(KeyModifierMask, KEY_MASK, CTRL); | ||||||
|  | 	BIND_CORE_BITFIELD_CLASS_FLAG(KeyModifierMask, KEY_MASK, KPAD); | ||||||
|  | 	BIND_CORE_BITFIELD_CLASS_FLAG(KeyModifierMask, KEY_MASK, GROUP_SWITCH); | ||||||
|  | 
 | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(KeyLocation, KEY_LOCATION, UNSPECIFIED); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(KeyLocation, KEY_LOCATION, LEFT); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(KeyLocation, KEY_LOCATION, RIGHT); | ||||||
|  | 
 | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(MouseButton, MOUSE_BUTTON, NONE); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(MouseButton, MOUSE_BUTTON, LEFT); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(MouseButton, MOUSE_BUTTON, RIGHT); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(MouseButton, MOUSE_BUTTON, MIDDLE); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(MouseButton, MOUSE_BUTTON, WHEEL_UP); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(MouseButton, MOUSE_BUTTON, WHEEL_DOWN); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(MouseButton, MOUSE_BUTTON, WHEEL_LEFT); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(MouseButton, MOUSE_BUTTON, WHEEL_RIGHT); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT_CUSTOM(MouseButton, MOUSE_BUTTON_XBUTTON1, MB_XBUTTON1); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT_CUSTOM(MouseButton, MOUSE_BUTTON_XBUTTON2, MB_XBUTTON2); | ||||||
|  | 
 | ||||||
|  | 	BIND_CORE_BITFIELD_CLASS_FLAG(MouseButtonMask, MOUSE_BUTTON_MASK, LEFT); | ||||||
|  | 	BIND_CORE_BITFIELD_CLASS_FLAG(MouseButtonMask, MOUSE_BUTTON_MASK, RIGHT); | ||||||
|  | 	BIND_CORE_BITFIELD_CLASS_FLAG(MouseButtonMask, MOUSE_BUTTON_MASK, MIDDLE); | ||||||
|  | 	BIND_CORE_BITFIELD_CLASS_FLAG(MouseButtonMask, MOUSE_BUTTON_MASK, MB_XBUTTON1); | ||||||
|  | 	BIND_CORE_BITFIELD_CLASS_FLAG(MouseButtonMask, MOUSE_BUTTON_MASK, MB_XBUTTON2); | ||||||
|  | 
 | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, INVALID); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, A); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, B); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, X); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, Y); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, BACK); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, GUIDE); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, START); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, LEFT_STICK); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, RIGHT_STICK); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, LEFT_SHOULDER); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, RIGHT_SHOULDER); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, DPAD_UP); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, DPAD_DOWN); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, DPAD_LEFT); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, DPAD_RIGHT); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, MISC1); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, PADDLE1); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, PADDLE2); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, PADDLE3); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, PADDLE4); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, TOUCHPAD); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, SDL_MAX); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(JoyButton, JOY_BUTTON, MAX); | ||||||
|  | 
 | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(JoyAxis, JOY_AXIS, INVALID); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(JoyAxis, JOY_AXIS, LEFT_X); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(JoyAxis, JOY_AXIS, LEFT_Y); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(JoyAxis, JOY_AXIS, RIGHT_X); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(JoyAxis, JOY_AXIS, RIGHT_Y); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(JoyAxis, JOY_AXIS, TRIGGER_LEFT); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(JoyAxis, JOY_AXIS, TRIGGER_RIGHT); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(JoyAxis, JOY_AXIS, SDL_MAX); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(JoyAxis, JOY_AXIS, MAX); | ||||||
|  | 
 | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(MIDIMessage, MIDI_MESSAGE, NONE); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(MIDIMessage, MIDI_MESSAGE, NOTE_OFF); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(MIDIMessage, MIDI_MESSAGE, NOTE_ON); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(MIDIMessage, MIDI_MESSAGE, AFTERTOUCH); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(MIDIMessage, MIDI_MESSAGE, CONTROL_CHANGE); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(MIDIMessage, MIDI_MESSAGE, PROGRAM_CHANGE); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(MIDIMessage, MIDI_MESSAGE, CHANNEL_PRESSURE); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(MIDIMessage, MIDI_MESSAGE, PITCH_BEND); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(MIDIMessage, MIDI_MESSAGE, SYSTEM_EXCLUSIVE); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(MIDIMessage, MIDI_MESSAGE, QUARTER_FRAME); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(MIDIMessage, MIDI_MESSAGE, SONG_POSITION_POINTER); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(MIDIMessage, MIDI_MESSAGE, SONG_SELECT); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(MIDIMessage, MIDI_MESSAGE, TUNE_REQUEST); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(MIDIMessage, MIDI_MESSAGE, TIMING_CLOCK); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(MIDIMessage, MIDI_MESSAGE, START); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(MIDIMessage, MIDI_MESSAGE, CONTINUE); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(MIDIMessage, MIDI_MESSAGE, STOP); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(MIDIMessage, MIDI_MESSAGE, ACTIVE_SENSING); | ||||||
|  | 	BIND_CORE_ENUM_CLASS_CONSTANT(MIDIMessage, MIDI_MESSAGE, SYSTEM_RESET); | ||||||
|  | 
 | ||||||
|  | 	// error list
 | ||||||
|  | 
 | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(OK); // (0)
 | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(FAILED); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(ERR_UNAVAILABLE); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(ERR_UNCONFIGURED); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(ERR_UNAUTHORIZED); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(ERR_PARAMETER_RANGE_ERROR); // (5)
 | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(ERR_OUT_OF_MEMORY); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(ERR_FILE_NOT_FOUND); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(ERR_FILE_BAD_DRIVE); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(ERR_FILE_BAD_PATH); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(ERR_FILE_NO_PERMISSION); // (10)
 | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(ERR_FILE_ALREADY_IN_USE); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(ERR_FILE_CANT_OPEN); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(ERR_FILE_CANT_WRITE); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(ERR_FILE_CANT_READ); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(ERR_FILE_UNRECOGNIZED); // (15)
 | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(ERR_FILE_CORRUPT); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(ERR_FILE_MISSING_DEPENDENCIES); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(ERR_FILE_EOF); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(ERR_CANT_OPEN); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(ERR_CANT_CREATE); // (20)
 | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(ERR_QUERY_FAILED); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(ERR_ALREADY_IN_USE); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(ERR_LOCKED); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(ERR_TIMEOUT); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(ERR_CANT_CONNECT); // (25)
 | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(ERR_CANT_RESOLVE); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(ERR_CONNECTION_ERROR); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(ERR_CANT_ACQUIRE_RESOURCE); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(ERR_CANT_FORK); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(ERR_INVALID_DATA); // (30)
 | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(ERR_INVALID_PARAMETER); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(ERR_ALREADY_EXISTS); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(ERR_DOES_NOT_EXIST); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(ERR_DATABASE_CANT_READ); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(ERR_DATABASE_CANT_WRITE); // (35)
 | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(ERR_COMPILATION_FAILED); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(ERR_METHOD_NOT_FOUND); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(ERR_LINK_FAILED); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(ERR_SCRIPT_FAILED); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(ERR_CYCLIC_LINK); // (40)
 | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(ERR_INVALID_DECLARATION); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(ERR_DUPLICATE_SYMBOL); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(ERR_PARSE_ERROR); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(ERR_BUSY); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(ERR_SKIP); // (45)
 | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(ERR_HELP); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(ERR_BUG); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(ERR_PRINTER_ON_FIRE); | ||||||
|  | 
 | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_NONE); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_RANGE); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_ENUM); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_ENUM_SUGGESTION); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_EXP_EASING); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_LINK); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_FLAGS); | ||||||
|  | 
 | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_LAYERS_2D_RENDER); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_LAYERS_2D_PHYSICS); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_LAYERS_2D_NAVIGATION); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_LAYERS_3D_RENDER); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_LAYERS_3D_PHYSICS); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_LAYERS_3D_NAVIGATION); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_LAYERS_AVOIDANCE); | ||||||
|  | 
 | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_FILE); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_DIR); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_GLOBAL_FILE); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_GLOBAL_DIR); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_RESOURCE_TYPE); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_MULTILINE_TEXT); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_EXPRESSION); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_PLACEHOLDER_TEXT); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_COLOR_NO_ALPHA); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_OBJECT_ID); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_TYPE_STRING); | ||||||
|  | 
 | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_OBJECT_TOO_BIG); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_NODE_PATH_VALID_TYPES); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_SAVE_FILE); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_GLOBAL_SAVE_FILE); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_INT_IS_OBJECTID); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_INT_IS_POINTER); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_ARRAY_TYPE); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_DICTIONARY_TYPE); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_LOCALE_ID); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_LOCALIZABLE_STRING); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_NODE_TYPE); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_HIDE_QUATERNION_EDIT); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_PASSWORD); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_TOOL_BUTTON); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_ONESHOT); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_MAX); | ||||||
|  | 
 | ||||||
|  | 	BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_NONE); | ||||||
|  | 	BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_STORAGE); | ||||||
|  | 	BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_EDITOR); | ||||||
|  | 	BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_INTERNAL); | ||||||
|  | 
 | ||||||
|  | 	BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_CHECKABLE); | ||||||
|  | 	BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_CHECKED); | ||||||
|  | 	BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_GROUP); | ||||||
|  | 	BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_CATEGORY); | ||||||
|  | 	BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_SUBGROUP); | ||||||
|  | 	BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_CLASS_IS_BITFIELD); | ||||||
|  | 	BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_NO_INSTANCE_STATE); | ||||||
|  | 	BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_RESTART_IF_CHANGED); | ||||||
|  | 	BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_SCRIPT_VARIABLE); | ||||||
|  | 
 | ||||||
|  | 	BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_STORE_IF_NULL); | ||||||
|  | 	BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED); | ||||||
|  | 	BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_SCRIPT_DEFAULT_VALUE); | ||||||
|  | 	BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_CLASS_IS_ENUM); | ||||||
|  | 	BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_NIL_IS_VARIANT); | ||||||
|  | 	BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_ARRAY); | ||||||
|  | 	BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_ALWAYS_DUPLICATE); | ||||||
|  | 	BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_NEVER_DUPLICATE); | ||||||
|  | 	BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_HIGH_END_GFX); | ||||||
|  | 	BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_NODE_PATH_FROM_SCENE_ROOT); | ||||||
|  | 	BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT); | ||||||
|  | 	BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_KEYING_INCREMENTS); | ||||||
|  | 	BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_DEFERRED_SET_RESOURCE); | ||||||
|  | 	BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT); | ||||||
|  | 	BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_EDITOR_BASIC_SETTING); | ||||||
|  | 	BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_READ_ONLY); | ||||||
|  | 	BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_SECRET); | ||||||
|  | 
 | ||||||
|  | 	BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_DEFAULT); | ||||||
|  | 	BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_NO_EDITOR); | ||||||
|  | 
 | ||||||
|  | 	BIND_CORE_BITFIELD_FLAG(METHOD_FLAG_NORMAL); | ||||||
|  | 	BIND_CORE_BITFIELD_FLAG(METHOD_FLAG_EDITOR); | ||||||
|  | 	BIND_CORE_BITFIELD_FLAG(METHOD_FLAG_CONST); | ||||||
|  | 	BIND_CORE_BITFIELD_FLAG(METHOD_FLAG_VIRTUAL); | ||||||
|  | 	BIND_CORE_BITFIELD_FLAG(METHOD_FLAG_VARARG); | ||||||
|  | 	BIND_CORE_BITFIELD_FLAG(METHOD_FLAG_STATIC); | ||||||
|  | 	BIND_CORE_BITFIELD_FLAG(METHOD_FLAG_OBJECT_CORE); | ||||||
|  | 	BIND_CORE_BITFIELD_FLAG(METHOD_FLAG_VIRTUAL_REQUIRED); | ||||||
|  | 	BIND_CORE_BITFIELD_FLAG(METHOD_FLAGS_DEFAULT); | ||||||
|  | 
 | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_NIL", Variant::NIL); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_BOOL", Variant::BOOL); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_INT", Variant::INT); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_FLOAT", Variant::FLOAT); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_STRING", Variant::STRING); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_VECTOR2", Variant::VECTOR2); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_VECTOR2I", Variant::VECTOR2I); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_RECT2", Variant::RECT2); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_RECT2I", Variant::RECT2I); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_VECTOR3", Variant::VECTOR3); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_VECTOR3I", Variant::VECTOR3I); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_TRANSFORM2D", Variant::TRANSFORM2D); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_VECTOR4", Variant::VECTOR4); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_VECTOR4I", Variant::VECTOR4I); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_PLANE", Variant::PLANE); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_QUATERNION", Variant::QUATERNION); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_AABB", Variant::AABB); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_BASIS", Variant::BASIS); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_TRANSFORM3D", Variant::TRANSFORM3D); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_PROJECTION", Variant::PROJECTION); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_COLOR", Variant::COLOR); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_STRING_NAME", Variant::STRING_NAME); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_NODE_PATH", Variant::NODE_PATH); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_RID", Variant::RID); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_OBJECT", Variant::OBJECT); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_CALLABLE", Variant::CALLABLE); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_SIGNAL", Variant::SIGNAL); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_DICTIONARY", Variant::DICTIONARY); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_ARRAY", Variant::ARRAY); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_PACKED_BYTE_ARRAY", Variant::PACKED_BYTE_ARRAY); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_PACKED_INT32_ARRAY", Variant::PACKED_INT32_ARRAY); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_PACKED_INT64_ARRAY", Variant::PACKED_INT64_ARRAY); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_PACKED_FLOAT32_ARRAY", Variant::PACKED_FLOAT32_ARRAY); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_PACKED_FLOAT64_ARRAY", Variant::PACKED_FLOAT64_ARRAY); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_PACKED_STRING_ARRAY", Variant::PACKED_STRING_ARRAY); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_PACKED_VECTOR2_ARRAY", Variant::PACKED_VECTOR2_ARRAY); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_PACKED_VECTOR3_ARRAY", Variant::PACKED_VECTOR3_ARRAY); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_PACKED_COLOR_ARRAY", Variant::PACKED_COLOR_ARRAY); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_PACKED_VECTOR4_ARRAY", Variant::PACKED_VECTOR4_ARRAY); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT_CUSTOM("TYPE_MAX", Variant::VARIANT_MAX); | ||||||
|  | 
 | ||||||
|  | 	//comparison
 | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT_CUSTOM("OP_EQUAL", Variant::OP_EQUAL); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT_CUSTOM("OP_NOT_EQUAL", Variant::OP_NOT_EQUAL); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT_CUSTOM("OP_LESS", Variant::OP_LESS); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT_CUSTOM("OP_LESS_EQUAL", Variant::OP_LESS_EQUAL); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT_CUSTOM("OP_GREATER", Variant::OP_GREATER); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT_CUSTOM("OP_GREATER_EQUAL", Variant::OP_GREATER_EQUAL); | ||||||
|  | 	//mathematic
 | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT_CUSTOM("OP_ADD", Variant::OP_ADD); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT_CUSTOM("OP_SUBTRACT", Variant::OP_SUBTRACT); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT_CUSTOM("OP_MULTIPLY", Variant::OP_MULTIPLY); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT_CUSTOM("OP_DIVIDE", Variant::OP_DIVIDE); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT_CUSTOM("OP_NEGATE", Variant::OP_NEGATE); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT_CUSTOM("OP_POSITIVE", Variant::OP_POSITIVE); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT_CUSTOM("OP_MODULE", Variant::OP_MODULE); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT_CUSTOM("OP_POWER", Variant::OP_POWER); | ||||||
|  | 	//bitwise
 | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT_CUSTOM("OP_SHIFT_LEFT", Variant::OP_SHIFT_LEFT); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT_CUSTOM("OP_SHIFT_RIGHT", Variant::OP_SHIFT_RIGHT); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT_CUSTOM("OP_BIT_AND", Variant::OP_BIT_AND); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT_CUSTOM("OP_BIT_OR", Variant::OP_BIT_OR); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT_CUSTOM("OP_BIT_XOR", Variant::OP_BIT_XOR); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT_CUSTOM("OP_BIT_NEGATE", Variant::OP_BIT_NEGATE); | ||||||
|  | 	//logic
 | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT_CUSTOM("OP_AND", Variant::OP_AND); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT_CUSTOM("OP_OR", Variant::OP_OR); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT_CUSTOM("OP_XOR", Variant::OP_XOR); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT_CUSTOM("OP_NOT", Variant::OP_NOT); | ||||||
|  | 	//containment
 | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT_CUSTOM("OP_IN", Variant::OP_IN); | ||||||
|  | 	BIND_CORE_ENUM_CONSTANT_CUSTOM("OP_MAX", Variant::OP_MAX); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void unregister_global_constants() { | ||||||
|  | 	_global_constants.clear(); | ||||||
|  | 	_global_constants_map.clear(); | ||||||
|  | 	_global_enums.clear(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int CoreConstants::get_global_constant_count() { | ||||||
|  | 	return _global_constants.size(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | StringName CoreConstants::get_global_constant_enum(int p_idx) { | ||||||
|  | 	return _global_constants[p_idx].enum_name; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #ifdef DEBUG_METHODS_ENABLED | ||||||
|  | bool CoreConstants::is_global_constant_bitfield(int p_idx) { | ||||||
|  | 	return _global_constants[p_idx].is_bitfield; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool CoreConstants::get_ignore_value_in_docs(int p_idx) { | ||||||
|  | 	return _global_constants[p_idx].ignore_value_in_docs; | ||||||
|  | } | ||||||
|  | #else | ||||||
|  | bool CoreConstants::is_global_constant_bitfield(int p_idx) { | ||||||
|  | 	return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool CoreConstants::get_ignore_value_in_docs(int p_idx) { | ||||||
|  | 	return false; | ||||||
|  | } | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | const char *CoreConstants::get_global_constant_name(int p_idx) { | ||||||
|  | 	return _global_constants[p_idx].name; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int64_t CoreConstants::get_global_constant_value(int p_idx) { | ||||||
|  | 	return _global_constants[p_idx].value; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool CoreConstants::is_global_constant(const StringName &p_name) { | ||||||
|  | 	return _global_constants_map.has(p_name); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int CoreConstants::get_global_constant_index(const StringName &p_name) { | ||||||
|  | 	ERR_FAIL_COND_V_MSG(!_global_constants_map.has(p_name), -1, "Trying to get index of non-existing constant."); | ||||||
|  | 	return _global_constants_map[p_name]; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool CoreConstants::is_global_enum(const StringName &p_enum) { | ||||||
|  | 	return _global_enums.has(p_enum); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void CoreConstants::get_enum_values(const StringName &p_enum, HashMap<StringName, int64_t> *p_values) { | ||||||
|  | 	ERR_FAIL_NULL_MSG(p_values, "Trying to get enum values with null map."); | ||||||
|  | 	ERR_FAIL_COND_MSG(!_global_enums.has(p_enum), "Trying to get values of non-existing enum."); | ||||||
|  | 	for (const _CoreConstant &constant : _global_enums[p_enum]) { | ||||||
|  | 		(*p_values)[constant.name] = constant.value; | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										48
									
								
								engine/core/core_constants.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								engine/core/core_constants.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,48 @@ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*  core_constants.h                                                      */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*                         This file is part of:                          */ | ||||||
|  | /*                             GODOT ENGINE                               */ | ||||||
|  | /*                        https://godotengine.org                         */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ | ||||||
|  | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */ | ||||||
|  | /*                                                                        */ | ||||||
|  | /* 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.                 */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include "core/string/string_name.h" | ||||||
|  | #include "core/templates/hash_map.h" | ||||||
|  | 
 | ||||||
|  | class CoreConstants { | ||||||
|  | public: | ||||||
|  | 	static int get_global_constant_count(); | ||||||
|  | 	static StringName get_global_constant_enum(int p_idx); | ||||||
|  | 	static bool is_global_constant_bitfield(int p_idx); | ||||||
|  | 	static bool get_ignore_value_in_docs(int p_idx); | ||||||
|  | 	static const char *get_global_constant_name(int p_idx); | ||||||
|  | 	static int64_t get_global_constant_value(int p_idx); | ||||||
|  | 	static bool is_global_constant(const StringName &p_name); | ||||||
|  | 	static int get_global_constant_index(const StringName &p_name); | ||||||
|  | 	static bool is_global_enum(const StringName &p_enum); | ||||||
|  | 	static void get_enum_values(const StringName &p_enum, HashMap<StringName, int64_t> *p_values); | ||||||
|  | }; | ||||||
							
								
								
									
										35
									
								
								engine/core/core_globals.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								engine/core/core_globals.cpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,35 @@ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*  core_globals.cpp                                                      */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*                         This file is part of:                          */ | ||||||
|  | /*                             GODOT ENGINE                               */ | ||||||
|  | /*                        https://godotengine.org                         */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ | ||||||
|  | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */ | ||||||
|  | /*                                                                        */ | ||||||
|  | /* 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 "core_globals.h" | ||||||
|  | 
 | ||||||
|  | bool CoreGlobals::leak_reporting_enabled = true; | ||||||
|  | bool CoreGlobals::print_line_enabled = true; | ||||||
|  | bool CoreGlobals::print_error_enabled = true; | ||||||
							
								
								
									
										41
									
								
								engine/core/core_globals.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								engine/core/core_globals.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,41 @@ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*  core_globals.h                                                        */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*                         This file is part of:                          */ | ||||||
|  | /*                             GODOT ENGINE                               */ | ||||||
|  | /*                        https://godotengine.org                         */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ | ||||||
|  | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */ | ||||||
|  | /*                                                                        */ | ||||||
|  | /* 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.                 */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | // Home for state needed from global functions
 | ||||||
|  | // that cannot be stored in Engine or OS due to e.g. circular includes
 | ||||||
|  | 
 | ||||||
|  | class CoreGlobals { | ||||||
|  | public: | ||||||
|  | 	static bool leak_reporting_enabled; | ||||||
|  | 	static bool print_line_enabled; | ||||||
|  | 	static bool print_error_enabled; | ||||||
|  | }; | ||||||
							
								
								
									
										88
									
								
								engine/core/core_string_names.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								engine/core/core_string_names.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,88 @@ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*  core_string_names.h                                                   */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*                         This file is part of:                          */ | ||||||
|  | /*                             GODOT ENGINE                               */ | ||||||
|  | /*                        https://godotengine.org                         */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ | ||||||
|  | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */ | ||||||
|  | /*                                                                        */ | ||||||
|  | /* 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.                 */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include "core/string/string_name.h" | ||||||
|  | 
 | ||||||
|  | class CoreStringNames { | ||||||
|  | 	inline static CoreStringNames *singleton = nullptr; | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  | 	static void create() { singleton = memnew(CoreStringNames); } | ||||||
|  | 	static void free() { | ||||||
|  | 		memdelete(singleton); | ||||||
|  | 		singleton = nullptr; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	_FORCE_INLINE_ static CoreStringNames *get_singleton() { return singleton; } | ||||||
|  | 
 | ||||||
|  | 	const StringName free_ = StaticCString::create("free"); // free would conflict with C++ keyword.
 | ||||||
|  | 	const StringName changed = StaticCString::create("changed"); | ||||||
|  | 	const StringName script = StaticCString::create("script"); | ||||||
|  | 	const StringName script_changed = StaticCString::create("script_changed"); | ||||||
|  | 	const StringName _iter_init = StaticCString::create("_iter_init"); | ||||||
|  | 	const StringName _iter_next = StaticCString::create("_iter_next"); | ||||||
|  | 	const StringName _iter_get = StaticCString::create("_iter_get"); | ||||||
|  | 	const StringName get_rid = StaticCString::create("get_rid"); | ||||||
|  | 	const StringName _to_string = StaticCString::create("_to_string"); | ||||||
|  | 	const StringName _custom_features = StaticCString::create("_custom_features"); | ||||||
|  | 
 | ||||||
|  | 	const StringName x = StaticCString::create("x"); | ||||||
|  | 	const StringName y = StaticCString::create("y"); | ||||||
|  | 	const StringName z = StaticCString::create("z"); | ||||||
|  | 	const StringName w = StaticCString::create("w"); | ||||||
|  | 	const StringName r = StaticCString::create("r"); | ||||||
|  | 	const StringName g = StaticCString::create("g"); | ||||||
|  | 	const StringName b = StaticCString::create("b"); | ||||||
|  | 	const StringName a = StaticCString::create("a"); | ||||||
|  | 	const StringName position = StaticCString::create("position"); | ||||||
|  | 	const StringName size = StaticCString::create("size"); | ||||||
|  | 	const StringName end = StaticCString::create("end"); | ||||||
|  | 	const StringName basis = StaticCString::create("basis"); | ||||||
|  | 	const StringName origin = StaticCString::create("origin"); | ||||||
|  | 	const StringName normal = StaticCString::create("normal"); | ||||||
|  | 	const StringName d = StaticCString::create("d"); | ||||||
|  | 	const StringName h = StaticCString::create("h"); | ||||||
|  | 	const StringName s = StaticCString::create("s"); | ||||||
|  | 	const StringName v = StaticCString::create("v"); | ||||||
|  | 	const StringName r8 = StaticCString::create("r8"); | ||||||
|  | 	const StringName g8 = StaticCString::create("g8"); | ||||||
|  | 	const StringName b8 = StaticCString::create("b8"); | ||||||
|  | 	const StringName a8 = StaticCString::create("a8"); | ||||||
|  | 
 | ||||||
|  | 	const StringName call = StaticCString::create("call"); | ||||||
|  | 	const StringName call_deferred = StaticCString::create("call_deferred"); | ||||||
|  | 	const StringName bind = StaticCString::create("bind"); | ||||||
|  | 	const StringName notification = StaticCString::create("notification"); | ||||||
|  | 	const StringName property_list_changed = StaticCString::create("property_list_changed"); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | #define CoreStringName(m_name) CoreStringNames::get_singleton()->m_name | ||||||
							
								
								
									
										65
									
								
								engine/core/crypto/SCsub
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								engine/core/crypto/SCsub
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,65 @@ | ||||||
|  | #!/usr/bin/env python | ||||||
|  | from misc.utility.scons_hints import * | ||||||
|  | 
 | ||||||
|  | Import("env") | ||||||
|  | 
 | ||||||
|  | env_crypto = env.Clone() | ||||||
|  | 
 | ||||||
|  | is_builtin = env["builtin_mbedtls"] | ||||||
|  | has_module = env["module_mbedtls_enabled"] | ||||||
|  | thirdparty_obj = [] | ||||||
|  | 
 | ||||||
|  | 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(CPPEXTPATH=["#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: | ||||||
|  |     # Minimal mbedTLS config file | ||||||
|  |     config_path = "thirdparty/mbedtls/include/godot_core_mbedtls_config.h" | ||||||
|  |     config_path = f"<{config_path}>" if env_crypto["ninja"] and env_crypto.msvc else f'\\"{config_path}\\"' | ||||||
|  |     env_crypto.Append(CPPDEFINES=[("MBEDTLS_CONFIG_FILE", config_path)]) | ||||||
|  |     # Build minimal mbedTLS library (MD5/SHA/Base64/AES). | ||||||
|  |     env_thirdparty = env_crypto.Clone() | ||||||
|  |     env_thirdparty.disable_warnings() | ||||||
|  |     thirdparty_mbedtls_dir = "#thirdparty/mbedtls/library/" | ||||||
|  |     thirdparty_mbedtls_sources = [ | ||||||
|  |         "aes.c", | ||||||
|  |         "base64.c", | ||||||
|  |         "constant_time.c", | ||||||
|  |         "ctr_drbg.c", | ||||||
|  |         "entropy.c", | ||||||
|  |         "md.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(thirdparty_obj, thirdparty_mbedtls_sources) | ||||||
|  |     # Needed to force rebuilding the library when the configuration file is updated. | ||||||
|  |     env_thirdparty.Depends(thirdparty_obj, "#thirdparty/mbedtls/include/godot_core_mbedtls_config.h") | ||||||
|  |     env.core_sources += thirdparty_obj | ||||||
|  | elif is_builtin: | ||||||
|  |     # Module mbedTLS config file | ||||||
|  |     config_path = "thirdparty/mbedtls/include/godot_module_mbedtls_config.h" | ||||||
|  |     config_path = f"<{config_path}>" if env_crypto["ninja"] and env_crypto.msvc else f'\\"{config_path}\\"' | ||||||
|  |     env_crypto.Append(CPPDEFINES=[("MBEDTLS_CONFIG_FILE", config_path)]) | ||||||
|  |     # Needed to force rebuilding the core files when the configuration file is updated. | ||||||
|  |     thirdparty_obj = ["#thirdparty/mbedtls/include/godot_module_mbedtls_config.h"] | ||||||
|  | 
 | ||||||
|  | # Godot source files | ||||||
|  | 
 | ||||||
|  | core_obj = [] | ||||||
|  | 
 | ||||||
|  | env_crypto.add_source_files(core_obj, "*.cpp") | ||||||
|  | env.core_sources += core_obj | ||||||
|  | 
 | ||||||
|  | # Needed to force rebuilding the core files when the thirdparty library is updated. | ||||||
|  | env.Depends(core_obj, thirdparty_obj) | ||||||
							
								
								
									
										115
									
								
								engine/core/crypto/aes_context.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										115
									
								
								engine/core/crypto/aes_context.cpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,115 @@ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*  aes_context.cpp                                                       */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*                         This file is part of:                          */ | ||||||
|  | /*                             GODOT ENGINE                               */ | ||||||
|  | /*                        https://godotengine.org                         */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ | ||||||
|  | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */ | ||||||
|  | /*                                                                        */ | ||||||
|  | /* 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 "core/crypto/aes_context.h" | ||||||
|  | 
 | ||||||
|  | Error AESContext::start(Mode p_mode, const PackedByteArray &p_key, const PackedByteArray &p_iv) { | ||||||
|  | 	ERR_FAIL_COND_V_MSG(mode != MODE_MAX, ERR_ALREADY_IN_USE, "AESContext already started. Call 'finish' before starting a new one."); | ||||||
|  | 	ERR_FAIL_COND_V_MSG(p_mode < 0 || p_mode >= MODE_MAX, ERR_INVALID_PARAMETER, "Invalid mode requested."); | ||||||
|  | 	// Key check.
 | ||||||
|  | 	int key_bits = p_key.size() << 3; | ||||||
|  | 	ERR_FAIL_COND_V_MSG(key_bits != 128 && key_bits != 256, ERR_INVALID_PARAMETER, "AES key must be either 16 or 32 bytes"); | ||||||
|  | 	// Initialization vector.
 | ||||||
|  | 	if (p_mode == MODE_CBC_ENCRYPT || p_mode == MODE_CBC_DECRYPT) { | ||||||
|  | 		ERR_FAIL_COND_V_MSG(p_iv.size() != 16, ERR_INVALID_PARAMETER, "The initialization vector (IV) must be exactly 16 bytes."); | ||||||
|  | 		iv.resize(0); | ||||||
|  | 		iv.append_array(p_iv); | ||||||
|  | 	} | ||||||
|  | 	// Encryption/decryption key.
 | ||||||
|  | 	if (p_mode == MODE_CBC_ENCRYPT || p_mode == MODE_ECB_ENCRYPT) { | ||||||
|  | 		ctx.set_encode_key(p_key.ptr(), key_bits); | ||||||
|  | 	} else { | ||||||
|  | 		ctx.set_decode_key(p_key.ptr(), key_bits); | ||||||
|  | 	} | ||||||
|  | 	mode = p_mode; | ||||||
|  | 	return OK; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | PackedByteArray AESContext::update(const PackedByteArray &p_src) { | ||||||
|  | 	ERR_FAIL_COND_V_MSG(mode < 0 || mode >= MODE_MAX, PackedByteArray(), "AESContext not started. Call 'start' before calling 'update'."); | ||||||
|  | 	int len = p_src.size(); | ||||||
|  | 	ERR_FAIL_COND_V_MSG(len % 16, PackedByteArray(), "The number of bytes to be encrypted must be multiple of 16. Add padding if needed"); | ||||||
|  | 	PackedByteArray out; | ||||||
|  | 	out.resize(len); | ||||||
|  | 	const uint8_t *src_ptr = p_src.ptr(); | ||||||
|  | 	uint8_t *out_ptr = out.ptrw(); | ||||||
|  | 	switch (mode) { | ||||||
|  | 		case MODE_ECB_ENCRYPT: { | ||||||
|  | 			for (int i = 0; i < len; i += 16) { | ||||||
|  | 				Error err = ctx.encrypt_ecb(src_ptr + i, out_ptr + i); | ||||||
|  | 				ERR_FAIL_COND_V(err != OK, PackedByteArray()); | ||||||
|  | 			} | ||||||
|  | 		} break; | ||||||
|  | 		case MODE_ECB_DECRYPT: { | ||||||
|  | 			for (int i = 0; i < len; i += 16) { | ||||||
|  | 				Error err = ctx.decrypt_ecb(src_ptr + i, out_ptr + i); | ||||||
|  | 				ERR_FAIL_COND_V(err != OK, PackedByteArray()); | ||||||
|  | 			} | ||||||
|  | 		} break; | ||||||
|  | 		case MODE_CBC_ENCRYPT: { | ||||||
|  | 			Error err = ctx.encrypt_cbc(len, iv.ptrw(), p_src.ptr(), out.ptrw()); | ||||||
|  | 			ERR_FAIL_COND_V(err != OK, PackedByteArray()); | ||||||
|  | 		} break; | ||||||
|  | 		case MODE_CBC_DECRYPT: { | ||||||
|  | 			Error err = ctx.decrypt_cbc(len, iv.ptrw(), p_src.ptr(), out.ptrw()); | ||||||
|  | 			ERR_FAIL_COND_V(err != OK, PackedByteArray()); | ||||||
|  | 		} break; | ||||||
|  | 		default: | ||||||
|  | 			ERR_FAIL_V_MSG(PackedByteArray(), "Bug!"); | ||||||
|  | 	} | ||||||
|  | 	return out; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | PackedByteArray AESContext::get_iv_state() { | ||||||
|  | 	ERR_FAIL_COND_V_MSG(mode != MODE_CBC_ENCRYPT && mode != MODE_CBC_DECRYPT, PackedByteArray(), "Calling 'get_iv_state' only makes sense when the context is started in CBC mode."); | ||||||
|  | 	PackedByteArray out; | ||||||
|  | 	out.append_array(iv); | ||||||
|  | 	return out; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void AESContext::finish() { | ||||||
|  | 	mode = MODE_MAX; | ||||||
|  | 	iv.resize(0); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void AESContext::_bind_methods() { | ||||||
|  | 	ClassDB::bind_method(D_METHOD("start", "mode", "key", "iv"), &AESContext::start, DEFVAL(PackedByteArray())); | ||||||
|  | 	ClassDB::bind_method(D_METHOD("update", "src"), &AESContext::update); | ||||||
|  | 	ClassDB::bind_method(D_METHOD("get_iv_state"), &AESContext::get_iv_state); | ||||||
|  | 	ClassDB::bind_method(D_METHOD("finish"), &AESContext::finish); | ||||||
|  | 	BIND_ENUM_CONSTANT(MODE_ECB_ENCRYPT); | ||||||
|  | 	BIND_ENUM_CONSTANT(MODE_ECB_DECRYPT); | ||||||
|  | 	BIND_ENUM_CONSTANT(MODE_CBC_ENCRYPT); | ||||||
|  | 	BIND_ENUM_CONSTANT(MODE_CBC_DECRYPT); | ||||||
|  | 	BIND_ENUM_CONSTANT(MODE_MAX); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | AESContext::AESContext() { | ||||||
|  | } | ||||||
							
								
								
									
										65
									
								
								engine/core/crypto/aes_context.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								engine/core/crypto/aes_context.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,65 @@ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*  aes_context.h                                                         */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*                         This file is part of:                          */ | ||||||
|  | /*                             GODOT ENGINE                               */ | ||||||
|  | /*                        https://godotengine.org                         */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ | ||||||
|  | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */ | ||||||
|  | /*                                                                        */ | ||||||
|  | /* 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.                 */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include "core/crypto/crypto_core.h" | ||||||
|  | #include "core/object/ref_counted.h" | ||||||
|  | 
 | ||||||
|  | class AESContext : public RefCounted { | ||||||
|  | 	GDCLASS(AESContext, RefCounted); | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  | 	enum Mode : int32_t { | ||||||
|  | 		MODE_ECB_ENCRYPT, | ||||||
|  | 		MODE_ECB_DECRYPT, | ||||||
|  | 		MODE_CBC_ENCRYPT, | ||||||
|  | 		MODE_CBC_DECRYPT, | ||||||
|  | 		MODE_MAX | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  | 	Mode mode = MODE_MAX; | ||||||
|  | 	CryptoCore::AESContext ctx; | ||||||
|  | 	PackedByteArray iv; | ||||||
|  | 
 | ||||||
|  | protected: | ||||||
|  | 	static void _bind_methods(); | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  | 	Error start(Mode p_mode, const PackedByteArray &p_key, const PackedByteArray &p_iv = PackedByteArray()); | ||||||
|  | 	PackedByteArray update(const PackedByteArray &p_src); | ||||||
|  | 	PackedByteArray get_iv_state(); | ||||||
|  | 	void finish(); | ||||||
|  | 
 | ||||||
|  | 	AESContext(); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | VARIANT_ENUM_CAST(AESContext::Mode); | ||||||
							
								
								
									
										259
									
								
								engine/core/crypto/crypto.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										259
									
								
								engine/core/crypto/crypto.cpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,259 @@ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*  crypto.cpp                                                            */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*                         This file is part of:                          */ | ||||||
|  | /*                             GODOT ENGINE                               */ | ||||||
|  | /*                        https://godotengine.org                         */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ | ||||||
|  | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */ | ||||||
|  | /*                                                                        */ | ||||||
|  | /* 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" | ||||||
|  | 
 | ||||||
|  | /// Resources
 | ||||||
|  | 
 | ||||||
|  | CryptoKey *(*CryptoKey::_create)(bool p_notify_postinitialize) = nullptr; | ||||||
|  | CryptoKey *CryptoKey::create(bool p_notify_postinitialize) { | ||||||
|  | 	if (_create) { | ||||||
|  | 		return _create(p_notify_postinitialize); | ||||||
|  | 	} | ||||||
|  | 	return nullptr; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void CryptoKey::_bind_methods() { | ||||||
|  | 	ClassDB::bind_method(D_METHOD("save", "path", "public_only"), &CryptoKey::save, DEFVAL(false)); | ||||||
|  | 	ClassDB::bind_method(D_METHOD("load", "path", "public_only"), &CryptoKey::load, DEFVAL(false)); | ||||||
|  | 	ClassDB::bind_method(D_METHOD("is_public_only"), &CryptoKey::is_public_only); | ||||||
|  | 	ClassDB::bind_method(D_METHOD("save_to_string", "public_only"), &CryptoKey::save_to_string, DEFVAL(false)); | ||||||
|  | 	ClassDB::bind_method(D_METHOD("load_from_string", "string_key", "public_only"), &CryptoKey::load_from_string, DEFVAL(false)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | X509Certificate *(*X509Certificate::_create)(bool p_notify_postinitialize) = nullptr; | ||||||
|  | X509Certificate *X509Certificate::create(bool p_notify_postinitialize) { | ||||||
|  | 	if (_create) { | ||||||
|  | 		return _create(p_notify_postinitialize); | ||||||
|  | 	} | ||||||
|  | 	return nullptr; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void X509Certificate::_bind_methods() { | ||||||
|  | 	ClassDB::bind_method(D_METHOD("save", "path"), &X509Certificate::save); | ||||||
|  | 	ClassDB::bind_method(D_METHOD("load", "path"), &X509Certificate::load); | ||||||
|  | 	ClassDB::bind_method(D_METHOD("save_to_string"), &X509Certificate::save_to_string); | ||||||
|  | 	ClassDB::bind_method(D_METHOD("load_from_string", "string"), &X509Certificate::load_from_string); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// TLSOptions
 | ||||||
|  | 
 | ||||||
|  | Ref<TLSOptions> TLSOptions::client(Ref<X509Certificate> p_trusted_chain, const String &p_common_name_override) { | ||||||
|  | 	Ref<TLSOptions> opts; | ||||||
|  | 	opts.instantiate(); | ||||||
|  | 	opts->mode = MODE_CLIENT; | ||||||
|  | 	opts->trusted_ca_chain = p_trusted_chain; | ||||||
|  | 	opts->common_name = p_common_name_override; | ||||||
|  | 	return opts; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Ref<TLSOptions> TLSOptions::client_unsafe(Ref<X509Certificate> p_trusted_chain) { | ||||||
|  | 	Ref<TLSOptions> opts; | ||||||
|  | 	opts.instantiate(); | ||||||
|  | 	opts->mode = MODE_CLIENT_UNSAFE; | ||||||
|  | 	opts->trusted_ca_chain = p_trusted_chain; | ||||||
|  | 	return opts; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Ref<TLSOptions> TLSOptions::server(Ref<CryptoKey> p_own_key, Ref<X509Certificate> p_own_certificate) { | ||||||
|  | 	Ref<TLSOptions> opts; | ||||||
|  | 	opts.instantiate(); | ||||||
|  | 	opts->mode = MODE_SERVER; | ||||||
|  | 	opts->own_certificate = p_own_certificate; | ||||||
|  | 	opts->private_key = p_own_key; | ||||||
|  | 	return opts; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void TLSOptions::_bind_methods() { | ||||||
|  | 	ClassDB::bind_static_method("TLSOptions", D_METHOD("client", "trusted_chain", "common_name_override"), &TLSOptions::client, DEFVAL(Ref<X509Certificate>()), DEFVAL(String())); | ||||||
|  | 	ClassDB::bind_static_method("TLSOptions", D_METHOD("client_unsafe", "trusted_chain"), &TLSOptions::client_unsafe, DEFVAL(Ref<X509Certificate>())); | ||||||
|  | 	ClassDB::bind_static_method("TLSOptions", D_METHOD("server", "key", "certificate"), &TLSOptions::server); | ||||||
|  | 
 | ||||||
|  | 	ClassDB::bind_method(D_METHOD("is_server"), &TLSOptions::is_server); | ||||||
|  | 	ClassDB::bind_method(D_METHOD("is_unsafe_client"), &TLSOptions::is_unsafe_client); | ||||||
|  | 	ClassDB::bind_method(D_METHOD("get_common_name_override"), &TLSOptions::get_common_name_override); | ||||||
|  | 	ClassDB::bind_method(D_METHOD("get_trusted_ca_chain"), &TLSOptions::get_trusted_ca_chain); | ||||||
|  | 	ClassDB::bind_method(D_METHOD("get_private_key"), &TLSOptions::get_private_key); | ||||||
|  | 	ClassDB::bind_method(D_METHOD("get_own_certificate"), &TLSOptions::get_own_certificate); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// HMACContext
 | ||||||
|  | 
 | ||||||
|  | void HMACContext::_bind_methods() { | ||||||
|  | 	ClassDB::bind_method(D_METHOD("start", "hash_type", "key"), &HMACContext::start); | ||||||
|  | 	ClassDB::bind_method(D_METHOD("update", "data"), &HMACContext::update); | ||||||
|  | 	ClassDB::bind_method(D_METHOD("finish"), &HMACContext::finish); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | HMACContext *(*HMACContext::_create)(bool p_notify_postinitialize) = nullptr; | ||||||
|  | HMACContext *HMACContext::create(bool p_notify_postinitialize) { | ||||||
|  | 	if (_create) { | ||||||
|  | 		return _create(p_notify_postinitialize); | ||||||
|  | 	} | ||||||
|  | 	ERR_FAIL_V_MSG(nullptr, "HMACContext is not available when the mbedtls module is disabled."); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// Crypto
 | ||||||
|  | 
 | ||||||
|  | void (*Crypto::_load_default_certificates)(const String &p_path) = nullptr; | ||||||
|  | Crypto *(*Crypto::_create)(bool p_notify_postinitialize) = nullptr; | ||||||
|  | Crypto *Crypto::create(bool p_notify_postinitialize) { | ||||||
|  | 	if (_create) { | ||||||
|  | 		return _create(p_notify_postinitialize); | ||||||
|  | 	} | ||||||
|  | 	ERR_FAIL_V_MSG(nullptr, "Crypto is not available when the mbedtls module is disabled."); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Crypto::load_default_certificates(const String &p_path) { | ||||||
|  | 	if (_load_default_certificates) { | ||||||
|  | 		_load_default_certificates(p_path); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | PackedByteArray Crypto::hmac_digest(HashingContext::HashType p_hash_type, const PackedByteArray &p_key, const PackedByteArray &p_msg) { | ||||||
|  | 	Ref<HMACContext> ctx = Ref<HMACContext>(HMACContext::create()); | ||||||
|  | 	ERR_FAIL_COND_V_MSG(ctx.is_null(), PackedByteArray(), "HMAC is not available without mbedtls module."); | ||||||
|  | 	Error err = ctx->start(p_hash_type, p_key); | ||||||
|  | 	ERR_FAIL_COND_V(err != OK, PackedByteArray()); | ||||||
|  | 	err = ctx->update(p_msg); | ||||||
|  | 	ERR_FAIL_COND_V(err != OK, PackedByteArray()); | ||||||
|  | 	return ctx->finish(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Compares two HMACS for equality without leaking timing information in order to prevent timing attacks.
 | ||||||
|  | // @see: https://paragonie.com/blog/2015/11/preventing-timing-attacks-on-string-comparison-with-double-hmac-strategy
 | ||||||
|  | bool Crypto::constant_time_compare(const PackedByteArray &p_trusted, const PackedByteArray &p_received) { | ||||||
|  | 	const uint8_t *t = p_trusted.ptr(); | ||||||
|  | 	const uint8_t *r = p_received.ptr(); | ||||||
|  | 	int tlen = p_trusted.size(); | ||||||
|  | 	int rlen = p_received.size(); | ||||||
|  | 	// If the lengths are different then nothing else matters.
 | ||||||
|  | 	if (tlen != rlen) { | ||||||
|  | 		return false; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	uint8_t v = 0; | ||||||
|  | 	for (int i = 0; i < tlen; i++) { | ||||||
|  | 		v |= t[i] ^ r[i]; | ||||||
|  | 	} | ||||||
|  | 	return v == 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 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")); | ||||||
|  | 	ClassDB::bind_method(D_METHOD("sign", "hash_type", "hash", "key"), &Crypto::sign); | ||||||
|  | 	ClassDB::bind_method(D_METHOD("verify", "hash_type", "hash", "signature", "key"), &Crypto::verify); | ||||||
|  | 	ClassDB::bind_method(D_METHOD("encrypt", "key", "plaintext"), &Crypto::encrypt); | ||||||
|  | 	ClassDB::bind_method(D_METHOD("decrypt", "key", "ciphertext"), &Crypto::decrypt); | ||||||
|  | 	ClassDB::bind_method(D_METHOD("hmac_digest", "hash_type", "key", "msg"), &Crypto::hmac_digest); | ||||||
|  | 	ClassDB::bind_method(D_METHOD("constant_time_compare", "trusted", "received"), &Crypto::constant_time_compare); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// Resource loader/saver
 | ||||||
|  | 
 | ||||||
|  | Ref<Resource> ResourceFormatLoaderCrypto::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) { | ||||||
|  | 	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, false); | ||||||
|  | 		} | ||||||
|  | 		return key; | ||||||
|  | 	} else if (el == "pub") { | ||||||
|  | 		CryptoKey *key = CryptoKey::create(); | ||||||
|  | 		if (key) { | ||||||
|  | 			key->load(p_path, true); | ||||||
|  | 		} | ||||||
|  | 		return key; | ||||||
|  | 	} | ||||||
|  | 	return nullptr; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ResourceFormatLoaderCrypto::get_recognized_extensions(List<String> *p_extensions) const { | ||||||
|  | 	p_extensions->push_back("crt"); | ||||||
|  | 	p_extensions->push_back("key"); | ||||||
|  | 	p_extensions->push_back("pub"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 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" || el == "pub") { | ||||||
|  | 		return "CryptoKey"; | ||||||
|  | 	} | ||||||
|  | 	return ""; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Error ResourceFormatSaverCrypto::save(const Ref<Resource> &p_resource, const String &p_path, 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()) { | ||||||
|  | 		String el = p_path.get_extension().to_lower(); | ||||||
|  | 		err = key->save(p_path, el == "pub"); | ||||||
|  | 	} else { | ||||||
|  | 		ERR_FAIL_V(ERR_INVALID_PARAMETER); | ||||||
|  | 	} | ||||||
|  | 	ERR_FAIL_COND_V_MSG(err != OK, err, vformat("Cannot save Crypto resource to file '%s'.", p_path)); | ||||||
|  | 	return OK; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ResourceFormatSaverCrypto::get_recognized_extensions(const Ref<Resource> &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) { | ||||||
|  | 		if (!key->is_public_only()) { | ||||||
|  | 			p_extensions->push_back("key"); | ||||||
|  | 		} | ||||||
|  | 		p_extensions->push_back("pub"); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool ResourceFormatSaverCrypto::recognize(const Ref<Resource> &p_resource) const { | ||||||
|  | 	return Object::cast_to<X509Certificate>(*p_resource) || Object::cast_to<CryptoKey>(*p_resource); | ||||||
|  | } | ||||||
							
								
								
									
										168
									
								
								engine/core/crypto/crypto.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										168
									
								
								engine/core/crypto/crypto.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,168 @@ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*  crypto.h                                                              */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*                         This file is part of:                          */ | ||||||
|  | /*                             GODOT ENGINE                               */ | ||||||
|  | /*                        https://godotengine.org                         */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ | ||||||
|  | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */ | ||||||
|  | /*                                                                        */ | ||||||
|  | /* 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.                 */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include "core/crypto/hashing_context.h" | ||||||
|  | #include "core/io/resource.h" | ||||||
|  | #include "core/io/resource_loader.h" | ||||||
|  | #include "core/io/resource_saver.h" | ||||||
|  | #include "core/object/ref_counted.h" | ||||||
|  | 
 | ||||||
|  | class CryptoKey : public Resource { | ||||||
|  | 	GDCLASS(CryptoKey, Resource); | ||||||
|  | 
 | ||||||
|  | protected: | ||||||
|  | 	static void _bind_methods(); | ||||||
|  | 	static CryptoKey *(*_create)(bool p_notify_postinitialize); | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  | 	static CryptoKey *create(bool p_notify_postinitialize = true); | ||||||
|  | 	virtual Error load(const String &p_path, bool p_public_only = false) = 0; | ||||||
|  | 	virtual Error save(const String &p_path, bool p_public_only = false) = 0; | ||||||
|  | 	virtual String save_to_string(bool p_public_only = false) = 0; | ||||||
|  | 	virtual Error load_from_string(const String &p_string_key, bool p_public_only = false) = 0; | ||||||
|  | 	virtual bool is_public_only() const = 0; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class X509Certificate : public Resource { | ||||||
|  | 	GDCLASS(X509Certificate, Resource); | ||||||
|  | 
 | ||||||
|  | protected: | ||||||
|  | 	static void _bind_methods(); | ||||||
|  | 	static X509Certificate *(*_create)(bool p_notify_postinitialize); | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  | 	static X509Certificate *create(bool p_notify_postinitialize = true); | ||||||
|  | 	virtual Error load(const String &p_path) = 0; | ||||||
|  | 	virtual Error load_from_memory(const uint8_t *p_buffer, int p_len) = 0; | ||||||
|  | 	virtual Error save(const String &p_path) = 0; | ||||||
|  | 	virtual String save_to_string() = 0; | ||||||
|  | 	virtual Error load_from_string(const String &string) = 0; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class TLSOptions : public RefCounted { | ||||||
|  | 	GDCLASS(TLSOptions, RefCounted); | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  | 	enum Mode { | ||||||
|  | 		MODE_CLIENT = 0, | ||||||
|  | 		MODE_CLIENT_UNSAFE = 1, | ||||||
|  | 		MODE_SERVER = 2, | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	Mode mode = MODE_CLIENT; | ||||||
|  | 	String common_name; | ||||||
|  | 	Ref<X509Certificate> trusted_ca_chain; | ||||||
|  | 	Ref<X509Certificate> own_certificate; | ||||||
|  | 	Ref<CryptoKey> private_key; | ||||||
|  | 
 | ||||||
|  | protected: | ||||||
|  | 	static void _bind_methods(); | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  | 	static Ref<TLSOptions> client(Ref<X509Certificate> p_trusted_chain = Ref<X509Certificate>(), const String &p_common_name_override = String()); | ||||||
|  | 	static Ref<TLSOptions> client_unsafe(Ref<X509Certificate> p_trusted_chain); | ||||||
|  | 	static Ref<TLSOptions> server(Ref<CryptoKey> p_own_key, Ref<X509Certificate> p_own_certificate); | ||||||
|  | 
 | ||||||
|  | 	String get_common_name_override() const { return common_name; } | ||||||
|  | 	Ref<X509Certificate> get_trusted_ca_chain() const { return trusted_ca_chain; } | ||||||
|  | 	Ref<X509Certificate> get_own_certificate() const { return own_certificate; } | ||||||
|  | 	Ref<CryptoKey> get_private_key() const { return private_key; } | ||||||
|  | 	bool is_server() const { return mode == MODE_SERVER; } | ||||||
|  | 	bool is_unsafe_client() const { return mode == MODE_CLIENT_UNSAFE; } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class HMACContext : public RefCounted { | ||||||
|  | 	GDCLASS(HMACContext, RefCounted); | ||||||
|  | 
 | ||||||
|  | protected: | ||||||
|  | 	static void _bind_methods(); | ||||||
|  | 	static HMACContext *(*_create)(bool p_notify_postinitialize); | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  | 	static HMACContext *create(bool p_notify_postinitialize = true); | ||||||
|  | 
 | ||||||
|  | 	virtual Error start(HashingContext::HashType p_hash_type, const PackedByteArray &p_key) = 0; | ||||||
|  | 	virtual Error update(const PackedByteArray &p_data) = 0; | ||||||
|  | 	virtual PackedByteArray finish() = 0; | ||||||
|  | 
 | ||||||
|  | 	HMACContext() {} | ||||||
|  | 	virtual ~HMACContext() {} | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class Crypto : public RefCounted { | ||||||
|  | 	GDCLASS(Crypto, RefCounted); | ||||||
|  | 
 | ||||||
|  | protected: | ||||||
|  | 	static void _bind_methods(); | ||||||
|  | 	static Crypto *(*_create)(bool p_notify_postinitialize); | ||||||
|  | 	static void (*_load_default_certificates)(const String &p_path); | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  | 	static Crypto *create(bool p_notify_postinitialize = true); | ||||||
|  | 	static void load_default_certificates(const String &p_path); | ||||||
|  | 
 | ||||||
|  | 	virtual PackedByteArray generate_random_bytes(int p_bytes) = 0; | ||||||
|  | 	virtual Ref<CryptoKey> generate_rsa(int p_bytes) = 0; | ||||||
|  | 	virtual Ref<X509Certificate> generate_self_signed_certificate(Ref<CryptoKey> p_key, const String &p_issuer_name, const String &p_not_before, const String &p_not_after) = 0; | ||||||
|  | 
 | ||||||
|  | 	virtual Vector<uint8_t> sign(HashingContext::HashType p_hash_type, const Vector<uint8_t> &p_hash, Ref<CryptoKey> p_key) = 0; | ||||||
|  | 	virtual bool verify(HashingContext::HashType p_hash_type, const Vector<uint8_t> &p_hash, const Vector<uint8_t> &p_signature, Ref<CryptoKey> p_key) = 0; | ||||||
|  | 	virtual Vector<uint8_t> encrypt(Ref<CryptoKey> p_key, const Vector<uint8_t> &p_plaintext) = 0; | ||||||
|  | 	virtual Vector<uint8_t> decrypt(Ref<CryptoKey> p_key, const Vector<uint8_t> &p_ciphertext) = 0; | ||||||
|  | 
 | ||||||
|  | 	PackedByteArray hmac_digest(HashingContext::HashType p_hash_type, const PackedByteArray &p_key, const PackedByteArray &p_msg); | ||||||
|  | 
 | ||||||
|  | 	// Compares two PackedByteArrays for equality without leaking timing information in order to prevent timing attacks.
 | ||||||
|  | 	// @see: https://paragonie.com/blog/2015/11/preventing-timing-attacks-on-string-comparison-with-double-hmac-strategy
 | ||||||
|  | 	bool constant_time_compare(const PackedByteArray &p_trusted, const PackedByteArray &p_received); | ||||||
|  | 
 | ||||||
|  | 	Crypto() {} | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class ResourceFormatLoaderCrypto : public ResourceFormatLoader { | ||||||
|  | public: | ||||||
|  | 	virtual Ref<Resource> load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE) override; | ||||||
|  | 	virtual void get_recognized_extensions(List<String> *p_extensions) const override; | ||||||
|  | 	virtual bool handles_type(const String &p_type) const override; | ||||||
|  | 	virtual String get_resource_type(const String &p_path) const override; | ||||||
|  | 
 | ||||||
|  | 	// Treat certificates as text files, do not generate a `*.{crt,key,pub}.uid` file.
 | ||||||
|  | 	virtual ResourceUID::ID get_resource_uid(const String &p_path) const override { return ResourceUID::INVALID_ID; } | ||||||
|  | 	virtual bool has_custom_uid_support() const override { return true; } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class ResourceFormatSaverCrypto : public ResourceFormatSaver { | ||||||
|  | public: | ||||||
|  | 	virtual Error save(const Ref<Resource> &p_resource, const String &p_path, uint32_t p_flags = 0) override; | ||||||
|  | 	virtual void get_recognized_extensions(const Ref<Resource> &p_resource, List<String> *p_extensions) const override; | ||||||
|  | 	virtual bool recognize(const Ref<Resource> &p_resource) const override; | ||||||
|  | }; | ||||||
							
								
								
									
										251
									
								
								engine/core/crypto/crypto_core.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										251
									
								
								engine/core/crypto/crypto_core.cpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,251 @@ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*  crypto_core.cpp                                                       */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*                         This file is part of:                          */ | ||||||
|  | /*                             GODOT ENGINE                               */ | ||||||
|  | /*                        https://godotengine.org                         */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ | ||||||
|  | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */ | ||||||
|  | /*                                                                        */ | ||||||
|  | /* 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_core.h" | ||||||
|  | 
 | ||||||
|  | #include "core/os/os.h" | ||||||
|  | 
 | ||||||
|  | #include <mbedtls/aes.h> | ||||||
|  | #include <mbedtls/base64.h> | ||||||
|  | #include <mbedtls/ctr_drbg.h> | ||||||
|  | #include <mbedtls/entropy.h> | ||||||
|  | #include <mbedtls/md5.h> | ||||||
|  | #include <mbedtls/sha1.h> | ||||||
|  | #include <mbedtls/sha256.h> | ||||||
|  | #if MBEDTLS_VERSION_MAJOR >= 3 | ||||||
|  | #include <mbedtls/compat-2.x.h> | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | // RandomGenerator
 | ||||||
|  | CryptoCore::RandomGenerator::RandomGenerator() { | ||||||
|  | 	entropy = memalloc(sizeof(mbedtls_entropy_context)); | ||||||
|  | 	mbedtls_entropy_init((mbedtls_entropy_context *)entropy); | ||||||
|  | 	mbedtls_entropy_add_source((mbedtls_entropy_context *)entropy, &CryptoCore::RandomGenerator::_entropy_poll, nullptr, 256, MBEDTLS_ENTROPY_SOURCE_STRONG); | ||||||
|  | 	ctx = memalloc(sizeof(mbedtls_ctr_drbg_context)); | ||||||
|  | 	mbedtls_ctr_drbg_init((mbedtls_ctr_drbg_context *)ctx); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | CryptoCore::RandomGenerator::~RandomGenerator() { | ||||||
|  | 	mbedtls_ctr_drbg_free((mbedtls_ctr_drbg_context *)ctx); | ||||||
|  | 	memfree(ctx); | ||||||
|  | 	mbedtls_entropy_free((mbedtls_entropy_context *)entropy); | ||||||
|  | 	memfree(entropy); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int CryptoCore::RandomGenerator::_entropy_poll(void *p_data, unsigned char *r_buffer, size_t p_len, size_t *r_len) { | ||||||
|  | 	*r_len = 0; | ||||||
|  | 	Error err = OS::get_singleton()->get_entropy(r_buffer, p_len); | ||||||
|  | 	ERR_FAIL_COND_V(err, MBEDTLS_ERR_ENTROPY_SOURCE_FAILED); | ||||||
|  | 	*r_len = p_len; | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Error CryptoCore::RandomGenerator::init() { | ||||||
|  | 	int ret = mbedtls_ctr_drbg_seed((mbedtls_ctr_drbg_context *)ctx, mbedtls_entropy_func, (mbedtls_entropy_context *)entropy, nullptr, 0); | ||||||
|  | 	if (ret) { | ||||||
|  | 		ERR_FAIL_COND_V_MSG(ret, FAILED, vformat(" failed\n  ! mbedtls_ctr_drbg_seed returned an error %d.", ret)); | ||||||
|  | 	} | ||||||
|  | 	return OK; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Error CryptoCore::RandomGenerator::get_random_bytes(uint8_t *r_buffer, size_t p_bytes) { | ||||||
|  | 	ERR_FAIL_NULL_V(ctx, ERR_UNCONFIGURED); | ||||||
|  | 	int ret = mbedtls_ctr_drbg_random((mbedtls_ctr_drbg_context *)ctx, r_buffer, p_bytes); | ||||||
|  | 	ERR_FAIL_COND_V_MSG(ret, FAILED, vformat(" failed\n  ! mbedtls_ctr_drbg_seed returned an error %d.", ret)); | ||||||
|  | 	return OK; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // MD5
 | ||||||
|  | CryptoCore::MD5Context::MD5Context() { | ||||||
|  | 	ctx = memalloc(sizeof(mbedtls_md5_context)); | ||||||
|  | 	mbedtls_md5_init((mbedtls_md5_context *)ctx); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | CryptoCore::MD5Context::~MD5Context() { | ||||||
|  | 	mbedtls_md5_free((mbedtls_md5_context *)ctx); | ||||||
|  | 	memfree((mbedtls_md5_context *)ctx); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Error CryptoCore::MD5Context::start() { | ||||||
|  | 	int ret = mbedtls_md5_starts_ret((mbedtls_md5_context *)ctx); | ||||||
|  | 	return ret ? FAILED : OK; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 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; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Error CryptoCore::MD5Context::finish(unsigned char r_hash[16]) { | ||||||
|  | 	int ret = mbedtls_md5_finish_ret((mbedtls_md5_context *)ctx, r_hash); | ||||||
|  | 	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)); | ||||||
|  | 	mbedtls_sha256_init((mbedtls_sha256_context *)ctx); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | CryptoCore::SHA256Context::~SHA256Context() { | ||||||
|  | 	mbedtls_sha256_free((mbedtls_sha256_context *)ctx); | ||||||
|  | 	memfree((mbedtls_sha256_context *)ctx); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Error CryptoCore::SHA256Context::start() { | ||||||
|  | 	int ret = mbedtls_sha256_starts_ret((mbedtls_sha256_context *)ctx, 0); | ||||||
|  | 	return ret ? FAILED : OK; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 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[32]) { | ||||||
|  | 	int ret = mbedtls_sha256_finish_ret((mbedtls_sha256_context *)ctx, r_hash); | ||||||
|  | 	return ret ? FAILED : OK; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // AES256
 | ||||||
|  | CryptoCore::AESContext::AESContext() { | ||||||
|  | 	ctx = memalloc(sizeof(mbedtls_aes_context)); | ||||||
|  | 	mbedtls_aes_init((mbedtls_aes_context *)ctx); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | CryptoCore::AESContext::~AESContext() { | ||||||
|  | 	mbedtls_aes_free((mbedtls_aes_context *)ctx); | ||||||
|  | 	memfree((mbedtls_aes_context *)ctx); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Error CryptoCore::AESContext::set_encode_key(const uint8_t *p_key, size_t p_bits) { | ||||||
|  | 	int ret = mbedtls_aes_setkey_enc((mbedtls_aes_context *)ctx, p_key, p_bits); | ||||||
|  | 	return ret ? FAILED : OK; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Error CryptoCore::AESContext::set_decode_key(const uint8_t *p_key, size_t p_bits) { | ||||||
|  | 	int ret = mbedtls_aes_setkey_dec((mbedtls_aes_context *)ctx, p_key, p_bits); | ||||||
|  | 	return ret ? FAILED : OK; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Error CryptoCore::AESContext::encrypt_ecb(const uint8_t p_src[16], uint8_t r_dst[16]) { | ||||||
|  | 	int ret = mbedtls_aes_crypt_ecb((mbedtls_aes_context *)ctx, MBEDTLS_AES_ENCRYPT, p_src, r_dst); | ||||||
|  | 	return ret ? FAILED : OK; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Error CryptoCore::AESContext::encrypt_cbc(size_t p_length, uint8_t r_iv[16], const uint8_t *p_src, uint8_t *r_dst) { | ||||||
|  | 	int ret = mbedtls_aes_crypt_cbc((mbedtls_aes_context *)ctx, MBEDTLS_AES_ENCRYPT, p_length, r_iv, p_src, r_dst); | ||||||
|  | 	return ret ? FAILED : OK; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Error CryptoCore::AESContext::encrypt_cfb(size_t p_length, uint8_t p_iv[16], const uint8_t *p_src, uint8_t *r_dst) { | ||||||
|  | 	size_t iv_off = 0; // Ignore and assume 16-byte alignment.
 | ||||||
|  | 	int ret = mbedtls_aes_crypt_cfb128((mbedtls_aes_context *)ctx, MBEDTLS_AES_ENCRYPT, p_length, &iv_off, p_iv, p_src, r_dst); | ||||||
|  | 	return ret ? FAILED : OK; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Error CryptoCore::AESContext::decrypt_ecb(const uint8_t p_src[16], uint8_t r_dst[16]) { | ||||||
|  | 	int ret = mbedtls_aes_crypt_ecb((mbedtls_aes_context *)ctx, MBEDTLS_AES_DECRYPT, p_src, r_dst); | ||||||
|  | 	return ret ? FAILED : OK; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Error CryptoCore::AESContext::decrypt_cbc(size_t p_length, uint8_t r_iv[16], const uint8_t *p_src, uint8_t *r_dst) { | ||||||
|  | 	int ret = mbedtls_aes_crypt_cbc((mbedtls_aes_context *)ctx, MBEDTLS_AES_DECRYPT, p_length, r_iv, p_src, r_dst); | ||||||
|  | 	return ret ? FAILED : OK; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Error CryptoCore::AESContext::decrypt_cfb(size_t p_length, uint8_t p_iv[16], const uint8_t *p_src, uint8_t *r_dst) { | ||||||
|  | 	size_t iv_off = 0; // Ignore and assume 16-byte alignment.
 | ||||||
|  | 	int ret = mbedtls_aes_crypt_cfb128((mbedtls_aes_context *)ctx, MBEDTLS_AES_DECRYPT, p_length, &iv_off, p_iv, p_src, r_dst); | ||||||
|  | 	return ret ? FAILED : OK; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // CryptoCore
 | ||||||
|  | String CryptoCore::b64_encode_str(const uint8_t *p_src, size_t p_src_len) { | ||||||
|  | 	size_t b64len = p_src_len / 3 * 4 + 4 + 1; | ||||||
|  | 	Vector<uint8_t> b64buff; | ||||||
|  | 	b64buff.resize(b64len); | ||||||
|  | 	uint8_t *w64 = b64buff.ptrw(); | ||||||
|  | 	size_t strlen = 0; | ||||||
|  | 	int ret = b64_encode(&w64[0], b64len, &strlen, p_src, p_src_len); | ||||||
|  | 	w64[strlen] = 0; | ||||||
|  | 	return ret ? String() : (const char *)&w64[0]; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Error CryptoCore::b64_encode(uint8_t *r_dst, size_t p_dst_len, size_t *r_len, const uint8_t *p_src, size_t p_src_len) { | ||||||
|  | 	int ret = mbedtls_base64_encode(r_dst, p_dst_len, r_len, p_src, p_src_len); | ||||||
|  | 	return ret ? FAILED : OK; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Error CryptoCore::b64_decode(uint8_t *r_dst, size_t p_dst_len, size_t *r_len, const uint8_t *p_src, size_t p_src_len) { | ||||||
|  | 	int ret = mbedtls_base64_decode(r_dst, p_dst_len, r_len, p_src, p_src_len); | ||||||
|  | 	return ret ? FAILED : OK; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Error CryptoCore::md5(const uint8_t *p_src, size_t p_src_len, unsigned char r_hash[16]) { | ||||||
|  | 	int ret = mbedtls_md5_ret(p_src, p_src_len, r_hash); | ||||||
|  | 	return ret ? FAILED : OK; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Error CryptoCore::sha1(const uint8_t *p_src, size_t p_src_len, unsigned char r_hash[20]) { | ||||||
|  | 	int ret = mbedtls_sha1_ret(p_src, p_src_len, r_hash); | ||||||
|  | 	return ret ? FAILED : OK; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Error CryptoCore::sha256(const uint8_t *p_src, size_t p_src_len, unsigned char r_hash[32]) { | ||||||
|  | 	int ret = mbedtls_sha256_ret(p_src, p_src_len, r_hash, 0); | ||||||
|  | 	return ret ? FAILED : OK; | ||||||
|  | } | ||||||
							
								
								
									
										116
									
								
								engine/core/crypto/crypto_core.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								engine/core/crypto/crypto_core.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,116 @@ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*  crypto_core.h                                                         */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*                         This file is part of:                          */ | ||||||
|  | /*                             GODOT ENGINE                               */ | ||||||
|  | /*                        https://godotengine.org                         */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ | ||||||
|  | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */ | ||||||
|  | /*                                                                        */ | ||||||
|  | /* 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.                 */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include "core/object/ref_counted.h" | ||||||
|  | 
 | ||||||
|  | class CryptoCore { | ||||||
|  | public: | ||||||
|  | 	class RandomGenerator { | ||||||
|  | 	private: | ||||||
|  | 		void *entropy = nullptr; | ||||||
|  | 		void *ctx = nullptr; | ||||||
|  | 
 | ||||||
|  | 		static int _entropy_poll(void *p_data, unsigned char *r_buffer, size_t p_len, size_t *r_len); | ||||||
|  | 
 | ||||||
|  | 	public: | ||||||
|  | 		RandomGenerator(); | ||||||
|  | 		~RandomGenerator(); | ||||||
|  | 
 | ||||||
|  | 		Error init(); | ||||||
|  | 		Error get_random_bytes(uint8_t *r_buffer, size_t p_bytes); | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	class MD5Context { | ||||||
|  | 	private: | ||||||
|  | 		void *ctx = nullptr; | ||||||
|  | 
 | ||||||
|  | 	public: | ||||||
|  | 		MD5Context(); | ||||||
|  | 		~MD5Context(); | ||||||
|  | 
 | ||||||
|  | 		Error start(); | ||||||
|  | 		Error update(const uint8_t *p_src, size_t p_len); | ||||||
|  | 		Error finish(unsigned char r_hash[16]); | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	class SHA1Context { | ||||||
|  | 	private: | ||||||
|  | 		void *ctx = nullptr; | ||||||
|  | 
 | ||||||
|  | 	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: | ||||||
|  | 		void *ctx = nullptr; | ||||||
|  | 
 | ||||||
|  | 	public: | ||||||
|  | 		SHA256Context(); | ||||||
|  | 		~SHA256Context(); | ||||||
|  | 
 | ||||||
|  | 		Error start(); | ||||||
|  | 		Error update(const uint8_t *p_src, size_t p_len); | ||||||
|  | 		Error finish(unsigned char r_hash[32]); | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	class AESContext { | ||||||
|  | 	private: | ||||||
|  | 		void *ctx = nullptr; | ||||||
|  | 
 | ||||||
|  | 	public: | ||||||
|  | 		AESContext(); | ||||||
|  | 		~AESContext(); | ||||||
|  | 
 | ||||||
|  | 		Error set_encode_key(const uint8_t *p_key, size_t p_bits); | ||||||
|  | 		Error set_decode_key(const uint8_t *p_key, size_t p_bits); | ||||||
|  | 		Error encrypt_ecb(const uint8_t p_src[16], uint8_t r_dst[16]); | ||||||
|  | 		Error decrypt_ecb(const uint8_t p_src[16], uint8_t r_dst[16]); | ||||||
|  | 		Error encrypt_cbc(size_t p_length, uint8_t r_iv[16], const uint8_t *p_src, uint8_t *r_dst); | ||||||
|  | 		Error decrypt_cbc(size_t p_length, uint8_t r_iv[16], const uint8_t *p_src, uint8_t *r_dst); | ||||||
|  | 		Error encrypt_cfb(size_t p_length, uint8_t p_iv[16], const uint8_t *p_src, uint8_t *r_dst); | ||||||
|  | 		Error decrypt_cfb(size_t p_length, uint8_t p_iv[16], const uint8_t *p_src, uint8_t *r_dst); | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	static String b64_encode_str(const uint8_t *p_src, size_t p_src_len); | ||||||
|  | 	static Error b64_encode(uint8_t *r_dst, size_t p_dst_len, size_t *r_len, const uint8_t *p_src, size_t p_src_len); | ||||||
|  | 	static Error b64_decode(uint8_t *r_dst, size_t p_dst_len, size_t *r_len, const uint8_t *p_src, size_t p_src_len); | ||||||
|  | 
 | ||||||
|  | 	static Error md5(const uint8_t *p_src, size_t p_src_len, unsigned char r_hash[16]); | ||||||
|  | 	static Error sha1(const uint8_t *p_src, size_t p_src_len, unsigned char r_hash[20]); | ||||||
|  | 	static Error sha256(const uint8_t *p_src, size_t p_src_len, unsigned char r_hash[32]); | ||||||
|  | }; | ||||||
							
								
								
									
										134
									
								
								engine/core/crypto/hashing_context.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										134
									
								
								engine/core/crypto/hashing_context.cpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,134 @@ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*  hashing_context.cpp                                                   */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*                         This file is part of:                          */ | ||||||
|  | /*                             GODOT ENGINE                               */ | ||||||
|  | /*                        https://godotengine.org                         */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ | ||||||
|  | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */ | ||||||
|  | /*                                                                        */ | ||||||
|  | /* 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 != nullptr, ERR_ALREADY_IN_USE); | ||||||
|  | 	_create_ctx(p_type); | ||||||
|  | 	ERR_FAIL_NULL_V(ctx, 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(const PackedByteArray &p_chunk) { | ||||||
|  | 	ERR_FAIL_NULL_V(ctx, ERR_UNCONFIGURED); | ||||||
|  | 	size_t len = p_chunk.size(); | ||||||
|  | 	ERR_FAIL_COND_V(len == 0, FAILED); | ||||||
|  | 	const uint8_t *r = p_chunk.ptr(); | ||||||
|  | 	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; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | PackedByteArray HashingContext::finish() { | ||||||
|  | 	ERR_FAIL_NULL_V(ctx, PackedByteArray()); | ||||||
|  | 	PackedByteArray out; | ||||||
|  | 	Error err = FAILED; | ||||||
|  | 	switch (type) { | ||||||
|  | 		case HASH_MD5: | ||||||
|  | 			out.resize(16); | ||||||
|  | 			err = ((CryptoCore::MD5Context *)ctx)->finish(out.ptrw()); | ||||||
|  | 			break; | ||||||
|  | 		case HASH_SHA1: | ||||||
|  | 			out.resize(20); | ||||||
|  | 			err = ((CryptoCore::SHA1Context *)ctx)->finish(out.ptrw()); | ||||||
|  | 			break; | ||||||
|  | 		case HASH_SHA256: | ||||||
|  | 			out.resize(32); | ||||||
|  | 			err = ((CryptoCore::SHA256Context *)ctx)->finish(out.ptrw()); | ||||||
|  | 			break; | ||||||
|  | 	} | ||||||
|  | 	_delete_ctx(); | ||||||
|  | 	ERR_FAIL_COND_V(err != OK, PackedByteArray()); | ||||||
|  | 	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 = nullptr; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void HashingContext::_delete_ctx() { | ||||||
|  | 	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 = nullptr; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 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() { | ||||||
|  | 	if (ctx != nullptr) { | ||||||
|  | 		_delete_ctx(); | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										63
									
								
								engine/core/crypto/hashing_context.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								engine/core/crypto/hashing_context.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,63 @@ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*  hashing_context.h                                                     */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*                         This file is part of:                          */ | ||||||
|  | /*                             GODOT ENGINE                               */ | ||||||
|  | /*                        https://godotengine.org                         */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ | ||||||
|  | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */ | ||||||
|  | /*                                                                        */ | ||||||
|  | /* 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.                 */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include "core/object/ref_counted.h" | ||||||
|  | 
 | ||||||
|  | class HashingContext : public RefCounted { | ||||||
|  | 	GDCLASS(HashingContext, RefCounted); | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  | 	enum HashType : int32_t { | ||||||
|  | 		HASH_MD5, | ||||||
|  | 		HASH_SHA1, | ||||||
|  | 		HASH_SHA256 | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  | 	void *ctx = nullptr; | ||||||
|  | 	HashType type = HASH_MD5; | ||||||
|  | 
 | ||||||
|  | protected: | ||||||
|  | 	static void _bind_methods(); | ||||||
|  | 	void _create_ctx(HashType p_type); | ||||||
|  | 	void _delete_ctx(); | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  | 	Error start(HashType p_type); | ||||||
|  | 	Error update(const PackedByteArray &p_chunk); | ||||||
|  | 	PackedByteArray finish(); | ||||||
|  | 
 | ||||||
|  | 	HashingContext() {} | ||||||
|  | 	~HashingContext(); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | VARIANT_ENUM_CAST(HashingContext::HashType); | ||||||
							
								
								
									
										6
									
								
								engine/core/debugger/SCsub
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								engine/core/debugger/SCsub
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,6 @@ | ||||||
|  | #!/usr/bin/env python | ||||||
|  | from misc.utility.scons_hints import * | ||||||
|  | 
 | ||||||
|  | Import("env") | ||||||
|  | 
 | ||||||
|  | env.add_source_files(env.core_sources, "*.cpp") | ||||||
							
								
								
									
										179
									
								
								engine/core/debugger/debugger_marshalls.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										179
									
								
								engine/core/debugger/debugger_marshalls.cpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,179 @@ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*  debugger_marshalls.cpp                                                */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*                         This file is part of:                          */ | ||||||
|  | /*                             GODOT ENGINE                               */ | ||||||
|  | /*                        https://godotengine.org                         */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ | ||||||
|  | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */ | ||||||
|  | /*                                                                        */ | ||||||
|  | /* 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 "debugger_marshalls.h" | ||||||
|  | 
 | ||||||
|  | #include "core/io/marshalls.h" | ||||||
|  | 
 | ||||||
|  | #define CHECK_SIZE(arr, expected, what) ERR_FAIL_COND_V_MSG((uint32_t)arr.size() < (uint32_t)(expected), false, String("Malformed ") + what + " message from script debugger, message too short. Expected size: " + itos(expected) + ", actual size: " + itos(arr.size())) | ||||||
|  | #define CHECK_END(arr, expected, what) ERR_FAIL_COND_V_MSG((uint32_t)arr.size() > (uint32_t)expected, false, String("Malformed ") + what + " message from script debugger, message too long. Expected size: " + itos(expected) + ", actual size: " + itos(arr.size())) | ||||||
|  | 
 | ||||||
|  | Array DebuggerMarshalls::ScriptStackDump::serialize() { | ||||||
|  | 	Array arr = { frames.size() * 3 }; | ||||||
|  | 	for (const ScriptLanguage::StackInfo &frame : frames) { | ||||||
|  | 		arr.push_back(frame.file); | ||||||
|  | 		arr.push_back(frame.line); | ||||||
|  | 		arr.push_back(frame.func); | ||||||
|  | 	} | ||||||
|  | 	return arr; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool DebuggerMarshalls::ScriptStackDump::deserialize(const Array &p_arr) { | ||||||
|  | 	CHECK_SIZE(p_arr, 1, "ScriptStackDump"); | ||||||
|  | 	uint32_t size = p_arr[0]; | ||||||
|  | 	CHECK_SIZE(p_arr, size, "ScriptStackDump"); | ||||||
|  | 	int idx = 1; | ||||||
|  | 	for (uint32_t i = 0; i < size / 3; i++) { | ||||||
|  | 		ScriptLanguage::StackInfo sf; | ||||||
|  | 		sf.file = p_arr[idx]; | ||||||
|  | 		sf.line = p_arr[idx + 1]; | ||||||
|  | 		sf.func = p_arr[idx + 2]; | ||||||
|  | 		frames.push_back(sf); | ||||||
|  | 		idx += 3; | ||||||
|  | 	} | ||||||
|  | 	CHECK_END(p_arr, idx, "ScriptStackDump"); | ||||||
|  | 	return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Array DebuggerMarshalls::ScriptStackVariable::serialize(int max_size) { | ||||||
|  | 	Array arr = { name, type, value.get_type() }; | ||||||
|  | 
 | ||||||
|  | 	Variant var = value; | ||||||
|  | 	if (value.get_type() == Variant::OBJECT && value.get_validated_object() == nullptr) { | ||||||
|  | 		var = Variant(); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	int len = 0; | ||||||
|  | 	Error err = encode_variant(var, nullptr, len, false); | ||||||
|  | 	if (err != OK) { | ||||||
|  | 		ERR_PRINT("Failed to encode variant."); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (len > max_size) { | ||||||
|  | 		arr.push_back(Variant()); | ||||||
|  | 	} else { | ||||||
|  | 		arr.push_back(var); | ||||||
|  | 	} | ||||||
|  | 	return arr; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool DebuggerMarshalls::ScriptStackVariable::deserialize(const Array &p_arr) { | ||||||
|  | 	CHECK_SIZE(p_arr, 4, "ScriptStackVariable"); | ||||||
|  | 	name = p_arr[0]; | ||||||
|  | 	type = p_arr[1]; | ||||||
|  | 	var_type = p_arr[2]; | ||||||
|  | 	value = p_arr[3]; | ||||||
|  | 	CHECK_END(p_arr, 4, "ScriptStackVariable"); | ||||||
|  | 	return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Array DebuggerMarshalls::OutputError::serialize() { | ||||||
|  | 	unsigned int size = callstack.size(); | ||||||
|  | 	Array arr = { | ||||||
|  | 		hr, | ||||||
|  | 		min, | ||||||
|  | 		sec, msec, | ||||||
|  | 		source_file, | ||||||
|  | 		source_func, | ||||||
|  | 		source_line, | ||||||
|  | 		error, | ||||||
|  | 		error_descr, | ||||||
|  | 		warning, | ||||||
|  | 		size * 3 | ||||||
|  | 	}; | ||||||
|  | 	const ScriptLanguage::StackInfo *r = callstack.ptr(); | ||||||
|  | 	for (int i = 0; i < callstack.size(); i++) { | ||||||
|  | 		arr.push_back(r[i].file); | ||||||
|  | 		arr.push_back(r[i].func); | ||||||
|  | 		arr.push_back(r[i].line); | ||||||
|  | 	} | ||||||
|  | 	return arr; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool DebuggerMarshalls::OutputError::deserialize(const Array &p_arr) { | ||||||
|  | 	CHECK_SIZE(p_arr, 11, "OutputError"); | ||||||
|  | 	hr = p_arr[0]; | ||||||
|  | 	min = p_arr[1]; | ||||||
|  | 	sec = p_arr[2]; | ||||||
|  | 	msec = p_arr[3]; | ||||||
|  | 	source_file = p_arr[4]; | ||||||
|  | 	source_func = p_arr[5]; | ||||||
|  | 	source_line = p_arr[6]; | ||||||
|  | 	error = p_arr[7]; | ||||||
|  | 	error_descr = p_arr[8]; | ||||||
|  | 	warning = p_arr[9]; | ||||||
|  | 	unsigned int stack_size = p_arr[10]; | ||||||
|  | 	CHECK_SIZE(p_arr, stack_size, "OutputError"); | ||||||
|  | 	int idx = 11; | ||||||
|  | 	callstack.resize(stack_size / 3); | ||||||
|  | 	ScriptLanguage::StackInfo *w = callstack.ptrw(); | ||||||
|  | 	for (unsigned int i = 0; i < stack_size / 3; i++) { | ||||||
|  | 		w[i].file = p_arr[idx]; | ||||||
|  | 		w[i].func = p_arr[idx + 1]; | ||||||
|  | 		w[i].line = p_arr[idx + 2]; | ||||||
|  | 		idx += 3; | ||||||
|  | 	} | ||||||
|  | 	CHECK_END(p_arr, idx, "OutputError"); | ||||||
|  | 	return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Array DebuggerMarshalls::serialize_key_shortcut(const Ref<Shortcut> &p_shortcut) { | ||||||
|  | 	ERR_FAIL_COND_V(p_shortcut.is_null(), Array()); | ||||||
|  | 	Array keys; | ||||||
|  | 	for (const Ref<InputEvent> ev : p_shortcut->get_events()) { | ||||||
|  | 		const Ref<InputEventKey> kev = ev; | ||||||
|  | 		ERR_CONTINUE(kev.is_null()); | ||||||
|  | 		if (kev->get_physical_keycode() != Key::NONE) { | ||||||
|  | 			keys.push_back(true); | ||||||
|  | 			keys.push_back(kev->get_physical_keycode_with_modifiers()); | ||||||
|  | 		} else { | ||||||
|  | 			keys.push_back(false); | ||||||
|  | 			keys.push_back(kev->get_keycode_with_modifiers()); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return keys; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Ref<Shortcut> DebuggerMarshalls::deserialize_key_shortcut(const Array &p_keys) { | ||||||
|  | 	Array key_events; | ||||||
|  | 	ERR_FAIL_COND_V(p_keys.size() % 2 != 0, Ref<Shortcut>()); | ||||||
|  | 	for (int i = 0; i < p_keys.size(); i += 2) { | ||||||
|  | 		ERR_CONTINUE(p_keys[i].get_type() != Variant::BOOL); | ||||||
|  | 		ERR_CONTINUE(p_keys[i + 1].get_type() != Variant::INT); | ||||||
|  | 		key_events.push_back(InputEventKey::create_reference((Key)p_keys[i + 1].operator int(), p_keys[i].operator bool())); | ||||||
|  | 	} | ||||||
|  | 	if (key_events.is_empty()) { | ||||||
|  | 		return Ref<Shortcut>(); | ||||||
|  | 	} | ||||||
|  | 	Ref<Shortcut> shortcut; | ||||||
|  | 	shortcut.instantiate(); | ||||||
|  | 	shortcut->set_events(key_events); | ||||||
|  | 	return shortcut; | ||||||
|  | } | ||||||
							
								
								
									
										74
									
								
								engine/core/debugger/debugger_marshalls.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								engine/core/debugger/debugger_marshalls.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,74 @@ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*  debugger_marshalls.h                                                  */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*                         This file is part of:                          */ | ||||||
|  | /*                             GODOT ENGINE                               */ | ||||||
|  | /*                        https://godotengine.org                         */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ | ||||||
|  | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */ | ||||||
|  | /*                                                                        */ | ||||||
|  | /* 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.                 */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include "core/input/shortcut.h" | ||||||
|  | #include "core/object/script_language.h" | ||||||
|  | 
 | ||||||
|  | struct DebuggerMarshalls { | ||||||
|  | 	struct ScriptStackVariable { | ||||||
|  | 		String name; | ||||||
|  | 		Variant value; | ||||||
|  | 		int type = -1; | ||||||
|  | 		int var_type = -1; | ||||||
|  | 
 | ||||||
|  | 		Array serialize(int max_size = 1 << 20); // 1 MiB default.
 | ||||||
|  | 		bool deserialize(const Array &p_arr); | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	struct ScriptStackDump { | ||||||
|  | 		List<ScriptLanguage::StackInfo> frames; | ||||||
|  | 		ScriptStackDump() {} | ||||||
|  | 
 | ||||||
|  | 		Array serialize(); | ||||||
|  | 		bool deserialize(const Array &p_arr); | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	struct OutputError { | ||||||
|  | 		int hr = -1; | ||||||
|  | 		int min = -1; | ||||||
|  | 		int sec = -1; | ||||||
|  | 		int msec = -1; | ||||||
|  | 		String source_file; | ||||||
|  | 		String source_func; | ||||||
|  | 		int source_line = -1; | ||||||
|  | 		String error; | ||||||
|  | 		String error_descr; | ||||||
|  | 		bool warning = false; | ||||||
|  | 		Vector<ScriptLanguage::StackInfo> callstack; | ||||||
|  | 
 | ||||||
|  | 		Array serialize(); | ||||||
|  | 		bool deserialize(const Array &p_arr); | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	static Array serialize_key_shortcut(const Ref<Shortcut> &p_shortcut); | ||||||
|  | 	static Ref<Shortcut> deserialize_key_shortcut(const Array &p_keys); | ||||||
|  | }; | ||||||
							
								
								
									
										203
									
								
								engine/core/debugger/engine_debugger.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										203
									
								
								engine/core/debugger/engine_debugger.cpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,203 @@ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*  engine_debugger.cpp                                                   */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*                         This file is part of:                          */ | ||||||
|  | /*                             GODOT ENGINE                               */ | ||||||
|  | /*                        https://godotengine.org                         */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ | ||||||
|  | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */ | ||||||
|  | /*                                                                        */ | ||||||
|  | /* 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 "engine_debugger.h" | ||||||
|  | 
 | ||||||
|  | #include "core/debugger/local_debugger.h" | ||||||
|  | #include "core/debugger/remote_debugger.h" | ||||||
|  | #include "core/debugger/remote_debugger_peer.h" | ||||||
|  | #include "core/debugger/script_debugger.h" | ||||||
|  | #include "core/os/os.h" | ||||||
|  | 
 | ||||||
|  | EngineDebugger *EngineDebugger::singleton = nullptr; | ||||||
|  | ScriptDebugger *EngineDebugger::script_debugger = nullptr; | ||||||
|  | 
 | ||||||
|  | HashMap<StringName, EngineDebugger::Profiler> EngineDebugger::profilers; | ||||||
|  | HashMap<StringName, EngineDebugger::Capture> EngineDebugger::captures; | ||||||
|  | HashMap<String, EngineDebugger::CreatePeerFunc> EngineDebugger::protocols; | ||||||
|  | 
 | ||||||
|  | void (*EngineDebugger::allow_focus_steal_fn)(); | ||||||
|  | 
 | ||||||
|  | void EngineDebugger::register_profiler(const StringName &p_name, const Profiler &p_func) { | ||||||
|  | 	ERR_FAIL_COND_MSG(profilers.has(p_name), vformat("Profiler already registered: '%s'.", p_name)); | ||||||
|  | 	profilers.insert(p_name, p_func); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void EngineDebugger::unregister_profiler(const StringName &p_name) { | ||||||
|  | 	ERR_FAIL_COND_MSG(!profilers.has(p_name), vformat("Profiler not registered: '%s'.", p_name)); | ||||||
|  | 	Profiler &p = profilers[p_name]; | ||||||
|  | 	if (p.active && p.toggle) { | ||||||
|  | 		p.toggle(p.data, false, Array()); | ||||||
|  | 		p.active = false; | ||||||
|  | 	} | ||||||
|  | 	profilers.erase(p_name); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void EngineDebugger::register_message_capture(const StringName &p_name, Capture p_func) { | ||||||
|  | 	ERR_FAIL_COND_MSG(captures.has(p_name), vformat("Capture already registered: '%s'.", p_name)); | ||||||
|  | 	captures.insert(p_name, p_func); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void EngineDebugger::unregister_message_capture(const StringName &p_name) { | ||||||
|  | 	ERR_FAIL_COND_MSG(!captures.has(p_name), vformat("Capture not registered: '%s'.", p_name)); | ||||||
|  | 	captures.erase(p_name); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void EngineDebugger::register_uri_handler(const String &p_protocol, CreatePeerFunc p_func) { | ||||||
|  | 	ERR_FAIL_COND_MSG(protocols.has(p_protocol), vformat("Protocol handler already registered: '%s'.", p_protocol)); | ||||||
|  | 	protocols.insert(p_protocol, p_func); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void EngineDebugger::profiler_enable(const StringName &p_name, bool p_enabled, const Array &p_opts) { | ||||||
|  | 	ERR_FAIL_COND_MSG(!profilers.has(p_name), vformat("Can't change profiler state, no profiler: '%s'.", p_name)); | ||||||
|  | 	Profiler &p = profilers[p_name]; | ||||||
|  | 	if (p.toggle) { | ||||||
|  | 		p.toggle(p.data, p_enabled, p_opts); | ||||||
|  | 	} | ||||||
|  | 	p.active = p_enabled; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void EngineDebugger::profiler_add_frame_data(const StringName &p_name, const Array &p_data) { | ||||||
|  | 	ERR_FAIL_COND_MSG(!profilers.has(p_name), vformat("Can't add frame data, no profiler: '%s'.", p_name)); | ||||||
|  | 	Profiler &p = profilers[p_name]; | ||||||
|  | 	if (p.add) { | ||||||
|  | 		p.add(p.data, p_data); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool EngineDebugger::is_profiling(const StringName &p_name) { | ||||||
|  | 	return profilers.has(p_name) && profilers[p_name].active; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool EngineDebugger::has_profiler(const StringName &p_name) { | ||||||
|  | 	return profilers.has(p_name); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool EngineDebugger::has_capture(const StringName &p_name) { | ||||||
|  | 	return captures.has(p_name); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Error EngineDebugger::capture_parse(const StringName &p_name, const String &p_msg, const Array &p_args, bool &r_captured) { | ||||||
|  | 	r_captured = false; | ||||||
|  | 	ERR_FAIL_COND_V_MSG(!captures.has(p_name), ERR_UNCONFIGURED, vformat("Capture not registered: '%s'.", p_name)); | ||||||
|  | 	const Capture &cap = captures[p_name]; | ||||||
|  | 	return cap.capture(cap.data, p_msg, p_args, r_captured); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void EngineDebugger::iteration(uint64_t p_frame_ticks, uint64_t p_process_ticks, uint64_t p_physics_ticks, double p_physics_frame_time) { | ||||||
|  | 	frame_time = USEC_TO_SEC(p_frame_ticks); | ||||||
|  | 	process_time = USEC_TO_SEC(p_process_ticks); | ||||||
|  | 	physics_time = USEC_TO_SEC(p_physics_ticks); | ||||||
|  | 	physics_frame_time = p_physics_frame_time; | ||||||
|  | 	// Notify tick to running profilers
 | ||||||
|  | 	for (KeyValue<StringName, Profiler> &E : profilers) { | ||||||
|  | 		Profiler &p = E.value; | ||||||
|  | 		if (!p.active || !p.tick) { | ||||||
|  | 			continue; | ||||||
|  | 		} | ||||||
|  | 		p.tick(p.data, frame_time, process_time, physics_time, physics_frame_time); | ||||||
|  | 	} | ||||||
|  | 	singleton->poll_events(true); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void EngineDebugger::initialize(const String &p_uri, bool p_skip_breakpoints, bool p_ignore_error_breaks, const Vector<String> &p_breakpoints, void (*p_allow_focus_steal_fn)()) { | ||||||
|  | 	register_uri_handler("tcp://", RemoteDebuggerPeerTCP::create); // TCP is the default protocol. Platforms/modules can add more.
 | ||||||
|  | 	if (p_uri.is_empty()) { | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 	if (p_uri == "local://") { | ||||||
|  | 		singleton = memnew(LocalDebugger); | ||||||
|  | 		script_debugger = memnew(ScriptDebugger); | ||||||
|  | 		// Tell the OS that we want to handle termination signals.
 | ||||||
|  | 		OS::get_singleton()->initialize_debugging(); | ||||||
|  | 	} else if (p_uri.contains("://")) { | ||||||
|  | 		const String proto = p_uri.substr(0, p_uri.find("://") + 3); | ||||||
|  | 		if (!protocols.has(proto)) { | ||||||
|  | 			return; | ||||||
|  | 		} | ||||||
|  | 		RemoteDebuggerPeer *peer = protocols[proto](p_uri); | ||||||
|  | 		if (!peer) { | ||||||
|  | 			return; | ||||||
|  | 		} | ||||||
|  | 		singleton = memnew(RemoteDebugger(Ref<RemoteDebuggerPeer>(peer))); | ||||||
|  | 		script_debugger = memnew(ScriptDebugger); | ||||||
|  | 		// Notify editor of our pid (to allow focus stealing).
 | ||||||
|  | 		Array msg = { OS::get_singleton()->get_process_id() }; | ||||||
|  | 		singleton->send_message("set_pid", msg); | ||||||
|  | 	} | ||||||
|  | 	if (!singleton) { | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// There is a debugger, parse breakpoints.
 | ||||||
|  | 	ScriptDebugger *singleton_script_debugger = singleton->get_script_debugger(); | ||||||
|  | 	singleton_script_debugger->set_skip_breakpoints(p_skip_breakpoints); | ||||||
|  | 	singleton_script_debugger->set_ignore_error_breaks(p_ignore_error_breaks); | ||||||
|  | 
 | ||||||
|  | 	for (int i = 0; i < p_breakpoints.size(); i++) { | ||||||
|  | 		const String &bp = p_breakpoints[i]; | ||||||
|  | 		int sp = bp.rfind_char(':'); | ||||||
|  | 		ERR_CONTINUE_MSG(sp == -1, vformat("Invalid breakpoint: '%s', expected file:line format.", bp)); | ||||||
|  | 
 | ||||||
|  | 		singleton_script_debugger->insert_breakpoint(bp.substr(sp + 1).to_int(), bp.substr(0, sp)); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	allow_focus_steal_fn = p_allow_focus_steal_fn; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void EngineDebugger::deinitialize() { | ||||||
|  | 	if (singleton) { | ||||||
|  | 		// Stop all profilers
 | ||||||
|  | 		for (const KeyValue<StringName, Profiler> &E : profilers) { | ||||||
|  | 			if (E.value.active) { | ||||||
|  | 				singleton->profiler_enable(E.key, false); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// Flush any remaining message
 | ||||||
|  | 		singleton->poll_events(false); | ||||||
|  | 
 | ||||||
|  | 		memdelete(singleton); | ||||||
|  | 		singleton = nullptr; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Clear profilers/captures/protocol handlers.
 | ||||||
|  | 	profilers.clear(); | ||||||
|  | 	captures.clear(); | ||||||
|  | 	protocols.clear(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | EngineDebugger::~EngineDebugger() { | ||||||
|  | 	if (script_debugger) { | ||||||
|  | 		memdelete(script_debugger); | ||||||
|  | 	} | ||||||
|  | 	script_debugger = nullptr; | ||||||
|  | 	singleton = nullptr; | ||||||
|  | } | ||||||
							
								
								
									
										141
									
								
								engine/core/debugger/engine_debugger.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										141
									
								
								engine/core/debugger/engine_debugger.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,141 @@ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*  engine_debugger.h                                                     */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*                         This file is part of:                          */ | ||||||
|  | /*                             GODOT ENGINE                               */ | ||||||
|  | /*                        https://godotengine.org                         */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ | ||||||
|  | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */ | ||||||
|  | /*                                                                        */ | ||||||
|  | /* 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.                 */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include "core/string/string_name.h" | ||||||
|  | #include "core/string/ustring.h" | ||||||
|  | #include "core/templates/hash_map.h" | ||||||
|  | #include "core/templates/vector.h" | ||||||
|  | #include "core/variant/array.h" | ||||||
|  | 
 | ||||||
|  | class RemoteDebuggerPeer; | ||||||
|  | class ScriptDebugger; | ||||||
|  | 
 | ||||||
|  | class EngineDebugger { | ||||||
|  | public: | ||||||
|  | 	typedef void (*ProfilingToggle)(void *p_user, bool p_enable, const Array &p_opts); | ||||||
|  | 	typedef void (*ProfilingTick)(void *p_user, double p_frame_time, double p_process_time, double p_physics_time, double p_physics_frame_time); | ||||||
|  | 	typedef void (*ProfilingAdd)(void *p_user, const Array &p_arr); | ||||||
|  | 
 | ||||||
|  | 	typedef Error (*CaptureFunc)(void *p_user, const String &p_msg, const Array &p_args, bool &r_captured); | ||||||
|  | 
 | ||||||
|  | 	typedef RemoteDebuggerPeer *(*CreatePeerFunc)(const String &p_uri); | ||||||
|  | 
 | ||||||
|  | 	class Profiler { | ||||||
|  | 		friend class EngineDebugger; | ||||||
|  | 
 | ||||||
|  | 		ProfilingToggle toggle = nullptr; | ||||||
|  | 		ProfilingAdd add = nullptr; | ||||||
|  | 		ProfilingTick tick = nullptr; | ||||||
|  | 		void *data = nullptr; | ||||||
|  | 		bool active = false; | ||||||
|  | 
 | ||||||
|  | 	public: | ||||||
|  | 		Profiler() {} | ||||||
|  | 		Profiler(void *p_data, ProfilingToggle p_toggle, ProfilingAdd p_add, ProfilingTick p_tick) { | ||||||
|  | 			data = p_data; | ||||||
|  | 			toggle = p_toggle; | ||||||
|  | 			add = p_add; | ||||||
|  | 			tick = p_tick; | ||||||
|  | 		} | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	class Capture { | ||||||
|  | 		friend class EngineDebugger; | ||||||
|  | 
 | ||||||
|  | 		CaptureFunc capture = nullptr; | ||||||
|  | 		void *data = nullptr; | ||||||
|  | 
 | ||||||
|  | 	public: | ||||||
|  | 		Capture() {} | ||||||
|  | 		Capture(void *p_data, CaptureFunc p_capture) { | ||||||
|  | 			data = p_data; | ||||||
|  | 			capture = p_capture; | ||||||
|  | 		} | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  | 	double frame_time = 0.0; | ||||||
|  | 	double process_time = 0.0; | ||||||
|  | 	double physics_time = 0.0; | ||||||
|  | 	double physics_frame_time = 0.0; | ||||||
|  | 
 | ||||||
|  | 	uint32_t poll_every = 0; | ||||||
|  | 
 | ||||||
|  | protected: | ||||||
|  | 	static EngineDebugger *singleton; | ||||||
|  | 	static ScriptDebugger *script_debugger; | ||||||
|  | 
 | ||||||
|  | 	static HashMap<StringName, Profiler> profilers; | ||||||
|  | 	static HashMap<StringName, Capture> captures; | ||||||
|  | 	static HashMap<String, CreatePeerFunc> protocols; | ||||||
|  | 
 | ||||||
|  | 	static void (*allow_focus_steal_fn)(); | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  | 	_FORCE_INLINE_ static EngineDebugger *get_singleton() { return singleton; } | ||||||
|  | 	_FORCE_INLINE_ static bool is_active() { return singleton != nullptr && script_debugger != nullptr; } | ||||||
|  | 
 | ||||||
|  | 	_FORCE_INLINE_ static ScriptDebugger *get_script_debugger() { return script_debugger; } | ||||||
|  | 
 | ||||||
|  | 	static void initialize(const String &p_uri, bool p_skip_breakpoints, bool p_ignore_error_breaks, const Vector<String> &p_breakpoints, void (*p_allow_focus_steal_fn)()); | ||||||
|  | 	static void deinitialize(); | ||||||
|  | 	static void register_profiler(const StringName &p_name, const Profiler &p_profiler); | ||||||
|  | 	static void unregister_profiler(const StringName &p_name); | ||||||
|  | 	static bool is_profiling(const StringName &p_name); | ||||||
|  | 	static bool has_profiler(const StringName &p_name); | ||||||
|  | 	static void profiler_add_frame_data(const StringName &p_name, const Array &p_data); | ||||||
|  | 
 | ||||||
|  | 	static void register_message_capture(const StringName &p_name, Capture p_func); | ||||||
|  | 	static void unregister_message_capture(const StringName &p_name); | ||||||
|  | 	static bool has_capture(const StringName &p_name); | ||||||
|  | 
 | ||||||
|  | 	static void register_uri_handler(const String &p_protocol, CreatePeerFunc p_func); | ||||||
|  | 
 | ||||||
|  | 	void iteration(uint64_t p_frame_ticks, uint64_t p_process_ticks, uint64_t p_physics_ticks, double p_physics_frame_time); | ||||||
|  | 	void profiler_enable(const StringName &p_name, bool p_enabled, const Array &p_opts = Array()); | ||||||
|  | 	Error capture_parse(const StringName &p_name, const String &p_msg, const Array &p_args, bool &r_captured); | ||||||
|  | 
 | ||||||
|  | 	void line_poll() { | ||||||
|  | 		// The purpose of this is just processing events every now and then when the script might get too busy otherwise bugs like infinite loops can't be caught.
 | ||||||
|  | 		if (unlikely(poll_every % 2048) == 0) { | ||||||
|  | 			poll_events(false); | ||||||
|  | 		} | ||||||
|  | 		poll_every++; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	virtual void poll_events(bool p_is_idle) {} | ||||||
|  | 	virtual void send_message(const String &p_msg, const Array &p_data) = 0; | ||||||
|  | 	virtual void send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, bool p_editor_notify, ErrorHandlerType p_type) = 0; | ||||||
|  | 	virtual void debug(bool p_can_continue = true, bool p_is_error_breakpoint = false) = 0; | ||||||
|  | 
 | ||||||
|  | 	virtual ~EngineDebugger(); | ||||||
|  | }; | ||||||
							
								
								
									
										82
									
								
								engine/core/debugger/engine_profiler.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								engine/core/debugger/engine_profiler.cpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,82 @@ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*  engine_profiler.cpp                                                   */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*                         This file is part of:                          */ | ||||||
|  | /*                             GODOT ENGINE                               */ | ||||||
|  | /*                        https://godotengine.org                         */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ | ||||||
|  | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */ | ||||||
|  | /*                                                                        */ | ||||||
|  | /* 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 "engine_profiler.h" | ||||||
|  | 
 | ||||||
|  | #include "core/debugger/engine_debugger.h" | ||||||
|  | 
 | ||||||
|  | void EngineProfiler::_bind_methods() { | ||||||
|  | 	GDVIRTUAL_BIND(_toggle, "enable", "options"); | ||||||
|  | 	GDVIRTUAL_BIND(_add_frame, "data"); | ||||||
|  | 	GDVIRTUAL_BIND(_tick, "frame_time", "process_time", "physics_time", "physics_frame_time"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void EngineProfiler::toggle(bool p_enable, const Array &p_array) { | ||||||
|  | 	GDVIRTUAL_CALL(_toggle, p_enable, p_array); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void EngineProfiler::add(const Array &p_data) { | ||||||
|  | 	GDVIRTUAL_CALL(_add_frame, p_data); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void EngineProfiler::tick(double p_frame_time, double p_process_time, double p_physics_time, double p_physics_frame_time) { | ||||||
|  | 	GDVIRTUAL_CALL(_tick, p_frame_time, p_process_time, p_physics_time, p_physics_frame_time); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Error EngineProfiler::bind(const String &p_name) { | ||||||
|  | 	ERR_FAIL_COND_V(is_bound(), ERR_ALREADY_IN_USE); | ||||||
|  | 	EngineDebugger::Profiler prof( | ||||||
|  | 			this, | ||||||
|  | 			[](void *p_user, bool p_enable, const Array &p_opts) { | ||||||
|  | 				static_cast<EngineProfiler *>(p_user)->toggle(p_enable, p_opts); | ||||||
|  | 			}, | ||||||
|  | 			[](void *p_user, const Array &p_data) { | ||||||
|  | 				static_cast<EngineProfiler *>(p_user)->add(p_data); | ||||||
|  | 			}, | ||||||
|  | 			[](void *p_user, double p_frame_time, double p_process_time, double p_physics_time, double p_physics_frame_time) { | ||||||
|  | 				static_cast<EngineProfiler *>(p_user)->tick(p_frame_time, p_process_time, p_physics_time, p_physics_frame_time); | ||||||
|  | 			}); | ||||||
|  | 	registration = p_name; | ||||||
|  | 	EngineDebugger::register_profiler(p_name, prof); | ||||||
|  | 	return OK; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Error EngineProfiler::unbind() { | ||||||
|  | 	ERR_FAIL_COND_V(!is_bound(), ERR_UNCONFIGURED); | ||||||
|  | 	EngineDebugger::unregister_profiler(registration); | ||||||
|  | 	registration.clear(); | ||||||
|  | 	return OK; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | EngineProfiler::~EngineProfiler() { | ||||||
|  | 	if (is_bound()) { | ||||||
|  | 		unbind(); | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										60
									
								
								engine/core/debugger/engine_profiler.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								engine/core/debugger/engine_profiler.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,60 @@ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*  engine_profiler.h                                                     */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*                         This file is part of:                          */ | ||||||
|  | /*                             GODOT ENGINE                               */ | ||||||
|  | /*                        https://godotengine.org                         */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ | ||||||
|  | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */ | ||||||
|  | /*                                                                        */ | ||||||
|  | /* 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.                 */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include "core/object/gdvirtual.gen.inc" | ||||||
|  | #include "core/object/ref_counted.h" | ||||||
|  | 
 | ||||||
|  | class EngineProfiler : public RefCounted { | ||||||
|  | 	GDCLASS(EngineProfiler, RefCounted); | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  | 	String registration; | ||||||
|  | 
 | ||||||
|  | protected: | ||||||
|  | 	static void _bind_methods(); | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  | 	virtual void toggle(bool p_enable, const Array &p_opts); | ||||||
|  | 	virtual void add(const Array &p_data); | ||||||
|  | 	virtual void tick(double p_frame_time, double p_process_time, double p_physics_time, double p_physics_frame_time); | ||||||
|  | 
 | ||||||
|  | 	Error bind(const String &p_name); | ||||||
|  | 	Error unbind(); | ||||||
|  | 	bool is_bound() const { return registration.length() > 0; } | ||||||
|  | 
 | ||||||
|  | 	GDVIRTUAL2(_toggle, bool, Array); | ||||||
|  | 	GDVIRTUAL1(_add_frame, Array); | ||||||
|  | 	GDVIRTUAL4(_tick, double, double, double, double); | ||||||
|  | 
 | ||||||
|  | 	EngineProfiler() {} | ||||||
|  | 	virtual ~EngineProfiler(); | ||||||
|  | }; | ||||||
							
								
								
									
										390
									
								
								engine/core/debugger/local_debugger.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										390
									
								
								engine/core/debugger/local_debugger.cpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,390 @@ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*  local_debugger.cpp                                                    */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*                         This file is part of:                          */ | ||||||
|  | /*                             GODOT ENGINE                               */ | ||||||
|  | /*                        https://godotengine.org                         */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ | ||||||
|  | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */ | ||||||
|  | /*                                                                        */ | ||||||
|  | /* 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 "local_debugger.h" | ||||||
|  | 
 | ||||||
|  | #include "core/debugger/script_debugger.h" | ||||||
|  | #include "core/os/os.h" | ||||||
|  | 
 | ||||||
|  | struct LocalDebugger::ScriptsProfiler { | ||||||
|  | 	struct ProfileInfoSort { | ||||||
|  | 		bool operator()(const ScriptLanguage::ProfilingInfo &A, const ScriptLanguage::ProfilingInfo &B) const { | ||||||
|  | 			return A.total_time > B.total_time; | ||||||
|  | 		} | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	double frame_time = 0; | ||||||
|  | 	uint64_t idle_accum = 0; | ||||||
|  | 	Vector<ScriptLanguage::ProfilingInfo> pinfo; | ||||||
|  | 
 | ||||||
|  | 	void toggle(bool p_enable, const Array &p_opts) { | ||||||
|  | 		if (p_enable) { | ||||||
|  | 			for (int i = 0; i < ScriptServer::get_language_count(); i++) { | ||||||
|  | 				ScriptServer::get_language(i)->profiling_start(); | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			print_line("BEGIN PROFILING"); | ||||||
|  | 			pinfo.resize(32768); | ||||||
|  | 		} else { | ||||||
|  | 			_print_frame_data(true); | ||||||
|  | 			for (int i = 0; i < ScriptServer::get_language_count(); i++) { | ||||||
|  | 				ScriptServer::get_language(i)->profiling_stop(); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	void tick(double p_frame_time, double p_process_time, double p_physics_time, double p_physics_frame_time) { | ||||||
|  | 		frame_time = p_frame_time; | ||||||
|  | 		_print_frame_data(false); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	void _print_frame_data(bool p_accumulated) { | ||||||
|  | 		uint64_t diff = OS::get_singleton()->get_ticks_usec() - idle_accum; | ||||||
|  | 
 | ||||||
|  | 		if (!p_accumulated && diff < 1000000) { //show every one second
 | ||||||
|  | 			return; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		idle_accum = OS::get_singleton()->get_ticks_usec(); | ||||||
|  | 
 | ||||||
|  | 		int ofs = 0; | ||||||
|  | 		for (int i = 0; i < ScriptServer::get_language_count(); i++) { | ||||||
|  | 			if (p_accumulated) { | ||||||
|  | 				ofs += ScriptServer::get_language(i)->profiling_get_accumulated_data(&pinfo.write[ofs], pinfo.size() - ofs); | ||||||
|  | 			} else { | ||||||
|  | 				ofs += ScriptServer::get_language(i)->profiling_get_frame_data(&pinfo.write[ofs], pinfo.size() - ofs); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		SortArray<ScriptLanguage::ProfilingInfo, ProfileInfoSort> sort; | ||||||
|  | 		sort.sort(pinfo.ptrw(), ofs); | ||||||
|  | 
 | ||||||
|  | 		// compute total script frame time
 | ||||||
|  | 		uint64_t script_time_us = 0; | ||||||
|  | 		for (int i = 0; i < ofs; i++) { | ||||||
|  | 			script_time_us += pinfo[i].self_time; | ||||||
|  | 		} | ||||||
|  | 		double script_time = USEC_TO_SEC(script_time_us); | ||||||
|  | 		double total_time = p_accumulated ? script_time : frame_time; | ||||||
|  | 
 | ||||||
|  | 		if (!p_accumulated) { | ||||||
|  | 			print_line("FRAME: total: " + rtos(total_time) + " script: " + rtos(script_time) + "/" + itos(script_time * 100 / total_time) + " %"); | ||||||
|  | 		} else { | ||||||
|  | 			print_line("ACCUMULATED: total: " + rtos(total_time)); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		for (int i = 0; i < ofs; i++) { | ||||||
|  | 			print_line(itos(i) + ":" + pinfo[i].signature); | ||||||
|  | 			double tt = USEC_TO_SEC(pinfo[i].total_time); | ||||||
|  | 			double st = USEC_TO_SEC(pinfo[i].self_time); | ||||||
|  | 			print_line("\ttotal: " + rtos(tt) + "/" + itos(tt * 100 / total_time) + " % \tself: " + rtos(st) + "/" + itos(st * 100 / total_time) + " % tcalls: " + itos(pinfo[i].call_count)); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	ScriptsProfiler() { | ||||||
|  | 		idle_accum = OS::get_singleton()->get_ticks_usec(); | ||||||
|  | 	} | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | void LocalDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) { | ||||||
|  | 	ScriptLanguage *script_lang = script_debugger->get_break_language(); | ||||||
|  | 
 | ||||||
|  | 	if (!target_function.is_empty()) { | ||||||
|  | 		String current_function = script_lang->debug_get_stack_level_function(0); | ||||||
|  | 		if (current_function != target_function) { | ||||||
|  | 			script_debugger->set_depth(0); | ||||||
|  | 			script_debugger->set_lines_left(1); | ||||||
|  | 			return; | ||||||
|  | 		} | ||||||
|  | 		target_function = ""; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	print_line("\nDebugger Break, Reason: '" + script_lang->debug_get_error() + "'"); | ||||||
|  | 	print_line("*Frame " + itos(0) + " - " + script_lang->debug_get_stack_level_source(0) + ":" + itos(script_lang->debug_get_stack_level_line(0)) + " in function '" + script_lang->debug_get_stack_level_function(0) + "'"); | ||||||
|  | 	print_line("Enter \"help\" for assistance."); | ||||||
|  | 	int current_frame = 0; | ||||||
|  | 	int total_frames = script_lang->debug_get_stack_level_count(); | ||||||
|  | 	while (true) { | ||||||
|  | 		OS::get_singleton()->print("debug> "); | ||||||
|  | 		String line = OS::get_singleton()->get_stdin_string().strip_edges(); | ||||||
|  | 
 | ||||||
|  | 		// Cache options
 | ||||||
|  | 		String variable_prefix = options["variable_prefix"]; | ||||||
|  | 
 | ||||||
|  | 		if (line.is_empty() && !feof(stdin)) { | ||||||
|  | 			print_line("\nDebugger Break, Reason: '" + script_lang->debug_get_error() + "'"); | ||||||
|  | 			print_line("*Frame " + itos(current_frame) + " - " + script_lang->debug_get_stack_level_source(current_frame) + ":" + itos(script_lang->debug_get_stack_level_line(current_frame)) + " in function '" + script_lang->debug_get_stack_level_function(current_frame) + "'"); | ||||||
|  | 			print_line("Enter \"help\" for assistance."); | ||||||
|  | 		} else if (line == "c" || line == "continue") { | ||||||
|  | 			break; | ||||||
|  | 		} else if (line == "bt" || line == "breakpoint") { | ||||||
|  | 			for (int i = 0; i < total_frames; i++) { | ||||||
|  | 				String cfi = (current_frame == i) ? "*" : " "; //current frame indicator
 | ||||||
|  | 				print_line(cfi + "Frame " + itos(i) + " - " + script_lang->debug_get_stack_level_source(i) + ":" + itos(script_lang->debug_get_stack_level_line(i)) + " in function '" + script_lang->debug_get_stack_level_function(i) + "'"); | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 		} else if (line.begins_with("fr") || line.begins_with("frame")) { | ||||||
|  | 			if (line.get_slice_count(" ") == 1) { | ||||||
|  | 				print_line("*Frame " + itos(current_frame) + " - " + script_lang->debug_get_stack_level_source(current_frame) + ":" + itos(script_lang->debug_get_stack_level_line(current_frame)) + " in function '" + script_lang->debug_get_stack_level_function(current_frame) + "'"); | ||||||
|  | 			} else { | ||||||
|  | 				int frame = line.get_slicec(' ', 1).to_int(); | ||||||
|  | 				if (frame < 0 || frame >= total_frames) { | ||||||
|  | 					print_line("Error: Invalid frame."); | ||||||
|  | 				} else { | ||||||
|  | 					current_frame = frame; | ||||||
|  | 					print_line("*Frame " + itos(frame) + " - " + script_lang->debug_get_stack_level_source(frame) + ":" + itos(script_lang->debug_get_stack_level_line(frame)) + " in function '" + script_lang->debug_get_stack_level_function(frame) + "'"); | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 		} else if (line.begins_with("set")) { | ||||||
|  | 			if (line.get_slice_count(" ") == 1) { | ||||||
|  | 				for (const KeyValue<String, String> &E : options) { | ||||||
|  | 					print_line("\t" + E.key + "=" + E.value); | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 			} else { | ||||||
|  | 				String key_value = line.get_slicec(' ', 1); | ||||||
|  | 				int value_pos = key_value.find_char('='); | ||||||
|  | 
 | ||||||
|  | 				if (value_pos < 0) { | ||||||
|  | 					print_line("Error: Invalid set format. Use: set key=value"); | ||||||
|  | 				} else { | ||||||
|  | 					String key = key_value.left(value_pos); | ||||||
|  | 
 | ||||||
|  | 					if (!options.has(key)) { | ||||||
|  | 						print_line("Error: Unknown option " + key); | ||||||
|  | 					} else { | ||||||
|  | 						// Allow explicit tab character
 | ||||||
|  | 						String value = key_value.substr(value_pos + 1).replace("\\t", "\t"); | ||||||
|  | 
 | ||||||
|  | 						options[key] = value; | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 		} else if (line == "lv" || line == "locals") { | ||||||
|  | 			List<String> locals; | ||||||
|  | 			List<Variant> values; | ||||||
|  | 			script_lang->debug_get_stack_level_locals(current_frame, &locals, &values); | ||||||
|  | 			print_variables(locals, values, variable_prefix); | ||||||
|  | 
 | ||||||
|  | 		} else if (line == "gv" || line == "globals") { | ||||||
|  | 			List<String> globals; | ||||||
|  | 			List<Variant> values; | ||||||
|  | 			script_lang->debug_get_globals(&globals, &values); | ||||||
|  | 			print_variables(globals, values, variable_prefix); | ||||||
|  | 
 | ||||||
|  | 		} else if (line == "mv" || line == "members") { | ||||||
|  | 			List<String> members; | ||||||
|  | 			List<Variant> values; | ||||||
|  | 			script_lang->debug_get_stack_level_members(current_frame, &members, &values); | ||||||
|  | 			print_variables(members, values, variable_prefix); | ||||||
|  | 
 | ||||||
|  | 		} else if (line.begins_with("p") || line.begins_with("print")) { | ||||||
|  | 			if (line.find_char(' ') < 0) { | ||||||
|  | 				print_line("Usage: print <expression>"); | ||||||
|  | 			} else { | ||||||
|  | 				String expr = line.split(" ", true, 1)[1]; | ||||||
|  | 				String res = script_lang->debug_parse_stack_level_expression(current_frame, expr); | ||||||
|  | 				print_line(res); | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 		} else if (line == "s" || line == "step") { | ||||||
|  | 			script_debugger->set_depth(-1); | ||||||
|  | 			script_debugger->set_lines_left(1); | ||||||
|  | 			break; | ||||||
|  | 		} else if (line == "n" || line == "next") { | ||||||
|  | 			script_debugger->set_depth(0); | ||||||
|  | 			script_debugger->set_lines_left(1); | ||||||
|  | 			break; | ||||||
|  | 		} else if (line == "fin" || line == "finish") { | ||||||
|  | 			String current_function = script_lang->debug_get_stack_level_function(0); | ||||||
|  | 
 | ||||||
|  | 			for (int i = 0; i < total_frames; i++) { | ||||||
|  | 				target_function = script_lang->debug_get_stack_level_function(i); | ||||||
|  | 				if (target_function != current_function) { | ||||||
|  | 					script_debugger->set_depth(0); | ||||||
|  | 					script_debugger->set_lines_left(1); | ||||||
|  | 					return; | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			print_line("Error: Reached last frame."); | ||||||
|  | 			target_function = ""; | ||||||
|  | 
 | ||||||
|  | 		} else if (line.begins_with("br") || line.begins_with("break")) { | ||||||
|  | 			if (line.get_slice_count(" ") <= 1) { | ||||||
|  | 				const HashMap<int, HashSet<StringName>> &breakpoints = script_debugger->get_breakpoints(); | ||||||
|  | 				if (breakpoints.is_empty()) { | ||||||
|  | 					print_line("No Breakpoints."); | ||||||
|  | 					continue; | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				print_line("Breakpoint(s): " + itos(breakpoints.size())); | ||||||
|  | 				for (const KeyValue<int, HashSet<StringName>> &E : breakpoints) { | ||||||
|  | 					print_line("\t" + String(*E.value.begin()) + ":" + itos(E.key)); | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 			} else { | ||||||
|  | 				Pair<String, int> breakpoint = to_breakpoint(line); | ||||||
|  | 
 | ||||||
|  | 				String source = breakpoint.first; | ||||||
|  | 				int linenr = breakpoint.second; | ||||||
|  | 
 | ||||||
|  | 				if (source.is_empty()) { | ||||||
|  | 					continue; | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				script_debugger->insert_breakpoint(linenr, source); | ||||||
|  | 
 | ||||||
|  | 				print_line("Added breakpoint at " + source + ":" + itos(linenr)); | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 		} else if (line == "q" || line == "quit" || | ||||||
|  | 				(line.is_empty() && feof(stdin))) { | ||||||
|  | 			// Do not stop again on quit
 | ||||||
|  | 			script_debugger->clear_breakpoints(); | ||||||
|  | 			script_debugger->set_depth(-1); | ||||||
|  | 			script_debugger->set_lines_left(-1); | ||||||
|  | 
 | ||||||
|  | 			MainLoop *main_loop = OS::get_singleton()->get_main_loop(); | ||||||
|  | 			if (main_loop->get_class() == "SceneTree") { | ||||||
|  | 				main_loop->call("quit"); | ||||||
|  | 			} | ||||||
|  | 			break; | ||||||
|  | 		} else if (line.begins_with("delete")) { | ||||||
|  | 			if (line.get_slice_count(" ") <= 1) { | ||||||
|  | 				script_debugger->clear_breakpoints(); | ||||||
|  | 			} else { | ||||||
|  | 				Pair<String, int> breakpoint = to_breakpoint(line); | ||||||
|  | 
 | ||||||
|  | 				String source = breakpoint.first; | ||||||
|  | 				int linenr = breakpoint.second; | ||||||
|  | 
 | ||||||
|  | 				if (source.is_empty()) { | ||||||
|  | 					continue; | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				script_debugger->remove_breakpoint(linenr, source); | ||||||
|  | 
 | ||||||
|  | 				print_line("Removed breakpoint at " + source + ":" + itos(linenr)); | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 		} else if (line == "h" || line == "help") { | ||||||
|  | 			print_line("Built-In Debugger command list:\n"); | ||||||
|  | 			print_line("\tc,continue\t\t Continue execution."); | ||||||
|  | 			print_line("\tbt,backtrace\t\t Show stack trace (frames)."); | ||||||
|  | 			print_line("\tfr,frame <frame>:\t Change current frame."); | ||||||
|  | 			print_line("\tlv,locals\t\t Show local variables for current frame."); | ||||||
|  | 			print_line("\tmv,members\t\t Show member variables for \"this\" in frame."); | ||||||
|  | 			print_line("\tgv,globals\t\t Show global variables."); | ||||||
|  | 			print_line("\tp,print <expr>\t\t Execute and print variable in expression."); | ||||||
|  | 			print_line("\ts,step\t\t\t Step to next line."); | ||||||
|  | 			print_line("\tn,next\t\t\t Next line."); | ||||||
|  | 			print_line("\tfin,finish\t\t Step out of current frame."); | ||||||
|  | 			print_line("\tbr,break [source:line]\t List all breakpoints or place a breakpoint."); | ||||||
|  | 			print_line("\tdelete [source:line]:\t Delete one/all breakpoints."); | ||||||
|  | 			print_line("\tset [key=value]:\t List all options, or set one."); | ||||||
|  | 			print_line("\tq,quit\t\t\t Quit application."); | ||||||
|  | 		} else { | ||||||
|  | 			print_line("Error: Invalid command, enter \"help\" for assistance."); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void LocalDebugger::print_variables(const List<String> &names, const List<Variant> &values, const String &variable_prefix) { | ||||||
|  | 	String value; | ||||||
|  | 	Vector<String> value_lines; | ||||||
|  | 	const List<Variant>::Element *V = values.front(); | ||||||
|  | 	for (const String &E : names) { | ||||||
|  | 		value = String(V->get()); | ||||||
|  | 
 | ||||||
|  | 		if (variable_prefix.is_empty()) { | ||||||
|  | 			print_line(E + ": " + String(V->get())); | ||||||
|  | 		} else { | ||||||
|  | 			print_line(E + ":"); | ||||||
|  | 			value_lines = value.split("\n"); | ||||||
|  | 			for (int i = 0; i < value_lines.size(); ++i) { | ||||||
|  | 				print_line(variable_prefix + value_lines[i]); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		V = V->next(); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Pair<String, int> LocalDebugger::to_breakpoint(const String &p_line) { | ||||||
|  | 	String breakpoint_part = p_line.get_slicec(' ', 1); | ||||||
|  | 	Pair<String, int> breakpoint; | ||||||
|  | 
 | ||||||
|  | 	int last_colon = breakpoint_part.rfind_char(':'); | ||||||
|  | 	if (last_colon < 0) { | ||||||
|  | 		print_line("Error: Invalid breakpoint format. Expected [source:line]"); | ||||||
|  | 		return breakpoint; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	breakpoint.first = script_debugger->breakpoint_find_source(breakpoint_part.left(last_colon).strip_edges()); | ||||||
|  | 	breakpoint.second = breakpoint_part.substr(last_colon).strip_edges().to_int(); | ||||||
|  | 
 | ||||||
|  | 	return breakpoint; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void LocalDebugger::send_message(const String &p_message, const Array &p_args) { | ||||||
|  | 	// This needs to be cleaned up entirely.
 | ||||||
|  | 	// print_line("MESSAGE: '" + p_message + "' - " + String(Variant(p_args)));
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void LocalDebugger::send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, bool p_editor_notify, ErrorHandlerType p_type) { | ||||||
|  | 	print_line("ERROR: '" + (p_descr.is_empty() ? p_err : p_descr) + "'"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | LocalDebugger::LocalDebugger() { | ||||||
|  | 	options["variable_prefix"] = ""; | ||||||
|  | 
 | ||||||
|  | 	// Bind scripts profiler.
 | ||||||
|  | 	scripts_profiler = memnew(ScriptsProfiler); | ||||||
|  | 	Profiler scr_prof( | ||||||
|  | 			scripts_profiler, | ||||||
|  | 			[](void *p_user, bool p_enable, const Array &p_opts) { | ||||||
|  | 				static_cast<ScriptsProfiler *>(p_user)->toggle(p_enable, p_opts); | ||||||
|  | 			}, | ||||||
|  | 			nullptr, | ||||||
|  | 			[](void *p_user, double p_frame_time, double p_process_time, double p_physics_time, double p_physics_frame_time) { | ||||||
|  | 				static_cast<ScriptsProfiler *>(p_user)->tick(p_frame_time, p_process_time, p_physics_time, p_physics_frame_time); | ||||||
|  | 			}); | ||||||
|  | 	register_profiler("scripts", scr_prof); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | LocalDebugger::~LocalDebugger() { | ||||||
|  | 	unregister_profiler("scripts"); | ||||||
|  | 	if (scripts_profiler) { | ||||||
|  | 		memdelete(scripts_profiler); | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										56
									
								
								engine/core/debugger/local_debugger.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								engine/core/debugger/local_debugger.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,56 @@ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*  local_debugger.h                                                      */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*                         This file is part of:                          */ | ||||||
|  | /*                             GODOT ENGINE                               */ | ||||||
|  | /*                        https://godotengine.org                         */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ | ||||||
|  | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */ | ||||||
|  | /*                                                                        */ | ||||||
|  | /* 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.                 */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include "core/debugger/engine_debugger.h" | ||||||
|  | #include "core/object/script_language.h" | ||||||
|  | #include "core/templates/list.h" | ||||||
|  | 
 | ||||||
|  | class LocalDebugger : public EngineDebugger { | ||||||
|  | private: | ||||||
|  | 	struct ScriptsProfiler; | ||||||
|  | 
 | ||||||
|  | 	ScriptsProfiler *scripts_profiler = nullptr; | ||||||
|  | 
 | ||||||
|  | 	String target_function; | ||||||
|  | 	HashMap<String, String> options; | ||||||
|  | 
 | ||||||
|  | 	Pair<String, int> to_breakpoint(const String &p_line); | ||||||
|  | 	void print_variables(const List<String> &names, const List<Variant> &values, const String &variable_prefix); | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  | 	void debug(bool p_can_continue, bool p_is_error_breakpoint); | ||||||
|  | 	void send_message(const String &p_message, const Array &p_args); | ||||||
|  | 	void send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, bool p_editor_notify, ErrorHandlerType p_type); | ||||||
|  | 
 | ||||||
|  | 	LocalDebugger(); | ||||||
|  | 	~LocalDebugger(); | ||||||
|  | }; | ||||||
							
								
								
									
										744
									
								
								engine/core/debugger/remote_debugger.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										744
									
								
								engine/core/debugger/remote_debugger.cpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,744 @@ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*  remote_debugger.cpp                                                   */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*                         This file is part of:                          */ | ||||||
|  | /*                             GODOT ENGINE                               */ | ||||||
|  | /*                        https://godotengine.org                         */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ | ||||||
|  | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */ | ||||||
|  | /*                                                                        */ | ||||||
|  | /* 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 "remote_debugger.h" | ||||||
|  | 
 | ||||||
|  | #include "core/config/project_settings.h" | ||||||
|  | #include "core/debugger/debugger_marshalls.h" | ||||||
|  | #include "core/debugger/engine_debugger.h" | ||||||
|  | #include "core/debugger/engine_profiler.h" | ||||||
|  | #include "core/debugger/script_debugger.h" | ||||||
|  | #include "core/input/input.h" | ||||||
|  | #include "core/io/resource_loader.h" | ||||||
|  | #include "core/math/expression.h" | ||||||
|  | #include "core/object/script_language.h" | ||||||
|  | #include "core/os/os.h" | ||||||
|  | #include "servers/display_server.h" | ||||||
|  | 
 | ||||||
|  | class RemoteDebugger::PerformanceProfiler : public EngineProfiler { | ||||||
|  | 	Object *performance = nullptr; | ||||||
|  | 	int last_perf_time = 0; | ||||||
|  | 	uint64_t last_monitor_modification_time = 0; | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  | 	void toggle(bool p_enable, const Array &p_opts) {} | ||||||
|  | 	void add(const Array &p_data) {} | ||||||
|  | 	void tick(double p_frame_time, double p_process_time, double p_physics_time, double p_physics_frame_time) { | ||||||
|  | 		if (!performance) { | ||||||
|  | 			return; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		uint64_t pt = OS::get_singleton()->get_ticks_msec(); | ||||||
|  | 		if (pt - last_perf_time < 1000) { | ||||||
|  | 			return; | ||||||
|  | 		} | ||||||
|  | 		last_perf_time = pt; | ||||||
|  | 
 | ||||||
|  | 		Array custom_monitor_names = performance->call("get_custom_monitor_names"); | ||||||
|  | 
 | ||||||
|  | 		uint64_t monitor_modification_time = performance->call("get_monitor_modification_time"); | ||||||
|  | 		if (monitor_modification_time > last_monitor_modification_time) { | ||||||
|  | 			last_monitor_modification_time = monitor_modification_time; | ||||||
|  | 			EngineDebugger::get_singleton()->send_message("performance:profile_names", custom_monitor_names); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		int max = performance->get("MONITOR_MAX"); | ||||||
|  | 		Array arr; | ||||||
|  | 		arr.resize(max + custom_monitor_names.size()); | ||||||
|  | 		for (int i = 0; i < max; i++) { | ||||||
|  | 			arr[i] = performance->call("get_monitor", i); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		for (int i = 0; i < custom_monitor_names.size(); i++) { | ||||||
|  | 			Variant monitor_value = performance->call("get_custom_monitor", custom_monitor_names[i]); | ||||||
|  | 			if (!monitor_value.is_num()) { | ||||||
|  | 				ERR_PRINT(vformat("Value of custom monitor '%s' is not a number.", String(custom_monitor_names[i]))); | ||||||
|  | 				arr[i + max] = Variant(); | ||||||
|  | 			} else { | ||||||
|  | 				arr[i + max] = monitor_value; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		EngineDebugger::get_singleton()->send_message("performance:profile_frame", arr); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	explicit PerformanceProfiler(Object *p_performance) { | ||||||
|  | 		performance = p_performance; | ||||||
|  | 	} | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | Error RemoteDebugger::_put_msg(const String &p_message, const Array &p_data) { | ||||||
|  | 	Array msg = { p_message, Thread::get_caller_id(), p_data }; | ||||||
|  | 	Error err = peer->put_message(msg); | ||||||
|  | 	if (err != OK) { | ||||||
|  | 		n_messages_dropped++; | ||||||
|  | 	} | ||||||
|  | 	return err; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void RemoteDebugger::_err_handler(void *p_this, const char *p_func, const char *p_file, int p_line, const char *p_err, const char *p_descr, bool p_editor_notify, ErrorHandlerType p_type) { | ||||||
|  | 	if (p_type == ERR_HANDLER_SCRIPT) { | ||||||
|  | 		return; //ignore script errors, those go through debugger
 | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	RemoteDebugger *rd = static_cast<RemoteDebugger *>(p_this); | ||||||
|  | 	if (rd->flushing && Thread::get_caller_id() == rd->flush_thread) { // Can't handle recursive errors during flush.
 | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	Vector<ScriptLanguage::StackInfo> si; | ||||||
|  | 
 | ||||||
|  | 	for (int i = 0; i < ScriptServer::get_language_count(); i++) { | ||||||
|  | 		si = ScriptServer::get_language(i)->debug_get_current_stack_info(); | ||||||
|  | 		if (si.size()) { | ||||||
|  | 			break; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// send_error will lock internally.
 | ||||||
|  | 	rd->script_debugger->send_error(String::utf8(p_func), String::utf8(p_file), p_line, String::utf8(p_err), String::utf8(p_descr), p_editor_notify, p_type, si); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void RemoteDebugger::_print_handler(void *p_this, const String &p_string, bool p_error, bool p_rich) { | ||||||
|  | 	RemoteDebugger *rd = static_cast<RemoteDebugger *>(p_this); | ||||||
|  | 
 | ||||||
|  | 	if (rd->flushing && Thread::get_caller_id() == rd->flush_thread) { // Can't handle recursive prints during flush.
 | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	String s = p_string; | ||||||
|  | 	int allowed_chars = MIN(MAX(rd->max_chars_per_second - rd->char_count, 0), s.length()); | ||||||
|  | 
 | ||||||
|  | 	if (allowed_chars == 0 && s.length() > 0) { | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (allowed_chars < s.length()) { | ||||||
|  | 		s = s.substr(0, allowed_chars); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	MutexLock lock(rd->mutex); | ||||||
|  | 
 | ||||||
|  | 	rd->char_count += allowed_chars; | ||||||
|  | 	bool overflowed = rd->char_count >= rd->max_chars_per_second; | ||||||
|  | 	if (rd->is_peer_connected()) { | ||||||
|  | 		if (overflowed) { | ||||||
|  | 			s += "[...]"; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		OutputString output_string; | ||||||
|  | 		output_string.message = s; | ||||||
|  | 		if (p_error) { | ||||||
|  | 			output_string.type = MESSAGE_TYPE_ERROR; | ||||||
|  | 		} else if (p_rich) { | ||||||
|  | 			output_string.type = MESSAGE_TYPE_LOG_RICH; | ||||||
|  | 		} else { | ||||||
|  | 			output_string.type = MESSAGE_TYPE_LOG; | ||||||
|  | 		} | ||||||
|  | 		rd->output_strings.push_back(output_string); | ||||||
|  | 
 | ||||||
|  | 		if (overflowed) { | ||||||
|  | 			output_string.message = "[output overflow, print less text!]"; | ||||||
|  | 			output_string.type = MESSAGE_TYPE_ERROR; | ||||||
|  | 			rd->output_strings.push_back(output_string); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | RemoteDebugger::ErrorMessage RemoteDebugger::_create_overflow_error(const String &p_what, const String &p_descr) { | ||||||
|  | 	ErrorMessage oe; | ||||||
|  | 	oe.error = p_what; | ||||||
|  | 	oe.error_descr = p_descr; | ||||||
|  | 	oe.warning = false; | ||||||
|  | 	uint64_t time = OS::get_singleton()->get_ticks_msec(); | ||||||
|  | 	oe.hr = time / 3600000; | ||||||
|  | 	oe.min = (time / 60000) % 60; | ||||||
|  | 	oe.sec = (time / 1000) % 60; | ||||||
|  | 	oe.msec = time % 1000; | ||||||
|  | 	return oe; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void RemoteDebugger::flush_output() { | ||||||
|  | 	MutexLock lock(mutex); | ||||||
|  | 	flush_thread = Thread::get_caller_id(); | ||||||
|  | 	flushing = true; | ||||||
|  | 	if (!is_peer_connected()) { | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (n_messages_dropped > 0) { | ||||||
|  | 		ErrorMessage err_msg = _create_overflow_error("TOO_MANY_MESSAGES", "Too many messages! " + String::num_int64(n_messages_dropped) + " messages were dropped. Profiling might misbheave, try raising 'network/limits/debugger/max_queued_messages' in project setting."); | ||||||
|  | 		if (_put_msg("error", err_msg.serialize()) == OK) { | ||||||
|  | 			n_messages_dropped = 0; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (output_strings.size()) { | ||||||
|  | 		// Join output strings so we generate less messages.
 | ||||||
|  | 		Vector<String> joined_log_strings; | ||||||
|  | 		Vector<String> strings; | ||||||
|  | 		Vector<int> types; | ||||||
|  | 		for (const OutputString &output_string : output_strings) { | ||||||
|  | 			if (output_string.type == MESSAGE_TYPE_ERROR) { | ||||||
|  | 				if (!joined_log_strings.is_empty()) { | ||||||
|  | 					strings.push_back(String("\n").join(joined_log_strings)); | ||||||
|  | 					types.push_back(MESSAGE_TYPE_LOG); | ||||||
|  | 					joined_log_strings.clear(); | ||||||
|  | 				} | ||||||
|  | 				strings.push_back(output_string.message); | ||||||
|  | 				types.push_back(MESSAGE_TYPE_ERROR); | ||||||
|  | 			} else if (output_string.type == MESSAGE_TYPE_LOG_RICH) { | ||||||
|  | 				if (!joined_log_strings.is_empty()) { | ||||||
|  | 					strings.push_back(String("\n").join(joined_log_strings)); | ||||||
|  | 					types.push_back(MESSAGE_TYPE_LOG_RICH); | ||||||
|  | 					joined_log_strings.clear(); | ||||||
|  | 				} | ||||||
|  | 				strings.push_back(output_string.message); | ||||||
|  | 				types.push_back(MESSAGE_TYPE_LOG_RICH); | ||||||
|  | 			} else { | ||||||
|  | 				joined_log_strings.push_back(output_string.message); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if (!joined_log_strings.is_empty()) { | ||||||
|  | 			strings.push_back(String("\n").join(joined_log_strings)); | ||||||
|  | 			types.push_back(MESSAGE_TYPE_LOG); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		Array arr = { strings, types }; | ||||||
|  | 		_put_msg("output", arr); | ||||||
|  | 		output_strings.clear(); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	while (errors.size()) { | ||||||
|  | 		ErrorMessage oe = errors.front()->get(); | ||||||
|  | 		_put_msg("error", oe.serialize()); | ||||||
|  | 		errors.pop_front(); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Update limits
 | ||||||
|  | 	uint64_t ticks = OS::get_singleton()->get_ticks_usec() / 1000; | ||||||
|  | 
 | ||||||
|  | 	if (ticks - last_reset > 1000) { | ||||||
|  | 		last_reset = ticks; | ||||||
|  | 		char_count = 0; | ||||||
|  | 		err_count = 0; | ||||||
|  | 		n_errors_dropped = 0; | ||||||
|  | 		warn_count = 0; | ||||||
|  | 		n_warnings_dropped = 0; | ||||||
|  | 	} | ||||||
|  | 	flushing = false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void RemoteDebugger::send_message(const String &p_message, const Array &p_args) { | ||||||
|  | 	MutexLock lock(mutex); | ||||||
|  | 	if (is_peer_connected()) { | ||||||
|  | 		_put_msg(p_message, p_args); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void RemoteDebugger::send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, bool p_editor_notify, ErrorHandlerType p_type) { | ||||||
|  | 	ErrorMessage oe; | ||||||
|  | 	oe.error = p_err; | ||||||
|  | 	oe.error_descr = p_descr; | ||||||
|  | 	oe.source_file = p_file; | ||||||
|  | 	oe.source_line = p_line; | ||||||
|  | 	oe.source_func = p_func; | ||||||
|  | 	oe.warning = p_type == ERR_HANDLER_WARNING; | ||||||
|  | 	uint64_t time = OS::get_singleton()->get_ticks_msec(); | ||||||
|  | 	oe.hr = time / 3600000; | ||||||
|  | 	oe.min = (time / 60000) % 60; | ||||||
|  | 	oe.sec = (time / 1000) % 60; | ||||||
|  | 	oe.msec = time % 1000; | ||||||
|  | 	oe.callstack.append_array(script_debugger->get_error_stack_info()); | ||||||
|  | 
 | ||||||
|  | 	if (flushing && Thread::get_caller_id() == flush_thread) { // Can't handle recursive errors during flush.
 | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	MutexLock lock(mutex); | ||||||
|  | 
 | ||||||
|  | 	if (oe.warning) { | ||||||
|  | 		warn_count++; | ||||||
|  | 	} else { | ||||||
|  | 		err_count++; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (is_peer_connected()) { | ||||||
|  | 		if (oe.warning) { | ||||||
|  | 			if (warn_count > max_warnings_per_second) { | ||||||
|  | 				n_warnings_dropped++; | ||||||
|  | 				if (n_warnings_dropped == 1) { | ||||||
|  | 					// Only print one message about dropping per second
 | ||||||
|  | 					ErrorMessage overflow = _create_overflow_error("TOO_MANY_WARNINGS", "Too many warnings! Ignoring warnings for up to 1 second."); | ||||||
|  | 					errors.push_back(overflow); | ||||||
|  | 				} | ||||||
|  | 			} else { | ||||||
|  | 				errors.push_back(oe); | ||||||
|  | 			} | ||||||
|  | 		} else { | ||||||
|  | 			if (err_count > max_errors_per_second) { | ||||||
|  | 				n_errors_dropped++; | ||||||
|  | 				if (n_errors_dropped == 1) { | ||||||
|  | 					// Only print one message about dropping per second
 | ||||||
|  | 					ErrorMessage overflow = _create_overflow_error("TOO_MANY_ERRORS", "Too many errors! Ignoring errors for up to 1 second."); | ||||||
|  | 					errors.push_back(overflow); | ||||||
|  | 				} | ||||||
|  | 			} else { | ||||||
|  | 				errors.push_back(oe); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void RemoteDebugger::_send_stack_vars(List<String> &p_names, List<Variant> &p_vals, int p_type) { | ||||||
|  | 	DebuggerMarshalls::ScriptStackVariable stvar; | ||||||
|  | 	List<String>::Element *E = p_names.front(); | ||||||
|  | 	List<Variant>::Element *F = p_vals.front(); | ||||||
|  | 	while (E) { | ||||||
|  | 		stvar.name = E->get(); | ||||||
|  | 		stvar.value = F->get(); | ||||||
|  | 		stvar.type = p_type; | ||||||
|  | 		send_message("stack_frame_var", stvar.serialize()); | ||||||
|  | 		E = E->next(); | ||||||
|  | 		F = F->next(); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Error RemoteDebugger::_try_capture(const String &p_msg, const Array &p_data, bool &r_captured) { | ||||||
|  | 	const int idx = p_msg.find_char(':'); | ||||||
|  | 	r_captured = false; | ||||||
|  | 	if (idx < 0) { // No prefix, unknown message.
 | ||||||
|  | 		return OK; | ||||||
|  | 	} | ||||||
|  | 	const String cap = p_msg.substr(0, idx); | ||||||
|  | 	if (!has_capture(cap)) { | ||||||
|  | 		return ERR_UNAVAILABLE; // Unknown message...
 | ||||||
|  | 	} | ||||||
|  | 	const String msg = p_msg.substr(idx + 1); | ||||||
|  | 	return capture_parse(cap, msg, p_data, r_captured); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void RemoteDebugger::_poll_messages() { | ||||||
|  | 	MutexLock mutex_lock(mutex); | ||||||
|  | 
 | ||||||
|  | 	peer->poll(); | ||||||
|  | 	while (peer->has_message()) { | ||||||
|  | 		Array cmd = peer->get_message(); | ||||||
|  | 		ERR_CONTINUE(cmd.size() != 3); | ||||||
|  | 		ERR_CONTINUE(cmd[0].get_type() != Variant::STRING); | ||||||
|  | 		ERR_CONTINUE(cmd[1].get_type() != Variant::INT); | ||||||
|  | 		ERR_CONTINUE(cmd[2].get_type() != Variant::ARRAY); | ||||||
|  | 
 | ||||||
|  | 		Thread::ID thread = cmd[1]; | ||||||
|  | 
 | ||||||
|  | 		if (!messages.has(thread)) { | ||||||
|  | 			continue; // This thread is not around to receive the messages
 | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		Message msg; | ||||||
|  | 		msg.message = cmd[0]; | ||||||
|  | 		msg.data = cmd[2]; | ||||||
|  | 		messages[thread].push_back(msg); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool RemoteDebugger::_has_messages() { | ||||||
|  | 	MutexLock mutex_lock(mutex); | ||||||
|  | 	return messages.has(Thread::get_caller_id()) && !messages[Thread::get_caller_id()].is_empty(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Array RemoteDebugger::_get_message() { | ||||||
|  | 	MutexLock mutex_lock(mutex); | ||||||
|  | 	ERR_FAIL_COND_V(!messages.has(Thread::get_caller_id()), Array()); | ||||||
|  | 	List<Message> &message_list = messages[Thread::get_caller_id()]; | ||||||
|  | 	ERR_FAIL_COND_V(message_list.is_empty(), Array()); | ||||||
|  | 
 | ||||||
|  | 	Array msg; | ||||||
|  | 	msg.resize(2); | ||||||
|  | 	msg[0] = message_list.front()->get().message; | ||||||
|  | 	msg[1] = message_list.front()->get().data; | ||||||
|  | 	message_list.pop_front(); | ||||||
|  | 	return msg; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void RemoteDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) { | ||||||
|  | 	//this function is called when there is a debugger break (bug on script)
 | ||||||
|  | 	//or when execution is paused from editor
 | ||||||
|  | 
 | ||||||
|  | 	{ | ||||||
|  | 		MutexLock lock(mutex); | ||||||
|  | 		// Tests that require mutex.
 | ||||||
|  | 		if (script_debugger->is_skipping_breakpoints() && !p_is_error_breakpoint) { | ||||||
|  | 			return; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		ERR_FAIL_COND_MSG(!is_peer_connected(), "Script Debugger failed to connect, but being used anyway."); | ||||||
|  | 
 | ||||||
|  | 		if (!peer->can_block()) { | ||||||
|  | 			return; // Peer does not support blocking IO. We could at least send the error though.
 | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	ScriptLanguage *script_lang = script_debugger->get_break_language(); | ||||||
|  | 	ERR_FAIL_NULL(script_lang); | ||||||
|  | 	const bool can_break = !(p_is_error_breakpoint && script_debugger->is_ignoring_error_breaks()); | ||||||
|  | 	const String error_str = script_lang ? script_lang->debug_get_error() : ""; | ||||||
|  | 
 | ||||||
|  | 	if (can_break) { | ||||||
|  | 		Array msg = { | ||||||
|  | 			p_can_continue, | ||||||
|  | 			error_str, | ||||||
|  | 			script_lang->debug_get_stack_level_count() > 0, | ||||||
|  | 			Thread::get_caller_id() | ||||||
|  | 		}; | ||||||
|  | 		if (allow_focus_steal_fn) { | ||||||
|  | 			allow_focus_steal_fn(); | ||||||
|  | 		} | ||||||
|  | 		send_message("debug_enter", msg); | ||||||
|  | 	} else { | ||||||
|  | 		ERR_PRINT(error_str); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	Input::MouseMode mouse_mode = Input::MOUSE_MODE_VISIBLE; | ||||||
|  | 
 | ||||||
|  | 	if (Thread::get_caller_id() == Thread::get_main_id()) { | ||||||
|  | 		mouse_mode = Input::get_singleton()->get_mouse_mode(); | ||||||
|  | 		if (mouse_mode != Input::MOUSE_MODE_VISIBLE) { | ||||||
|  | 			Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_VISIBLE); | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		MutexLock mutex_lock(mutex); | ||||||
|  | 		messages.insert(Thread::get_caller_id(), List<Message>()); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	while (is_peer_connected()) { | ||||||
|  | 		flush_output(); | ||||||
|  | 
 | ||||||
|  | 		_poll_messages(); | ||||||
|  | 
 | ||||||
|  | 		if (_has_messages()) { | ||||||
|  | 			Array cmd = _get_message(); | ||||||
|  | 
 | ||||||
|  | 			ERR_CONTINUE(cmd.size() != 2); | ||||||
|  | 			ERR_CONTINUE(cmd[0].get_type() != Variant::STRING); | ||||||
|  | 			ERR_CONTINUE(cmd[1].get_type() != Variant::ARRAY); | ||||||
|  | 
 | ||||||
|  | 			String command = cmd[0]; | ||||||
|  | 			Array data = cmd[1]; | ||||||
|  | 
 | ||||||
|  | 			if (command == "step") { | ||||||
|  | 				script_debugger->set_depth(-1); | ||||||
|  | 				script_debugger->set_lines_left(1); | ||||||
|  | 				break; | ||||||
|  | 
 | ||||||
|  | 			} else if (command == "next") { | ||||||
|  | 				script_debugger->set_depth(0); | ||||||
|  | 				script_debugger->set_lines_left(1); | ||||||
|  | 				break; | ||||||
|  | 
 | ||||||
|  | 			} else if (command == "continue") { | ||||||
|  | 				script_debugger->set_depth(-1); | ||||||
|  | 				script_debugger->set_lines_left(-1); | ||||||
|  | 				break; | ||||||
|  | 
 | ||||||
|  | 			} else if (command == "break") { | ||||||
|  | 				ERR_PRINT("Got break when already broke!"); | ||||||
|  | 				break; | ||||||
|  | 
 | ||||||
|  | 			} else if (command == "get_stack_dump") { | ||||||
|  | 				DebuggerMarshalls::ScriptStackDump dump; | ||||||
|  | 				int slc = script_lang->debug_get_stack_level_count(); | ||||||
|  | 				for (int i = 0; i < slc; i++) { | ||||||
|  | 					ScriptLanguage::StackInfo frame; | ||||||
|  | 					frame.file = script_lang->debug_get_stack_level_source(i); | ||||||
|  | 					frame.line = script_lang->debug_get_stack_level_line(i); | ||||||
|  | 					frame.func = script_lang->debug_get_stack_level_function(i); | ||||||
|  | 					dump.frames.push_back(frame); | ||||||
|  | 				} | ||||||
|  | 				send_message("stack_dump", dump.serialize()); | ||||||
|  | 
 | ||||||
|  | 			} else if (command == "get_stack_frame_vars") { | ||||||
|  | 				ERR_FAIL_COND(data.size() != 1); | ||||||
|  | 				ERR_FAIL_NULL(script_lang); | ||||||
|  | 				int lv = data[0]; | ||||||
|  | 
 | ||||||
|  | 				List<String> members; | ||||||
|  | 				List<Variant> member_vals; | ||||||
|  | 				if (ScriptInstance *inst = script_lang->debug_get_stack_level_instance(lv)) { | ||||||
|  | 					members.push_back("self"); | ||||||
|  | 					member_vals.push_back(inst->get_owner()); | ||||||
|  | 				} | ||||||
|  | 				script_lang->debug_get_stack_level_members(lv, &members, &member_vals); | ||||||
|  | 				ERR_FAIL_COND(members.size() != member_vals.size()); | ||||||
|  | 
 | ||||||
|  | 				List<String> locals; | ||||||
|  | 				List<Variant> local_vals; | ||||||
|  | 				script_lang->debug_get_stack_level_locals(lv, &locals, &local_vals); | ||||||
|  | 				ERR_FAIL_COND(locals.size() != local_vals.size()); | ||||||
|  | 
 | ||||||
|  | 				List<String> globals; | ||||||
|  | 				List<Variant> globals_vals; | ||||||
|  | 				script_lang->debug_get_globals(&globals, &globals_vals); | ||||||
|  | 				ERR_FAIL_COND(globals.size() != globals_vals.size()); | ||||||
|  | 
 | ||||||
|  | 				Array var_size = { local_vals.size() + member_vals.size() + globals_vals.size() }; | ||||||
|  | 				send_message("stack_frame_vars", var_size); | ||||||
|  | 				_send_stack_vars(locals, local_vals, 0); | ||||||
|  | 				_send_stack_vars(members, member_vals, 1); | ||||||
|  | 				_send_stack_vars(globals, globals_vals, 2); | ||||||
|  | 
 | ||||||
|  | 			} else if (command == "reload_scripts") { | ||||||
|  | 				script_paths_to_reload = data; | ||||||
|  | 			} else if (command == "reload_all_scripts") { | ||||||
|  | 				reload_all_scripts = true; | ||||||
|  | 			} else if (command == "breakpoint") { | ||||||
|  | 				ERR_FAIL_COND(data.size() < 3); | ||||||
|  | 				bool set = data[2]; | ||||||
|  | 				if (set) { | ||||||
|  | 					script_debugger->insert_breakpoint(data[1], data[0]); | ||||||
|  | 				} else { | ||||||
|  | 					script_debugger->remove_breakpoint(data[1], data[0]); | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 			} else if (command == "set_skip_breakpoints") { | ||||||
|  | 				ERR_FAIL_COND(data.is_empty()); | ||||||
|  | 				script_debugger->set_skip_breakpoints(data[0]); | ||||||
|  | 			} else if (command == "set_ignore_error_breaks") { | ||||||
|  | 				ERR_FAIL_COND(data.is_empty()); | ||||||
|  | 				script_debugger->set_ignore_error_breaks(data[0]); | ||||||
|  | 			} else if (command == "evaluate") { | ||||||
|  | 				String expression_str = data[0]; | ||||||
|  | 				int frame = data[1]; | ||||||
|  | 
 | ||||||
|  | 				ScriptInstance *breaked_instance = script_debugger->get_break_language()->debug_get_stack_level_instance(frame); | ||||||
|  | 				if (!breaked_instance) { | ||||||
|  | 					break; | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				List<String> locals; | ||||||
|  | 				List<Variant> local_vals; | ||||||
|  | 
 | ||||||
|  | 				script_debugger->get_break_language()->debug_get_stack_level_locals(frame, &locals, &local_vals); | ||||||
|  | 				ERR_FAIL_COND(locals.size() != local_vals.size()); | ||||||
|  | 
 | ||||||
|  | 				PackedStringArray locals_vector; | ||||||
|  | 				for (const String &S : locals) { | ||||||
|  | 					locals_vector.append(S); | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				Array local_vals_array; | ||||||
|  | 				for (const Variant &V : local_vals) { | ||||||
|  | 					local_vals_array.append(V); | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				Expression expression; | ||||||
|  | 				expression.parse(expression_str, locals_vector); | ||||||
|  | 				const Variant return_val = expression.execute(local_vals_array, breaked_instance->get_owner()); | ||||||
|  | 
 | ||||||
|  | 				DebuggerMarshalls::ScriptStackVariable stvar; | ||||||
|  | 				stvar.name = expression_str; | ||||||
|  | 				stvar.value = return_val; | ||||||
|  | 				stvar.type = 3; | ||||||
|  | 
 | ||||||
|  | 				send_message("evaluation_return", stvar.serialize()); | ||||||
|  | 			} else { | ||||||
|  | 				bool captured = false; | ||||||
|  | 				ERR_CONTINUE(_try_capture(command, data, captured) != OK); | ||||||
|  | 				if (!captured) { | ||||||
|  | 					WARN_PRINT(vformat("Unknown message received from debugger: %s.", command)); | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} else { | ||||||
|  | 			OS::get_singleton()->delay_usec(10000); | ||||||
|  | 			if (Thread::get_caller_id() == Thread::get_main_id()) { | ||||||
|  | 				// If this is a busy loop on the main thread, events still need to be processed.
 | ||||||
|  | 				DisplayServer::get_singleton()->force_process_and_drop_events(); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	send_message("debug_exit", Array()); | ||||||
|  | 
 | ||||||
|  | 	if (Thread::get_caller_id() == Thread::get_main_id()) { | ||||||
|  | 		if (mouse_mode != Input::MOUSE_MODE_VISIBLE) { | ||||||
|  | 			Input::get_singleton()->set_mouse_mode(mouse_mode); | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		MutexLock mutex_lock(mutex); | ||||||
|  | 		messages.erase(Thread::get_caller_id()); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void RemoteDebugger::poll_events(bool p_is_idle) { | ||||||
|  | 	if (peer.is_null()) { | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	flush_output(); | ||||||
|  | 
 | ||||||
|  | 	_poll_messages(); | ||||||
|  | 
 | ||||||
|  | 	while (_has_messages()) { | ||||||
|  | 		Array arr = _get_message(); | ||||||
|  | 
 | ||||||
|  | 		ERR_CONTINUE(arr.size() != 2); | ||||||
|  | 		ERR_CONTINUE(arr[0].get_type() != Variant::STRING); | ||||||
|  | 		ERR_CONTINUE(arr[1].get_type() != Variant::ARRAY); | ||||||
|  | 
 | ||||||
|  | 		const String cmd = arr[0]; | ||||||
|  | 		const int idx = cmd.find_char(':'); | ||||||
|  | 		bool parsed = false; | ||||||
|  | 		if (idx < 0) { // Not prefix, use scripts capture.
 | ||||||
|  | 			capture_parse("core", cmd, arr[1], parsed); | ||||||
|  | 			continue; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		const String cap = cmd.substr(0, idx); | ||||||
|  | 		if (!has_capture(cap)) { | ||||||
|  | 			continue; // Unknown message...
 | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		const String msg = cmd.substr(idx + 1); | ||||||
|  | 		capture_parse(cap, msg, arr[1], parsed); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Reload scripts during idle poll only.
 | ||||||
|  | 	if (p_is_idle) { | ||||||
|  | 		if (reload_all_scripts) { | ||||||
|  | 			for (int i = 0; i < ScriptServer::get_language_count(); i++) { | ||||||
|  | 				ScriptServer::get_language(i)->reload_all_scripts(); | ||||||
|  | 			} | ||||||
|  | 			reload_all_scripts = false; | ||||||
|  | 		} else if (!script_paths_to_reload.is_empty()) { | ||||||
|  | 			Array scripts_to_reload; | ||||||
|  | 			for (int i = 0; i < script_paths_to_reload.size(); ++i) { | ||||||
|  | 				String path = script_paths_to_reload[i]; | ||||||
|  | 				Error err = OK; | ||||||
|  | 				Ref<Script> script = ResourceLoader::load(path, "", ResourceFormatLoader::CACHE_MODE_REUSE, &err); | ||||||
|  | 				ERR_CONTINUE_MSG(err != OK, vformat("Could not reload script '%s': %s", path, error_names[err])); | ||||||
|  | 				ERR_CONTINUE_MSG(script.is_null(), vformat("Could not reload script '%s': Not a script!", path, error_names[err])); | ||||||
|  | 				scripts_to_reload.push_back(script); | ||||||
|  | 			} | ||||||
|  | 			for (int i = 0; i < ScriptServer::get_language_count(); i++) { | ||||||
|  | 				ScriptServer::get_language(i)->reload_scripts(scripts_to_reload, true); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		script_paths_to_reload.clear(); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Error RemoteDebugger::_core_capture(const String &p_cmd, const Array &p_data, bool &r_captured) { | ||||||
|  | 	r_captured = true; | ||||||
|  | 	if (p_cmd == "reload_scripts") { | ||||||
|  | 		script_paths_to_reload = p_data; | ||||||
|  | 	} else if (p_cmd == "reload_all_scripts") { | ||||||
|  | 		reload_all_scripts = true; | ||||||
|  | 	} else if (p_cmd == "breakpoint") { | ||||||
|  | 		ERR_FAIL_COND_V(p_data.size() < 3, ERR_INVALID_DATA); | ||||||
|  | 		bool set = p_data[2]; | ||||||
|  | 		if (set) { | ||||||
|  | 			script_debugger->insert_breakpoint(p_data[1], p_data[0]); | ||||||
|  | 		} else { | ||||||
|  | 			script_debugger->remove_breakpoint(p_data[1], p_data[0]); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 	} else if (p_cmd == "set_skip_breakpoints") { | ||||||
|  | 		ERR_FAIL_COND_V(p_data.is_empty(), ERR_INVALID_DATA); | ||||||
|  | 		script_debugger->set_skip_breakpoints(p_data[0]); | ||||||
|  | 	} else if (p_cmd == "set_ignore_error_breaks") { | ||||||
|  | 		ERR_FAIL_COND_V(p_data.is_empty(), ERR_INVALID_DATA); | ||||||
|  | 		script_debugger->set_ignore_error_breaks(p_data[0]); | ||||||
|  | 	} else if (p_cmd == "break") { | ||||||
|  | 		script_debugger->debug(script_debugger->get_break_language()); | ||||||
|  | 	} else { | ||||||
|  | 		r_captured = false; | ||||||
|  | 	} | ||||||
|  | 	return OK; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Error RemoteDebugger::_profiler_capture(const String &p_cmd, const Array &p_data, bool &r_captured) { | ||||||
|  | 	r_captured = false; | ||||||
|  | 	ERR_FAIL_COND_V(p_data.is_empty(), ERR_INVALID_DATA); | ||||||
|  | 	ERR_FAIL_COND_V(p_data[0].get_type() != Variant::BOOL, ERR_INVALID_DATA); | ||||||
|  | 	ERR_FAIL_COND_V(!has_profiler(p_cmd), ERR_UNAVAILABLE); | ||||||
|  | 	Array opts; | ||||||
|  | 	if (p_data.size() > 1) { // Optional profiler parameters.
 | ||||||
|  | 		ERR_FAIL_COND_V(p_data[1].get_type() != Variant::ARRAY, ERR_INVALID_DATA); | ||||||
|  | 		opts = p_data[1]; | ||||||
|  | 	} | ||||||
|  | 	r_captured = true; | ||||||
|  | 	profiler_enable(p_cmd, p_data[0], opts); | ||||||
|  | 	return OK; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | RemoteDebugger::RemoteDebugger(Ref<RemoteDebuggerPeer> p_peer) { | ||||||
|  | 	peer = p_peer; | ||||||
|  | 	max_chars_per_second = GLOBAL_GET("network/limits/debugger/max_chars_per_second"); | ||||||
|  | 	max_errors_per_second = GLOBAL_GET("network/limits/debugger/max_errors_per_second"); | ||||||
|  | 	max_warnings_per_second = GLOBAL_GET("network/limits/debugger/max_warnings_per_second"); | ||||||
|  | 
 | ||||||
|  | 	// Performance Profiler
 | ||||||
|  | 	Object *perf = Engine::get_singleton()->get_singleton_object("Performance"); | ||||||
|  | 	if (perf) { | ||||||
|  | 		performance_profiler.instantiate(perf); | ||||||
|  | 		performance_profiler->bind("performance"); | ||||||
|  | 		profiler_enable("performance", true); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Core and profiler captures.
 | ||||||
|  | 	Capture core_cap(this, | ||||||
|  | 			[](void *p_user, const String &p_cmd, const Array &p_data, bool &r_captured) { | ||||||
|  | 				return static_cast<RemoteDebugger *>(p_user)->_core_capture(p_cmd, p_data, r_captured); | ||||||
|  | 			}); | ||||||
|  | 	register_message_capture("core", core_cap); | ||||||
|  | 	Capture profiler_cap(this, | ||||||
|  | 			[](void *p_user, const String &p_cmd, const Array &p_data, bool &r_captured) { | ||||||
|  | 				return static_cast<RemoteDebugger *>(p_user)->_profiler_capture(p_cmd, p_data, r_captured); | ||||||
|  | 			}); | ||||||
|  | 	register_message_capture("profiler", profiler_cap); | ||||||
|  | 
 | ||||||
|  | 	// Error handlers
 | ||||||
|  | 	phl.printfunc = _print_handler; | ||||||
|  | 	phl.userdata = this; | ||||||
|  | 	add_print_handler(&phl); | ||||||
|  | 
 | ||||||
|  | 	eh.errfunc = _err_handler; | ||||||
|  | 	eh.userdata = this; | ||||||
|  | 	add_error_handler(&eh); | ||||||
|  | 
 | ||||||
|  | 	messages.insert(Thread::get_main_id(), List<Message>()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | RemoteDebugger::~RemoteDebugger() { | ||||||
|  | 	remove_print_handler(&phl); | ||||||
|  | 	remove_error_handler(&eh); | ||||||
|  | } | ||||||
							
								
								
									
										123
									
								
								engine/core/debugger/remote_debugger.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										123
									
								
								engine/core/debugger/remote_debugger.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,123 @@ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*  remote_debugger.h                                                     */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*                         This file is part of:                          */ | ||||||
|  | /*                             GODOT ENGINE                               */ | ||||||
|  | /*                        https://godotengine.org                         */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ | ||||||
|  | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */ | ||||||
|  | /*                                                                        */ | ||||||
|  | /* 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.                 */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include "core/debugger/debugger_marshalls.h" | ||||||
|  | #include "core/debugger/engine_debugger.h" | ||||||
|  | #include "core/debugger/remote_debugger_peer.h" | ||||||
|  | #include "core/object/class_db.h" | ||||||
|  | #include "core/string/string_name.h" | ||||||
|  | #include "core/string/ustring.h" | ||||||
|  | #include "core/variant/array.h" | ||||||
|  | 
 | ||||||
|  | class RemoteDebugger : public EngineDebugger { | ||||||
|  | public: | ||||||
|  | 	enum MessageType { | ||||||
|  | 		MESSAGE_TYPE_LOG, | ||||||
|  | 		MESSAGE_TYPE_ERROR, | ||||||
|  | 		MESSAGE_TYPE_LOG_RICH, | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  | 	typedef DebuggerMarshalls::OutputError ErrorMessage; | ||||||
|  | 
 | ||||||
|  | 	class PerformanceProfiler; | ||||||
|  | 
 | ||||||
|  | 	Ref<PerformanceProfiler> performance_profiler; | ||||||
|  | 
 | ||||||
|  | 	Ref<RemoteDebuggerPeer> peer; | ||||||
|  | 
 | ||||||
|  | 	struct OutputString { | ||||||
|  | 		String message; | ||||||
|  | 		MessageType type; | ||||||
|  | 	}; | ||||||
|  | 	List<OutputString> output_strings; | ||||||
|  | 	List<ErrorMessage> errors; | ||||||
|  | 
 | ||||||
|  | 	int n_messages_dropped = 0; | ||||||
|  | 	int max_errors_per_second = 0; | ||||||
|  | 	int max_chars_per_second = 0; | ||||||
|  | 	int max_warnings_per_second = 0; | ||||||
|  | 	int n_errors_dropped = 0; | ||||||
|  | 	int n_warnings_dropped = 0; | ||||||
|  | 	int char_count = 0; | ||||||
|  | 	int err_count = 0; | ||||||
|  | 	int warn_count = 0; | ||||||
|  | 	int last_reset = 0; | ||||||
|  | 	bool reload_all_scripts = false; | ||||||
|  | 	Array script_paths_to_reload; | ||||||
|  | 
 | ||||||
|  | 	// Make handlers and send_message thread safe.
 | ||||||
|  | 	Mutex mutex; | ||||||
|  | 	bool flushing = false; | ||||||
|  | 	Thread::ID flush_thread = 0; | ||||||
|  | 
 | ||||||
|  | 	struct Message { | ||||||
|  | 		String message; | ||||||
|  | 		Array data; | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	HashMap<Thread::ID, List<Message>> messages; | ||||||
|  | 
 | ||||||
|  | 	void _poll_messages(); | ||||||
|  | 	bool _has_messages(); | ||||||
|  | 	Array _get_message(); | ||||||
|  | 
 | ||||||
|  | 	PrintHandlerList phl; | ||||||
|  | 	static void _print_handler(void *p_this, const String &p_string, bool p_error, bool p_rich); | ||||||
|  | 	ErrorHandlerList eh; | ||||||
|  | 	static void _err_handler(void *p_this, const char *p_func, const char *p_file, int p_line, const char *p_err, const char *p_descr, bool p_editor_notify, ErrorHandlerType p_type); | ||||||
|  | 
 | ||||||
|  | 	ErrorMessage _create_overflow_error(const String &p_what, const String &p_descr); | ||||||
|  | 	Error _put_msg(const String &p_message, const Array &p_data); | ||||||
|  | 
 | ||||||
|  | 	bool is_peer_connected() { return peer->is_peer_connected(); } | ||||||
|  | 	void flush_output(); | ||||||
|  | 
 | ||||||
|  | 	void _send_stack_vars(List<String> &p_names, List<Variant> &p_vals, int p_type); | ||||||
|  | 
 | ||||||
|  | 	Error _profiler_capture(const String &p_cmd, const Array &p_data, bool &r_captured); | ||||||
|  | 	Error _core_capture(const String &p_cmd, const Array &p_data, bool &r_captured); | ||||||
|  | 
 | ||||||
|  | 	template <typename T> | ||||||
|  | 	void _bind_profiler(const String &p_name, T *p_prof); | ||||||
|  | 	Error _try_capture(const String &p_name, const Array &p_data, bool &r_captured); | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  | 	// Overrides
 | ||||||
|  | 	void poll_events(bool p_is_idle); | ||||||
|  | 	void send_message(const String &p_message, const Array &p_args); | ||||||
|  | 	void send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, bool p_editor_notify, ErrorHandlerType p_type); | ||||||
|  | 	void debug(bool p_can_continue = true, bool p_is_error_breakpoint = false); | ||||||
|  | 
 | ||||||
|  | 	explicit RemoteDebugger(Ref<RemoteDebuggerPeer> p_peer); | ||||||
|  | 	~RemoteDebugger(); | ||||||
|  | }; | ||||||
							
								
								
									
										243
									
								
								engine/core/debugger/remote_debugger_peer.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										243
									
								
								engine/core/debugger/remote_debugger_peer.cpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,243 @@ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*  remote_debugger_peer.cpp                                              */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*                         This file is part of:                          */ | ||||||
|  | /*                             GODOT ENGINE                               */ | ||||||
|  | /*                        https://godotengine.org                         */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ | ||||||
|  | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */ | ||||||
|  | /*                                                                        */ | ||||||
|  | /* 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 "remote_debugger_peer.h" | ||||||
|  | 
 | ||||||
|  | #include "core/config/project_settings.h" | ||||||
|  | #include "core/io/marshalls.h" | ||||||
|  | #include "core/os/os.h" | ||||||
|  | 
 | ||||||
|  | bool RemoteDebuggerPeerTCP::is_peer_connected() { | ||||||
|  | 	return connected; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool RemoteDebuggerPeerTCP::has_message() { | ||||||
|  | 	return in_queue.size() > 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Array RemoteDebuggerPeerTCP::get_message() { | ||||||
|  | 	MutexLock lock(mutex); | ||||||
|  | 	ERR_FAIL_COND_V(!has_message(), Array()); | ||||||
|  | 	Array out = in_queue.front()->get(); | ||||||
|  | 	in_queue.pop_front(); | ||||||
|  | 	return out; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Error RemoteDebuggerPeerTCP::put_message(const Array &p_arr) { | ||||||
|  | 	MutexLock lock(mutex); | ||||||
|  | 	if (out_queue.size() >= max_queued_messages) { | ||||||
|  | 		return ERR_OUT_OF_MEMORY; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	out_queue.push_back(p_arr); | ||||||
|  | 	return OK; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int RemoteDebuggerPeerTCP::get_max_message_size() const { | ||||||
|  | 	return 8 << 20; // 8 MiB
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void RemoteDebuggerPeerTCP::close() { | ||||||
|  | 	running = false; | ||||||
|  | 	if (thread.is_started()) { | ||||||
|  | 		thread.wait_to_finish(); | ||||||
|  | 	} | ||||||
|  | 	tcp_client->disconnect_from_host(); | ||||||
|  | 	out_buf.clear(); | ||||||
|  | 	in_buf.clear(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | RemoteDebuggerPeerTCP::RemoteDebuggerPeerTCP(Ref<StreamPeerTCP> p_tcp) { | ||||||
|  | 	// This means remote debugger takes 16 MiB just because it exists...
 | ||||||
|  | 	in_buf.resize((8 << 20) + 4); // 8 MiB should be way more than enough (need 4 extra bytes for encoding packet size).
 | ||||||
|  | 	out_buf.resize(8 << 20); // 8 MiB should be way more than enough
 | ||||||
|  | 	tcp_client = p_tcp; | ||||||
|  | 	if (tcp_client.is_valid()) { // Attaching to an already connected stream.
 | ||||||
|  | 		connected = true; | ||||||
|  | 		running = true; | ||||||
|  | 		thread.start(_thread_func, this); | ||||||
|  | 	} else { | ||||||
|  | 		tcp_client.instantiate(); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | RemoteDebuggerPeerTCP::~RemoteDebuggerPeerTCP() { | ||||||
|  | 	close(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void RemoteDebuggerPeerTCP::_write_out() { | ||||||
|  | 	while (tcp_client->get_status() == StreamPeerTCP::STATUS_CONNECTED && tcp_client->wait(NetSocket::POLL_TYPE_OUT) == OK) { | ||||||
|  | 		uint8_t *buf = out_buf.ptrw(); | ||||||
|  | 		if (out_left <= 0) { | ||||||
|  | 			if (out_queue.is_empty()) { | ||||||
|  | 				break; // Nothing left to send
 | ||||||
|  | 			} | ||||||
|  | 			mutex.lock(); | ||||||
|  | 			Variant var = out_queue.front()->get(); | ||||||
|  | 			out_queue.pop_front(); | ||||||
|  | 			mutex.unlock(); | ||||||
|  | 			int size = 0; | ||||||
|  | 			Error err = encode_variant(var, nullptr, size); | ||||||
|  | 			ERR_CONTINUE(err != OK || size > out_buf.size() - 4); // 4 bytes separator.
 | ||||||
|  | 			encode_uint32(size, buf); | ||||||
|  | 			encode_variant(var, buf + 4, size); | ||||||
|  | 			out_left = size + 4; | ||||||
|  | 			out_pos = 0; | ||||||
|  | 		} | ||||||
|  | 		int sent = 0; | ||||||
|  | 		tcp_client->put_partial_data(buf + out_pos, out_left, sent); | ||||||
|  | 		out_left -= sent; | ||||||
|  | 		out_pos += sent; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void RemoteDebuggerPeerTCP::_read_in() { | ||||||
|  | 	while (tcp_client->get_status() == StreamPeerTCP::STATUS_CONNECTED && tcp_client->wait(NetSocket::POLL_TYPE_IN) == OK) { | ||||||
|  | 		uint8_t *buf = in_buf.ptrw(); | ||||||
|  | 		if (in_left <= 0) { | ||||||
|  | 			if (in_queue.size() > max_queued_messages) { | ||||||
|  | 				break; // Too many messages already in queue.
 | ||||||
|  | 			} | ||||||
|  | 			if (tcp_client->get_available_bytes() < 4) { | ||||||
|  | 				break; // Need 4 more bytes.
 | ||||||
|  | 			} | ||||||
|  | 			uint32_t size = 0; | ||||||
|  | 			int read = 0; | ||||||
|  | 			Error err = tcp_client->get_partial_data((uint8_t *)&size, 4, read); | ||||||
|  | 			ERR_CONTINUE(read != 4 || err != OK || size > (uint32_t)in_buf.size()); | ||||||
|  | 			in_left = size; | ||||||
|  | 			in_pos = 0; | ||||||
|  | 		} | ||||||
|  | 		int read = 0; | ||||||
|  | 		tcp_client->get_partial_data(buf + in_pos, in_left, read); | ||||||
|  | 		in_left -= read; | ||||||
|  | 		in_pos += read; | ||||||
|  | 		if (in_left == 0) { | ||||||
|  | 			Variant var; | ||||||
|  | 			Error err = decode_variant(var, buf, in_pos, &read); | ||||||
|  | 			ERR_CONTINUE(read != in_pos || err != OK); | ||||||
|  | 			ERR_CONTINUE_MSG(var.get_type() != Variant::ARRAY, "Malformed packet received, not an Array."); | ||||||
|  | 			MutexLock lock(mutex); | ||||||
|  | 			in_queue.push_back(var); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Error RemoteDebuggerPeerTCP::connect_to_host(const String &p_host, uint16_t p_port) { | ||||||
|  | 	IPAddress ip; | ||||||
|  | 	if (p_host.is_valid_ip_address()) { | ||||||
|  | 		ip = p_host; | ||||||
|  | 	} else { | ||||||
|  | 		ip = IP::get_singleton()->resolve_hostname(p_host); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	int port = p_port; | ||||||
|  | 
 | ||||||
|  | 	const int tries = 6; | ||||||
|  | 	const int waits[tries] = { 1, 10, 100, 1000, 1000, 1000 }; | ||||||
|  | 
 | ||||||
|  | 	tcp_client->connect_to_host(ip, port); | ||||||
|  | 
 | ||||||
|  | 	for (int i = 0; i < tries; i++) { | ||||||
|  | 		tcp_client->poll(); | ||||||
|  | 		if (tcp_client->get_status() == StreamPeerTCP::STATUS_CONNECTED) { | ||||||
|  | 			print_verbose("Remote Debugger: Connected!"); | ||||||
|  | 			break; | ||||||
|  | 		} else { | ||||||
|  | 			const int ms = waits[i]; | ||||||
|  | 			OS::get_singleton()->delay_usec(ms * 1000); | ||||||
|  | 			print_verbose("Remote Debugger: Connection failed with status: '" + String::num_int64(tcp_client->get_status()) + "', retrying in " + String::num_int64(ms) + " msec."); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (tcp_client->get_status() != StreamPeerTCP::STATUS_CONNECTED) { | ||||||
|  | 		ERR_PRINT(vformat("Remote Debugger: Unable to connect. Status: %s.", String::num_int64(tcp_client->get_status()))); | ||||||
|  | 		return FAILED; | ||||||
|  | 	} | ||||||
|  | 	connected = true; | ||||||
|  | 	running = true; | ||||||
|  | 	thread.start(_thread_func, this); | ||||||
|  | 	return OK; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void RemoteDebuggerPeerTCP::_thread_func(void *p_ud) { | ||||||
|  | 	// Update in time for 144hz monitors
 | ||||||
|  | 	const uint64_t min_tick = 6900; | ||||||
|  | 	RemoteDebuggerPeerTCP *peer = static_cast<RemoteDebuggerPeerTCP *>(p_ud); | ||||||
|  | 	while (peer->running && peer->is_peer_connected()) { | ||||||
|  | 		uint64_t ticks_usec = OS::get_singleton()->get_ticks_usec(); | ||||||
|  | 		peer->_poll(); | ||||||
|  | 		if (!peer->is_peer_connected()) { | ||||||
|  | 			break; | ||||||
|  | 		} | ||||||
|  | 		ticks_usec = OS::get_singleton()->get_ticks_usec() - ticks_usec; | ||||||
|  | 		if (ticks_usec < min_tick) { | ||||||
|  | 			OS::get_singleton()->delay_usec(min_tick - ticks_usec); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void RemoteDebuggerPeerTCP::poll() { | ||||||
|  | 	// Nothing to do, polling is done in thread.
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void RemoteDebuggerPeerTCP::_poll() { | ||||||
|  | 	tcp_client->poll(); | ||||||
|  | 	if (connected) { | ||||||
|  | 		_write_out(); | ||||||
|  | 		_read_in(); | ||||||
|  | 		connected = tcp_client->get_status() == StreamPeerTCP::STATUS_CONNECTED; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | RemoteDebuggerPeer *RemoteDebuggerPeerTCP::create(const String &p_uri) { | ||||||
|  | 	ERR_FAIL_COND_V(!p_uri.begins_with("tcp://"), nullptr); | ||||||
|  | 
 | ||||||
|  | 	String debug_host = p_uri.replace("tcp://", ""); | ||||||
|  | 	uint16_t debug_port = 6007; | ||||||
|  | 
 | ||||||
|  | 	if (debug_host.contains_char(':')) { | ||||||
|  | 		int sep_pos = debug_host.rfind_char(':'); | ||||||
|  | 		debug_port = debug_host.substr(sep_pos + 1).to_int(); | ||||||
|  | 		debug_host = debug_host.substr(0, sep_pos); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	RemoteDebuggerPeerTCP *peer = memnew(RemoteDebuggerPeerTCP); | ||||||
|  | 	Error err = peer->connect_to_host(debug_host, debug_port); | ||||||
|  | 	if (err != OK) { | ||||||
|  | 		memdelete(peer); | ||||||
|  | 		return nullptr; | ||||||
|  | 	} | ||||||
|  | 	return peer; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | RemoteDebuggerPeer::RemoteDebuggerPeer() { | ||||||
|  | 	max_queued_messages = (int)GLOBAL_GET("network/limits/debugger/max_queued_messages"); | ||||||
|  | } | ||||||
							
								
								
									
										93
									
								
								engine/core/debugger/remote_debugger_peer.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								engine/core/debugger/remote_debugger_peer.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,93 @@ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*  remote_debugger_peer.h                                                */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*                         This file is part of:                          */ | ||||||
|  | /*                             GODOT ENGINE                               */ | ||||||
|  | /*                        https://godotengine.org                         */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ | ||||||
|  | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */ | ||||||
|  | /*                                                                        */ | ||||||
|  | /* 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.                 */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include "core/io/stream_peer_tcp.h" | ||||||
|  | #include "core/object/ref_counted.h" | ||||||
|  | #include "core/os/mutex.h" | ||||||
|  | #include "core/os/thread.h" | ||||||
|  | #include "core/string/ustring.h" | ||||||
|  | 
 | ||||||
|  | class RemoteDebuggerPeer : public RefCounted { | ||||||
|  | protected: | ||||||
|  | 	int max_queued_messages = 4096; | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  | 	virtual bool is_peer_connected() = 0; | ||||||
|  | 	virtual int get_max_message_size() const = 0; | ||||||
|  | 	virtual bool has_message() = 0; | ||||||
|  | 	virtual Error put_message(const Array &p_arr) = 0; | ||||||
|  | 	virtual Array get_message() = 0; | ||||||
|  | 	virtual void close() = 0; | ||||||
|  | 	virtual void poll() = 0; | ||||||
|  | 	virtual bool can_block() const { return true; } // If blocking io is allowed on main thread (debug).
 | ||||||
|  | 
 | ||||||
|  | 	RemoteDebuggerPeer(); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class RemoteDebuggerPeerTCP : public RemoteDebuggerPeer { | ||||||
|  | private: | ||||||
|  | 	Ref<StreamPeerTCP> tcp_client; | ||||||
|  | 	Mutex mutex; | ||||||
|  | 	Thread thread; | ||||||
|  | 	List<Array> in_queue; | ||||||
|  | 	List<Array> out_queue; | ||||||
|  | 	int out_left = 0; | ||||||
|  | 	int out_pos = 0; | ||||||
|  | 	Vector<uint8_t> out_buf; | ||||||
|  | 	int in_left = 0; | ||||||
|  | 	int in_pos = 0; | ||||||
|  | 	Vector<uint8_t> in_buf; | ||||||
|  | 	bool connected = false; | ||||||
|  | 	bool running = false; | ||||||
|  | 
 | ||||||
|  | 	static void _thread_func(void *p_ud); | ||||||
|  | 
 | ||||||
|  | 	void _poll(); | ||||||
|  | 	void _write_out(); | ||||||
|  | 	void _read_in(); | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  | 	static RemoteDebuggerPeer *create(const String &p_uri); | ||||||
|  | 
 | ||||||
|  | 	Error connect_to_host(const String &p_host, uint16_t p_port); | ||||||
|  | 
 | ||||||
|  | 	bool is_peer_connected() override; | ||||||
|  | 	int get_max_message_size() const override; | ||||||
|  | 	bool has_message() override; | ||||||
|  | 	Error put_message(const Array &p_arr) override; | ||||||
|  | 	Array get_message() override; | ||||||
|  | 	void poll() override; | ||||||
|  | 	void close() override; | ||||||
|  | 
 | ||||||
|  | 	RemoteDebuggerPeerTCP(Ref<StreamPeerTCP> p_stream = Ref<StreamPeerTCP>()); | ||||||
|  | 	~RemoteDebuggerPeerTCP(); | ||||||
|  | }; | ||||||
							
								
								
									
										110
									
								
								engine/core/debugger/script_debugger.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								engine/core/debugger/script_debugger.cpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,110 @@ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*  script_debugger.cpp                                                   */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*                         This file is part of:                          */ | ||||||
|  | /*                             GODOT ENGINE                               */ | ||||||
|  | /*                        https://godotengine.org                         */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ | ||||||
|  | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */ | ||||||
|  | /*                                                                        */ | ||||||
|  | /* 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 "script_debugger.h" | ||||||
|  | 
 | ||||||
|  | #include "core/debugger/engine_debugger.h" | ||||||
|  | 
 | ||||||
|  | thread_local int ScriptDebugger::lines_left = -1; | ||||||
|  | thread_local int ScriptDebugger::depth = -1; | ||||||
|  | thread_local ScriptLanguage *ScriptDebugger::break_lang = nullptr; | ||||||
|  | thread_local Vector<ScriptDebugger::StackInfo> ScriptDebugger::error_stack_info; | ||||||
|  | 
 | ||||||
|  | void ScriptDebugger::set_lines_left(int p_left) { | ||||||
|  | 	lines_left = p_left; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ScriptDebugger::set_depth(int p_depth) { | ||||||
|  | 	depth = p_depth; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ScriptDebugger::insert_breakpoint(int p_line, const StringName &p_source) { | ||||||
|  | 	if (!breakpoints.has(p_line)) { | ||||||
|  | 		breakpoints[p_line] = HashSet<StringName>(); | ||||||
|  | 	} | ||||||
|  | 	breakpoints[p_line].insert(p_source); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ScriptDebugger::remove_breakpoint(int p_line, const StringName &p_source) { | ||||||
|  | 	if (!breakpoints.has(p_line)) { | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	breakpoints[p_line].erase(p_source); | ||||||
|  | 	if (breakpoints[p_line].is_empty()) { | ||||||
|  | 		breakpoints.erase(p_line); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | String ScriptDebugger::breakpoint_find_source(const String &p_source) const { | ||||||
|  | 	return p_source; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ScriptDebugger::clear_breakpoints() { | ||||||
|  | 	breakpoints.clear(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ScriptDebugger::set_skip_breakpoints(bool p_skip_breakpoints) { | ||||||
|  | 	skip_breakpoints = p_skip_breakpoints; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool ScriptDebugger::is_skipping_breakpoints() { | ||||||
|  | 	return skip_breakpoints; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ScriptDebugger::set_ignore_error_breaks(bool p_ignore) { | ||||||
|  | 	ignore_error_breaks = p_ignore; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool ScriptDebugger::is_ignoring_error_breaks() { | ||||||
|  | 	return ignore_error_breaks; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ScriptDebugger::debug(ScriptLanguage *p_lang, bool p_can_continue, bool p_is_error_breakpoint) { | ||||||
|  | 	ScriptLanguage *prev = break_lang; | ||||||
|  | 	break_lang = p_lang; | ||||||
|  | 	EngineDebugger::get_singleton()->debug(p_can_continue, p_is_error_breakpoint); | ||||||
|  | 	break_lang = prev; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ScriptDebugger::send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, bool p_editor_notify, ErrorHandlerType p_type, const Vector<StackInfo> &p_stack_info) { | ||||||
|  | 	// Store stack info, this is ugly, but allows us to separate EngineDebugger and ScriptDebugger. There might be a better way.
 | ||||||
|  | 	error_stack_info.append_array(p_stack_info); | ||||||
|  | 	EngineDebugger::get_singleton()->send_error(p_func, p_file, p_line, p_err, p_descr, p_editor_notify, p_type); | ||||||
|  | 	error_stack_info.clear(); // Clear because this is thread local
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Vector<ScriptLanguage::StackInfo> ScriptDebugger::get_error_stack_info() const { | ||||||
|  | 	return error_stack_info; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | ScriptLanguage *ScriptDebugger::get_break_language() const { | ||||||
|  | 	return break_lang; | ||||||
|  | } | ||||||
							
								
								
									
										86
									
								
								engine/core/debugger/script_debugger.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								engine/core/debugger/script_debugger.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,86 @@ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*  script_debugger.h                                                     */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*                         This file is part of:                          */ | ||||||
|  | /*                             GODOT ENGINE                               */ | ||||||
|  | /*                        https://godotengine.org                         */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ | ||||||
|  | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */ | ||||||
|  | /*                                                                        */ | ||||||
|  | /* 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.                 */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include "core/object/script_language.h" | ||||||
|  | #include "core/string/string_name.h" | ||||||
|  | #include "core/templates/hash_set.h" | ||||||
|  | #include "core/templates/vector.h" | ||||||
|  | 
 | ||||||
|  | class ScriptDebugger { | ||||||
|  | 	typedef ScriptLanguage::StackInfo StackInfo; | ||||||
|  | 
 | ||||||
|  | 	bool skip_breakpoints = false; | ||||||
|  | 	bool ignore_error_breaks = false; | ||||||
|  | 
 | ||||||
|  | 	HashMap<int, HashSet<StringName>> breakpoints; | ||||||
|  | 
 | ||||||
|  | 	static thread_local int lines_left; | ||||||
|  | 	static thread_local int depth; | ||||||
|  | 	static thread_local ScriptLanguage *break_lang; | ||||||
|  | 	static thread_local Vector<StackInfo> error_stack_info; | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  | 	void set_lines_left(int p_left); | ||||||
|  | 	_ALWAYS_INLINE_ int get_lines_left() const { | ||||||
|  | 		return lines_left; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	void set_depth(int p_depth); | ||||||
|  | 	_ALWAYS_INLINE_ int get_depth() const { | ||||||
|  | 		return depth; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	String breakpoint_find_source(const String &p_source) const; | ||||||
|  | 	void set_break_language(ScriptLanguage *p_lang) { break_lang = p_lang; } | ||||||
|  | 	ScriptLanguage *get_break_language() { return break_lang; } | ||||||
|  | 	void set_skip_breakpoints(bool p_skip_breakpoints); | ||||||
|  | 	bool is_skipping_breakpoints(); | ||||||
|  | 	void set_ignore_error_breaks(bool p_ignore); | ||||||
|  | 	bool is_ignoring_error_breaks(); | ||||||
|  | 	void insert_breakpoint(int p_line, const StringName &p_source); | ||||||
|  | 	void remove_breakpoint(int p_line, const StringName &p_source); | ||||||
|  | 	_ALWAYS_INLINE_ bool is_breakpoint(int p_line, const StringName &p_source) const { | ||||||
|  | 		if (likely(!breakpoints.has(p_line))) { | ||||||
|  | 			return false; | ||||||
|  | 		} | ||||||
|  | 		return breakpoints[p_line].has(p_source); | ||||||
|  | 	} | ||||||
|  | 	void clear_breakpoints(); | ||||||
|  | 	const HashMap<int, HashSet<StringName>> &get_breakpoints() const { return breakpoints; } | ||||||
|  | 
 | ||||||
|  | 	void debug(ScriptLanguage *p_lang, bool p_can_continue = true, bool p_is_error_breakpoint = false); | ||||||
|  | 	ScriptLanguage *get_break_language() const; | ||||||
|  | 
 | ||||||
|  | 	void send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, bool p_editor_notify, ErrorHandlerType p_type, const Vector<StackInfo> &p_stack_info); | ||||||
|  | 	Vector<StackInfo> get_error_stack_info() const; | ||||||
|  | 	ScriptDebugger() {} | ||||||
|  | }; | ||||||
							
								
								
									
										149
									
								
								engine/core/doc_data.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										149
									
								
								engine/core/doc_data.cpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,149 @@ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*  doc_data.cpp                                                          */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*                         This file is part of:                          */ | ||||||
|  | /*                             GODOT ENGINE                               */ | ||||||
|  | /*                        https://godotengine.org                         */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ | ||||||
|  | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */ | ||||||
|  | /*                                                                        */ | ||||||
|  | /* 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 "doc_data.h" | ||||||
|  | 
 | ||||||
|  | String DocData::get_default_value_string(const Variant &p_value) { | ||||||
|  | 	if (p_value.get_type() == Variant::ARRAY) { | ||||||
|  | 		return Variant(Array(p_value, 0, StringName(), Variant())).get_construct_string().replace_char('\n', ' '); | ||||||
|  | 	} else if (p_value.get_type() == Variant::DICTIONARY) { | ||||||
|  | 		return Variant(Dictionary(p_value, 0, StringName(), Variant(), 0, StringName(), Variant())).get_construct_string().replace_char('\n', ' '); | ||||||
|  | 	} else { | ||||||
|  | 		return p_value.get_construct_string().replace_char('\n', ' '); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void DocData::return_doc_from_retinfo(DocData::MethodDoc &p_method, const PropertyInfo &p_retinfo) { | ||||||
|  | 	if (p_retinfo.type == Variant::INT && p_retinfo.hint == PROPERTY_HINT_INT_IS_POINTER) { | ||||||
|  | 		p_method.return_type = p_retinfo.hint_string; | ||||||
|  | 		if (p_method.return_type.is_empty()) { | ||||||
|  | 			p_method.return_type = "void*"; | ||||||
|  | 		} else { | ||||||
|  | 			p_method.return_type += "*"; | ||||||
|  | 		} | ||||||
|  | 	} else if (p_retinfo.type == Variant::INT && p_retinfo.usage & (PROPERTY_USAGE_CLASS_IS_ENUM | PROPERTY_USAGE_CLASS_IS_BITFIELD)) { | ||||||
|  | 		p_method.return_enum = p_retinfo.class_name; | ||||||
|  | 		if (p_method.return_enum.begins_with("_")) { //proxy class
 | ||||||
|  | 			p_method.return_enum = p_method.return_enum.substr(1); | ||||||
|  | 		} | ||||||
|  | 		p_method.return_is_bitfield = p_retinfo.usage & PROPERTY_USAGE_CLASS_IS_BITFIELD; | ||||||
|  | 		p_method.return_type = "int"; | ||||||
|  | 	} else if (p_retinfo.class_name != StringName()) { | ||||||
|  | 		p_method.return_type = p_retinfo.class_name; | ||||||
|  | 	} else if (p_retinfo.type == Variant::ARRAY && p_retinfo.hint == PROPERTY_HINT_ARRAY_TYPE) { | ||||||
|  | 		p_method.return_type = p_retinfo.hint_string + "[]"; | ||||||
|  | 	} else if (p_retinfo.type == Variant::DICTIONARY && p_retinfo.hint == PROPERTY_HINT_DICTIONARY_TYPE) { | ||||||
|  | 		p_method.return_type = "Dictionary[" + p_retinfo.hint_string.replace(";", ", ") + "]"; | ||||||
|  | 	} else if (p_retinfo.hint == PROPERTY_HINT_RESOURCE_TYPE) { | ||||||
|  | 		p_method.return_type = p_retinfo.hint_string; | ||||||
|  | 	} else if (p_retinfo.type == Variant::NIL && p_retinfo.usage & PROPERTY_USAGE_NIL_IS_VARIANT) { | ||||||
|  | 		p_method.return_type = "Variant"; | ||||||
|  | 	} else if (p_retinfo.type == Variant::NIL) { | ||||||
|  | 		p_method.return_type = "void"; | ||||||
|  | 	} else { | ||||||
|  | 		p_method.return_type = Variant::get_type_name(p_retinfo.type); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void DocData::argument_doc_from_arginfo(DocData::ArgumentDoc &p_argument, const PropertyInfo &p_arginfo) { | ||||||
|  | 	p_argument.name = p_arginfo.name; | ||||||
|  | 
 | ||||||
|  | 	if (p_arginfo.type == Variant::INT && p_arginfo.hint == PROPERTY_HINT_INT_IS_POINTER) { | ||||||
|  | 		p_argument.type = p_arginfo.hint_string; | ||||||
|  | 		if (p_argument.type.is_empty()) { | ||||||
|  | 			p_argument.type = "void*"; | ||||||
|  | 		} else { | ||||||
|  | 			p_argument.type += "*"; | ||||||
|  | 		} | ||||||
|  | 	} else if (p_arginfo.type == Variant::INT && p_arginfo.usage & (PROPERTY_USAGE_CLASS_IS_ENUM | PROPERTY_USAGE_CLASS_IS_BITFIELD)) { | ||||||
|  | 		p_argument.enumeration = p_arginfo.class_name; | ||||||
|  | 		if (p_argument.enumeration.begins_with("_")) { //proxy class
 | ||||||
|  | 			p_argument.enumeration = p_argument.enumeration.substr(1); | ||||||
|  | 		} | ||||||
|  | 		p_argument.is_bitfield = p_arginfo.usage & PROPERTY_USAGE_CLASS_IS_BITFIELD; | ||||||
|  | 		p_argument.type = "int"; | ||||||
|  | 	} else if (p_arginfo.class_name != StringName()) { | ||||||
|  | 		p_argument.type = p_arginfo.class_name; | ||||||
|  | 	} else if (p_arginfo.type == Variant::ARRAY && p_arginfo.hint == PROPERTY_HINT_ARRAY_TYPE) { | ||||||
|  | 		p_argument.type = p_arginfo.hint_string + "[]"; | ||||||
|  | 	} else if (p_arginfo.type == Variant::DICTIONARY && p_arginfo.hint == PROPERTY_HINT_DICTIONARY_TYPE) { | ||||||
|  | 		p_argument.type = "Dictionary[" + p_arginfo.hint_string.replace(";", ", ") + "]"; | ||||||
|  | 	} else if (p_arginfo.hint == PROPERTY_HINT_RESOURCE_TYPE) { | ||||||
|  | 		p_argument.type = p_arginfo.hint_string; | ||||||
|  | 	} else if (p_arginfo.type == Variant::NIL) { | ||||||
|  | 		// Parameters cannot be void, so PROPERTY_USAGE_NIL_IS_VARIANT is not necessary
 | ||||||
|  | 		p_argument.type = "Variant"; | ||||||
|  | 	} else { | ||||||
|  | 		p_argument.type = Variant::get_type_name(p_arginfo.type); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void DocData::method_doc_from_methodinfo(DocData::MethodDoc &p_method, const MethodInfo &p_methodinfo, const String &p_desc) { | ||||||
|  | 	p_method.name = p_methodinfo.name; | ||||||
|  | 	p_method.description = p_desc; | ||||||
|  | 
 | ||||||
|  | 	if (p_methodinfo.flags & METHOD_FLAG_VIRTUAL) { | ||||||
|  | 		p_method.qualifiers = "virtual"; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (p_methodinfo.flags & METHOD_FLAG_CONST) { | ||||||
|  | 		if (!p_method.qualifiers.is_empty()) { | ||||||
|  | 			p_method.qualifiers += " "; | ||||||
|  | 		} | ||||||
|  | 		p_method.qualifiers += "const"; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (p_methodinfo.flags & METHOD_FLAG_VARARG) { | ||||||
|  | 		if (!p_method.qualifiers.is_empty()) { | ||||||
|  | 			p_method.qualifiers += " "; | ||||||
|  | 		} | ||||||
|  | 		p_method.qualifiers += "vararg"; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (p_methodinfo.flags & METHOD_FLAG_STATIC) { | ||||||
|  | 		if (!p_method.qualifiers.is_empty()) { | ||||||
|  | 			p_method.qualifiers += " "; | ||||||
|  | 		} | ||||||
|  | 		p_method.qualifiers += "static"; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return_doc_from_retinfo(p_method, p_methodinfo.return_val); | ||||||
|  | 
 | ||||||
|  | 	for (int64_t i = 0; i < p_methodinfo.arguments.size(); ++i) { | ||||||
|  | 		DocData::ArgumentDoc argument; | ||||||
|  | 		argument_doc_from_arginfo(argument, p_methodinfo.arguments[i]); | ||||||
|  | 		int64_t default_arg_index = i - (p_methodinfo.arguments.size() - p_methodinfo.default_arguments.size()); | ||||||
|  | 		if (default_arg_index >= 0) { | ||||||
|  | 			Variant default_arg = p_methodinfo.default_arguments[default_arg_index]; | ||||||
|  | 			argument.default_value = get_default_value_string(default_arg); | ||||||
|  | 		} | ||||||
|  | 		p_method.arguments.push_back(argument); | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										981
									
								
								engine/core/doc_data.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										981
									
								
								engine/core/doc_data.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,981 @@ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*  doc_data.h                                                            */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*                         This file is part of:                          */ | ||||||
|  | /*                             GODOT ENGINE                               */ | ||||||
|  | /*                        https://godotengine.org                         */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ | ||||||
|  | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */ | ||||||
|  | /*                                                                        */ | ||||||
|  | /* 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.                 */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include "core/io/xml_parser.h" | ||||||
|  | #include "core/variant/variant.h" | ||||||
|  | 
 | ||||||
|  | class DocData { | ||||||
|  | public: | ||||||
|  | 	struct ArgumentDoc { | ||||||
|  | 		String name; | ||||||
|  | 		String type; | ||||||
|  | 		String enumeration; | ||||||
|  | 		bool is_bitfield = false; | ||||||
|  | 		String default_value; | ||||||
|  | 		bool operator<(const ArgumentDoc &p_arg) const { | ||||||
|  | 			if (name == p_arg.name) { | ||||||
|  | 				return type < p_arg.type; | ||||||
|  | 			} | ||||||
|  | 			return name < p_arg.name; | ||||||
|  | 		} | ||||||
|  | 		static ArgumentDoc from_dict(const Dictionary &p_dict) { | ||||||
|  | 			ArgumentDoc doc; | ||||||
|  | 
 | ||||||
|  | 			if (p_dict.has("name")) { | ||||||
|  | 				doc.name = p_dict["name"]; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (p_dict.has("type")) { | ||||||
|  | 				doc.type = p_dict["type"]; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (p_dict.has("enumeration")) { | ||||||
|  | 				doc.enumeration = p_dict["enumeration"]; | ||||||
|  | 				if (p_dict.has("is_bitfield")) { | ||||||
|  | 					doc.is_bitfield = p_dict["is_bitfield"]; | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (p_dict.has("default_value")) { | ||||||
|  | 				doc.default_value = p_dict["default_value"]; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			return doc; | ||||||
|  | 		} | ||||||
|  | 		static Dictionary to_dict(const ArgumentDoc &p_doc) { | ||||||
|  | 			Dictionary dict; | ||||||
|  | 
 | ||||||
|  | 			if (!p_doc.name.is_empty()) { | ||||||
|  | 				dict["name"] = p_doc.name; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (!p_doc.type.is_empty()) { | ||||||
|  | 				dict["type"] = p_doc.type; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (!p_doc.enumeration.is_empty()) { | ||||||
|  | 				dict["enumeration"] = p_doc.enumeration; | ||||||
|  | 				dict["is_bitfield"] = p_doc.is_bitfield; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (!p_doc.default_value.is_empty()) { | ||||||
|  | 				dict["default_value"] = p_doc.default_value; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			return dict; | ||||||
|  | 		} | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	struct MethodDoc { | ||||||
|  | 		String name; | ||||||
|  | 		String return_type; | ||||||
|  | 		String return_enum; | ||||||
|  | 		bool return_is_bitfield = false; | ||||||
|  | 		String qualifiers; | ||||||
|  | 		String description; | ||||||
|  | 		bool is_deprecated = false; | ||||||
|  | 		String deprecated_message; | ||||||
|  | 		bool is_experimental = false; | ||||||
|  | 		String experimental_message; | ||||||
|  | 		Vector<ArgumentDoc> arguments; | ||||||
|  | 		Vector<int> errors_returned; | ||||||
|  | 		String keywords; | ||||||
|  | 		bool operator<(const MethodDoc &p_method) const { | ||||||
|  | 			if (name == p_method.name) { | ||||||
|  | 				// Must be an operator or a constructor since there is no other overloading
 | ||||||
|  | 				if (name.left(8) == "operator") { | ||||||
|  | 					if (arguments.size() == p_method.arguments.size()) { | ||||||
|  | 						if (arguments.is_empty()) { | ||||||
|  | 							return false; | ||||||
|  | 						} | ||||||
|  | 						return arguments[0].type < p_method.arguments[0].type; | ||||||
|  | 					} | ||||||
|  | 					return arguments.size() < p_method.arguments.size(); | ||||||
|  | 				} else { | ||||||
|  | 					// Must be a constructor
 | ||||||
|  | 					// We want this arbitrary order for a class "Foo":
 | ||||||
|  | 					// - 1. Default constructor: Foo()
 | ||||||
|  | 					// - 2. Copy constructor: Foo(Foo)
 | ||||||
|  | 					// - 3+. Other constructors Foo(Bar, ...) based on first argument's name
 | ||||||
|  | 					if (arguments.is_empty() || p_method.arguments.is_empty()) { // 1.
 | ||||||
|  | 						return arguments.size() < p_method.arguments.size(); | ||||||
|  | 					} | ||||||
|  | 					if (arguments[0].type == return_type || p_method.arguments[0].type == p_method.return_type) { // 2.
 | ||||||
|  | 						return (arguments[0].type == return_type) || (p_method.arguments[0].type != p_method.return_type); | ||||||
|  | 					} | ||||||
|  | 					return arguments[0] < p_method.arguments[0]; | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			return name.naturalcasecmp_to(p_method.name) < 0; | ||||||
|  | 		} | ||||||
|  | 		static MethodDoc from_dict(const Dictionary &p_dict) { | ||||||
|  | 			MethodDoc doc; | ||||||
|  | 
 | ||||||
|  | 			if (p_dict.has("name")) { | ||||||
|  | 				doc.name = p_dict["name"]; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (p_dict.has("return_type")) { | ||||||
|  | 				doc.return_type = p_dict["return_type"]; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (p_dict.has("return_enum")) { | ||||||
|  | 				doc.return_enum = p_dict["return_enum"]; | ||||||
|  | 				if (p_dict.has("return_is_bitfield")) { | ||||||
|  | 					doc.return_is_bitfield = p_dict["return_is_bitfield"]; | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (p_dict.has("qualifiers")) { | ||||||
|  | 				doc.qualifiers = p_dict["qualifiers"]; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (p_dict.has("description")) { | ||||||
|  | 				doc.description = p_dict["description"]; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | #ifndef DISABLE_DEPRECATED | ||||||
|  | 			if (p_dict.has("is_deprecated")) { | ||||||
|  | 				doc.is_deprecated = p_dict["is_deprecated"]; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (p_dict.has("is_experimental")) { | ||||||
|  | 				doc.is_experimental = p_dict["is_experimental"]; | ||||||
|  | 			} | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 			if (p_dict.has("deprecated")) { | ||||||
|  | 				doc.is_deprecated = true; | ||||||
|  | 				doc.deprecated_message = p_dict["deprecated"]; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (p_dict.has("experimental")) { | ||||||
|  | 				doc.is_experimental = true; | ||||||
|  | 				doc.experimental_message = p_dict["experimental"]; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			Array arguments; | ||||||
|  | 			if (p_dict.has("arguments")) { | ||||||
|  | 				arguments = p_dict["arguments"]; | ||||||
|  | 			} | ||||||
|  | 			for (int i = 0; i < arguments.size(); i++) { | ||||||
|  | 				doc.arguments.push_back(ArgumentDoc::from_dict(arguments[i])); | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			Array errors_returned; | ||||||
|  | 			if (p_dict.has("errors_returned")) { | ||||||
|  | 				errors_returned = p_dict["errors_returned"]; | ||||||
|  | 			} | ||||||
|  | 			for (int i = 0; i < errors_returned.size(); i++) { | ||||||
|  | 				doc.errors_returned.push_back(errors_returned[i]); | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (p_dict.has("keywords")) { | ||||||
|  | 				doc.keywords = p_dict["keywords"]; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			return doc; | ||||||
|  | 		} | ||||||
|  | 		static Dictionary to_dict(const MethodDoc &p_doc) { | ||||||
|  | 			Dictionary dict; | ||||||
|  | 
 | ||||||
|  | 			if (!p_doc.name.is_empty()) { | ||||||
|  | 				dict["name"] = p_doc.name; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (!p_doc.return_type.is_empty()) { | ||||||
|  | 				dict["return_type"] = p_doc.return_type; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (!p_doc.return_enum.is_empty()) { | ||||||
|  | 				dict["return_enum"] = p_doc.return_enum; | ||||||
|  | 				dict["return_is_bitfield"] = p_doc.return_is_bitfield; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (!p_doc.qualifiers.is_empty()) { | ||||||
|  | 				dict["qualifiers"] = p_doc.qualifiers; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (!p_doc.description.is_empty()) { | ||||||
|  | 				dict["description"] = p_doc.description; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (p_doc.is_deprecated) { | ||||||
|  | 				dict["deprecated"] = p_doc.deprecated_message; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (p_doc.is_experimental) { | ||||||
|  | 				dict["experimental"] = p_doc.experimental_message; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (!p_doc.keywords.is_empty()) { | ||||||
|  | 				dict["keywords"] = p_doc.keywords; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (!p_doc.arguments.is_empty()) { | ||||||
|  | 				Array arguments; | ||||||
|  | 				for (int i = 0; i < p_doc.arguments.size(); i++) { | ||||||
|  | 					arguments.push_back(ArgumentDoc::to_dict(p_doc.arguments[i])); | ||||||
|  | 				} | ||||||
|  | 				dict["arguments"] = arguments; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (!p_doc.errors_returned.is_empty()) { | ||||||
|  | 				Array errors_returned; | ||||||
|  | 				for (int i = 0; i < p_doc.errors_returned.size(); i++) { | ||||||
|  | 					errors_returned.push_back(p_doc.errors_returned[i]); | ||||||
|  | 				} | ||||||
|  | 				dict["errors_returned"] = errors_returned; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			return dict; | ||||||
|  | 		} | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	struct ConstantDoc { | ||||||
|  | 		String name; | ||||||
|  | 		String value; | ||||||
|  | 		bool is_value_valid = false; | ||||||
|  | 		String type; | ||||||
|  | 		String enumeration; | ||||||
|  | 		bool is_bitfield = false; | ||||||
|  | 		String description; | ||||||
|  | 		bool is_deprecated = false; | ||||||
|  | 		String deprecated_message; | ||||||
|  | 		bool is_experimental = false; | ||||||
|  | 		String experimental_message; | ||||||
|  | 		String keywords; | ||||||
|  | 		bool operator<(const ConstantDoc &p_const) const { | ||||||
|  | 			return name < p_const.name; | ||||||
|  | 		} | ||||||
|  | 		static ConstantDoc from_dict(const Dictionary &p_dict) { | ||||||
|  | 			ConstantDoc doc; | ||||||
|  | 
 | ||||||
|  | 			if (p_dict.has("name")) { | ||||||
|  | 				doc.name = p_dict["name"]; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (p_dict.has("value")) { | ||||||
|  | 				doc.value = p_dict["value"]; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (p_dict.has("is_value_valid")) { | ||||||
|  | 				doc.is_value_valid = p_dict["is_value_valid"]; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (p_dict.has("type")) { | ||||||
|  | 				doc.type = p_dict["type"]; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (p_dict.has("enumeration")) { | ||||||
|  | 				doc.enumeration = p_dict["enumeration"]; | ||||||
|  | 				if (p_dict.has("is_bitfield")) { | ||||||
|  | 					doc.is_bitfield = p_dict["is_bitfield"]; | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (p_dict.has("description")) { | ||||||
|  | 				doc.description = p_dict["description"]; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | #ifndef DISABLE_DEPRECATED | ||||||
|  | 			if (p_dict.has("is_deprecated")) { | ||||||
|  | 				doc.is_deprecated = p_dict["is_deprecated"]; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (p_dict.has("is_experimental")) { | ||||||
|  | 				doc.is_experimental = p_dict["is_experimental"]; | ||||||
|  | 			} | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 			if (p_dict.has("deprecated")) { | ||||||
|  | 				doc.is_deprecated = true; | ||||||
|  | 				doc.deprecated_message = p_dict["deprecated"]; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (p_dict.has("experimental")) { | ||||||
|  | 				doc.is_experimental = true; | ||||||
|  | 				doc.experimental_message = p_dict["experimental"]; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (p_dict.has("keywords")) { | ||||||
|  | 				doc.keywords = p_dict["keywords"]; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			return doc; | ||||||
|  | 		} | ||||||
|  | 		static Dictionary to_dict(const ConstantDoc &p_doc) { | ||||||
|  | 			Dictionary dict; | ||||||
|  | 
 | ||||||
|  | 			if (!p_doc.name.is_empty()) { | ||||||
|  | 				dict["name"] = p_doc.name; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (!p_doc.value.is_empty()) { | ||||||
|  | 				dict["value"] = p_doc.value; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			dict["is_value_valid"] = p_doc.is_value_valid; | ||||||
|  | 
 | ||||||
|  | 			dict["type"] = p_doc.type; | ||||||
|  | 
 | ||||||
|  | 			if (!p_doc.enumeration.is_empty()) { | ||||||
|  | 				dict["enumeration"] = p_doc.enumeration; | ||||||
|  | 				dict["is_bitfield"] = p_doc.is_bitfield; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (!p_doc.description.is_empty()) { | ||||||
|  | 				dict["description"] = p_doc.description; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (p_doc.is_deprecated) { | ||||||
|  | 				dict["deprecated"] = p_doc.deprecated_message; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (p_doc.is_experimental) { | ||||||
|  | 				dict["experimental"] = p_doc.experimental_message; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (!p_doc.keywords.is_empty()) { | ||||||
|  | 				dict["keywords"] = p_doc.keywords; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			return dict; | ||||||
|  | 		} | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	struct PropertyDoc { | ||||||
|  | 		String name; | ||||||
|  | 		String type; | ||||||
|  | 		String enumeration; | ||||||
|  | 		bool is_bitfield = false; | ||||||
|  | 		String description; | ||||||
|  | 		String setter, getter; | ||||||
|  | 		String default_value; | ||||||
|  | 		bool overridden = false; | ||||||
|  | 		String overrides; | ||||||
|  | 		bool is_deprecated = false; | ||||||
|  | 		String deprecated_message; | ||||||
|  | 		bool is_experimental = false; | ||||||
|  | 		String experimental_message; | ||||||
|  | 		String keywords; | ||||||
|  | 		bool operator<(const PropertyDoc &p_prop) const { | ||||||
|  | 			return name.naturalcasecmp_to(p_prop.name) < 0; | ||||||
|  | 		} | ||||||
|  | 		static PropertyDoc from_dict(const Dictionary &p_dict) { | ||||||
|  | 			PropertyDoc doc; | ||||||
|  | 
 | ||||||
|  | 			if (p_dict.has("name")) { | ||||||
|  | 				doc.name = p_dict["name"]; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (p_dict.has("type")) { | ||||||
|  | 				doc.type = p_dict["type"]; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (p_dict.has("enumeration")) { | ||||||
|  | 				doc.enumeration = p_dict["enumeration"]; | ||||||
|  | 				if (p_dict.has("is_bitfield")) { | ||||||
|  | 					doc.is_bitfield = p_dict["is_bitfield"]; | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (p_dict.has("description")) { | ||||||
|  | 				doc.description = p_dict["description"]; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (p_dict.has("setter")) { | ||||||
|  | 				doc.setter = p_dict["setter"]; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (p_dict.has("getter")) { | ||||||
|  | 				doc.getter = p_dict["getter"]; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (p_dict.has("default_value")) { | ||||||
|  | 				doc.default_value = p_dict["default_value"]; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (p_dict.has("overridden")) { | ||||||
|  | 				doc.overridden = p_dict["overridden"]; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (p_dict.has("overrides")) { | ||||||
|  | 				doc.overrides = p_dict["overrides"]; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | #ifndef DISABLE_DEPRECATED | ||||||
|  | 			if (p_dict.has("is_deprecated")) { | ||||||
|  | 				doc.is_deprecated = p_dict["is_deprecated"]; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (p_dict.has("is_experimental")) { | ||||||
|  | 				doc.is_experimental = p_dict["is_experimental"]; | ||||||
|  | 			} | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 			if (p_dict.has("deprecated")) { | ||||||
|  | 				doc.is_deprecated = true; | ||||||
|  | 				doc.deprecated_message = p_dict["deprecated"]; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (p_dict.has("experimental")) { | ||||||
|  | 				doc.is_experimental = true; | ||||||
|  | 				doc.experimental_message = p_dict["experimental"]; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (p_dict.has("keywords")) { | ||||||
|  | 				doc.keywords = p_dict["keywords"]; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			return doc; | ||||||
|  | 		} | ||||||
|  | 		static Dictionary to_dict(const PropertyDoc &p_doc) { | ||||||
|  | 			Dictionary dict; | ||||||
|  | 
 | ||||||
|  | 			if (!p_doc.name.is_empty()) { | ||||||
|  | 				dict["name"] = p_doc.name; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (!p_doc.type.is_empty()) { | ||||||
|  | 				dict["type"] = p_doc.type; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (!p_doc.enumeration.is_empty()) { | ||||||
|  | 				dict["enumeration"] = p_doc.enumeration; | ||||||
|  | 				dict["is_bitfield"] = p_doc.is_bitfield; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (!p_doc.description.is_empty()) { | ||||||
|  | 				dict["description"] = p_doc.description; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (!p_doc.setter.is_empty()) { | ||||||
|  | 				dict["setter"] = p_doc.setter; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (!p_doc.getter.is_empty()) { | ||||||
|  | 				dict["getter"] = p_doc.getter; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (!p_doc.default_value.is_empty()) { | ||||||
|  | 				dict["default_value"] = p_doc.default_value; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			dict["overridden"] = p_doc.overridden; | ||||||
|  | 
 | ||||||
|  | 			if (!p_doc.overrides.is_empty()) { | ||||||
|  | 				dict["overrides"] = p_doc.overrides; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (p_doc.is_deprecated) { | ||||||
|  | 				dict["deprecated"] = p_doc.deprecated_message; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (p_doc.is_experimental) { | ||||||
|  | 				dict["experimental"] = p_doc.experimental_message; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (!p_doc.keywords.is_empty()) { | ||||||
|  | 				dict["keywords"] = p_doc.keywords; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			return dict; | ||||||
|  | 		} | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	struct ThemeItemDoc { | ||||||
|  | 		String name; | ||||||
|  | 		String type; | ||||||
|  | 		String data_type; | ||||||
|  | 		String description; | ||||||
|  | 		bool is_deprecated = false; | ||||||
|  | 		String deprecated_message; | ||||||
|  | 		bool is_experimental = false; | ||||||
|  | 		String experimental_message; | ||||||
|  | 		String default_value; | ||||||
|  | 		String keywords; | ||||||
|  | 		bool operator<(const ThemeItemDoc &p_theme_item) const { | ||||||
|  | 			// First sort by the data type, then by name.
 | ||||||
|  | 			if (data_type == p_theme_item.data_type) { | ||||||
|  | 				return name.naturalcasecmp_to(p_theme_item.name) < 0; | ||||||
|  | 			} | ||||||
|  | 			return data_type < p_theme_item.data_type; | ||||||
|  | 		} | ||||||
|  | 		static ThemeItemDoc from_dict(const Dictionary &p_dict) { | ||||||
|  | 			ThemeItemDoc doc; | ||||||
|  | 
 | ||||||
|  | 			if (p_dict.has("name")) { | ||||||
|  | 				doc.name = p_dict["name"]; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (p_dict.has("type")) { | ||||||
|  | 				doc.type = p_dict["type"]; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (p_dict.has("data_type")) { | ||||||
|  | 				doc.data_type = p_dict["data_type"]; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (p_dict.has("description")) { | ||||||
|  | 				doc.description = p_dict["description"]; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (p_dict.has("deprecated")) { | ||||||
|  | 				doc.is_deprecated = true; | ||||||
|  | 				doc.deprecated_message = p_dict["deprecated"]; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (p_dict.has("experimental")) { | ||||||
|  | 				doc.is_experimental = true; | ||||||
|  | 				doc.experimental_message = p_dict["experimental"]; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (p_dict.has("default_value")) { | ||||||
|  | 				doc.default_value = p_dict["default_value"]; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (p_dict.has("keywords")) { | ||||||
|  | 				doc.keywords = p_dict["keywords"]; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			return doc; | ||||||
|  | 		} | ||||||
|  | 		static Dictionary to_dict(const ThemeItemDoc &p_doc) { | ||||||
|  | 			Dictionary dict; | ||||||
|  | 
 | ||||||
|  | 			if (!p_doc.name.is_empty()) { | ||||||
|  | 				dict["name"] = p_doc.name; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (!p_doc.type.is_empty()) { | ||||||
|  | 				dict["type"] = p_doc.type; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (!p_doc.data_type.is_empty()) { | ||||||
|  | 				dict["data_type"] = p_doc.data_type; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (!p_doc.description.is_empty()) { | ||||||
|  | 				dict["description"] = p_doc.description; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (p_doc.is_deprecated) { | ||||||
|  | 				dict["deprecated"] = p_doc.deprecated_message; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (p_doc.is_experimental) { | ||||||
|  | 				dict["experimental"] = p_doc.experimental_message; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (!p_doc.default_value.is_empty()) { | ||||||
|  | 				dict["default_value"] = p_doc.default_value; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (!p_doc.keywords.is_empty()) { | ||||||
|  | 				dict["keywords"] = p_doc.keywords; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			return dict; | ||||||
|  | 		} | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	struct TutorialDoc { | ||||||
|  | 		String link; | ||||||
|  | 		String title; | ||||||
|  | 		static TutorialDoc from_dict(const Dictionary &p_dict) { | ||||||
|  | 			TutorialDoc doc; | ||||||
|  | 
 | ||||||
|  | 			if (p_dict.has("link")) { | ||||||
|  | 				doc.link = p_dict["link"]; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (p_dict.has("title")) { | ||||||
|  | 				doc.title = p_dict["title"]; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			return doc; | ||||||
|  | 		} | ||||||
|  | 		static Dictionary to_dict(const TutorialDoc &p_doc) { | ||||||
|  | 			Dictionary dict; | ||||||
|  | 
 | ||||||
|  | 			if (!p_doc.link.is_empty()) { | ||||||
|  | 				dict["link"] = p_doc.link; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (!p_doc.title.is_empty()) { | ||||||
|  | 				dict["title"] = p_doc.title; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			return dict; | ||||||
|  | 		} | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	struct EnumDoc { | ||||||
|  | 		String description; | ||||||
|  | 		bool is_deprecated = false; | ||||||
|  | 		String deprecated_message; | ||||||
|  | 		bool is_experimental = false; | ||||||
|  | 		String experimental_message; | ||||||
|  | 		static EnumDoc from_dict(const Dictionary &p_dict) { | ||||||
|  | 			EnumDoc doc; | ||||||
|  | 
 | ||||||
|  | 			if (p_dict.has("description")) { | ||||||
|  | 				doc.description = p_dict["description"]; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | #ifndef DISABLE_DEPRECATED | ||||||
|  | 			if (p_dict.has("is_deprecated")) { | ||||||
|  | 				doc.is_deprecated = p_dict["is_deprecated"]; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (p_dict.has("is_experimental")) { | ||||||
|  | 				doc.is_experimental = p_dict["is_experimental"]; | ||||||
|  | 			} | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 			if (p_dict.has("deprecated")) { | ||||||
|  | 				doc.is_deprecated = true; | ||||||
|  | 				doc.deprecated_message = p_dict["deprecated"]; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (p_dict.has("experimental")) { | ||||||
|  | 				doc.is_experimental = true; | ||||||
|  | 				doc.experimental_message = p_dict["experimental"]; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			return doc; | ||||||
|  | 		} | ||||||
|  | 		static Dictionary to_dict(const EnumDoc &p_doc) { | ||||||
|  | 			Dictionary dict; | ||||||
|  | 
 | ||||||
|  | 			if (!p_doc.description.is_empty()) { | ||||||
|  | 				dict["description"] = p_doc.description; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (p_doc.is_deprecated) { | ||||||
|  | 				dict["deprecated"] = p_doc.deprecated_message; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (p_doc.is_experimental) { | ||||||
|  | 				dict["experimental"] = p_doc.experimental_message; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			return dict; | ||||||
|  | 		} | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	struct ClassDoc { | ||||||
|  | 		String name; | ||||||
|  | 		String inherits; | ||||||
|  | 		String brief_description; | ||||||
|  | 		String description; | ||||||
|  | 		String keywords; | ||||||
|  | 		Vector<TutorialDoc> tutorials; | ||||||
|  | 		Vector<MethodDoc> constructors; | ||||||
|  | 		Vector<MethodDoc> methods; | ||||||
|  | 		Vector<MethodDoc> operators; | ||||||
|  | 		Vector<MethodDoc> signals; | ||||||
|  | 		Vector<ConstantDoc> constants; | ||||||
|  | 		HashMap<String, EnumDoc> enums; | ||||||
|  | 		Vector<PropertyDoc> properties; | ||||||
|  | 		Vector<MethodDoc> annotations; | ||||||
|  | 		Vector<ThemeItemDoc> theme_properties; | ||||||
|  | 		bool is_deprecated = false; | ||||||
|  | 		String deprecated_message; | ||||||
|  | 		bool is_experimental = false; | ||||||
|  | 		String experimental_message; | ||||||
|  | 		bool is_script_doc = false; | ||||||
|  | 		String script_path; | ||||||
|  | 		bool operator<(const ClassDoc &p_class) const { | ||||||
|  | 			return name < p_class.name; | ||||||
|  | 		} | ||||||
|  | 		static ClassDoc from_dict(const Dictionary &p_dict) { | ||||||
|  | 			ClassDoc doc; | ||||||
|  | 
 | ||||||
|  | 			if (p_dict.has("name")) { | ||||||
|  | 				doc.name = p_dict["name"]; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (p_dict.has("inherits")) { | ||||||
|  | 				doc.inherits = p_dict["inherits"]; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (p_dict.has("brief_description")) { | ||||||
|  | 				doc.brief_description = p_dict["brief_description"]; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (p_dict.has("description")) { | ||||||
|  | 				doc.description = p_dict["description"]; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (p_dict.has("keywords")) { | ||||||
|  | 				doc.keywords = p_dict["keywords"]; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			Array tutorials; | ||||||
|  | 			if (p_dict.has("tutorials")) { | ||||||
|  | 				tutorials = p_dict["tutorials"]; | ||||||
|  | 			} | ||||||
|  | 			for (int i = 0; i < tutorials.size(); i++) { | ||||||
|  | 				doc.tutorials.push_back(TutorialDoc::from_dict(tutorials[i])); | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			Array constructors; | ||||||
|  | 			if (p_dict.has("constructors")) { | ||||||
|  | 				constructors = p_dict["constructors"]; | ||||||
|  | 			} | ||||||
|  | 			for (int i = 0; i < constructors.size(); i++) { | ||||||
|  | 				doc.constructors.push_back(MethodDoc::from_dict(constructors[i])); | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			Array methods; | ||||||
|  | 			if (p_dict.has("methods")) { | ||||||
|  | 				methods = p_dict["methods"]; | ||||||
|  | 			} | ||||||
|  | 			for (int i = 0; i < methods.size(); i++) { | ||||||
|  | 				doc.methods.push_back(MethodDoc::from_dict(methods[i])); | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			Array operators; | ||||||
|  | 			if (p_dict.has("operators")) { | ||||||
|  | 				operators = p_dict["operators"]; | ||||||
|  | 			} | ||||||
|  | 			for (int i = 0; i < operators.size(); i++) { | ||||||
|  | 				doc.operators.push_back(MethodDoc::from_dict(operators[i])); | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			Array signals; | ||||||
|  | 			if (p_dict.has("signals")) { | ||||||
|  | 				signals = p_dict["signals"]; | ||||||
|  | 			} | ||||||
|  | 			for (int i = 0; i < signals.size(); i++) { | ||||||
|  | 				doc.signals.push_back(MethodDoc::from_dict(signals[i])); | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			Array constants; | ||||||
|  | 			if (p_dict.has("constants")) { | ||||||
|  | 				constants = p_dict["constants"]; | ||||||
|  | 			} | ||||||
|  | 			for (int i = 0; i < constants.size(); i++) { | ||||||
|  | 				doc.constants.push_back(ConstantDoc::from_dict(constants[i])); | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			Dictionary enums; | ||||||
|  | 			if (p_dict.has("enums")) { | ||||||
|  | 				enums = p_dict["enums"]; | ||||||
|  | 			} | ||||||
|  | 			for (const KeyValue<Variant, Variant> &kv : enums) { | ||||||
|  | 				doc.enums[kv.key] = EnumDoc::from_dict(kv.value); | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			Array properties; | ||||||
|  | 			if (p_dict.has("properties")) { | ||||||
|  | 				properties = p_dict["properties"]; | ||||||
|  | 			} | ||||||
|  | 			for (int i = 0; i < properties.size(); i++) { | ||||||
|  | 				doc.properties.push_back(PropertyDoc::from_dict(properties[i])); | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			Array annotations; | ||||||
|  | 			if (p_dict.has("annotations")) { | ||||||
|  | 				annotations = p_dict["annotations"]; | ||||||
|  | 			} | ||||||
|  | 			for (int i = 0; i < annotations.size(); i++) { | ||||||
|  | 				doc.annotations.push_back(MethodDoc::from_dict(annotations[i])); | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			Array theme_properties; | ||||||
|  | 			if (p_dict.has("theme_properties")) { | ||||||
|  | 				theme_properties = p_dict["theme_properties"]; | ||||||
|  | 			} | ||||||
|  | 			for (int i = 0; i < theme_properties.size(); i++) { | ||||||
|  | 				doc.theme_properties.push_back(ThemeItemDoc::from_dict(theme_properties[i])); | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | #ifndef DISABLE_DEPRECATED | ||||||
|  | 			if (p_dict.has("is_deprecated")) { | ||||||
|  | 				doc.is_deprecated = p_dict["is_deprecated"]; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (p_dict.has("is_experimental")) { | ||||||
|  | 				doc.is_experimental = p_dict["is_experimental"]; | ||||||
|  | 			} | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 			if (p_dict.has("deprecated")) { | ||||||
|  | 				doc.is_deprecated = true; | ||||||
|  | 				doc.deprecated_message = p_dict["deprecated"]; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (p_dict.has("experimental")) { | ||||||
|  | 				doc.is_experimental = true; | ||||||
|  | 				doc.experimental_message = p_dict["experimental"]; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (p_dict.has("is_script_doc")) { | ||||||
|  | 				doc.is_script_doc = p_dict["is_script_doc"]; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (p_dict.has("script_path")) { | ||||||
|  | 				doc.script_path = p_dict["script_path"]; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			return doc; | ||||||
|  | 		} | ||||||
|  | 		static Dictionary to_dict(const ClassDoc &p_doc) { | ||||||
|  | 			Dictionary dict; | ||||||
|  | 
 | ||||||
|  | 			if (!p_doc.name.is_empty()) { | ||||||
|  | 				dict["name"] = p_doc.name; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (!p_doc.inherits.is_empty()) { | ||||||
|  | 				dict["inherits"] = p_doc.inherits; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (!p_doc.brief_description.is_empty()) { | ||||||
|  | 				dict["brief_description"] = p_doc.brief_description; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (!p_doc.description.is_empty()) { | ||||||
|  | 				dict["description"] = p_doc.description; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (!p_doc.tutorials.is_empty()) { | ||||||
|  | 				Array tutorials; | ||||||
|  | 				for (int i = 0; i < p_doc.tutorials.size(); i++) { | ||||||
|  | 					tutorials.push_back(TutorialDoc::to_dict(p_doc.tutorials[i])); | ||||||
|  | 				} | ||||||
|  | 				dict["tutorials"] = tutorials; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (!p_doc.constructors.is_empty()) { | ||||||
|  | 				Array constructors; | ||||||
|  | 				for (int i = 0; i < p_doc.constructors.size(); i++) { | ||||||
|  | 					constructors.push_back(MethodDoc::to_dict(p_doc.constructors[i])); | ||||||
|  | 				} | ||||||
|  | 				dict["constructors"] = constructors; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (!p_doc.methods.is_empty()) { | ||||||
|  | 				Array methods; | ||||||
|  | 				for (int i = 0; i < p_doc.methods.size(); i++) { | ||||||
|  | 					methods.push_back(MethodDoc::to_dict(p_doc.methods[i])); | ||||||
|  | 				} | ||||||
|  | 				dict["methods"] = methods; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (!p_doc.operators.is_empty()) { | ||||||
|  | 				Array operators; | ||||||
|  | 				for (int i = 0; i < p_doc.operators.size(); i++) { | ||||||
|  | 					operators.push_back(MethodDoc::to_dict(p_doc.operators[i])); | ||||||
|  | 				} | ||||||
|  | 				dict["operators"] = operators; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (!p_doc.signals.is_empty()) { | ||||||
|  | 				Array signals; | ||||||
|  | 				for (int i = 0; i < p_doc.signals.size(); i++) { | ||||||
|  | 					signals.push_back(MethodDoc::to_dict(p_doc.signals[i])); | ||||||
|  | 				} | ||||||
|  | 				dict["signals"] = signals; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (!p_doc.constants.is_empty()) { | ||||||
|  | 				Array constants; | ||||||
|  | 				for (int i = 0; i < p_doc.constants.size(); i++) { | ||||||
|  | 					constants.push_back(ConstantDoc::to_dict(p_doc.constants[i])); | ||||||
|  | 				} | ||||||
|  | 				dict["constants"] = constants; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (!p_doc.enums.is_empty()) { | ||||||
|  | 				Dictionary enums; | ||||||
|  | 				for (const KeyValue<String, EnumDoc> &E : p_doc.enums) { | ||||||
|  | 					enums[E.key] = EnumDoc::to_dict(E.value); | ||||||
|  | 				} | ||||||
|  | 				dict["enums"] = enums; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (!p_doc.properties.is_empty()) { | ||||||
|  | 				Array properties; | ||||||
|  | 				for (int i = 0; i < p_doc.properties.size(); i++) { | ||||||
|  | 					properties.push_back(PropertyDoc::to_dict(p_doc.properties[i])); | ||||||
|  | 				} | ||||||
|  | 				dict["properties"] = properties; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (!p_doc.annotations.is_empty()) { | ||||||
|  | 				Array annotations; | ||||||
|  | 				for (int i = 0; i < p_doc.annotations.size(); i++) { | ||||||
|  | 					annotations.push_back(MethodDoc::to_dict(p_doc.annotations[i])); | ||||||
|  | 				} | ||||||
|  | 				dict["annotations"] = annotations; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (!p_doc.theme_properties.is_empty()) { | ||||||
|  | 				Array theme_properties; | ||||||
|  | 				for (int i = 0; i < p_doc.theme_properties.size(); i++) { | ||||||
|  | 					theme_properties.push_back(ThemeItemDoc::to_dict(p_doc.theme_properties[i])); | ||||||
|  | 				} | ||||||
|  | 				dict["theme_properties"] = theme_properties; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (p_doc.is_deprecated) { | ||||||
|  | 				dict["deprecated"] = p_doc.deprecated_message; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (p_doc.is_experimental) { | ||||||
|  | 				dict["experimental"] = p_doc.experimental_message; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			dict["is_script_doc"] = p_doc.is_script_doc; | ||||||
|  | 
 | ||||||
|  | 			if (!p_doc.script_path.is_empty()) { | ||||||
|  | 				dict["script_path"] = p_doc.script_path; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (!p_doc.keywords.is_empty()) { | ||||||
|  | 				dict["keywords"] = p_doc.keywords; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			return dict; | ||||||
|  | 		} | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	static String get_default_value_string(const Variant &p_value); | ||||||
|  | 
 | ||||||
|  | 	static void return_doc_from_retinfo(DocData::MethodDoc &p_method, const PropertyInfo &p_retinfo); | ||||||
|  | 	static void argument_doc_from_arginfo(DocData::ArgumentDoc &p_argument, const PropertyInfo &p_arginfo); | ||||||
|  | 	static void method_doc_from_methodinfo(DocData::MethodDoc &p_method, const MethodInfo &p_methodinfo, const String &p_desc); | ||||||
|  | }; | ||||||
							
								
								
									
										8
									
								
								engine/core/error/SCsub
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								engine/core/error/SCsub
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,8 @@ | ||||||
|  | #!/usr/bin/env python | ||||||
|  | from misc.utility.scons_hints import * | ||||||
|  | 
 | ||||||
|  | Import("env") | ||||||
|  | 
 | ||||||
|  | env_error = env.Clone() | ||||||
|  | 
 | ||||||
|  | env_error.add_source_files(env.core_sources, "*.cpp") | ||||||
							
								
								
									
										87
									
								
								engine/core/error/error_list.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								engine/core/error/error_list.cpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,87 @@ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*  error_list.cpp                                                        */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*                         This file is part of:                          */ | ||||||
|  | /*                             GODOT ENGINE                               */ | ||||||
|  | /*                        https://godotengine.org                         */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ | ||||||
|  | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */ | ||||||
|  | /*                                                                        */ | ||||||
|  | /* 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 "error_list.h" | ||||||
|  | 
 | ||||||
|  | #include <iterator> | ||||||
|  | 
 | ||||||
|  | const char *error_names[] = { | ||||||
|  | 	"OK", // OK
 | ||||||
|  | 	"Failed", // FAILED
 | ||||||
|  | 	"Unavailable", // ERR_UNAVAILABLE
 | ||||||
|  | 	"Unconfigured", // ERR_UNCONFIGURED
 | ||||||
|  | 	"Unauthorized", // ERR_UNAUTHORIZED
 | ||||||
|  | 	"Parameter out of range", // ERR_PARAMETER_RANGE_ERROR
 | ||||||
|  | 	"Out of memory", // ERR_OUT_OF_MEMORY
 | ||||||
|  | 	"File not found", // ERR_FILE_NOT_FOUND
 | ||||||
|  | 	"File: Bad drive", // ERR_FILE_BAD_DRIVE
 | ||||||
|  | 	"File: Bad path", // ERR_FILE_BAD_PATH
 | ||||||
|  | 	"File: Permission denied", // ERR_FILE_NO_PERMISSION
 | ||||||
|  | 	"File already in use", // ERR_FILE_ALREADY_IN_USE
 | ||||||
|  | 	"Can't open file", // ERR_FILE_CANT_OPEN
 | ||||||
|  | 	"Can't write file", // ERR_FILE_CANT_WRITE
 | ||||||
|  | 	"Can't read file", // ERR_FILE_CANT_READ
 | ||||||
|  | 	"File unrecognized", // ERR_FILE_UNRECOGNIZED
 | ||||||
|  | 	"File corrupt", // ERR_FILE_CORRUPT
 | ||||||
|  | 	"Missing dependencies for file", // ERR_FILE_MISSING_DEPENDENCIES
 | ||||||
|  | 	"End of file", // ERR_FILE_EOF
 | ||||||
|  | 	"Can't open", // ERR_CANT_OPEN
 | ||||||
|  | 	"Can't create", // ERR_CANT_CREATE
 | ||||||
|  | 	"Query failed", // ERR_QUERY_FAILED
 | ||||||
|  | 	"Already in use", // ERR_ALREADY_IN_USE
 | ||||||
|  | 	"Locked", // ERR_LOCKED
 | ||||||
|  | 	"Timeout", // ERR_TIMEOUT
 | ||||||
|  | 	"Can't connect", // ERR_CANT_CONNECT
 | ||||||
|  | 	"Can't resolve", // ERR_CANT_RESOLVE
 | ||||||
|  | 	"Connection error", // ERR_CONNECTION_ERROR
 | ||||||
|  | 	"Can't acquire resource", // ERR_CANT_ACQUIRE_RESOURCE
 | ||||||
|  | 	"Can't fork", // ERR_CANT_FORK
 | ||||||
|  | 	"Invalid data", // ERR_INVALID_DATA
 | ||||||
|  | 	"Invalid parameter", // ERR_INVALID_PARAMETER
 | ||||||
|  | 	"Already exists", // ERR_ALREADY_EXISTS
 | ||||||
|  | 	"Does not exist", // ERR_DOES_NOT_EXIST
 | ||||||
|  | 	"Can't read database", // ERR_DATABASE_CANT_READ
 | ||||||
|  | 	"Can't write database", // ERR_DATABASE_CANT_WRITE
 | ||||||
|  | 	"Compilation failed", // ERR_COMPILATION_FAILED
 | ||||||
|  | 	"Method not found", // ERR_METHOD_NOT_FOUND
 | ||||||
|  | 	"Link failed", // ERR_LINK_FAILED
 | ||||||
|  | 	"Script failed", // ERR_SCRIPT_FAILED
 | ||||||
|  | 	"Cyclic link detected", // ERR_CYCLIC_LINK
 | ||||||
|  | 	"Invalid declaration", // ERR_INVALID_DECLARATION
 | ||||||
|  | 	"Duplicate symbol", // ERR_DUPLICATE_SYMBOL
 | ||||||
|  | 	"Parse error", // ERR_PARSE_ERROR
 | ||||||
|  | 	"Busy", // ERR_BUSY
 | ||||||
|  | 	"Skip", // ERR_SKIP
 | ||||||
|  | 	"Help", // ERR_HELP
 | ||||||
|  | 	"Bug", // ERR_BUG
 | ||||||
|  | 	"Printer on fire", // ERR_PRINTER_ON_FIRE
 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static_assert(std::size(error_names) == ERR_MAX); | ||||||
							
								
								
									
										99
									
								
								engine/core/error/error_list.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								engine/core/error/error_list.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,99 @@ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*  error_list.h                                                          */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*                         This file is part of:                          */ | ||||||
|  | /*                             GODOT ENGINE                               */ | ||||||
|  | /*                        https://godotengine.org                         */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ | ||||||
|  | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */ | ||||||
|  | /*                                                                        */ | ||||||
|  | /* 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.                 */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | /** Error List. Please never compare an error against FAILED
 | ||||||
|  |  * Either do result != OK , or !result. This way, Error fail | ||||||
|  |  * values can be more detailed in the future. | ||||||
|  |  * | ||||||
|  |  * This is a generic error list, mainly for organizing a language of returning errors. | ||||||
|  |  * | ||||||
|  |  * Errors: | ||||||
|  |  * - Are added to the Error enum in core/error/error_list.h | ||||||
|  |  * - Have a description added to error_names in core/error/error_list.cpp | ||||||
|  |  * - Are bound with BIND_CORE_ENUM_CONSTANT() in core/core_constants.cpp | ||||||
|  |  * - Have a matching Android version in platform/android/java/lib/src/org/godotengine/godot/error/Error.kt | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | enum Error { | ||||||
|  | 	OK, // (0)
 | ||||||
|  | 	FAILED, ///< Generic fail error
 | ||||||
|  | 	ERR_UNAVAILABLE, ///< What is requested is unsupported/unavailable
 | ||||||
|  | 	ERR_UNCONFIGURED, ///< The object being used hasn't been properly set up yet
 | ||||||
|  | 	ERR_UNAUTHORIZED, ///< Missing credentials for requested resource
 | ||||||
|  | 	ERR_PARAMETER_RANGE_ERROR, ///< Parameter given out of range (5)
 | ||||||
|  | 	ERR_OUT_OF_MEMORY, ///< Out of memory
 | ||||||
|  | 	ERR_FILE_NOT_FOUND, | ||||||
|  | 	ERR_FILE_BAD_DRIVE, | ||||||
|  | 	ERR_FILE_BAD_PATH, | ||||||
|  | 	ERR_FILE_NO_PERMISSION, // (10)
 | ||||||
|  | 	ERR_FILE_ALREADY_IN_USE, | ||||||
|  | 	ERR_FILE_CANT_OPEN, | ||||||
|  | 	ERR_FILE_CANT_WRITE, | ||||||
|  | 	ERR_FILE_CANT_READ, | ||||||
|  | 	ERR_FILE_UNRECOGNIZED, // (15)
 | ||||||
|  | 	ERR_FILE_CORRUPT, | ||||||
|  | 	ERR_FILE_MISSING_DEPENDENCIES, | ||||||
|  | 	ERR_FILE_EOF, | ||||||
|  | 	ERR_CANT_OPEN, ///< Can't open a resource/socket/file
 | ||||||
|  | 	ERR_CANT_CREATE, // (20)
 | ||||||
|  | 	ERR_QUERY_FAILED, | ||||||
|  | 	ERR_ALREADY_IN_USE, | ||||||
|  | 	ERR_LOCKED, ///< resource is locked
 | ||||||
|  | 	ERR_TIMEOUT, | ||||||
|  | 	ERR_CANT_CONNECT, // (25)
 | ||||||
|  | 	ERR_CANT_RESOLVE, | ||||||
|  | 	ERR_CONNECTION_ERROR, | ||||||
|  | 	ERR_CANT_ACQUIRE_RESOURCE, | ||||||
|  | 	ERR_CANT_FORK, | ||||||
|  | 	ERR_INVALID_DATA, ///< Data passed is invalid (30)
 | ||||||
|  | 	ERR_INVALID_PARAMETER, ///< Parameter passed is invalid
 | ||||||
|  | 	ERR_ALREADY_EXISTS, ///< When adding, item already exists
 | ||||||
|  | 	ERR_DOES_NOT_EXIST, ///< When retrieving/erasing, if item does not exist
 | ||||||
|  | 	ERR_DATABASE_CANT_READ, ///< database is full
 | ||||||
|  | 	ERR_DATABASE_CANT_WRITE, ///< database is full (35)
 | ||||||
|  | 	ERR_COMPILATION_FAILED, | ||||||
|  | 	ERR_METHOD_NOT_FOUND, | ||||||
|  | 	ERR_LINK_FAILED, | ||||||
|  | 	ERR_SCRIPT_FAILED, | ||||||
|  | 	ERR_CYCLIC_LINK, // (40)
 | ||||||
|  | 	ERR_INVALID_DECLARATION, | ||||||
|  | 	ERR_DUPLICATE_SYMBOL, | ||||||
|  | 	ERR_PARSE_ERROR, | ||||||
|  | 	ERR_BUSY, | ||||||
|  | 	ERR_SKIP, // (45)
 | ||||||
|  | 	ERR_HELP, ///< user requested help!!
 | ||||||
|  | 	ERR_BUG, ///< a bug in the software certainly happened, due to a double check failing or unexpected behavior.
 | ||||||
|  | 	ERR_PRINTER_ON_FIRE, /// the parallel port printer is engulfed in flames
 | ||||||
|  | 	ERR_MAX, // Not being returned, value represents the number of errors
 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | extern const char *error_names[]; | ||||||
							
								
								
									
										204
									
								
								engine/core/error/error_macros.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										204
									
								
								engine/core/error/error_macros.cpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,204 @@ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*  error_macros.cpp                                                      */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*                         This file is part of:                          */ | ||||||
|  | /*                             GODOT ENGINE                               */ | ||||||
|  | /*                        https://godotengine.org                         */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ | ||||||
|  | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */ | ||||||
|  | /*                                                                        */ | ||||||
|  | /* 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 "error_macros.h" | ||||||
|  | 
 | ||||||
|  | #include "core/io/logger.h" | ||||||
|  | #include "core/object/object_id.h" | ||||||
|  | #include "core/os/os.h" | ||||||
|  | #include "core/string/ustring.h" | ||||||
|  | 
 | ||||||
|  | // Optional physics interpolation warnings try to include the path to the relevant node.
 | ||||||
|  | #if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED) | ||||||
|  | #include "core/config/project_settings.h" | ||||||
|  | #include "scene/main/node.h" | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | static ErrorHandlerList *error_handler_list = nullptr; | ||||||
|  | 
 | ||||||
|  | void add_error_handler(ErrorHandlerList *p_handler) { | ||||||
|  | 	// If p_handler is already in error_handler_list
 | ||||||
|  | 	// we'd better remove it first then we can add it.
 | ||||||
|  | 	// This prevent cyclic redundancy.
 | ||||||
|  | 	remove_error_handler(p_handler); | ||||||
|  | 
 | ||||||
|  | 	_global_lock(); | ||||||
|  | 
 | ||||||
|  | 	p_handler->next = error_handler_list; | ||||||
|  | 	error_handler_list = p_handler; | ||||||
|  | 
 | ||||||
|  | 	_global_unlock(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void remove_error_handler(const ErrorHandlerList *p_handler) { | ||||||
|  | 	_global_lock(); | ||||||
|  | 
 | ||||||
|  | 	ErrorHandlerList *prev = nullptr; | ||||||
|  | 	ErrorHandlerList *l = error_handler_list; | ||||||
|  | 
 | ||||||
|  | 	while (l) { | ||||||
|  | 		if (l == p_handler) { | ||||||
|  | 			if (prev) { | ||||||
|  | 				prev->next = l->next; | ||||||
|  | 			} else { | ||||||
|  | 				error_handler_list = l->next; | ||||||
|  | 			} | ||||||
|  | 			break; | ||||||
|  | 		} | ||||||
|  | 		prev = l; | ||||||
|  | 		l = l->next; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	_global_unlock(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Errors without messages.
 | ||||||
|  | void _err_print_error(const char *p_function, const char *p_file, int p_line, const char *p_error, bool p_editor_notify, ErrorHandlerType p_type) { | ||||||
|  | 	_err_print_error(p_function, p_file, p_line, p_error, "", p_editor_notify, p_type); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, bool p_editor_notify, ErrorHandlerType p_type) { | ||||||
|  | 	_err_print_error(p_function, p_file, p_line, p_error.utf8().get_data(), "", p_editor_notify, p_type); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Main error printing function.
 | ||||||
|  | void _err_print_error(const char *p_function, const char *p_file, int p_line, const char *p_error, const char *p_message, bool p_editor_notify, ErrorHandlerType p_type) { | ||||||
|  | 	if (OS::get_singleton()) { | ||||||
|  | 		OS::get_singleton()->print_error(p_function, p_file, p_line, p_error, p_message, p_editor_notify, (Logger::ErrorType)p_type); | ||||||
|  | 	} else { | ||||||
|  | 		// Fallback if errors happen before OS init or after it's destroyed.
 | ||||||
|  | 		const char *err_details = (p_message && *p_message) ? p_message : p_error; | ||||||
|  | 		fprintf(stderr, "ERROR: %s\n   at: %s (%s:%i)\n", err_details, p_function, p_file, p_line); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	_global_lock(); | ||||||
|  | 	ErrorHandlerList *l = error_handler_list; | ||||||
|  | 	while (l) { | ||||||
|  | 		l->errfunc(l->userdata, p_function, p_file, p_line, p_error, p_message, p_editor_notify, p_type); | ||||||
|  | 		l = l->next; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	_global_unlock(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // For printing errors when we may crash at any point, so we must flush ASAP a lot of lines
 | ||||||
|  | // but we don't want to make it noisy by printing lots of file & line info (because it's already
 | ||||||
|  | // been printing by a preceding _err_print_error).
 | ||||||
|  | void _err_print_error_asap(const String &p_error, ErrorHandlerType p_type) { | ||||||
|  | 	if (OS::get_singleton()) { | ||||||
|  | 		OS::get_singleton()->printerr("ERROR: %s\n", p_error.utf8().get_data()); | ||||||
|  | 	} else { | ||||||
|  | 		// Fallback if errors happen before OS init or after it's destroyed.
 | ||||||
|  | 		const char *err_details = p_error.utf8().get_data(); | ||||||
|  | 		fprintf(stderr, "ERROR: %s\n", err_details); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	_global_lock(); | ||||||
|  | 	ErrorHandlerList *l = error_handler_list; | ||||||
|  | 	while (l) { | ||||||
|  | 		l->errfunc(l->userdata, "", "", 0, p_error.utf8().get_data(), "", false, p_type); | ||||||
|  | 		l = l->next; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	_global_unlock(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Errors with message. (All combinations of p_error and p_message as String or char*.)
 | ||||||
|  | void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, const char *p_message, bool p_editor_notify, ErrorHandlerType p_type) { | ||||||
|  | 	_err_print_error(p_function, p_file, p_line, p_error.utf8().get_data(), p_message, p_editor_notify, p_type); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void _err_print_error(const char *p_function, const char *p_file, int p_line, const char *p_error, const String &p_message, bool p_editor_notify, ErrorHandlerType p_type) { | ||||||
|  | 	_err_print_error(p_function, p_file, p_line, p_error, p_message.utf8().get_data(), p_editor_notify, p_type); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, const String &p_message, bool p_editor_notify, ErrorHandlerType p_type) { | ||||||
|  | 	_err_print_error(p_function, p_file, p_line, p_error.utf8().get_data(), p_message.utf8().get_data(), p_editor_notify, p_type); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Index errors. (All combinations of p_message as String or char*.)
 | ||||||
|  | void _err_print_index_error(const char *p_function, const char *p_file, int p_line, int64_t p_index, int64_t p_size, const char *p_index_str, const char *p_size_str, const char *p_message, bool p_editor_notify, bool p_fatal) { | ||||||
|  | 	String fstr(p_fatal ? "FATAL: " : ""); | ||||||
|  | 	String err(fstr + "Index " + p_index_str + " = " + itos(p_index) + " is out of bounds (" + p_size_str + " = " + itos(p_size) + ")."); | ||||||
|  | 	_err_print_error(p_function, p_file, p_line, err.utf8().get_data(), p_message, p_editor_notify, ERR_HANDLER_ERROR); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void _err_print_index_error(const char *p_function, const char *p_file, int p_line, int64_t p_index, int64_t p_size, const char *p_index_str, const char *p_size_str, const String &p_message, bool p_editor_notify, bool p_fatal) { | ||||||
|  | 	_err_print_index_error(p_function, p_file, p_line, p_index, p_size, p_index_str, p_size_str, p_message.utf8().get_data(), p_editor_notify, p_fatal); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void _err_flush_stdout() { | ||||||
|  | 	fflush(stdout); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Prevent error spam by limiting the warnings to a certain frequency.
 | ||||||
|  | void _physics_interpolation_warning(const char *p_function, const char *p_file, int p_line, ObjectID p_id, const char *p_warn_string) { | ||||||
|  | #if defined(DEBUG_ENABLED) && defined(TOOLS_ENABLED) | ||||||
|  | 	const uint32_t warn_max = 2048; | ||||||
|  | 	const uint32_t warn_timeout_seconds = 15; | ||||||
|  | 
 | ||||||
|  | 	static uint32_t warn_count = warn_max; | ||||||
|  | 	static uint32_t warn_timeout = warn_timeout_seconds; | ||||||
|  | 
 | ||||||
|  | 	uint32_t time_now = UINT32_MAX; | ||||||
|  | 
 | ||||||
|  | 	if (warn_count) { | ||||||
|  | 		warn_count--; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (!warn_count) { | ||||||
|  | 		time_now = OS::get_singleton()->get_ticks_msec() / 1000; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if ((warn_count == 0) && (time_now >= warn_timeout)) { | ||||||
|  | 		warn_count = warn_max; | ||||||
|  | 		warn_timeout = time_now + warn_timeout_seconds; | ||||||
|  | 
 | ||||||
|  | 		if (GLOBAL_GET("debug/settings/physics_interpolation/enable_warnings")) { | ||||||
|  | 			// UINT64_MAX means unused.
 | ||||||
|  | 			if (p_id.operator uint64_t() == UINT64_MAX) { | ||||||
|  | 				_err_print_error(p_function, p_file, p_line, "[Physics interpolation] " + String(p_warn_string) + " (possibly benign).", false, ERR_HANDLER_WARNING); | ||||||
|  | 			} else { | ||||||
|  | 				String node_name; | ||||||
|  | 				if (p_id.is_valid()) { | ||||||
|  | 					Node *node = ObjectDB::get_instance<Node>(p_id); | ||||||
|  | 					if (node && node->is_inside_tree()) { | ||||||
|  | 						node_name = "\"" + String(node->get_path()) + "\""; | ||||||
|  | 					} else { | ||||||
|  | 						node_name = "\"unknown\""; | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				_err_print_error(p_function, p_file, p_line, "[Physics interpolation] " + String(p_warn_string) + ": " + node_name + " (possibly benign).", false, ERR_HANDLER_WARNING); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | #endif | ||||||
|  | } | ||||||
							
								
								
									
										846
									
								
								engine/core/error/error_macros.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										846
									
								
								engine/core/error/error_macros.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,846 @@ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*  error_macros.h                                                        */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*                         This file is part of:                          */ | ||||||
|  | /*                             GODOT ENGINE                               */ | ||||||
|  | /*                        https://godotengine.org                         */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ | ||||||
|  | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */ | ||||||
|  | /*                                                                        */ | ||||||
|  | /* 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.                 */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include "core/typedefs.h" | ||||||
|  | 
 | ||||||
|  | #include <atomic> // IWYU pragma: keep // Used in macro. We'd normally use `safe_refcount.h`, but that would cause circular includes.
 | ||||||
|  | 
 | ||||||
|  | class String; | ||||||
|  | class ObjectID; | ||||||
|  | 
 | ||||||
|  | enum ErrorHandlerType { | ||||||
|  | 	ERR_HANDLER_ERROR, | ||||||
|  | 	ERR_HANDLER_WARNING, | ||||||
|  | 	ERR_HANDLER_SCRIPT, | ||||||
|  | 	ERR_HANDLER_SHADER, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // Pointer to the error handler printing function. Reassign to any function to have errors printed.
 | ||||||
|  | // Parameters: userdata, function, file, line, error, explanation, type.
 | ||||||
|  | typedef void (*ErrorHandlerFunc)(void *, const char *, const char *, int p_line, const char *, const char *, bool p_editor_notify, ErrorHandlerType p_type); | ||||||
|  | 
 | ||||||
|  | struct ErrorHandlerList { | ||||||
|  | 	ErrorHandlerFunc errfunc = nullptr; | ||||||
|  | 	void *userdata = nullptr; | ||||||
|  | 
 | ||||||
|  | 	ErrorHandlerList *next = nullptr; | ||||||
|  | 
 | ||||||
|  | 	ErrorHandlerList() {} | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | void add_error_handler(ErrorHandlerList *p_handler); | ||||||
|  | void remove_error_handler(const ErrorHandlerList *p_handler); | ||||||
|  | 
 | ||||||
|  | // Functions used by the error macros.
 | ||||||
|  | _NO_INLINE_ void _err_print_error(const char *p_function, const char *p_file, int p_line, const char *p_error, bool p_editor_notify = false, ErrorHandlerType p_type = ERR_HANDLER_ERROR); | ||||||
|  | _NO_INLINE_ void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, bool p_editor_notify = false, ErrorHandlerType p_type = ERR_HANDLER_ERROR); | ||||||
|  | _NO_INLINE_ void _err_print_error(const char *p_function, const char *p_file, int p_line, const char *p_error, const char *p_message, bool p_editor_notify = false, ErrorHandlerType p_type = ERR_HANDLER_ERROR); | ||||||
|  | _NO_INLINE_ void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, const char *p_message, bool p_editor_notify = false, ErrorHandlerType p_type = ERR_HANDLER_ERROR); | ||||||
|  | _NO_INLINE_ void _err_print_error(const char *p_function, const char *p_file, int p_line, const char *p_error, const String &p_message, bool p_editor_notify = false, ErrorHandlerType p_type = ERR_HANDLER_ERROR); | ||||||
|  | _NO_INLINE_ void _err_print_error(const char *p_function, const char *p_file, int p_line, const String &p_error, const String &p_message, bool p_editor_notify = false, ErrorHandlerType p_type = ERR_HANDLER_ERROR); | ||||||
|  | void _err_print_error_asap(const String &p_error, ErrorHandlerType p_type = ERR_HANDLER_ERROR); | ||||||
|  | _NO_INLINE_ void _err_print_index_error(const char *p_function, const char *p_file, int p_line, int64_t p_index, int64_t p_size, const char *p_index_str, const char *p_size_str, const char *p_message = "", bool p_editor_notify = false, bool fatal = false); | ||||||
|  | _NO_INLINE_ void _err_print_index_error(const char *p_function, const char *p_file, int p_line, int64_t p_index, int64_t p_size, const char *p_index_str, const char *p_size_str, const String &p_message, bool p_editor_notify = false, bool fatal = false); | ||||||
|  | _NO_INLINE_ void _err_flush_stdout(); | ||||||
|  | 
 | ||||||
|  | void _physics_interpolation_warning(const char *p_function, const char *p_file, int p_line, ObjectID p_id, const char *p_warn_string); | ||||||
|  | 
 | ||||||
|  | #ifdef __GNUC__ | ||||||
|  | //#define FUNCTION_STR __PRETTY_FUNCTION__ - too annoying
 | ||||||
|  | #define FUNCTION_STR __FUNCTION__ | ||||||
|  | #else | ||||||
|  | #define FUNCTION_STR __FUNCTION__ | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #ifdef _MSC_VER | ||||||
|  | /**
 | ||||||
|  |  * Don't use GENERATE_TRAP() directly, should only be used be the macros below. | ||||||
|  |  */ | ||||||
|  | #define GENERATE_TRAP() __debugbreak() | ||||||
|  | #else | ||||||
|  | /**
 | ||||||
|  |  * Don't use GENERATE_TRAP() directly, should only be used be the macros below. | ||||||
|  |  */ | ||||||
|  | #define GENERATE_TRAP() __builtin_trap() | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Error macros. | ||||||
|  |  * WARNING: These macros work in the opposite way to assert(). | ||||||
|  |  * | ||||||
|  |  * Unlike exceptions and asserts, these macros try to maintain consistency and stability. | ||||||
|  |  * In most cases, bugs and/or invalid data are not fatal. They should never allow a perfectly | ||||||
|  |  * running application to fail or crash. | ||||||
|  |  * Always try to return processable data, so the engine can keep running well. | ||||||
|  |  * Use the _MSG versions to print a meaningful message to help with debugging. | ||||||
|  |  * | ||||||
|  |  * The `((void)0)` no-op statement is used as a trick to force us to put a semicolon after | ||||||
|  |  * those macros, making them look like proper statements. | ||||||
|  |  * The if wrappers are used to ensure that the macro replacement does not trigger unexpected | ||||||
|  |  * issues when expanded e.g. after an `if (cond) ERR_FAIL();` without braces. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | // Index out of bounds error macros.
 | ||||||
|  | // These macros should be used instead of `ERR_FAIL_COND` for bounds checking.
 | ||||||
|  | 
 | ||||||
|  | // Integer index out of bounds error macros.
 | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Try using `ERR_FAIL_INDEX_MSG`. | ||||||
|  |  * Only use this macro if there is no sensible error message. | ||||||
|  |  * | ||||||
|  |  * Ensures an integer index `m_index` is less than `m_size` and greater than or equal to 0. | ||||||
|  |  * If not, the current function returns. | ||||||
|  |  */ | ||||||
|  | #define ERR_FAIL_INDEX(m_index, m_size)                                                                         \ | ||||||
|  | 	if (unlikely((m_index) < 0 || (m_index) >= (m_size))) {                                                     \ | ||||||
|  | 		_err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size)); \ | ||||||
|  | 		return;                                                                                                 \ | ||||||
|  | 	} else                                                                                                      \ | ||||||
|  | 		((void)0) | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Ensures an integer index `m_index` is less than `m_size` and greater than or equal to 0. | ||||||
|  |  * If not, prints `m_msg` and the current function returns. | ||||||
|  |  */ | ||||||
|  | #define ERR_FAIL_INDEX_MSG(m_index, m_size, m_msg)                                                                     \ | ||||||
|  | 	if (unlikely((m_index) < 0 || (m_index) >= (m_size))) {                                                            \ | ||||||
|  | 		_err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg); \ | ||||||
|  | 		return;                                                                                                        \ | ||||||
|  | 	} else                                                                                                             \ | ||||||
|  | 		((void)0) | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Same as `ERR_FAIL_INDEX_MSG` but also notifies the editor. | ||||||
|  |  */ | ||||||
|  | #define ERR_FAIL_INDEX_EDMSG(m_index, m_size, m_msg)                                                                         \ | ||||||
|  | 	if (unlikely((m_index) < 0 || (m_index) >= (m_size))) {                                                                  \ | ||||||
|  | 		_err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg, true); \ | ||||||
|  | 		return;                                                                                                              \ | ||||||
|  | 	} else                                                                                                                   \ | ||||||
|  | 		((void)0) | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Try using `ERR_FAIL_INDEX_V_MSG`. | ||||||
|  |  * Only use this macro if there is no sensible error message. | ||||||
|  |  * | ||||||
|  |  * Ensures an integer index `m_index` is less than `m_size` and greater than or equal to 0. | ||||||
|  |  * If not, the current function returns `m_retval`. | ||||||
|  |  */ | ||||||
|  | #define ERR_FAIL_INDEX_V(m_index, m_size, m_retval)                                                             \ | ||||||
|  | 	if (unlikely((m_index) < 0 || (m_index) >= (m_size))) {                                                     \ | ||||||
|  | 		_err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size)); \ | ||||||
|  | 		return m_retval;                                                                                        \ | ||||||
|  | 	} else                                                                                                      \ | ||||||
|  | 		((void)0) | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Ensures an integer index `m_index` is less than `m_size` and greater than or equal to 0. | ||||||
|  |  * If not, prints `m_msg` and the current function returns `m_retval`. | ||||||
|  |  */ | ||||||
|  | #define ERR_FAIL_INDEX_V_MSG(m_index, m_size, m_retval, m_msg)                                                         \ | ||||||
|  | 	if (unlikely((m_index) < 0 || (m_index) >= (m_size))) {                                                            \ | ||||||
|  | 		_err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg); \ | ||||||
|  | 		return m_retval;                                                                                               \ | ||||||
|  | 	} else                                                                                                             \ | ||||||
|  | 		((void)0) | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Same as `ERR_FAIL_INDEX_V_MSG` but also notifies the editor. | ||||||
|  |  */ | ||||||
|  | #define ERR_FAIL_INDEX_V_EDMSG(m_index, m_size, m_retval, m_msg)                                                             \ | ||||||
|  | 	if (unlikely((m_index) < 0 || (m_index) >= (m_size))) {                                                                  \ | ||||||
|  | 		_err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg, true); \ | ||||||
|  | 		return m_retval;                                                                                                     \ | ||||||
|  | 	} else                                                                                                                   \ | ||||||
|  | 		((void)0) | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Try using `ERR_FAIL_INDEX_MSG` or `ERR_FAIL_INDEX_V_MSG`. | ||||||
|  |  * Only use this macro if there is no sensible fallback i.e. the error is unrecoverable, and | ||||||
|  |  * there is no sensible error message. | ||||||
|  |  * | ||||||
|  |  * Ensures an integer index `m_index` is less than `m_size` and greater than or equal to 0. | ||||||
|  |  * If not, the application crashes. | ||||||
|  |  */ | ||||||
|  | #define CRASH_BAD_INDEX(m_index, m_size)                                                                                         \ | ||||||
|  | 	if (unlikely((m_index) < 0 || (m_index) >= (m_size))) {                                                                      \ | ||||||
|  | 		_err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), "", false, true); \ | ||||||
|  | 		_err_flush_stdout();                                                                                                     \ | ||||||
|  | 		GENERATE_TRAP();                                                                                                         \ | ||||||
|  | 	} else                                                                                                                       \ | ||||||
|  | 		((void)0) | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Try using `ERR_FAIL_INDEX_MSG` or `ERR_FAIL_INDEX_V_MSG`. | ||||||
|  |  * Only use this macro if there is no sensible fallback i.e. the error is unrecoverable. | ||||||
|  |  * | ||||||
|  |  * Ensures an integer index `m_index` is less than `m_size` and greater than or equal to 0. | ||||||
|  |  * If not, prints `m_msg` and the application crashes. | ||||||
|  |  */ | ||||||
|  | #define CRASH_BAD_INDEX_MSG(m_index, m_size, m_msg)                                                                                 \ | ||||||
|  | 	if (unlikely((m_index) < 0 || (m_index) >= (m_size))) {                                                                         \ | ||||||
|  | 		_err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg, false, true); \ | ||||||
|  | 		_err_flush_stdout();                                                                                                        \ | ||||||
|  | 		GENERATE_TRAP();                                                                                                            \ | ||||||
|  | 	} else                                                                                                                          \ | ||||||
|  | 		((void)0) | ||||||
|  | 
 | ||||||
|  | // Unsigned integer index out of bounds error macros.
 | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Try using `ERR_FAIL_UNSIGNED_INDEX_MSG`. | ||||||
|  |  * Only use this macro if there is no sensible error message. | ||||||
|  |  * | ||||||
|  |  * Ensures an unsigned integer index `m_index` is less than `m_size`. | ||||||
|  |  * If not, the current function returns. | ||||||
|  |  */ | ||||||
|  | #define ERR_FAIL_UNSIGNED_INDEX(m_index, m_size)                                                                \ | ||||||
|  | 	if (unlikely((m_index) >= (m_size))) {                                                                      \ | ||||||
|  | 		_err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size)); \ | ||||||
|  | 		return;                                                                                                 \ | ||||||
|  | 	} else                                                                                                      \ | ||||||
|  | 		((void)0) | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Ensures an unsigned integer index `m_index` is less than `m_size`. | ||||||
|  |  * If not, prints `m_msg` and the current function returns. | ||||||
|  |  */ | ||||||
|  | #define ERR_FAIL_UNSIGNED_INDEX_MSG(m_index, m_size, m_msg)                                                            \ | ||||||
|  | 	if (unlikely((m_index) >= (m_size))) {                                                                             \ | ||||||
|  | 		_err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg); \ | ||||||
|  | 		return;                                                                                                        \ | ||||||
|  | 	} else                                                                                                             \ | ||||||
|  | 		((void)0) | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Same as `ERR_FAIL_UNSIGNED_INDEX_MSG` but also notifies the editor. | ||||||
|  |  */ | ||||||
|  | #define ERR_FAIL_UNSIGNED_INDEX_EDMSG(m_index, m_size, m_msg)                                                                \ | ||||||
|  | 	if (unlikely((m_index) >= (m_size))) {                                                                                   \ | ||||||
|  | 		_err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg, true); \ | ||||||
|  | 		return;                                                                                                              \ | ||||||
|  | 	} else                                                                                                                   \ | ||||||
|  | 		((void)0) | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Try using `ERR_FAIL_UNSIGNED_INDEX_V_MSG`. | ||||||
|  |  * Only use this macro if there is no sensible error message. | ||||||
|  |  * | ||||||
|  |  * Ensures an unsigned integer index `m_index` is less than `m_size`. | ||||||
|  |  * If not, the current function returns `m_retval`. | ||||||
|  |  */ | ||||||
|  | #define ERR_FAIL_UNSIGNED_INDEX_V(m_index, m_size, m_retval)                                                    \ | ||||||
|  | 	if (unlikely((m_index) >= (m_size))) {                                                                      \ | ||||||
|  | 		_err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size)); \ | ||||||
|  | 		return m_retval;                                                                                        \ | ||||||
|  | 	} else                                                                                                      \ | ||||||
|  | 		((void)0) | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Ensures an unsigned integer index `m_index` is less than `m_size`. | ||||||
|  |  * If not, prints `m_msg` and the current function returns `m_retval`. | ||||||
|  |  */ | ||||||
|  | #define ERR_FAIL_UNSIGNED_INDEX_V_MSG(m_index, m_size, m_retval, m_msg)                                                \ | ||||||
|  | 	if (unlikely((m_index) >= (m_size))) {                                                                             \ | ||||||
|  | 		_err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg); \ | ||||||
|  | 		return m_retval;                                                                                               \ | ||||||
|  | 	} else                                                                                                             \ | ||||||
|  | 		((void)0) | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Same as `ERR_FAIL_UNSIGNED_INDEX_V_EDMSG` but also notifies the editor. | ||||||
|  |  */ | ||||||
|  | #define ERR_FAIL_UNSIGNED_INDEX_V_EDMSG(m_index, m_size, m_retval, m_msg)                                                    \ | ||||||
|  | 	if (unlikely((m_index) >= (m_size))) {                                                                                   \ | ||||||
|  | 		_err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg, true); \ | ||||||
|  | 		return m_retval;                                                                                                     \ | ||||||
|  | 	} else                                                                                                                   \ | ||||||
|  | 		((void)0) | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Try using `ERR_FAIL_UNSIGNED_INDEX_MSG` or `ERR_FAIL_UNSIGNED_INDEX_V_MSG`. | ||||||
|  |  * Only use this macro if there is no sensible fallback i.e. the error is unrecoverable, and | ||||||
|  |  * there is no sensible error message. | ||||||
|  |  * | ||||||
|  |  * Ensures an unsigned integer index `m_index` is less than `m_size`. | ||||||
|  |  * If not, the application crashes. | ||||||
|  |  */ | ||||||
|  | #define CRASH_BAD_UNSIGNED_INDEX(m_index, m_size)                                                                                \ | ||||||
|  | 	if (unlikely((m_index) >= (m_size))) {                                                                                       \ | ||||||
|  | 		_err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), "", false, true); \ | ||||||
|  | 		_err_flush_stdout();                                                                                                     \ | ||||||
|  | 		GENERATE_TRAP();                                                                                                         \ | ||||||
|  | 	} else                                                                                                                       \ | ||||||
|  | 		((void)0) | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Try using `ERR_FAIL_UNSIGNED_INDEX_MSG` or `ERR_FAIL_UNSIGNED_INDEX_V_MSG`. | ||||||
|  |  * Only use this macro if there is no sensible fallback i.e. the error is unrecoverable. | ||||||
|  |  * | ||||||
|  |  * Ensures an unsigned integer index `m_index` is less than `m_size`. | ||||||
|  |  * If not, prints `m_msg` and the application crashes. | ||||||
|  |  */ | ||||||
|  | #define CRASH_BAD_UNSIGNED_INDEX_MSG(m_index, m_size, m_msg)                                                                        \ | ||||||
|  | 	if (unlikely((m_index) >= (m_size))) {                                                                                          \ | ||||||
|  | 		_err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg, false, true); \ | ||||||
|  | 		_err_flush_stdout();                                                                                                        \ | ||||||
|  | 		GENERATE_TRAP();                                                                                                            \ | ||||||
|  | 	} else                                                                                                                          \ | ||||||
|  | 		((void)0) | ||||||
|  | 
 | ||||||
|  | // Null reference error macros.
 | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Try using `ERR_FAIL_NULL_MSG`. | ||||||
|  |  * Only use this macro if there is no sensible error message. | ||||||
|  |  * | ||||||
|  |  * Ensures a pointer `m_param` is not null. | ||||||
|  |  * If it is null, the current function returns. | ||||||
|  |  */ | ||||||
|  | #define ERR_FAIL_NULL(m_param)                                                                          \ | ||||||
|  | 	if (unlikely(m_param == nullptr)) {                                                                 \ | ||||||
|  | 		_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null."); \ | ||||||
|  | 		return;                                                                                         \ | ||||||
|  | 	} else                                                                                              \ | ||||||
|  | 		((void)0) | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Ensures a pointer `m_param` is not null. | ||||||
|  |  * If it is null, prints `m_msg` and the current function returns. | ||||||
|  |  */ | ||||||
|  | #define ERR_FAIL_NULL_MSG(m_param, m_msg)                                                                      \ | ||||||
|  | 	if (unlikely(m_param == nullptr)) {                                                                        \ | ||||||
|  | 		_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null.", m_msg); \ | ||||||
|  | 		return;                                                                                                \ | ||||||
|  | 	} else                                                                                                     \ | ||||||
|  | 		((void)0) | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Same as `ERR_FAIL_NULL_MSG` but also notifies the editor. | ||||||
|  |  */ | ||||||
|  | #define ERR_FAIL_NULL_EDMSG(m_param, m_msg)                                                                          \ | ||||||
|  | 	if (unlikely(m_param == nullptr)) {                                                                              \ | ||||||
|  | 		_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null.", m_msg, true); \ | ||||||
|  | 		return;                                                                                                      \ | ||||||
|  | 	} else                                                                                                           \ | ||||||
|  | 		((void)0) | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Try using `ERR_FAIL_NULL_V_MSG`. | ||||||
|  |  * Only use this macro if there is no sensible error message. | ||||||
|  |  * | ||||||
|  |  * Ensures a pointer `m_param` is not null. | ||||||
|  |  * If it is null, the current function returns `m_retval`. | ||||||
|  |  */ | ||||||
|  | #define ERR_FAIL_NULL_V(m_param, m_retval)                                                              \ | ||||||
|  | 	if (unlikely(m_param == nullptr)) {                                                                 \ | ||||||
|  | 		_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null."); \ | ||||||
|  | 		return m_retval;                                                                                \ | ||||||
|  | 	} else                                                                                              \ | ||||||
|  | 		((void)0) | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Ensures a pointer `m_param` is not null. | ||||||
|  |  * If it is null, prints `m_msg` and the current function returns `m_retval`. | ||||||
|  |  */ | ||||||
|  | #define ERR_FAIL_NULL_V_MSG(m_param, m_retval, m_msg)                                                          \ | ||||||
|  | 	if (unlikely(m_param == nullptr)) {                                                                        \ | ||||||
|  | 		_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null.", m_msg); \ | ||||||
|  | 		return m_retval;                                                                                       \ | ||||||
|  | 	} else                                                                                                     \ | ||||||
|  | 		((void)0) | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Same as `ERR_FAIL_NULL_V_MSG` but also notifies the editor. | ||||||
|  |  */ | ||||||
|  | #define ERR_FAIL_NULL_V_EDMSG(m_param, m_retval, m_msg)                                                              \ | ||||||
|  | 	if (unlikely(m_param == nullptr)) {                                                                              \ | ||||||
|  | 		_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null.", m_msg, true); \ | ||||||
|  | 		return m_retval;                                                                                             \ | ||||||
|  | 	} else                                                                                                           \ | ||||||
|  | 		((void)0) | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Try using `ERR_FAIL_COND_MSG`. | ||||||
|  |  * Only use this macro if there is no sensible error message. | ||||||
|  |  * If checking for null use ERR_FAIL_NULL_MSG instead. | ||||||
|  |  * If checking index bounds use ERR_FAIL_INDEX_MSG instead. | ||||||
|  |  * | ||||||
|  |  * Ensures `m_cond` is false. | ||||||
|  |  * If `m_cond` is true, the current function returns. | ||||||
|  |  */ | ||||||
|  | #define ERR_FAIL_COND(m_cond)                                                                          \ | ||||||
|  | 	if (unlikely(m_cond)) {                                                                            \ | ||||||
|  | 		_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true."); \ | ||||||
|  | 		return;                                                                                        \ | ||||||
|  | 	} else                                                                                             \ | ||||||
|  | 		((void)0) | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Ensures `m_cond` is false. | ||||||
|  |  * If `m_cond` is true, prints `m_msg` and the current function returns. | ||||||
|  |  * | ||||||
|  |  * If checking for null use ERR_FAIL_NULL_MSG instead. | ||||||
|  |  * If checking index bounds use ERR_FAIL_INDEX_MSG instead. | ||||||
|  |  */ | ||||||
|  | #define ERR_FAIL_COND_MSG(m_cond, m_msg)                                                                      \ | ||||||
|  | 	if (unlikely(m_cond)) {                                                                                   \ | ||||||
|  | 		_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true.", m_msg); \ | ||||||
|  | 		return;                                                                                               \ | ||||||
|  | 	} else                                                                                                    \ | ||||||
|  | 		((void)0) | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Same as `ERR_FAIL_COND_MSG` but also notifies the editor. | ||||||
|  |  */ | ||||||
|  | #define ERR_FAIL_COND_EDMSG(m_cond, m_msg)                                                                          \ | ||||||
|  | 	if (unlikely(m_cond)) {                                                                                         \ | ||||||
|  | 		_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true.", m_msg, true); \ | ||||||
|  | 		return;                                                                                                     \ | ||||||
|  | 	} else                                                                                                          \ | ||||||
|  | 		((void)0) | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Try using `ERR_FAIL_COND_V_MSG`. | ||||||
|  |  * Only use this macro if there is no sensible error message. | ||||||
|  |  * If checking for null use ERR_FAIL_NULL_V_MSG instead. | ||||||
|  |  * If checking index bounds use ERR_FAIL_INDEX_V_MSG instead. | ||||||
|  |  * | ||||||
|  |  * Ensures `m_cond` is false. | ||||||
|  |  * If `m_cond` is true, the current function returns `m_retval`. | ||||||
|  |  */ | ||||||
|  | #define ERR_FAIL_COND_V(m_cond, m_retval)                                                                                         \ | ||||||
|  | 	if (unlikely(m_cond)) {                                                                                                       \ | ||||||
|  | 		_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Returning: " _STR(m_retval)); \ | ||||||
|  | 		return m_retval;                                                                                                          \ | ||||||
|  | 	} else                                                                                                                        \ | ||||||
|  | 		((void)0) | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Ensures `m_cond` is false. | ||||||
|  |  * If `m_cond` is true, prints `m_msg` and the current function returns `m_retval`. | ||||||
|  |  * | ||||||
|  |  * If checking for null use ERR_FAIL_NULL_V_MSG instead. | ||||||
|  |  * If checking index bounds use ERR_FAIL_INDEX_V_MSG instead. | ||||||
|  |  */ | ||||||
|  | #define ERR_FAIL_COND_V_MSG(m_cond, m_retval, m_msg)                                                                                     \ | ||||||
|  | 	if (unlikely(m_cond)) {                                                                                                              \ | ||||||
|  | 		_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Returning: " _STR(m_retval), m_msg); \ | ||||||
|  | 		return m_retval;                                                                                                                 \ | ||||||
|  | 	} else                                                                                                                               \ | ||||||
|  | 		((void)0) | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Same as `ERR_FAIL_COND_V_MSG` but also notifies the editor. | ||||||
|  |  */ | ||||||
|  | #define ERR_FAIL_COND_V_EDMSG(m_cond, m_retval, m_msg)                                                                                         \ | ||||||
|  | 	if (unlikely(m_cond)) {                                                                                                                    \ | ||||||
|  | 		_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Returning: " _STR(m_retval), m_msg, true); \ | ||||||
|  | 		return m_retval;                                                                                                                       \ | ||||||
|  | 	} else                                                                                                                                     \ | ||||||
|  | 		((void)0) | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Try using `ERR_CONTINUE_MSG`. | ||||||
|  |  * Only use this macro if there is no sensible error message. | ||||||
|  |  * | ||||||
|  |  * Ensures `m_cond` is false. | ||||||
|  |  * If `m_cond` is true, the current loop continues. | ||||||
|  |  */ | ||||||
|  | #define ERR_CONTINUE(m_cond)                                                                                       \ | ||||||
|  | 	if (unlikely(m_cond)) {                                                                                        \ | ||||||
|  | 		_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Continuing."); \ | ||||||
|  | 		continue;                                                                                                  \ | ||||||
|  | 	} else                                                                                                         \ | ||||||
|  | 		((void)0) | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Ensures `m_cond` is false. | ||||||
|  |  * If `m_cond` is true, prints `m_msg` and the current loop continues. | ||||||
|  |  */ | ||||||
|  | #define ERR_CONTINUE_MSG(m_cond, m_msg)                                                                                   \ | ||||||
|  | 	if (unlikely(m_cond)) {                                                                                               \ | ||||||
|  | 		_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Continuing.", m_msg); \ | ||||||
|  | 		continue;                                                                                                         \ | ||||||
|  | 	} else                                                                                                                \ | ||||||
|  | 		((void)0) | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Same as `ERR_CONTINUE_MSG` but also notifies the editor. | ||||||
|  |  */ | ||||||
|  | #define ERR_CONTINUE_EDMSG(m_cond, m_msg)                                                                                       \ | ||||||
|  | 	if (unlikely(m_cond)) {                                                                                                     \ | ||||||
|  | 		_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Continuing.", m_msg, true); \ | ||||||
|  | 		continue;                                                                                                               \ | ||||||
|  | 	} else                                                                                                                      \ | ||||||
|  | 		((void)0) | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Try using `ERR_BREAK_MSG`. | ||||||
|  |  * Only use this macro if there is no sensible error message. | ||||||
|  |  * | ||||||
|  |  * Ensures `m_cond` is false. | ||||||
|  |  * If `m_cond` is true, the current loop breaks. | ||||||
|  |  */ | ||||||
|  | #define ERR_BREAK(m_cond)                                                                                        \ | ||||||
|  | 	if (unlikely(m_cond)) {                                                                                      \ | ||||||
|  | 		_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Breaking."); \ | ||||||
|  | 		break;                                                                                                   \ | ||||||
|  | 	} else                                                                                                       \ | ||||||
|  | 		((void)0) | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Ensures `m_cond` is false. | ||||||
|  |  * If `m_cond` is true, prints `m_msg` and the current loop breaks. | ||||||
|  |  */ | ||||||
|  | #define ERR_BREAK_MSG(m_cond, m_msg)                                                                                    \ | ||||||
|  | 	if (unlikely(m_cond)) {                                                                                             \ | ||||||
|  | 		_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Breaking.", m_msg); \ | ||||||
|  | 		break;                                                                                                          \ | ||||||
|  | 	} else                                                                                                              \ | ||||||
|  | 		((void)0) | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Same as `ERR_BREAK_MSG` but also notifies the editor. | ||||||
|  |  */ | ||||||
|  | #define ERR_BREAK_EDMSG(m_cond, m_msg)                                                                                        \ | ||||||
|  | 	if (unlikely(m_cond)) {                                                                                                   \ | ||||||
|  | 		_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Breaking.", m_msg, true); \ | ||||||
|  | 		break;                                                                                                                \ | ||||||
|  | 	} else                                                                                                                    \ | ||||||
|  | 		((void)0) | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Try using `ERR_FAIL_COND_MSG` or `ERR_FAIL_COND_V_MSG`. | ||||||
|  |  * Only use this macro if there is no sensible fallback i.e. the error is unrecoverable, and | ||||||
|  |  * there is no sensible error message. | ||||||
|  |  * | ||||||
|  |  * Ensures `m_cond` is false. | ||||||
|  |  * If `m_cond` is true, the application crashes. | ||||||
|  |  */ | ||||||
|  | #define CRASH_COND(m_cond)                                                                                    \ | ||||||
|  | 	if (unlikely(m_cond)) {                                                                                   \ | ||||||
|  | 		_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: Condition \"" _STR(m_cond) "\" is true."); \ | ||||||
|  | 		_err_flush_stdout();                                                                                  \ | ||||||
|  | 		GENERATE_TRAP();                                                                                      \ | ||||||
|  | 	} else                                                                                                    \ | ||||||
|  | 		((void)0) | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Try using `ERR_FAIL_COND_MSG` or `ERR_FAIL_COND_V_MSG`. | ||||||
|  |  * Only use this macro if there is no sensible fallback i.e. the error is unrecoverable. | ||||||
|  |  * | ||||||
|  |  * Ensures `m_cond` is false. | ||||||
|  |  * If `m_cond` is true, prints `m_msg` and the application crashes. | ||||||
|  |  */ | ||||||
|  | #define CRASH_COND_MSG(m_cond, m_msg)                                                                                \ | ||||||
|  | 	if (unlikely(m_cond)) {                                                                                          \ | ||||||
|  | 		_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: Condition \"" _STR(m_cond) "\" is true.", m_msg); \ | ||||||
|  | 		_err_flush_stdout();                                                                                         \ | ||||||
|  | 		GENERATE_TRAP();                                                                                             \ | ||||||
|  | 	} else                                                                                                           \ | ||||||
|  | 		((void)0) | ||||||
|  | 
 | ||||||
|  | // Generic error macros.
 | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Try using `ERR_FAIL_COND_MSG` or `ERR_FAIL_MSG`. | ||||||
|  |  * Only use this macro if more complex error detection or recovery is required, and | ||||||
|  |  * there is no sensible error message. | ||||||
|  |  * | ||||||
|  |  * The current function returns. | ||||||
|  |  */ | ||||||
|  | #define ERR_FAIL()                                                                     \ | ||||||
|  | 	if (true) {                                                                        \ | ||||||
|  | 		_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/function failed."); \ | ||||||
|  | 		return;                                                                        \ | ||||||
|  | 	} else                                                                             \ | ||||||
|  | 		((void)0) | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Try using `ERR_FAIL_COND_MSG`. | ||||||
|  |  * Only use this macro if more complex error detection or recovery is required. | ||||||
|  |  * | ||||||
|  |  * Prints `m_msg`, and the current function returns. | ||||||
|  |  */ | ||||||
|  | #define ERR_FAIL_MSG(m_msg)                                                                   \ | ||||||
|  | 	if (true) {                                                                               \ | ||||||
|  | 		_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/function failed.", m_msg); \ | ||||||
|  | 		return;                                                                               \ | ||||||
|  | 	} else                                                                                    \ | ||||||
|  | 		((void)0) | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Same as `ERR_FAIL_MSG` but also notifies the editor. | ||||||
|  |  */ | ||||||
|  | #define ERR_FAIL_EDMSG(m_msg)                                                                       \ | ||||||
|  | 	if (true) {                                                                                     \ | ||||||
|  | 		_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/function failed.", m_msg, true); \ | ||||||
|  | 		return;                                                                                     \ | ||||||
|  | 	} else                                                                                          \ | ||||||
|  | 		((void)0) | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Try using `ERR_FAIL_COND_V_MSG` or `ERR_FAIL_V_MSG`. | ||||||
|  |  * Only use this macro if more complex error detection or recovery is required, and | ||||||
|  |  * there is no sensible error message. | ||||||
|  |  * | ||||||
|  |  * The current function returns `m_retval`. | ||||||
|  |  */ | ||||||
|  | #define ERR_FAIL_V(m_retval)                                                                                      \ | ||||||
|  | 	if (true) {                                                                                                   \ | ||||||
|  | 		_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/function failed. Returning: " _STR(m_retval)); \ | ||||||
|  | 		return m_retval;                                                                                          \ | ||||||
|  | 	} else                                                                                                        \ | ||||||
|  | 		((void)0) | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Try using `ERR_FAIL_COND_V_MSG`. | ||||||
|  |  * Only use this macro if more complex error detection or recovery is required. | ||||||
|  |  * | ||||||
|  |  * Prints `m_msg`, and the current function returns `m_retval`. | ||||||
|  |  */ | ||||||
|  | #define ERR_FAIL_V_MSG(m_retval, m_msg)                                                                                  \ | ||||||
|  | 	if (true) {                                                                                                          \ | ||||||
|  | 		_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/function failed. Returning: " _STR(m_retval), m_msg); \ | ||||||
|  | 		return m_retval;                                                                                                 \ | ||||||
|  | 	} else                                                                                                               \ | ||||||
|  | 		((void)0) | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Same as `ERR_FAIL_V_MSG` but also notifies the editor. | ||||||
|  |  */ | ||||||
|  | #define ERR_FAIL_V_EDMSG(m_retval, m_msg)                                                                                      \ | ||||||
|  | 	if (true) {                                                                                                                \ | ||||||
|  | 		_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/function failed. Returning: " _STR(m_retval), m_msg, true); \ | ||||||
|  | 		return m_retval;                                                                                                       \ | ||||||
|  | 	} else                                                                                                                     \ | ||||||
|  | 		((void)0) | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Try using `ERR_FAIL_COND_MSG`, `ERR_FAIL_COND_V_MSG`, `ERR_CONTINUE_MSG` or `ERR_BREAK_MSG`. | ||||||
|  |  * Only use this macro at the start of a function that has not been implemented yet, or | ||||||
|  |  * if more complex error detection or recovery is required. | ||||||
|  |  * | ||||||
|  |  * Prints `m_msg`. | ||||||
|  |  */ | ||||||
|  | #define ERR_PRINT(m_msg) \ | ||||||
|  | 	_err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_msg) | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Same as `ERR_PRINT` but also notifies the editor. | ||||||
|  |  */ | ||||||
|  | #define ERR_PRINT_ED(m_msg) \ | ||||||
|  | 	_err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_msg, true) | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Prints `m_msg` once during the application lifetime. | ||||||
|  |  */ | ||||||
|  | #define ERR_PRINT_ONCE(m_msg)                                          \ | ||||||
|  | 	if (true) {                                                        \ | ||||||
|  | 		static bool first_print = true;                                \ | ||||||
|  | 		if (first_print) {                                             \ | ||||||
|  | 			_err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_msg); \ | ||||||
|  | 			first_print = false;                                       \ | ||||||
|  | 		}                                                              \ | ||||||
|  | 	} else                                                             \ | ||||||
|  | 		((void)0) | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Same as `ERR_PRINT_ONCE` but also notifies the editor. | ||||||
|  |  */ | ||||||
|  | #define ERR_PRINT_ONCE_ED(m_msg)                                             \ | ||||||
|  | 	if (true) {                                                              \ | ||||||
|  | 		static bool first_print = true;                                      \ | ||||||
|  | 		if (first_print) {                                                   \ | ||||||
|  | 			_err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_msg, true); \ | ||||||
|  | 			first_print = false;                                             \ | ||||||
|  | 		}                                                                    \ | ||||||
|  | 	} else                                                                   \ | ||||||
|  | 		((void)0) | ||||||
|  | 
 | ||||||
|  | // Print warning message macros.
 | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Prints `m_msg`. | ||||||
|  |  * | ||||||
|  |  * If warning about deprecated usage, use `WARN_DEPRECATED` or `WARN_DEPRECATED_MSG` instead. | ||||||
|  |  */ | ||||||
|  | #define WARN_PRINT(m_msg) \ | ||||||
|  | 	_err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_msg, false, ERR_HANDLER_WARNING) | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Same as `WARN_PRINT` but also notifies the editor. | ||||||
|  |  */ | ||||||
|  | #define WARN_PRINT_ED(m_msg) \ | ||||||
|  | 	_err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_msg, true, ERR_HANDLER_WARNING) | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Prints `m_msg` once during the application lifetime. | ||||||
|  |  * | ||||||
|  |  * If warning about deprecated usage, use `WARN_DEPRECATED` or `WARN_DEPRECATED_MSG` instead. | ||||||
|  |  */ | ||||||
|  | #define WARN_PRINT_ONCE(m_msg)                                                                     \ | ||||||
|  | 	if (true) {                                                                                    \ | ||||||
|  | 		static bool first_print = true;                                                            \ | ||||||
|  | 		if (first_print) {                                                                         \ | ||||||
|  | 			_err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_msg, false, ERR_HANDLER_WARNING); \ | ||||||
|  | 			first_print = false;                                                                   \ | ||||||
|  | 		}                                                                                          \ | ||||||
|  | 	} else                                                                                         \ | ||||||
|  | 		((void)0) | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Same as `WARN_PRINT_ONCE` but also notifies the editor. | ||||||
|  |  */ | ||||||
|  | #define WARN_PRINT_ONCE_ED(m_msg)                                                                 \ | ||||||
|  | 	if (true) {                                                                                   \ | ||||||
|  | 		static bool first_print = true;                                                           \ | ||||||
|  | 		if (first_print) {                                                                        \ | ||||||
|  | 			_err_print_error(FUNCTION_STR, __FILE__, __LINE__, m_msg, true, ERR_HANDLER_WARNING); \ | ||||||
|  | 			first_print = false;                                                                  \ | ||||||
|  | 		}                                                                                         \ | ||||||
|  | 	} else                                                                                        \ | ||||||
|  | 		((void)0) | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Warns about `m_msg` only when verbose mode is enabled. | ||||||
|  |  */ | ||||||
|  | #define WARN_VERBOSE(m_msg)               \ | ||||||
|  | 	{                                     \ | ||||||
|  | 		if (is_print_verbose_enabled()) { \ | ||||||
|  | 			WARN_PRINT(m_msg);            \ | ||||||
|  | 		}                                 \ | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | // Print deprecated warning message macros.
 | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Warns that the current function is deprecated. | ||||||
|  |  */ | ||||||
|  | #define WARN_DEPRECATED                                                                                                                                           \ | ||||||
|  | 	if (true) {                                                                                                                                                   \ | ||||||
|  | 		static std::atomic<bool> warning_shown;                                                                                                                   \ | ||||||
|  | 		if (!warning_shown.load()) {                                                                                                                              \ | ||||||
|  | 			_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "This method has been deprecated and will be removed in the future.", false, ERR_HANDLER_WARNING); \ | ||||||
|  | 			warning_shown.store(true);                                                                                                                            \ | ||||||
|  | 		}                                                                                                                                                         \ | ||||||
|  | 	} else                                                                                                                                                        \ | ||||||
|  | 		((void)0) | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Warns that the current function is deprecated and prints `m_msg`. | ||||||
|  |  */ | ||||||
|  | #define WARN_DEPRECATED_MSG(m_msg)                                                                                                                                       \ | ||||||
|  | 	if (true) {                                                                                                                                                          \ | ||||||
|  | 		static std::atomic<bool> warning_shown;                                                                                                                          \ | ||||||
|  | 		if (!warning_shown.load()) {                                                                                                                                     \ | ||||||
|  | 			_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "This method has been deprecated and will be removed in the future.", m_msg, false, ERR_HANDLER_WARNING); \ | ||||||
|  | 			warning_shown.store(true);                                                                                                                                   \ | ||||||
|  | 		}                                                                                                                                                                \ | ||||||
|  | 	} else                                                                                                                                                               \ | ||||||
|  | 		((void)0) | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Do not use. | ||||||
|  |  * If the application should never reach this point use CRASH_NOW_MSG(m_msg) to explain why. | ||||||
|  |  * | ||||||
|  |  * The application crashes. | ||||||
|  |  */ | ||||||
|  | #define CRASH_NOW()                                                                           \ | ||||||
|  | 	if (true) {                                                                               \ | ||||||
|  | 		_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: Method/function failed."); \ | ||||||
|  | 		_err_flush_stdout();                                                                  \ | ||||||
|  | 		GENERATE_TRAP();                                                                      \ | ||||||
|  | 	} else                                                                                    \ | ||||||
|  | 		((void)0) | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Only use if the application should never reach this point. | ||||||
|  |  * | ||||||
|  |  * Prints `m_msg`, and then the application crashes. | ||||||
|  |  */ | ||||||
|  | #define CRASH_NOW_MSG(m_msg)                                                                         \ | ||||||
|  | 	if (true) {                                                                                      \ | ||||||
|  | 		_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: Method/function failed.", m_msg); \ | ||||||
|  | 		_err_flush_stdout();                                                                         \ | ||||||
|  | 		GENERATE_TRAP();                                                                             \ | ||||||
|  | 	} else                                                                                           \ | ||||||
|  | 		((void)0) | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Note: IN MOST CASES YOU SHOULD NOT USE THIS MACRO. | ||||||
|  |  * Do not use unless you understand the trade-offs. | ||||||
|  |  * | ||||||
|  |  * DEV macros will be compiled out in releases, they are wrapped in DEV_ENABLED. | ||||||
|  |  * | ||||||
|  |  * Prefer WARNINGS / ERR_FAIL macros (which fail without crashing) - ERR_FAIL should be used in most cases. | ||||||
|  |  * Then CRASH_NOW_MSG macros (on rare occasions where error cannot be recovered). | ||||||
|  |  * | ||||||
|  |  * DEV_ASSERT should generally only be used when both of the following conditions are met: | ||||||
|  |  * 1) Bottleneck code where a check in release would be too expensive. | ||||||
|  |  * 2) Situations where the check would fail obviously and straight away during the maintenance of the code | ||||||
|  |  *    (i.e. strict conditions that should be true no matter what) | ||||||
|  |  *    and that can't fail for other contributors once the code is finished and merged. | ||||||
|  |  */ | ||||||
|  | #ifdef DEV_ENABLED | ||||||
|  | #define DEV_ASSERT(m_cond)                                                                                              \ | ||||||
|  | 	if (unlikely(!(m_cond))) {                                                                                          \ | ||||||
|  | 		_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "FATAL: DEV_ASSERT failed  \"" _STR(m_cond) "\" is false."); \ | ||||||
|  | 		_err_flush_stdout();                                                                                            \ | ||||||
|  | 		GENERATE_TRAP();                                                                                                \ | ||||||
|  | 	} else                                                                                                              \ | ||||||
|  | 		((void)0) | ||||||
|  | #else | ||||||
|  | #define DEV_ASSERT(m_cond) | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #ifdef DEV_ENABLED | ||||||
|  | #define DEV_CHECK_ONCE(m_cond)                                                   \ | ||||||
|  | 	if (unlikely(!(m_cond))) {                                                   \ | ||||||
|  | 		ERR_PRINT_ONCE("DEV_CHECK_ONCE failed  \"" _STR(m_cond) "\" is false."); \ | ||||||
|  | 	} else                                                                       \ | ||||||
|  | 		((void)0) | ||||||
|  | #else | ||||||
|  | #define DEV_CHECK_ONCE(m_cond) | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Physics Interpolation warnings. | ||||||
|  |  * These are spam protection warnings. | ||||||
|  |  */ | ||||||
|  | #define PHYSICS_INTERPOLATION_NODE_WARNING(m_object_id, m_string) \ | ||||||
|  | 	_physics_interpolation_warning(FUNCTION_STR, __FILE__, __LINE__, m_object_id, m_string) | ||||||
|  | 
 | ||||||
|  | #define PHYSICS_INTERPOLATION_WARNING(m_string) \ | ||||||
|  | 	_physics_interpolation_warning(FUNCTION_STR, __FILE__, __LINE__, ObjectID(UINT64_MAX), m_string) | ||||||
							
								
								
									
										18
									
								
								engine/core/extension/SCsub
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								engine/core/extension/SCsub
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,18 @@ | ||||||
|  | #!/usr/bin/env python | ||||||
|  | from misc.utility.scons_hints import * | ||||||
|  | 
 | ||||||
|  | Import("env") | ||||||
|  | 
 | ||||||
|  | import make_interface_dumper | ||||||
|  | import make_wrappers | ||||||
|  | 
 | ||||||
|  | env.CommandNoCache(["ext_wrappers.gen.inc"], "make_wrappers.py", env.Run(make_wrappers.run)) | ||||||
|  | env.CommandNoCache( | ||||||
|  |     "gdextension_interface_dump.gen.h", | ||||||
|  |     ["gdextension_interface.h", "make_interface_dumper.py"], | ||||||
|  |     env.Run(make_interface_dumper.run), | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | env_extension = env.Clone() | ||||||
|  | 
 | ||||||
|  | env_extension.add_source_files(env.core_sources, "*.cpp") | ||||||
							
								
								
									
										1679
									
								
								engine/core/extension/extension_api_dump.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1679
									
								
								engine/core/extension/extension_api_dump.cpp
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										43
									
								
								engine/core/extension/extension_api_dump.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								engine/core/extension/extension_api_dump.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,43 @@ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*  extension_api_dump.h                                                  */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*                         This file is part of:                          */ | ||||||
|  | /*                             GODOT ENGINE                               */ | ||||||
|  | /*                        https://godotengine.org                         */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ | ||||||
|  | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */ | ||||||
|  | /*                                                                        */ | ||||||
|  | /* 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.                 */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include "core/extension/gdextension.h" | ||||||
|  | 
 | ||||||
|  | #ifdef TOOLS_ENABLED | ||||||
|  | 
 | ||||||
|  | class GDExtensionAPIDump { | ||||||
|  | public: | ||||||
|  | 	static Dictionary generate_extension_api(bool p_include_docs = false); | ||||||
|  | 	static void generate_extension_json_file(const String &p_path, bool p_include_docs = false); | ||||||
|  | 	static Error validate_extension_json_file(const String &p_path); | ||||||
|  | }; | ||||||
|  | #endif | ||||||
							
								
								
									
										49
									
								
								engine/core/extension/gdextension.compat.inc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								engine/core/extension/gdextension.compat.inc
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,49 @@ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*  gdextension.compat.inc                                                */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*                         This file is part of:                          */ | ||||||
|  | /*                             GODOT ENGINE                               */ | ||||||
|  | /*                        https://godotengine.org                         */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ | ||||||
|  | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */ | ||||||
|  | /*                                                                        */ | ||||||
|  | /* 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 DISABLE_DEPRECATED | ||||||
|  | 
 | ||||||
|  | Error GDExtension::_open_library_bind_compat_88418(const String &p_path, const String &p_entry_symbol) { | ||||||
|  | 	return ERR_UNAVAILABLE; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void GDExtension::_close_library_bind_compat_88418() { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void GDExtension::_initialize_library_bind_compat_88418(InitializationLevel p_level) { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void GDExtension::_bind_compatibility_methods() { | ||||||
|  | 	ClassDB::bind_compatibility_method(D_METHOD("open_library", "path", "entry_symbol"), &GDExtension::_open_library_bind_compat_88418); | ||||||
|  | 	ClassDB::bind_compatibility_method(D_METHOD("close_library"), &GDExtension::_close_library_bind_compat_88418); | ||||||
|  | 	ClassDB::bind_compatibility_method(D_METHOD("initialize_library", "level"), &GDExtension::_initialize_library_bind_compat_88418); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
							
								
								
									
										1085
									
								
								engine/core/extension/gdextension.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1085
									
								
								engine/core/extension/gdextension.cpp
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										231
									
								
								engine/core/extension/gdextension.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										231
									
								
								engine/core/extension/gdextension.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,231 @@ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*  gdextension.h                                                         */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*                         This file is part of:                          */ | ||||||
|  | /*                             GODOT ENGINE                               */ | ||||||
|  | /*                        https://godotengine.org                         */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ | ||||||
|  | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */ | ||||||
|  | /*                                                                        */ | ||||||
|  | /* 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.                 */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include "core/extension/gdextension_interface.h" | ||||||
|  | #include "core/extension/gdextension_loader.h" | ||||||
|  | #include "core/io/config_file.h" | ||||||
|  | #include "core/io/resource_loader.h" | ||||||
|  | #include "core/object/ref_counted.h" | ||||||
|  | 
 | ||||||
|  | class GDExtensionMethodBind; | ||||||
|  | 
 | ||||||
|  | class GDExtension : public Resource { | ||||||
|  | 	GDCLASS(GDExtension, Resource) | ||||||
|  | 
 | ||||||
|  | 	friend class GDExtensionManager; | ||||||
|  | 
 | ||||||
|  | 	Ref<GDExtensionLoader> loader; | ||||||
|  | 
 | ||||||
|  | 	bool reloadable = false; | ||||||
|  | 
 | ||||||
|  | 	struct Extension { | ||||||
|  | 		ObjectGDExtension gdextension; | ||||||
|  | 
 | ||||||
|  | #ifdef TOOLS_ENABLED | ||||||
|  | 		bool is_reloading = false; | ||||||
|  | 		HashMap<StringName, GDExtensionMethodBind *> methods; | ||||||
|  | 		HashSet<ObjectID> instances; | ||||||
|  | 
 | ||||||
|  | 		struct InstanceState { | ||||||
|  | 			List<Pair<String, Variant>> properties; | ||||||
|  | 			bool is_placeholder = false; | ||||||
|  | 		}; | ||||||
|  | 		HashMap<ObjectID, InstanceState> instance_state; | ||||||
|  | #endif | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	HashMap<StringName, Extension> extension_classes; | ||||||
|  | 
 | ||||||
|  | 	struct ClassCreationDeprecatedInfo { | ||||||
|  | #ifndef DISABLE_DEPRECATED | ||||||
|  | 		GDExtensionClassNotification notification_func = nullptr; | ||||||
|  | 		GDExtensionClassFreePropertyList free_property_list_func = nullptr; | ||||||
|  | 		GDExtensionClassCreateInstance create_instance_func = nullptr; | ||||||
|  | 		GDExtensionClassGetRID get_rid_func = nullptr; | ||||||
|  | 		GDExtensionClassGetVirtual get_virtual_func = nullptr; | ||||||
|  | 		GDExtensionClassGetVirtualCallData get_virtual_call_data_func = nullptr; | ||||||
|  | #endif // DISABLE_DEPRECATED
 | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | #ifndef DISABLE_DEPRECATED | ||||||
|  | 	static void _register_extension_class(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo *p_extension_funcs); | ||||||
|  | 	static void _register_extension_class2(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo2 *p_extension_funcs); | ||||||
|  | 	static void _register_extension_class3(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo3 *p_extension_funcs); | ||||||
|  | #endif // DISABLE_DEPRECATED
 | ||||||
|  | 	static void _register_extension_class4(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo4 *p_extension_funcs); | ||||||
|  | 	static void _register_extension_class_internal(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo4 *p_extension_funcs, const ClassCreationDeprecatedInfo *p_deprecated_funcs = nullptr); | ||||||
|  | 	static void _register_extension_class_method(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionClassMethodInfo *p_method_info); | ||||||
|  | 	static void _register_extension_class_virtual_method(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionClassVirtualMethodInfo *p_method_info); | ||||||
|  | 	static void _register_extension_class_integer_constant(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_enum_name, GDExtensionConstStringNamePtr p_constant_name, GDExtensionInt p_constant_value, GDExtensionBool p_is_bitfield); | ||||||
|  | 	static void _register_extension_class_property(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionPropertyInfo *p_info, GDExtensionConstStringNamePtr p_setter, GDExtensionConstStringNamePtr p_getter); | ||||||
|  | 	static void _register_extension_class_property_indexed(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionPropertyInfo *p_info, GDExtensionConstStringNamePtr p_setter, GDExtensionConstStringNamePtr p_getter, GDExtensionInt p_index); | ||||||
|  | 	static void _register_extension_class_property_group(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_group_name, GDExtensionConstStringNamePtr p_prefix); | ||||||
|  | 	static void _register_extension_class_property_subgroup(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_subgroup_name, GDExtensionConstStringNamePtr p_prefix); | ||||||
|  | 	static void _register_extension_class_signal(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_signal_name, const GDExtensionPropertyInfo *p_argument_info, GDExtensionInt p_argument_count); | ||||||
|  | 	static void _unregister_extension_class(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name); | ||||||
|  | 	static void _get_library_path(GDExtensionClassLibraryPtr p_library, GDExtensionStringPtr r_path); | ||||||
|  | 	static void _register_get_classes_used_callback(GDExtensionClassLibraryPtr p_library, GDExtensionEditorGetClassesUsedCallback p_callback); | ||||||
|  | 
 | ||||||
|  | 	GDExtensionInitialization initialization; | ||||||
|  | 	int32_t level_initialized = -1; | ||||||
|  | 
 | ||||||
|  | #ifdef TOOLS_ENABLED | ||||||
|  | 	bool is_reloading = false; | ||||||
|  | 	Vector<GDExtensionMethodBind *> invalid_methods; | ||||||
|  | 	Vector<ObjectID> instance_bindings; | ||||||
|  | 	GDExtensionEditorGetClassesUsedCallback get_classes_used_callback = nullptr; | ||||||
|  | 
 | ||||||
|  | 	static void _track_instance(void *p_user_data, void *p_instance); | ||||||
|  | 	static void _untrack_instance(void *p_user_data, void *p_instance); | ||||||
|  | 
 | ||||||
|  | 	void _clear_extension(Extension *p_extension); | ||||||
|  | 
 | ||||||
|  | 	// Only called by GDExtensionManager during the reload process.
 | ||||||
|  | 	void prepare_reload(); | ||||||
|  | 	void finish_reload(); | ||||||
|  | 	void clear_instance_bindings(); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 	static HashMap<StringName, GDExtensionInterfaceFunctionPtr> gdextension_interface_functions; | ||||||
|  | 
 | ||||||
|  | protected: | ||||||
|  | 	static void _bind_methods(); | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  | 	HashMap<String, String> class_icon_paths; | ||||||
|  | 
 | ||||||
|  | 	virtual bool editor_can_reload_from_file() override { return false; } // Reloading is handled in a special way.
 | ||||||
|  | 
 | ||||||
|  | 	static String get_extension_list_config_file(); | ||||||
|  | 
 | ||||||
|  | 	const Ref<GDExtensionLoader> get_loader() const { return loader; } | ||||||
|  | 
 | ||||||
|  | 	Error open_library(const String &p_path, const Ref<GDExtensionLoader> &p_loader); | ||||||
|  | 	void close_library(); | ||||||
|  | 	bool is_library_open() const; | ||||||
|  | 
 | ||||||
|  | 	enum InitializationLevel { | ||||||
|  | 		INITIALIZATION_LEVEL_CORE = GDEXTENSION_INITIALIZATION_CORE, | ||||||
|  | 		INITIALIZATION_LEVEL_SERVERS = GDEXTENSION_INITIALIZATION_SERVERS, | ||||||
|  | 		INITIALIZATION_LEVEL_SCENE = GDEXTENSION_INITIALIZATION_SCENE, | ||||||
|  | 		INITIALIZATION_LEVEL_EDITOR = GDEXTENSION_INITIALIZATION_EDITOR | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | protected: | ||||||
|  | #ifndef DISABLE_DEPRECATED | ||||||
|  | 	Error _open_library_bind_compat_88418(const String &p_path, const String &p_entry_symbol); | ||||||
|  | 	void _close_library_bind_compat_88418(); | ||||||
|  | 	void _initialize_library_bind_compat_88418(InitializationLevel p_level); | ||||||
|  | 	static void _bind_compatibility_methods(); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  | #ifdef TOOLS_ENABLED | ||||||
|  | 	bool is_reloadable() const { return reloadable; } | ||||||
|  | 	void set_reloadable(bool p_reloadable) { reloadable = p_reloadable; } | ||||||
|  | 
 | ||||||
|  | 	bool has_library_changed() const; | ||||||
|  | 
 | ||||||
|  | 	void track_instance_binding(Object *p_object); | ||||||
|  | 	void untrack_instance_binding(Object *p_object); | ||||||
|  | 
 | ||||||
|  | 	PackedStringArray get_classes_used() const; | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 	InitializationLevel get_minimum_library_initialization_level() const; | ||||||
|  | 	void initialize_library(InitializationLevel p_level); | ||||||
|  | 	void deinitialize_library(InitializationLevel p_level); | ||||||
|  | 
 | ||||||
|  | 	static void register_interface_function(const StringName &p_function_name, GDExtensionInterfaceFunctionPtr p_function_pointer); | ||||||
|  | 	static GDExtensionInterfaceFunctionPtr get_interface_function(const StringName &p_function_name); | ||||||
|  | 	static void initialize_gdextensions(); | ||||||
|  | 	static void finalize_gdextensions(); | ||||||
|  | 
 | ||||||
|  | 	GDExtension(); | ||||||
|  | 	~GDExtension(); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | VARIANT_ENUM_CAST(GDExtension::InitializationLevel) | ||||||
|  | 
 | ||||||
|  | class GDExtensionResourceLoader : public ResourceFormatLoader { | ||||||
|  | public: | ||||||
|  | 	static Error load_gdextension_resource(const String &p_path, Ref<GDExtension> &p_extension); | ||||||
|  | 
 | ||||||
|  | 	virtual Ref<Resource> load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE) override; | ||||||
|  | 	virtual void get_recognized_extensions(List<String> *p_extensions) const override; | ||||||
|  | 	virtual bool handles_type(const String &p_type) const override; | ||||||
|  | 	virtual String get_resource_type(const String &p_path) const override; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | #ifdef TOOLS_ENABLED | ||||||
|  | class GDExtensionEditorPlugins { | ||||||
|  | private: | ||||||
|  | 	static Vector<StringName> extension_classes; | ||||||
|  | 
 | ||||||
|  | protected: | ||||||
|  | 	friend class EditorNode; | ||||||
|  | 
 | ||||||
|  | 	// Since this in core, we can't directly reference EditorNode, so it will
 | ||||||
|  | 	// set these function pointers in its constructor.
 | ||||||
|  | 	typedef void (*EditorPluginRegisterFunc)(const StringName &p_class_name); | ||||||
|  | 	static EditorPluginRegisterFunc editor_node_add_plugin; | ||||||
|  | 	static EditorPluginRegisterFunc editor_node_remove_plugin; | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  | 	static void add_extension_class(const StringName &p_class_name); | ||||||
|  | 	static void remove_extension_class(const StringName &p_class_name); | ||||||
|  | 
 | ||||||
|  | 	static const Vector<StringName> &get_extension_classes() { | ||||||
|  | 		return extension_classes; | ||||||
|  | 	} | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class GDExtensionEditorHelp { | ||||||
|  | protected: | ||||||
|  | 	friend class EditorHelp; | ||||||
|  | 
 | ||||||
|  | 	// Similarly to EditorNode above, we need to be able to ask EditorHelp to parse
 | ||||||
|  | 	// new documentation data. Note though that, differently from EditorHelp, this
 | ||||||
|  | 	// is initialized even _before_ it gets instantiated, as we need to rely on
 | ||||||
|  | 	// this method while initializing the engine.
 | ||||||
|  | 	typedef void (*EditorHelpLoadXmlBufferFunc)(const uint8_t *p_buffer, int p_size); | ||||||
|  | 	static EditorHelpLoadXmlBufferFunc editor_help_load_xml_buffer; | ||||||
|  | 
 | ||||||
|  | 	typedef void (*EditorHelpRemoveClassFunc)(const String &p_class); | ||||||
|  | 	static EditorHelpRemoveClassFunc editor_help_remove_class; | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  | 	static void load_xml_buffer(const uint8_t *p_buffer, int p_size); | ||||||
|  | 	static void remove_class(const String &p_class); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | #endif // TOOLS_ENABLED
 | ||||||
							
								
								
									
										1849
									
								
								engine/core/extension/gdextension_interface.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1849
									
								
								engine/core/extension/gdextension_interface.cpp
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										3135
									
								
								engine/core/extension/gdextension_interface.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3135
									
								
								engine/core/extension/gdextension_interface.h
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										394
									
								
								engine/core/extension/gdextension_library_loader.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										394
									
								
								engine/core/extension/gdextension_library_loader.cpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,394 @@ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*  gdextension_library_loader.cpp                                        */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*                         This file is part of:                          */ | ||||||
|  | /*                             GODOT ENGINE                               */ | ||||||
|  | /*                        https://godotengine.org                         */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ | ||||||
|  | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */ | ||||||
|  | /*                                                                        */ | ||||||
|  | /* 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 "gdextension_library_loader.h" | ||||||
|  | 
 | ||||||
|  | #include "core/config/project_settings.h" | ||||||
|  | #include "core/io/dir_access.h" | ||||||
|  | #include "core/version.h" | ||||||
|  | #include "gdextension.h" | ||||||
|  | 
 | ||||||
|  | Vector<SharedObject> GDExtensionLibraryLoader::find_extension_dependencies(const String &p_path, Ref<ConfigFile> p_config, std::function<bool(String)> p_has_feature) { | ||||||
|  | 	Vector<SharedObject> dependencies_shared_objects; | ||||||
|  | 	if (p_config->has_section("dependencies")) { | ||||||
|  | 		List<String> config_dependencies; | ||||||
|  | 		p_config->get_section_keys("dependencies", &config_dependencies); | ||||||
|  | 
 | ||||||
|  | 		for (const String &dependency : config_dependencies) { | ||||||
|  | 			Vector<String> dependency_tags = dependency.split("."); | ||||||
|  | 			bool all_tags_met = true; | ||||||
|  | 			for (int i = 0; i < dependency_tags.size(); i++) { | ||||||
|  | 				String tag = dependency_tags[i].strip_edges(); | ||||||
|  | 				if (!p_has_feature(tag)) { | ||||||
|  | 					all_tags_met = false; | ||||||
|  | 					break; | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (all_tags_met) { | ||||||
|  | 				Dictionary dependency_value = p_config->get_value("dependencies", dependency); | ||||||
|  | 				for (const Variant *key = dependency_value.next(nullptr); key; key = dependency_value.next(key)) { | ||||||
|  | 					String dependency_path = *key; | ||||||
|  | 					String target_path = dependency_value[*key]; | ||||||
|  | 					if (dependency_path.is_relative_path()) { | ||||||
|  | 						dependency_path = p_path.get_base_dir().path_join(dependency_path); | ||||||
|  | 					} | ||||||
|  | 					dependencies_shared_objects.push_back(SharedObject(dependency_path, dependency_tags, target_path)); | ||||||
|  | 				} | ||||||
|  | 				break; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return dependencies_shared_objects; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | String GDExtensionLibraryLoader::find_extension_library(const String &p_path, Ref<ConfigFile> p_config, std::function<bool(String)> p_has_feature, PackedStringArray *r_tags) { | ||||||
|  | 	// First, check the explicit libraries.
 | ||||||
|  | 	if (p_config->has_section("libraries")) { | ||||||
|  | 		List<String> libraries; | ||||||
|  | 		p_config->get_section_keys("libraries", &libraries); | ||||||
|  | 
 | ||||||
|  | 		// Iterate the libraries, finding the best matching tags.
 | ||||||
|  | 		String best_library_path; | ||||||
|  | 		Vector<String> best_library_tags; | ||||||
|  | 		for (const String &E : libraries) { | ||||||
|  | 			Vector<String> tags = E.split("."); | ||||||
|  | 			bool all_tags_met = true; | ||||||
|  | 			for (int i = 0; i < tags.size(); i++) { | ||||||
|  | 				String tag = tags[i].strip_edges(); | ||||||
|  | 				if (!p_has_feature(tag)) { | ||||||
|  | 					all_tags_met = false; | ||||||
|  | 					break; | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (all_tags_met && tags.size() > best_library_tags.size()) { | ||||||
|  | 				best_library_path = p_config->get_value("libraries", E); | ||||||
|  | 				best_library_tags = tags; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if (!best_library_path.is_empty()) { | ||||||
|  | 			if (best_library_path.is_relative_path()) { | ||||||
|  | 				best_library_path = p_path.get_base_dir().path_join(best_library_path); | ||||||
|  | 			} | ||||||
|  | 			if (r_tags != nullptr) { | ||||||
|  | 				r_tags->append_array(best_library_tags); | ||||||
|  | 			} | ||||||
|  | 			return best_library_path; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Second, try to autodetect.
 | ||||||
|  | 	String autodetect_library_prefix; | ||||||
|  | 	if (p_config->has_section_key("configuration", "autodetect_library_prefix")) { | ||||||
|  | 		autodetect_library_prefix = p_config->get_value("configuration", "autodetect_library_prefix"); | ||||||
|  | 	} | ||||||
|  | 	if (!autodetect_library_prefix.is_empty()) { | ||||||
|  | 		String autodetect_path = autodetect_library_prefix; | ||||||
|  | 		if (autodetect_path.is_relative_path()) { | ||||||
|  | 			autodetect_path = p_path.get_base_dir().path_join(autodetect_path); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// Find the folder and file parts of the prefix.
 | ||||||
|  | 		String folder; | ||||||
|  | 		String file_prefix; | ||||||
|  | 		if (DirAccess::dir_exists_absolute(autodetect_path)) { | ||||||
|  | 			folder = autodetect_path; | ||||||
|  | 		} else if (DirAccess::dir_exists_absolute(autodetect_path.get_base_dir())) { | ||||||
|  | 			folder = autodetect_path.get_base_dir(); | ||||||
|  | 			file_prefix = autodetect_path.get_file(); | ||||||
|  | 		} else { | ||||||
|  | 			ERR_FAIL_V_MSG(String(), vformat("Error in extension: %s. Could not find folder for automatic detection of libraries files. autodetect_library_prefix=\"%s\"", p_path, autodetect_library_prefix)); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// Open the folder.
 | ||||||
|  | 		Ref<DirAccess> dir = DirAccess::open(folder); | ||||||
|  | 		ERR_FAIL_COND_V_MSG(dir.is_null(), String(), vformat("Error in extension: %s. Could not open folder for automatic detection of libraries files. autodetect_library_prefix=\"%s\"", p_path, autodetect_library_prefix)); | ||||||
|  | 
 | ||||||
|  | 		// Iterate the files and check the prefixes, finding the best matching file.
 | ||||||
|  | 		String best_file; | ||||||
|  | 		Vector<String> best_file_tags; | ||||||
|  | 		dir->list_dir_begin(); | ||||||
|  | 		String file_name = dir->_get_next(); | ||||||
|  | 		while (file_name != "") { | ||||||
|  | 			if (!dir->current_is_dir() && file_name.begins_with(file_prefix)) { | ||||||
|  | 				// Check if the files matches all requested feature tags.
 | ||||||
|  | 				String tags_str = file_name.trim_prefix(file_prefix); | ||||||
|  | 				tags_str = tags_str.trim_suffix(tags_str.get_extension()); | ||||||
|  | 
 | ||||||
|  | 				Vector<String> tags = tags_str.split(".", false); | ||||||
|  | 				bool all_tags_met = true; | ||||||
|  | 				for (int i = 0; i < tags.size(); i++) { | ||||||
|  | 					String tag = tags[i].strip_edges(); | ||||||
|  | 					if (!p_has_feature(tag)) { | ||||||
|  | 						all_tags_met = false; | ||||||
|  | 						break; | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				// If all tags are found in the feature list, and we found more tags than before, use this file.
 | ||||||
|  | 				if (all_tags_met && tags.size() > best_file_tags.size()) { | ||||||
|  | 					best_file_tags = tags; | ||||||
|  | 					best_file = file_name; | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			file_name = dir->_get_next(); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if (!best_file.is_empty()) { | ||||||
|  | 			String library_path = folder.path_join(best_file); | ||||||
|  | 			if (r_tags != nullptr) { | ||||||
|  | 				r_tags->append_array(best_file_tags); | ||||||
|  | 			} | ||||||
|  | 			return library_path; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return String(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Error GDExtensionLibraryLoader::open_library(const String &p_path) { | ||||||
|  | 	Error err = parse_gdextension_file(p_path); | ||||||
|  | 	if (err != OK) { | ||||||
|  | 		return err; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	String abs_path = ProjectSettings::get_singleton()->globalize_path(library_path); | ||||||
|  | 
 | ||||||
|  | 	Vector<String> abs_dependencies_paths; | ||||||
|  | 	if (!library_dependencies.is_empty()) { | ||||||
|  | 		for (const SharedObject &dependency : library_dependencies) { | ||||||
|  | 			abs_dependencies_paths.push_back(ProjectSettings::get_singleton()->globalize_path(dependency.path)); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	OS::GDExtensionData data = { | ||||||
|  | 		true, // also_set_library_path
 | ||||||
|  | 		&library_path, // r_resolved_path
 | ||||||
|  | 		Engine::get_singleton()->is_editor_hint(), // generate_temp_files
 | ||||||
|  | 		&abs_dependencies_paths, // library_dependencies
 | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	err = OS::get_singleton()->open_dynamic_library(is_static_library ? String() : abs_path, library, &data); | ||||||
|  | 	if (err != OK) { | ||||||
|  | 		return err; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return OK; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Error GDExtensionLibraryLoader::initialize(GDExtensionInterfaceGetProcAddress p_get_proc_address, const Ref<GDExtension> &p_extension, GDExtensionInitialization *r_initialization) { | ||||||
|  | #ifdef TOOLS_ENABLED | ||||||
|  | 	p_extension->set_reloadable(is_reloadable && Engine::get_singleton()->is_extension_reloading_enabled()); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 	for (const KeyValue<String, String> &icon : class_icon_paths) { | ||||||
|  | 		p_extension->class_icon_paths[icon.key] = icon.value; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	void *entry_funcptr = nullptr; | ||||||
|  | 
 | ||||||
|  | 	Error err = OS::get_singleton()->get_dynamic_library_symbol_handle(library, entry_symbol, entry_funcptr, false); | ||||||
|  | 
 | ||||||
|  | 	if (err != OK) { | ||||||
|  | 		ERR_PRINT(vformat("GDExtension entry point '%s' not found in library %s.", entry_symbol, library_path)); | ||||||
|  | 		return err; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	GDExtensionInitializationFunction initialization_function = (GDExtensionInitializationFunction)entry_funcptr; | ||||||
|  | 
 | ||||||
|  | 	GDExtensionBool ret = initialization_function(p_get_proc_address, p_extension.ptr(), r_initialization); | ||||||
|  | 
 | ||||||
|  | 	if (ret) { | ||||||
|  | 		return OK; | ||||||
|  | 	} else { | ||||||
|  | 		ERR_PRINT(vformat("GDExtension initialization function '%s' returned an error.", entry_symbol)); | ||||||
|  | 		return FAILED; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void GDExtensionLibraryLoader::close_library() { | ||||||
|  | 	OS::get_singleton()->close_dynamic_library(library); | ||||||
|  | 	library = nullptr; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool GDExtensionLibraryLoader::is_library_open() const { | ||||||
|  | 	return library != nullptr; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool GDExtensionLibraryLoader::has_library_changed() const { | ||||||
|  | #ifdef TOOLS_ENABLED | ||||||
|  | 	// Check only that the last modified time is different (rather than checking
 | ||||||
|  | 	// that it's newer) since some OS's (namely Windows) will preserve the modified
 | ||||||
|  | 	// time by default when copying files.
 | ||||||
|  | 	if (FileAccess::get_modified_time(resource_path) != resource_last_modified_time) { | ||||||
|  | 		return true; | ||||||
|  | 	} | ||||||
|  | 	if (FileAccess::get_modified_time(library_path) != library_last_modified_time) { | ||||||
|  | 		return true; | ||||||
|  | 	} | ||||||
|  | #endif | ||||||
|  | 	return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool GDExtensionLibraryLoader::library_exists() const { | ||||||
|  | 	return FileAccess::exists(resource_path); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Error GDExtensionLibraryLoader::parse_gdextension_file(const String &p_path) { | ||||||
|  | 	resource_path = p_path; | ||||||
|  | 
 | ||||||
|  | 	Ref<ConfigFile> config; | ||||||
|  | 	config.instantiate(); | ||||||
|  | 
 | ||||||
|  | 	Error err = config->load(p_path); | ||||||
|  | 
 | ||||||
|  | 	if (err != OK) { | ||||||
|  | 		ERR_PRINT(vformat("Error loading GDExtension configuration file: '%s'.", p_path)); | ||||||
|  | 		return err; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (!config->has_section_key("configuration", "entry_symbol")) { | ||||||
|  | 		ERR_PRINT(vformat("GDExtension configuration file must contain a \"configuration/entry_symbol\" key: '%s'.", p_path)); | ||||||
|  | 		return ERR_INVALID_DATA; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	entry_symbol = config->get_value("configuration", "entry_symbol"); | ||||||
|  | 
 | ||||||
|  | 	uint32_t compatibility_minimum[3] = { 0, 0, 0 }; | ||||||
|  | 	if (config->has_section_key("configuration", "compatibility_minimum")) { | ||||||
|  | 		String compat_string = config->get_value("configuration", "compatibility_minimum"); | ||||||
|  | 		Vector<int> parts = compat_string.split_ints("."); | ||||||
|  | 		for (int i = 0; i < parts.size(); i++) { | ||||||
|  | 			if (i >= 3) { | ||||||
|  | 				break; | ||||||
|  | 			} | ||||||
|  | 			if (parts[i] >= 0) { | ||||||
|  | 				compatibility_minimum[i] = parts[i]; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		ERR_PRINT(vformat("GDExtension configuration file must contain a \"configuration/compatibility_minimum\" key: '%s'.", p_path)); | ||||||
|  | 		return ERR_INVALID_DATA; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (compatibility_minimum[0] < 4 || (compatibility_minimum[0] == 4 && compatibility_minimum[1] == 0)) { | ||||||
|  | 		ERR_PRINT(vformat("GDExtension's compatibility_minimum (%d.%d.%d) must be at least 4.1.0: %s", compatibility_minimum[0], compatibility_minimum[1], compatibility_minimum[2], p_path)); | ||||||
|  | 		return ERR_INVALID_DATA; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	bool compatible = true; | ||||||
|  | 	// Check version lexicographically.
 | ||||||
|  | 	if (GODOT_VERSION_MAJOR != compatibility_minimum[0]) { | ||||||
|  | 		compatible = GODOT_VERSION_MAJOR > compatibility_minimum[0]; | ||||||
|  | 	} else if (GODOT_VERSION_MINOR != compatibility_minimum[1]) { | ||||||
|  | 		compatible = GODOT_VERSION_MINOR > compatibility_minimum[1]; | ||||||
|  | 	} else { | ||||||
|  | 		compatible = GODOT_VERSION_PATCH >= compatibility_minimum[2]; | ||||||
|  | 	} | ||||||
|  | 	if (!compatible) { | ||||||
|  | 		ERR_PRINT(vformat("GDExtension only compatible with Godot version %d.%d.%d or later: %s", compatibility_minimum[0], compatibility_minimum[1], compatibility_minimum[2], p_path)); | ||||||
|  | 		return ERR_INVALID_DATA; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Optionally check maximum compatibility.
 | ||||||
|  | 	if (config->has_section_key("configuration", "compatibility_maximum")) { | ||||||
|  | 		uint32_t compatibility_maximum[3] = { 0, 0, 0 }; | ||||||
|  | 		String compat_string = config->get_value("configuration", "compatibility_maximum"); | ||||||
|  | 		Vector<int> parts = compat_string.split_ints("."); | ||||||
|  | 		for (int i = 0; i < 3; i++) { | ||||||
|  | 			if (i < parts.size() && parts[i] >= 0) { | ||||||
|  | 				compatibility_maximum[i] = parts[i]; | ||||||
|  | 			} else { | ||||||
|  | 				// If a version part is missing, set the maximum to an arbitrary high value.
 | ||||||
|  | 				compatibility_maximum[i] = 9999; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		compatible = true; | ||||||
|  | 		if (GODOT_VERSION_MAJOR != compatibility_maximum[0]) { | ||||||
|  | 			compatible = GODOT_VERSION_MAJOR < compatibility_maximum[0]; | ||||||
|  | 		} else if (GODOT_VERSION_MINOR != compatibility_maximum[1]) { | ||||||
|  | 			compatible = GODOT_VERSION_MINOR < compatibility_maximum[1]; | ||||||
|  | 		} | ||||||
|  | #if GODOT_VERSION_PATCH | ||||||
|  | 		// #if check to avoid -Wtype-limits warning when 0.
 | ||||||
|  | 		else { | ||||||
|  | 			compatible = GODOT_VERSION_PATCH <= compatibility_maximum[2]; | ||||||
|  | 		} | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 		if (!compatible) { | ||||||
|  | 			ERR_PRINT(vformat("GDExtension only compatible with Godot version %s or earlier: %s", compat_string, p_path)); | ||||||
|  | 			return ERR_INVALID_DATA; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	library_path = find_extension_library(p_path, config, [](const String &p_feature) { return OS::get_singleton()->has_feature(p_feature); }); | ||||||
|  | 
 | ||||||
|  | 	if (library_path.is_empty()) { | ||||||
|  | 		const String os_arch = OS::get_singleton()->get_name().to_lower() + "." + Engine::get_singleton()->get_architecture_name(); | ||||||
|  | 		ERR_PRINT(vformat("No GDExtension library found for current OS and architecture (%s) in configuration file: %s", os_arch, p_path)); | ||||||
|  | 		return ERR_FILE_NOT_FOUND; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	is_static_library = library_path.ends_with(".a") || library_path.ends_with(".xcframework"); | ||||||
|  | 
 | ||||||
|  | 	if (!library_path.is_resource_file() && !library_path.is_absolute_path()) { | ||||||
|  | 		library_path = p_path.get_base_dir().path_join(library_path); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | #ifdef TOOLS_ENABLED | ||||||
|  | 	is_reloadable = config->get_value("configuration", "reloadable", false); | ||||||
|  | 
 | ||||||
|  | 	update_last_modified_time( | ||||||
|  | 			FileAccess::get_modified_time(resource_path), | ||||||
|  | 			FileAccess::get_modified_time(library_path)); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 	library_dependencies = find_extension_dependencies(p_path, config, [](const String &p_feature) { return OS::get_singleton()->has_feature(p_feature); }); | ||||||
|  | 
 | ||||||
|  | 	// Handle icons if any are specified.
 | ||||||
|  | 	if (config->has_section("icons")) { | ||||||
|  | 		List<String> keys; | ||||||
|  | 		config->get_section_keys("icons", &keys); | ||||||
|  | 		for (const String &key : keys) { | ||||||
|  | 			String icon_path = config->get_value("icons", key); | ||||||
|  | 			if (icon_path.is_relative_path()) { | ||||||
|  | 				icon_path = p_path.get_base_dir().path_join(icon_path); | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			class_icon_paths[key] = icon_path; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return OK; | ||||||
|  | } | ||||||
							
								
								
									
										84
									
								
								engine/core/extension/gdextension_library_loader.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								engine/core/extension/gdextension_library_loader.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,84 @@ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*  gdextension_library_loader.h                                          */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*                         This file is part of:                          */ | ||||||
|  | /*                             GODOT ENGINE                               */ | ||||||
|  | /*                        https://godotengine.org                         */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ | ||||||
|  | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */ | ||||||
|  | /*                                                                        */ | ||||||
|  | /* 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.                 */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <functional> | ||||||
|  | 
 | ||||||
|  | #include "core/extension/gdextension_loader.h" | ||||||
|  | #include "core/io/config_file.h" | ||||||
|  | #include "core/os/shared_object.h" | ||||||
|  | 
 | ||||||
|  | class GDExtensionLibraryLoader : public GDExtensionLoader { | ||||||
|  | 	GDSOFTCLASS(GDExtensionLibraryLoader, GDExtensionLoader); | ||||||
|  | 
 | ||||||
|  | 	friend class GDExtensionManager; | ||||||
|  | 	friend class GDExtension; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  | 	String resource_path; | ||||||
|  | 
 | ||||||
|  | 	void *library = nullptr; // pointer if valid.
 | ||||||
|  | 	String library_path; | ||||||
|  | 	String entry_symbol; | ||||||
|  | 
 | ||||||
|  | 	bool is_static_library = false; | ||||||
|  | 
 | ||||||
|  | #ifdef TOOLS_ENABLED | ||||||
|  | 	bool is_reloadable = false; | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 	Vector<SharedObject> library_dependencies; | ||||||
|  | 
 | ||||||
|  | 	HashMap<String, String> class_icon_paths; | ||||||
|  | 
 | ||||||
|  | #ifdef TOOLS_ENABLED | ||||||
|  | 	uint64_t resource_last_modified_time = 0; | ||||||
|  | 	uint64_t library_last_modified_time = 0; | ||||||
|  | 
 | ||||||
|  | 	void update_last_modified_time(uint64_t p_resource_last_modified_time, uint64_t p_library_last_modified_time) { | ||||||
|  | 		resource_last_modified_time = p_resource_last_modified_time; | ||||||
|  | 		library_last_modified_time = p_library_last_modified_time; | ||||||
|  | 	} | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  | 	static String find_extension_library(const String &p_path, Ref<ConfigFile> p_config, std::function<bool(String)> p_has_feature, PackedStringArray *r_tags = nullptr); | ||||||
|  | 	static Vector<SharedObject> find_extension_dependencies(const String &p_path, Ref<ConfigFile> p_config, std::function<bool(String)> p_has_feature); | ||||||
|  | 
 | ||||||
|  | 	virtual Error open_library(const String &p_path) override; | ||||||
|  | 	virtual Error initialize(GDExtensionInterfaceGetProcAddress p_get_proc_address, const Ref<GDExtension> &p_extension, GDExtensionInitialization *r_initialization) override; | ||||||
|  | 	virtual void close_library() override; | ||||||
|  | 	virtual bool is_library_open() const override; | ||||||
|  | 	virtual bool has_library_changed() const override; | ||||||
|  | 	virtual bool library_exists() const override; | ||||||
|  | 
 | ||||||
|  | 	Error parse_gdextension_file(const String &p_path); | ||||||
|  | }; | ||||||
							
								
								
									
										47
									
								
								engine/core/extension/gdextension_loader.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								engine/core/extension/gdextension_loader.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,47 @@ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*  gdextension_loader.h                                                  */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*                         This file is part of:                          */ | ||||||
|  | /*                             GODOT ENGINE                               */ | ||||||
|  | /*                        https://godotengine.org                         */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ | ||||||
|  | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */ | ||||||
|  | /*                                                                        */ | ||||||
|  | /* 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.                 */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include "core/object/ref_counted.h" | ||||||
|  | 
 | ||||||
|  | class GDExtension; | ||||||
|  | 
 | ||||||
|  | class GDExtensionLoader : public RefCounted { | ||||||
|  | 	GDSOFTCLASS(GDExtensionLoader, GDExtensionLoader); | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  | 	virtual Error open_library(const String &p_path) = 0; | ||||||
|  | 	virtual Error initialize(GDExtensionInterfaceGetProcAddress p_get_proc_address, const Ref<GDExtension> &p_extension, GDExtensionInitialization *r_initialization) = 0; | ||||||
|  | 	virtual void close_library() = 0; | ||||||
|  | 	virtual bool is_library_open() const = 0; | ||||||
|  | 	virtual bool has_library_changed() const = 0; | ||||||
|  | 	virtual bool library_exists() const = 0; | ||||||
|  | }; | ||||||
							
								
								
									
										432
									
								
								engine/core/extension/gdextension_manager.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										432
									
								
								engine/core/extension/gdextension_manager.cpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,432 @@ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*  gdextension_manager.cpp                                               */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*                         This file is part of:                          */ | ||||||
|  | /*                             GODOT ENGINE                               */ | ||||||
|  | /*                        https://godotengine.org                         */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ | ||||||
|  | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */ | ||||||
|  | /*                                                                        */ | ||||||
|  | /* 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 "gdextension_manager.h" | ||||||
|  | 
 | ||||||
|  | #include "core/extension/gdextension_library_loader.h" | ||||||
|  | #include "core/extension/gdextension_special_compat_hashes.h" | ||||||
|  | #include "core/io/dir_access.h" | ||||||
|  | #include "core/io/file_access.h" | ||||||
|  | #include "core/object/script_language.h" | ||||||
|  | 
 | ||||||
|  | GDExtensionManager::LoadStatus GDExtensionManager::_load_extension_internal(const Ref<GDExtension> &p_extension, bool p_first_load) { | ||||||
|  | 	if (level >= 0) { // Already initialized up to some level.
 | ||||||
|  | 		int32_t minimum_level = 0; | ||||||
|  | 		if (!p_first_load) { | ||||||
|  | 			minimum_level = p_extension->get_minimum_library_initialization_level(); | ||||||
|  | 			if (minimum_level < MIN(level, GDExtension::INITIALIZATION_LEVEL_SCENE)) { | ||||||
|  | 				return LOAD_STATUS_NEEDS_RESTART; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		// Initialize up to current level.
 | ||||||
|  | 		for (int32_t i = minimum_level; i <= level; i++) { | ||||||
|  | 			p_extension->initialize_library(GDExtension::InitializationLevel(i)); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for (const KeyValue<String, String> &kv : p_extension->class_icon_paths) { | ||||||
|  | 		gdextension_class_icon_paths[kv.key] = kv.value; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | #ifdef TOOLS_ENABLED | ||||||
|  | 	// Signals that a new extension is loaded so GDScript can register new class names.
 | ||||||
|  | 	emit_signal("extension_loaded", p_extension); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 	return LOAD_STATUS_OK; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | GDExtensionManager::LoadStatus GDExtensionManager::_unload_extension_internal(const Ref<GDExtension> &p_extension) { | ||||||
|  | #ifdef TOOLS_ENABLED | ||||||
|  | 	// Signals that a new extension is unloading so GDScript can unregister class names.
 | ||||||
|  | 	emit_signal("extension_unloading", p_extension); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 	if (level >= 0) { // Already initialized up to some level.
 | ||||||
|  | 		// Deinitialize down from current level.
 | ||||||
|  | 		for (int32_t i = level; i >= GDExtension::INITIALIZATION_LEVEL_CORE; i--) { | ||||||
|  | 			p_extension->deinitialize_library(GDExtension::InitializationLevel(i)); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for (const KeyValue<String, String> &kv : p_extension->class_icon_paths) { | ||||||
|  | 		gdextension_class_icon_paths.erase(kv.key); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return LOAD_STATUS_OK; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | GDExtensionManager::LoadStatus GDExtensionManager::load_extension(const String &p_path) { | ||||||
|  | 	if (Engine::get_singleton()->is_recovery_mode_hint()) { | ||||||
|  | 		return LOAD_STATUS_FAILED; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	Ref<GDExtensionLibraryLoader> loader; | ||||||
|  | 	loader.instantiate(); | ||||||
|  | 	return GDExtensionManager::get_singleton()->load_extension_with_loader(p_path, loader); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | GDExtensionManager::LoadStatus GDExtensionManager::load_extension_with_loader(const String &p_path, const Ref<GDExtensionLoader> &p_loader) { | ||||||
|  | 	DEV_ASSERT(p_loader.is_valid()); | ||||||
|  | 
 | ||||||
|  | 	if (gdextension_map.has(p_path)) { | ||||||
|  | 		return LOAD_STATUS_ALREADY_LOADED; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	Ref<GDExtension> extension; | ||||||
|  | 	extension.instantiate(); | ||||||
|  | 	Error err = extension->open_library(p_path, p_loader); | ||||||
|  | 	if (err != OK) { | ||||||
|  | 		return LOAD_STATUS_FAILED; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	LoadStatus status = _load_extension_internal(extension, true); | ||||||
|  | 	if (status != LOAD_STATUS_OK) { | ||||||
|  | 		return status; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	extension->set_path(p_path); | ||||||
|  | 	gdextension_map[p_path] = extension; | ||||||
|  | 	return LOAD_STATUS_OK; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | GDExtensionManager::LoadStatus GDExtensionManager::reload_extension(const String &p_path) { | ||||||
|  | #ifndef TOOLS_ENABLED | ||||||
|  | 	ERR_FAIL_V_MSG(LOAD_STATUS_FAILED, "GDExtensions can only be reloaded in an editor build."); | ||||||
|  | #else | ||||||
|  | 	ERR_FAIL_COND_V_MSG(!Engine::get_singleton()->is_extension_reloading_enabled(), LOAD_STATUS_FAILED, "GDExtension reloading is disabled."); | ||||||
|  | 
 | ||||||
|  | 	if (Engine::get_singleton()->is_recovery_mode_hint()) { | ||||||
|  | 		return LOAD_STATUS_FAILED; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (!gdextension_map.has(p_path)) { | ||||||
|  | 		return LOAD_STATUS_NOT_LOADED; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	Ref<GDExtension> extension = gdextension_map[p_path]; | ||||||
|  | 	ERR_FAIL_COND_V_MSG(!extension->is_reloadable(), LOAD_STATUS_FAILED, vformat("This GDExtension is not marked as 'reloadable' or doesn't support reloading: %s.", p_path)); | ||||||
|  | 
 | ||||||
|  | 	LoadStatus status; | ||||||
|  | 
 | ||||||
|  | 	extension->prepare_reload(); | ||||||
|  | 
 | ||||||
|  | 	// Unload library if it's open. It may not be open if the developer made a
 | ||||||
|  | 	// change that broke loading in a previous hot-reload attempt.
 | ||||||
|  | 	if (extension->is_library_open()) { | ||||||
|  | 		status = _unload_extension_internal(extension); | ||||||
|  | 		if (status != LOAD_STATUS_OK) { | ||||||
|  | 			// We need to clear these no matter what.
 | ||||||
|  | 			extension->clear_instance_bindings(); | ||||||
|  | 			return status; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		extension->clear_instance_bindings(); | ||||||
|  | 		extension->close_library(); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	Error err = extension->open_library(p_path, extension->loader); | ||||||
|  | 	if (err != OK) { | ||||||
|  | 		return LOAD_STATUS_FAILED; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	status = _load_extension_internal(extension, false); | ||||||
|  | 	if (status != LOAD_STATUS_OK) { | ||||||
|  | 		return status; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	extension->finish_reload(); | ||||||
|  | 
 | ||||||
|  | 	return LOAD_STATUS_OK; | ||||||
|  | #endif | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | GDExtensionManager::LoadStatus GDExtensionManager::unload_extension(const String &p_path) { | ||||||
|  | 	if (Engine::get_singleton()->is_recovery_mode_hint()) { | ||||||
|  | 		return LOAD_STATUS_FAILED; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (!gdextension_map.has(p_path)) { | ||||||
|  | 		return LOAD_STATUS_NOT_LOADED; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	Ref<GDExtension> extension = gdextension_map[p_path]; | ||||||
|  | 
 | ||||||
|  | 	LoadStatus status = _unload_extension_internal(extension); | ||||||
|  | 	if (status != LOAD_STATUS_OK) { | ||||||
|  | 		return status; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	gdextension_map.erase(p_path); | ||||||
|  | 	return LOAD_STATUS_OK; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool GDExtensionManager::is_extension_loaded(const String &p_path) const { | ||||||
|  | 	return gdextension_map.has(p_path); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Vector<String> GDExtensionManager::get_loaded_extensions() const { | ||||||
|  | 	Vector<String> ret; | ||||||
|  | 	for (const KeyValue<String, Ref<GDExtension>> &E : gdextension_map) { | ||||||
|  | 		ret.push_back(E.key); | ||||||
|  | 	} | ||||||
|  | 	return ret; | ||||||
|  | } | ||||||
|  | Ref<GDExtension> GDExtensionManager::get_extension(const String &p_path) { | ||||||
|  | 	HashMap<String, Ref<GDExtension>>::Iterator E = gdextension_map.find(p_path); | ||||||
|  | 	ERR_FAIL_COND_V(!E, Ref<GDExtension>()); | ||||||
|  | 	return E->value; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool GDExtensionManager::class_has_icon_path(const String &p_class) const { | ||||||
|  | 	// TODO: Check that the icon belongs to a registered class somehow.
 | ||||||
|  | 	return gdextension_class_icon_paths.has(p_class); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | String GDExtensionManager::class_get_icon_path(const String &p_class) const { | ||||||
|  | 	// TODO: Check that the icon belongs to a registered class somehow.
 | ||||||
|  | 	if (gdextension_class_icon_paths.has(p_class)) { | ||||||
|  | 		return gdextension_class_icon_paths[p_class]; | ||||||
|  | 	} | ||||||
|  | 	return ""; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void GDExtensionManager::initialize_extensions(GDExtension::InitializationLevel p_level) { | ||||||
|  | 	if (Engine::get_singleton()->is_recovery_mode_hint()) { | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	ERR_FAIL_COND(int32_t(p_level) - 1 != level); | ||||||
|  | 	for (KeyValue<String, Ref<GDExtension>> &E : gdextension_map) { | ||||||
|  | 		E.value->initialize_library(p_level); | ||||||
|  | 
 | ||||||
|  | 		if (p_level == GDExtension::INITIALIZATION_LEVEL_EDITOR) { | ||||||
|  | 			for (const KeyValue<String, String> &kv : E.value->class_icon_paths) { | ||||||
|  | 				gdextension_class_icon_paths[kv.key] = kv.value; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	level = p_level; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void GDExtensionManager::deinitialize_extensions(GDExtension::InitializationLevel p_level) { | ||||||
|  | 	if (Engine::get_singleton()->is_recovery_mode_hint()) { | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	ERR_FAIL_COND(int32_t(p_level) != level); | ||||||
|  | 	for (KeyValue<String, Ref<GDExtension>> &E : gdextension_map) { | ||||||
|  | 		E.value->deinitialize_library(p_level); | ||||||
|  | 	} | ||||||
|  | 	level = int32_t(p_level) - 1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #ifdef TOOLS_ENABLED | ||||||
|  | void GDExtensionManager::track_instance_binding(void *p_token, Object *p_object) { | ||||||
|  | 	for (KeyValue<String, Ref<GDExtension>> &E : gdextension_map) { | ||||||
|  | 		if (E.value.ptr() == p_token) { | ||||||
|  | 			if (E.value->is_reloadable()) { | ||||||
|  | 				E.value->track_instance_binding(p_object); | ||||||
|  | 				return; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void GDExtensionManager::untrack_instance_binding(void *p_token, Object *p_object) { | ||||||
|  | 	for (KeyValue<String, Ref<GDExtension>> &E : gdextension_map) { | ||||||
|  | 		if (E.value.ptr() == p_token) { | ||||||
|  | 			if (E.value->is_reloadable()) { | ||||||
|  | 				E.value->untrack_instance_binding(p_object); | ||||||
|  | 				return; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void GDExtensionManager::_reload_all_scripts() { | ||||||
|  | 	for (int i = 0; i < ScriptServer::get_language_count(); i++) { | ||||||
|  | 		ScriptServer::get_language(i)->reload_all_scripts(); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | #endif // TOOLS_ENABLED
 | ||||||
|  | 
 | ||||||
|  | void GDExtensionManager::load_extensions() { | ||||||
|  | 	if (Engine::get_singleton()->is_recovery_mode_hint()) { | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	Ref<FileAccess> f = FileAccess::open(GDExtension::get_extension_list_config_file(), FileAccess::READ); | ||||||
|  | 	while (f.is_valid() && !f->eof_reached()) { | ||||||
|  | 		String s = f->get_line().strip_edges(); | ||||||
|  | 		if (!s.is_empty()) { | ||||||
|  | 			LoadStatus err = load_extension(s); | ||||||
|  | 			ERR_CONTINUE_MSG(err == LOAD_STATUS_FAILED, vformat("Error loading extension: '%s'.", s)); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	OS::get_singleton()->load_platform_gdextensions(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void GDExtensionManager::reload_extensions() { | ||||||
|  | #ifdef TOOLS_ENABLED | ||||||
|  | 	if (Engine::get_singleton()->is_recovery_mode_hint()) { | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 	bool reloaded = false; | ||||||
|  | 	for (const KeyValue<String, Ref<GDExtension>> &E : gdextension_map) { | ||||||
|  | 		if (!E.value->is_reloadable()) { | ||||||
|  | 			continue; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if (E.value->has_library_changed()) { | ||||||
|  | 			reloaded = true; | ||||||
|  | 			reload_extension(E.value->get_path()); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (reloaded) { | ||||||
|  | 		emit_signal("extensions_reloaded"); | ||||||
|  | 
 | ||||||
|  | 		// Reload all scripts to clear out old references.
 | ||||||
|  | 		callable_mp_static(&GDExtensionManager::_reload_all_scripts).call_deferred(); | ||||||
|  | 	} | ||||||
|  | #endif | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool GDExtensionManager::ensure_extensions_loaded(const HashSet<String> &p_extensions) { | ||||||
|  | 	Vector<String> extensions_added; | ||||||
|  | 	Vector<String> extensions_removed; | ||||||
|  | 
 | ||||||
|  | 	for (const String &E : p_extensions) { | ||||||
|  | 		if (!is_extension_loaded(E)) { | ||||||
|  | 			extensions_added.push_back(E); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	Vector<String> loaded_extensions = get_loaded_extensions(); | ||||||
|  | 	for (const String &loaded_extension : loaded_extensions) { | ||||||
|  | 		if (!p_extensions.has(loaded_extension)) { | ||||||
|  | 			// The extension may not have a .gdextension file.
 | ||||||
|  | 			const Ref<GDExtension> extension = GDExtensionManager::get_singleton()->get_extension(loaded_extension); | ||||||
|  | 			if (!extension->get_loader()->library_exists()) { | ||||||
|  | 				extensions_removed.push_back(loaded_extension); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	String extension_list_config_file = GDExtension::get_extension_list_config_file(); | ||||||
|  | 	if (p_extensions.size()) { | ||||||
|  | 		if (extensions_added.size() || extensions_removed.size()) { | ||||||
|  | 			// Extensions were added or removed.
 | ||||||
|  | 			Ref<FileAccess> f = FileAccess::open(extension_list_config_file, FileAccess::WRITE); | ||||||
|  | 			for (const String &E : p_extensions) { | ||||||
|  | 				f->store_line(E); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		if (loaded_extensions.size() || FileAccess::exists(extension_list_config_file)) { | ||||||
|  | 			// Extensions were removed.
 | ||||||
|  | 			Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES); | ||||||
|  | 			da->remove(extension_list_config_file); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	bool needs_restart = false; | ||||||
|  | 	for (const String &extension : extensions_added) { | ||||||
|  | 		GDExtensionManager::LoadStatus st = GDExtensionManager::get_singleton()->load_extension(extension); | ||||||
|  | 		if (st == GDExtensionManager::LOAD_STATUS_NEEDS_RESTART) { | ||||||
|  | 			needs_restart = true; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for (const String &extension : extensions_removed) { | ||||||
|  | 		GDExtensionManager::LoadStatus st = GDExtensionManager::get_singleton()->unload_extension(extension); | ||||||
|  | 		if (st == GDExtensionManager::LOAD_STATUS_NEEDS_RESTART) { | ||||||
|  | 			needs_restart = true; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | #ifdef TOOLS_ENABLED | ||||||
|  | 	if (extensions_added.size() || extensions_removed.size()) { | ||||||
|  | 		// Emitting extensions_reloaded so EditorNode can reload Inspector and regenerate documentation.
 | ||||||
|  | 		emit_signal("extensions_reloaded"); | ||||||
|  | 
 | ||||||
|  | 		// Reload all scripts to clear out old references.
 | ||||||
|  | 		callable_mp_static(&GDExtensionManager::_reload_all_scripts).call_deferred(); | ||||||
|  | 	} | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 	return needs_restart; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | GDExtensionManager *GDExtensionManager::get_singleton() { | ||||||
|  | 	return singleton; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void GDExtensionManager::_bind_methods() { | ||||||
|  | 	ClassDB::bind_method(D_METHOD("load_extension", "path"), &GDExtensionManager::load_extension); | ||||||
|  | 	ClassDB::bind_method(D_METHOD("reload_extension", "path"), &GDExtensionManager::reload_extension); | ||||||
|  | 	ClassDB::bind_method(D_METHOD("unload_extension", "path"), &GDExtensionManager::unload_extension); | ||||||
|  | 	ClassDB::bind_method(D_METHOD("is_extension_loaded", "path"), &GDExtensionManager::is_extension_loaded); | ||||||
|  | 
 | ||||||
|  | 	ClassDB::bind_method(D_METHOD("get_loaded_extensions"), &GDExtensionManager::get_loaded_extensions); | ||||||
|  | 	ClassDB::bind_method(D_METHOD("get_extension", "path"), &GDExtensionManager::get_extension); | ||||||
|  | 
 | ||||||
|  | 	BIND_ENUM_CONSTANT(LOAD_STATUS_OK); | ||||||
|  | 	BIND_ENUM_CONSTANT(LOAD_STATUS_FAILED); | ||||||
|  | 	BIND_ENUM_CONSTANT(LOAD_STATUS_ALREADY_LOADED); | ||||||
|  | 	BIND_ENUM_CONSTANT(LOAD_STATUS_NOT_LOADED); | ||||||
|  | 	BIND_ENUM_CONSTANT(LOAD_STATUS_NEEDS_RESTART); | ||||||
|  | 
 | ||||||
|  | 	ADD_SIGNAL(MethodInfo("extensions_reloaded")); | ||||||
|  | 	ADD_SIGNAL(MethodInfo("extension_loaded", PropertyInfo(Variant::OBJECT, "extension", PROPERTY_HINT_RESOURCE_TYPE, "GDExtension"))); | ||||||
|  | 	ADD_SIGNAL(MethodInfo("extension_unloading", PropertyInfo(Variant::OBJECT, "extension", PROPERTY_HINT_RESOURCE_TYPE, "GDExtension"))); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | GDExtensionManager *GDExtensionManager::singleton = nullptr; | ||||||
|  | 
 | ||||||
|  | GDExtensionManager::GDExtensionManager() { | ||||||
|  | 	ERR_FAIL_COND(singleton != nullptr); | ||||||
|  | 	singleton = this; | ||||||
|  | 
 | ||||||
|  | #ifndef DISABLE_DEPRECATED | ||||||
|  | 	GDExtensionSpecialCompatHashes::initialize(); | ||||||
|  | #endif | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | GDExtensionManager::~GDExtensionManager() { | ||||||
|  | 	if (singleton == this) { | ||||||
|  | 		singleton = nullptr; | ||||||
|  | 	} | ||||||
|  | #ifndef DISABLE_DEPRECATED | ||||||
|  | 	GDExtensionSpecialCompatHashes::finalize(); | ||||||
|  | #endif | ||||||
|  | } | ||||||
							
								
								
									
										93
									
								
								engine/core/extension/gdextension_manager.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								engine/core/extension/gdextension_manager.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,93 @@ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*  gdextension_manager.h                                                 */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*                         This file is part of:                          */ | ||||||
|  | /*                             GODOT ENGINE                               */ | ||||||
|  | /*                        https://godotengine.org                         */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ | ||||||
|  | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */ | ||||||
|  | /*                                                                        */ | ||||||
|  | /* 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.                 */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include "core/extension/gdextension.h" | ||||||
|  | 
 | ||||||
|  | class GDExtensionManager : public Object { | ||||||
|  | 	GDCLASS(GDExtensionManager, Object); | ||||||
|  | 
 | ||||||
|  | 	int32_t level = -1; | ||||||
|  | 	HashMap<String, Ref<GDExtension>> gdextension_map; | ||||||
|  | 	HashMap<String, String> gdextension_class_icon_paths; | ||||||
|  | 
 | ||||||
|  | 	static void _bind_methods(); | ||||||
|  | 
 | ||||||
|  | 	static GDExtensionManager *singleton; | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  | 	enum LoadStatus { | ||||||
|  | 		LOAD_STATUS_OK, | ||||||
|  | 		LOAD_STATUS_FAILED, | ||||||
|  | 		LOAD_STATUS_ALREADY_LOADED, | ||||||
|  | 		LOAD_STATUS_NOT_LOADED, | ||||||
|  | 		LOAD_STATUS_NEEDS_RESTART, | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  | 	LoadStatus _load_extension_internal(const Ref<GDExtension> &p_extension, bool p_first_load); | ||||||
|  | 	LoadStatus _unload_extension_internal(const Ref<GDExtension> &p_extension); | ||||||
|  | 
 | ||||||
|  | #ifdef TOOLS_ENABLED | ||||||
|  | 	static void _reload_all_scripts(); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  | 	LoadStatus load_extension(const String &p_path); | ||||||
|  | 	LoadStatus load_extension_with_loader(const String &p_path, const Ref<GDExtensionLoader> &p_loader); | ||||||
|  | 	LoadStatus reload_extension(const String &p_path); | ||||||
|  | 	LoadStatus unload_extension(const String &p_path); | ||||||
|  | 	bool is_extension_loaded(const String &p_path) const; | ||||||
|  | 	Vector<String> get_loaded_extensions() const; | ||||||
|  | 	Ref<GDExtension> get_extension(const String &p_path); | ||||||
|  | 
 | ||||||
|  | 	bool class_has_icon_path(const String &p_class) const; | ||||||
|  | 	String class_get_icon_path(const String &p_class) const; | ||||||
|  | 
 | ||||||
|  | 	void initialize_extensions(GDExtension::InitializationLevel p_level); | ||||||
|  | 	void deinitialize_extensions(GDExtension::InitializationLevel p_level); | ||||||
|  | 
 | ||||||
|  | #ifdef TOOLS_ENABLED | ||||||
|  | 	void track_instance_binding(void *p_token, Object *p_object); | ||||||
|  | 	void untrack_instance_binding(void *p_token, Object *p_object); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 	static GDExtensionManager *get_singleton(); | ||||||
|  | 
 | ||||||
|  | 	void load_extensions(); | ||||||
|  | 	void reload_extensions(); | ||||||
|  | 	bool ensure_extensions_loaded(const HashSet<String> &p_extensions); | ||||||
|  | 
 | ||||||
|  | 	GDExtensionManager(); | ||||||
|  | 	~GDExtensionManager(); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | VARIANT_ENUM_CAST(GDExtensionManager::LoadStatus) | ||||||
							
								
								
									
										1028
									
								
								engine/core/extension/gdextension_special_compat_hashes.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1028
									
								
								engine/core/extension/gdextension_special_compat_hashes.cpp
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										59
									
								
								engine/core/extension/gdextension_special_compat_hashes.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								engine/core/extension/gdextension_special_compat_hashes.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,59 @@ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*  gdextension_special_compat_hashes.h                                   */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*                         This file is part of:                          */ | ||||||
|  | /*                             GODOT ENGINE                               */ | ||||||
|  | /*                        https://godotengine.org                         */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ | ||||||
|  | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */ | ||||||
|  | /*                                                                        */ | ||||||
|  | /* 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.                 */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #ifndef DISABLE_DEPRECATED | ||||||
|  | 
 | ||||||
|  | #include "core/string/string_name.h" | ||||||
|  | #include "core/templates/hash_map.h" | ||||||
|  | #include "core/templates/local_vector.h" | ||||||
|  | 
 | ||||||
|  | // Note: In most situations, compatibility methods should be registered via ClassDB::bind_compatibility_method().
 | ||||||
|  | //       This class is only meant to be used in exceptional circumstances, for example, when Godot's hashing
 | ||||||
|  | //       algorithm changes and registering compatibility methods for all affect methods would be onerous.
 | ||||||
|  | 
 | ||||||
|  | class GDExtensionSpecialCompatHashes { | ||||||
|  | 	struct Mapping { | ||||||
|  | 		StringName method; | ||||||
|  | 		uint32_t legacy_hash; | ||||||
|  | 		uint32_t current_hash; | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	static HashMap<StringName, LocalVector<Mapping>> mappings; | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  | 	static void initialize(); | ||||||
|  | 	static void finalize(); | ||||||
|  | 	static bool lookup_current_hash(const StringName &p_class, const StringName &p_method, uint32_t p_legacy_hash, uint32_t *r_current_hash); | ||||||
|  | 	static bool get_legacy_hashes(const StringName &p_class, const StringName &p_method, Array &r_hashes, bool p_check_valid = true); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | #endif // DISABLE_DEPRECATED
 | ||||||
							
								
								
									
										37
									
								
								engine/core/extension/make_interface_dumper.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								engine/core/extension/make_interface_dumper.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,37 @@ | ||||||
|  | import methods | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def run(target, source, env): | ||||||
|  |     buffer = methods.get_buffer(str(source[0])) | ||||||
|  |     decomp_size = len(buffer) | ||||||
|  |     buffer = methods.compress_buffer(buffer) | ||||||
|  | 
 | ||||||
|  |     with methods.generated_wrapper(str(target[0])) as file: | ||||||
|  |         file.write(f"""\ | ||||||
|  | #ifdef TOOLS_ENABLED | ||||||
|  | 
 | ||||||
|  | #include "core/io/compression.h" | ||||||
|  | #include "core/io/file_access.h" | ||||||
|  | #include "core/string/ustring.h" | ||||||
|  | 
 | ||||||
|  | inline constexpr int _gdextension_interface_data_compressed_size = {len(buffer)}; | ||||||
|  | inline constexpr int _gdextension_interface_data_uncompressed_size = {decomp_size}; | ||||||
|  | inline constexpr unsigned char _gdextension_interface_data_compressed[] = {{ | ||||||
|  | 	{methods.format_buffer(buffer, 1)} | ||||||
|  | }}; | ||||||
|  | 
 | ||||||
|  | class GDExtensionInterfaceDump {{ | ||||||
|  | 	public: | ||||||
|  | 		static void generate_gdextension_interface_file(const String &p_path) {{ | ||||||
|  | 			Ref<FileAccess> fa = FileAccess::open(p_path, FileAccess::WRITE); | ||||||
|  | 			ERR_FAIL_COND_MSG(fa.is_null(), vformat("Cannot open file '%s' for writing.", p_path)); | ||||||
|  | 			Vector<uint8_t> data; | ||||||
|  | 			data.resize(_gdextension_interface_data_uncompressed_size); | ||||||
|  | 			int ret = Compression::decompress(data.ptrw(), _gdextension_interface_data_uncompressed_size, _gdextension_interface_data_compressed, _gdextension_interface_data_compressed_size, Compression::MODE_DEFLATE); | ||||||
|  | 			ERR_FAIL_COND_MSG(ret == -1, "Compressed file is corrupt."); | ||||||
|  | 			fa->store_buffer(data.ptr(), data.size()); | ||||||
|  | 		}}; | ||||||
|  | }}; | ||||||
|  | 
 | ||||||
|  | #endif // TOOLS_ENABLED | ||||||
|  | """) | ||||||
							
								
								
									
										139
									
								
								engine/core/extension/make_wrappers.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										139
									
								
								engine/core/extension/make_wrappers.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,139 @@ | ||||||
|  | proto_mod = """ | ||||||
|  | #define MODBIND$VER($RETTYPE m_name$ARG) \\ | ||||||
|  | virtual $RETVAL _##m_name($FUNCARGS) $CONST; \\ | ||||||
|  | _FORCE_INLINE_ virtual $RETVAL m_name($FUNCARGS) $CONST override { \\ | ||||||
|  |     $RETX _##m_name($CALLARGS);\\ | ||||||
|  | } | ||||||
|  | """ | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def generate_mod_version(argcount, const=False, returns=False): | ||||||
|  |     s = proto_mod | ||||||
|  |     sproto = str(argcount) | ||||||
|  |     if returns: | ||||||
|  |         sproto += "R" | ||||||
|  |         s = s.replace("$RETTYPE", "m_ret, ") | ||||||
|  |         s = s.replace("$RETVAL", "m_ret") | ||||||
|  |         s = s.replace("$RETX", "return") | ||||||
|  | 
 | ||||||
|  |     else: | ||||||
|  |         s = s.replace("$RETTYPE", "") | ||||||
|  |         s = s.replace("$RETVAL", "void") | ||||||
|  |         s = s.replace("$RETX", "") | ||||||
|  | 
 | ||||||
|  |     if const: | ||||||
|  |         sproto += "C" | ||||||
|  |         s = s.replace("$CONST", "const") | ||||||
|  |     else: | ||||||
|  |         s = s.replace("$CONST", "") | ||||||
|  | 
 | ||||||
|  |     s = s.replace("$VER", sproto) | ||||||
|  |     argtext = "" | ||||||
|  |     funcargs = "" | ||||||
|  |     callargs = "" | ||||||
|  | 
 | ||||||
|  |     for i in range(argcount): | ||||||
|  |         if i > 0: | ||||||
|  |             funcargs += ", " | ||||||
|  |             callargs += ", " | ||||||
|  | 
 | ||||||
|  |         argtext += ", m_type" + str(i + 1) | ||||||
|  |         funcargs += "m_type" + str(i + 1) + " arg" + str(i + 1) | ||||||
|  |         callargs += "arg" + str(i + 1) | ||||||
|  | 
 | ||||||
|  |     if argcount: | ||||||
|  |         s = s.replace("$ARG", argtext) | ||||||
|  |         s = s.replace("$FUNCARGS", funcargs) | ||||||
|  |         s = s.replace("$CALLARGS", callargs) | ||||||
|  |     else: | ||||||
|  |         s = s.replace("$ARG", "") | ||||||
|  |         s = s.replace("$FUNCARGS", funcargs) | ||||||
|  |         s = s.replace("$CALLARGS", callargs) | ||||||
|  | 
 | ||||||
|  |     return s | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | proto_ex = """ | ||||||
|  | #define EXBIND$VER($RETTYPE m_name$ARG) \\ | ||||||
|  | GDVIRTUAL$VER_REQUIRED($RETTYPE_##m_name$ARG)\\ | ||||||
|  | virtual $RETVAL m_name($FUNCARGS) $CONST override { \\ | ||||||
|  |     $RETPRE\\ | ||||||
|  |     GDVIRTUAL_CALL(_##m_name$CALLARGS$RETREF);\\ | ||||||
|  |     $RETPOST\\ | ||||||
|  | } | ||||||
|  | """ | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def generate_ex_version(argcount, const=False, returns=False): | ||||||
|  |     s = proto_ex | ||||||
|  |     sproto = str(argcount) | ||||||
|  |     if returns: | ||||||
|  |         sproto += "R" | ||||||
|  |         s = s.replace("$RETTYPE", "m_ret, ") | ||||||
|  |         s = s.replace("$RETVAL", "m_ret") | ||||||
|  |         s = s.replace("$RETPRE", "m_ret ret; ZeroInitializer<m_ret>::initialize(ret);\\\n") | ||||||
|  |         s = s.replace("$RETPOST", "return ret;\\\n") | ||||||
|  | 
 | ||||||
|  |     else: | ||||||
|  |         s = s.replace("$RETTYPE", "") | ||||||
|  |         s = s.replace("$RETVAL", "void") | ||||||
|  |         s = s.replace("$RETPRE", "") | ||||||
|  |         s = s.replace("$RETPOST", "return;") | ||||||
|  | 
 | ||||||
|  |     if const: | ||||||
|  |         sproto += "C" | ||||||
|  |         s = s.replace("$CONST", "const") | ||||||
|  |     else: | ||||||
|  |         s = s.replace("$CONST", "") | ||||||
|  | 
 | ||||||
|  |     s = s.replace("$VER", sproto) | ||||||
|  |     argtext = "" | ||||||
|  |     funcargs = "" | ||||||
|  |     callargs = "" | ||||||
|  | 
 | ||||||
|  |     for i in range(argcount): | ||||||
|  |         if i > 0: | ||||||
|  |             funcargs += ", " | ||||||
|  | 
 | ||||||
|  |         argtext += ", m_type" + str(i + 1) | ||||||
|  |         funcargs += "m_type" + str(i + 1) + " arg" + str(i + 1) | ||||||
|  |         callargs += ", arg" + str(i + 1) | ||||||
|  | 
 | ||||||
|  |     if argcount: | ||||||
|  |         s = s.replace("$ARG", argtext) | ||||||
|  |         s = s.replace("$FUNCARGS", funcargs) | ||||||
|  |         s = s.replace("$CALLARGS", callargs) | ||||||
|  |     else: | ||||||
|  |         s = s.replace("$ARG", "") | ||||||
|  |         s = s.replace("$FUNCARGS", funcargs) | ||||||
|  |         s = s.replace("$CALLARGS", callargs) | ||||||
|  | 
 | ||||||
|  |     if returns: | ||||||
|  |         s = s.replace("$RETREF", ", ret") | ||||||
|  |     else: | ||||||
|  |         s = s.replace("$RETREF", "") | ||||||
|  | 
 | ||||||
|  |     return s | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def run(target, source, env): | ||||||
|  |     max_versions = 12 | ||||||
|  | 
 | ||||||
|  |     txt = "#pragma once" | ||||||
|  | 
 | ||||||
|  |     for i in range(max_versions + 1): | ||||||
|  |         txt += "\n/* Extension Wrapper " + str(i) + " Arguments */\n" | ||||||
|  |         txt += generate_ex_version(i, False, False) | ||||||
|  |         txt += generate_ex_version(i, False, True) | ||||||
|  |         txt += generate_ex_version(i, True, False) | ||||||
|  |         txt += generate_ex_version(i, True, True) | ||||||
|  | 
 | ||||||
|  |     for i in range(max_versions + 1): | ||||||
|  |         txt += "\n/* Module Wrapper " + str(i) + " Arguments */\n" | ||||||
|  |         txt += generate_mod_version(i, False, False) | ||||||
|  |         txt += generate_mod_version(i, False, True) | ||||||
|  |         txt += generate_mod_version(i, True, False) | ||||||
|  |         txt += generate_mod_version(i, True, True) | ||||||
|  | 
 | ||||||
|  |     with open(str(target[0]), "w", encoding="utf-8", newline="\n") as f: | ||||||
|  |         f.write(txt) | ||||||
							
								
								
									
										21
									
								
								engine/core/input/SCsub
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								engine/core/input/SCsub
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,21 @@ | ||||||
|  | #!/usr/bin/env python | ||||||
|  | from misc.utility.scons_hints import * | ||||||
|  | 
 | ||||||
|  | Import("env") | ||||||
|  | 
 | ||||||
|  | import input_builders | ||||||
|  | 
 | ||||||
|  | # Order matters here. Higher index controller database files write on top of lower index database files. | ||||||
|  | controller_databases = [ | ||||||
|  |     "gamecontrollerdb.txt", | ||||||
|  |     "godotcontrollerdb.txt", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | gensource = env.CommandNoCache( | ||||||
|  |     "default_controller_mappings.gen.cpp", | ||||||
|  |     controller_databases, | ||||||
|  |     env.Run(input_builders.make_default_controller_mappings), | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | env.add_source_files(env.core_sources, "*.cpp") | ||||||
|  | env.add_source_files(env.core_sources, gensource) | ||||||
							
								
								
									
										36
									
								
								engine/core/input/default_controller_mappings.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								engine/core/input/default_controller_mappings.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,36 @@ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*  default_controller_mappings.h                                         */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*                         This file is part of:                          */ | ||||||
|  | /*                             GODOT ENGINE                               */ | ||||||
|  | /*                        https://godotengine.org                         */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ | ||||||
|  | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */ | ||||||
|  | /*                                                                        */ | ||||||
|  | /* 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.                 */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | class DefaultControllerMappings { | ||||||
|  | public: | ||||||
|  | 	static const char *mappings[]; | ||||||
|  | }; | ||||||
							
								
								
									
										2089
									
								
								engine/core/input/gamecontrollerdb.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2089
									
								
								engine/core/input/gamecontrollerdb.txt
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										35
									
								
								engine/core/input/godotcontrollerdb.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								engine/core/input/godotcontrollerdb.txt
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,35 @@ | ||||||
|  | # Game Controller DB for Godot in SDL 2.0.16 format | ||||||
|  | # Source: https://github.com/godotengine/godot | ||||||
|  | 
 | ||||||
|  | # Windows | ||||||
|  | __XINPUT_DEVICE__,XInput Gamepad,a:b12,b:b13,x:b14,y:b15,start:b4,guide:b10,back:b5,leftstick:b6,rightstick:b7,leftshoulder:b8,rightshoulder:b9,dpup:b0,dpdown:b1,dpleft:b2,dpright:b3,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,platform:Windows, | ||||||
|  | 
 | ||||||
|  | # Android | ||||||
|  | Default Android Gamepad,Default Controller,leftx:a0,lefty:a1,dpdown:h0.4,rightstick:b8,rightshoulder:b10,rightx:a2,start:b6,righty:a3,dpleft:h0.8,lefttrigger:a4,x:b2,dpup:h0.1,back:b4,leftstick:b7,leftshoulder:b9,y:b3,a:b0,dpright:h0.2,righttrigger:a5,b:b1,platform:Android, | ||||||
|  | 
 | ||||||
|  | # Web | ||||||
|  | standard,Standard Gamepad Mapping,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:+a4,righttrigger:+a5,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b8,start:b9,leftstick:b10,rightstick:b11,dpup:b12,dpdown:b13,dpleft:b14,dpright:b15,guide:b16,leftstick:b10,rightstick:b11,platform:Web, | ||||||
|  | Linux24c6581a,PowerA Xbox One Cabled,a:b0,b:b1,y:b3,x:b2,start:b7,guide:b8,back:b6,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:-a7,dpleft:-a6,dpdown:+a7,dpright:+a6,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Web, | ||||||
|  | Linux0e6f0301,Logic 3 Controller (xbox compatible),a:b0,b:b1,y:b3,x:b2,start:b7,guide:b8,back:b6,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:-a7,dpleft:-a6,dpdown:+a7,dpright:+a6,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Web, | ||||||
|  | Linux045e028e,Microsoft X-Box 360 pad,a:b0,b:b1,y:b3,x:b2,start:b7,guide:b8,back:b6,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:-a7,dpleft:-a6,dpdown:+a7,dpright:+a6,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Web, | ||||||
|  | Linux045e02d1,Microsoft X-Box One pad,a:b0,b:b1,y:b3,x:b2,start:b7,guide:b8,back:b6,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:-a7,dpleft:-a6,dpdown:+a7,dpright:+a6,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Web, | ||||||
|  | Linux045e02ea,Microsoft X-Box One S pad,a:b0,b:b1,y:b3,x:b2,start:b7,guide:b8,back:b6,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:-a7,dpleft:-a6,dpdown:+a7,dpright:+a6,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Web, | ||||||
|  | Linux045e0b12,Microsoft X-Box Series X pad,a:b0,b:b1,y:b3,x:b2,start:b7,guide:b8,back:b6,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:-a7,dpleft:-a6,dpdown:+a7,dpright:+a6,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Web, | ||||||
|  | Linux044fb315,Thrustmaster dual analog 3.2,a:b0,b:b2,y:b3,x:b1,start:b9,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b6,dpup:-a5,dpleft:-a4,dpdown:+a5,dpright:+a4,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b5,righttrigger:b7,platform:Web, | ||||||
|  | Linux0e8f0003,PS3 Controller,a:b2,b:b1,back:b8,dpdown:+a5,dpleft:-a4,dpright:+a4,dpup:-a5,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Web, | ||||||
|  | MacOSX24c6581a,PowerA Xbox One Cabled,a:b11,b:b12,y:b14,x:b13,start:b4,back:b5,leftstick:b6,rightstick:b7,leftshoulder:b8,rightshoulder:b9,dpup:b0,dpleft:b2,dpdown:b1,dpright:b3,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,platform:Web | ||||||
|  | MacOSX045e028e,Xbox 360 Wired Controller,a:b11,b:b12,y:b14,x:b13,start:b4,back:b5,leftstick:b6,rightstick:b7,leftshoulder:b8,rightshoulder:b9,dpup:b0,dpleft:b2,dpdown:b1,dpright:b3,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,platform:Web | ||||||
|  | MacOSX045e02d1,Xbox One Controller,a:b11,b:b12,y:b14,x:b13,start:b4,back:b5,leftstick:b6,rightstick:b7,leftshoulder:b8,rightshoulder:b9,dpup:b0,dpleft:b2,dpdown:b1,dpright:b3,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,platform:Web | ||||||
|  | MacOSX045e02ea,Xbox One S Controller,a:b11,b:b12,y:b14,x:b13,start:b4,back:b5,leftstick:b6,rightstick:b7,leftshoulder:b8,rightshoulder:b9,dpup:b0,dpleft:b2,dpdown:b1,dpright:b3,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,platform:Web | ||||||
|  | MacOSX045e0b12,Xbox Series X Controller,a:b11,b:b12,y:b14,x:b13,start:b4,back:b5,leftstick:b6,rightstick:b7,leftshoulder:b8,rightshoulder:b9,dpup:b0,dpleft:b2,dpdown:b1,dpright:b3,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,platform:Web | ||||||
|  | Linux15320a14,Razer Wolverine Ultimate,a:b0,b:b1,y:b3,x:b2,start:b7,back:b6,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:-a7,dpleft:-a6,dpdown:+a7,dpright:+a6,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Web | ||||||
|  | Linux05832060,iBuffalo BSGP801,a:b1,b:b0,y:b2,x:b3,start:b7,back:b6,leftshoulder:b4,rightshoulder:b5,dpup:-a1,dpleft:-a0,dpdown:+a1,dpright:+a0,platform:Web | ||||||
|  | MacOSX05832060,iBuffalo BSGP801,a:b1,b:b0,y:b2,x:b3,start:b7,back:b6,leftshoulder:b4,rightshoulder:b5,dpup:-a1,dpleft:-a0,dpdown:+a1,dpright:+a0,platform:Web | ||||||
|  | Linux0e8f3013,HuiJia USB GamePad,a:b2,b:b1,y:b0,x:b3,start:b9,back:b8,leftshoulder:b6,rightshoulder:b7,dpup:-a1,dpleft:-a0,dpdown:+a1,dpright:+a0,platform:Web | ||||||
|  | Windows0e8f3013,HuiJia USB GamePad,a:b2,b:b1,y:b0,x:b3,start:b9,back:b8,leftshoulder:b6,rightshoulder:b7,dpup:-a1,dpleft:-a0,dpdown:+a1,dpright:+a0,platform:Web | ||||||
|  | MacOSX0e8f3013,HuiJia USB GamePad,a:b2,b:b1,y:b0,x:b3,start:b9,back:b8,leftshoulder:b6,rightshoulder:b7,dpup:-a4,dpleft:-a3,dpdown:+a4,dpright:+a3,platform:Web | ||||||
|  | Linux046dc216,046d-c216-Logitech Logitech Dual Action,a:b1,b:b2,y:b3,x:b0,start:b9,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:-a5,dpleft:-a4,dpdown:+a5,dpright:+a4,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Web | ||||||
|  | Linux20d6a713,Bensussen Deutsch & Associates Inc.(BDA) NSW Wired controller,a:b1,b:b2,y:b3,x:b0,start:b9,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:-a5,dpleft:-a4,dpdown:+a5,dpright:+a4,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Web | ||||||
|  | Linux054c05c4,Sony Computer Entertainment Wireless Controller,a:b0,b:b1,y:b2,x:b3,start:b9,back:b8,leftstick:b11,rightstick:b12,leftshoulder:b4,rightshoulder:b5,dpup:-a7,dpleft:-a6,dpdown:+a7,dpright:+a6,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Web | ||||||
|  | Linux18d19400,18d1-9400-Google LLC Stadia Controller rev. A,a:b0,b:b1,y:b3,x:b2,start:b7,back:b6,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:-a7,dpleft:-a6,dpdown:+a7,dpright:+a6,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a5,righttrigger:a4,platform:Web | ||||||
|  | Linux054c0268,054c-0268-Sony PLAYSTATION(R)3 Controller,a:b0,b:b1,y:b2,x:b3,start:b9,guide:b10,back:b8,leftstick:b11,rightstick:b12,leftshoulder:b4,rightshoulder:b5,dpup:b13,dpleft:b15,dpdown:b14,dpright:b16,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Web | ||||||
							
								
								
									
										41
									
								
								engine/core/input/input.compat.inc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								engine/core/input/input.compat.inc
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,41 @@ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*  input.compat.inc                                                      */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*                         This file is part of:                          */ | ||||||
|  | /*                             GODOT ENGINE                               */ | ||||||
|  | /*                        https://godotengine.org                         */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ | ||||||
|  | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */ | ||||||
|  | /*                                                                        */ | ||||||
|  | /* 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 DISABLE_DEPRECATED | ||||||
|  | 
 | ||||||
|  | void Input::_vibrate_handheld_bind_compat_91143(int p_duration_ms) { | ||||||
|  | 	vibrate_handheld(p_duration_ms, -1.0); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Input::_bind_compatibility_methods() { | ||||||
|  | 	ClassDB::bind_compatibility_method(D_METHOD("vibrate_handheld", "duration_ms"), &Input::_vibrate_handheld_bind_compat_91143, DEFVAL(500)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #endif // DISABLE_DEPRECATED
 | ||||||
							
								
								
									
										1900
									
								
								engine/core/input/input.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1900
									
								
								engine/core/input/input.cpp
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										404
									
								
								engine/core/input/input.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										404
									
								
								engine/core/input/input.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,404 @@ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*  input.h                                                               */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*                         This file is part of:                          */ | ||||||
|  | /*                             GODOT ENGINE                               */ | ||||||
|  | /*                        https://godotengine.org                         */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ | ||||||
|  | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */ | ||||||
|  | /*                                                                        */ | ||||||
|  | /* 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.                 */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include "core/input/input_event.h" | ||||||
|  | #include "core/object/object.h" | ||||||
|  | #include "core/os/keyboard.h" | ||||||
|  | #include "core/os/thread_safe.h" | ||||||
|  | #include "core/templates/rb_set.h" | ||||||
|  | #include "core/variant/typed_array.h" | ||||||
|  | 
 | ||||||
|  | class Input : public Object { | ||||||
|  | 	GDCLASS(Input, Object); | ||||||
|  | 	_THREAD_SAFE_CLASS_ | ||||||
|  | 
 | ||||||
|  | 	static Input *singleton; | ||||||
|  | 
 | ||||||
|  | 	static constexpr uint64_t MAX_EVENT = 32; | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  | 	// Keep synced with "DisplayServer::MouseMode" enum.
 | ||||||
|  | 	enum MouseMode { | ||||||
|  | 		MOUSE_MODE_VISIBLE, | ||||||
|  | 		MOUSE_MODE_HIDDEN, | ||||||
|  | 		MOUSE_MODE_CAPTURED, | ||||||
|  | 		MOUSE_MODE_CONFINED, | ||||||
|  | 		MOUSE_MODE_CONFINED_HIDDEN, | ||||||
|  | 		MOUSE_MODE_MAX, | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | #undef CursorShape | ||||||
|  | 	enum CursorShape { | ||||||
|  | 		CURSOR_ARROW, | ||||||
|  | 		CURSOR_IBEAM, | ||||||
|  | 		CURSOR_POINTING_HAND, | ||||||
|  | 		CURSOR_CROSS, | ||||||
|  | 		CURSOR_WAIT, | ||||||
|  | 		CURSOR_BUSY, | ||||||
|  | 		CURSOR_DRAG, | ||||||
|  | 		CURSOR_CAN_DROP, | ||||||
|  | 		CURSOR_FORBIDDEN, | ||||||
|  | 		CURSOR_VSIZE, | ||||||
|  | 		CURSOR_HSIZE, | ||||||
|  | 		CURSOR_BDIAGSIZE, | ||||||
|  | 		CURSOR_FDIAGSIZE, | ||||||
|  | 		CURSOR_MOVE, | ||||||
|  | 		CURSOR_VSPLIT, | ||||||
|  | 		CURSOR_HSPLIT, | ||||||
|  | 		CURSOR_HELP, | ||||||
|  | 		CURSOR_MAX | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	enum { | ||||||
|  | 		JOYPADS_MAX = 16, | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	typedef void (*EventDispatchFunc)(const Ref<InputEvent> &p_event); | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  | 	BitField<MouseButtonMask> mouse_button_mask = MouseButtonMask::NONE; | ||||||
|  | 
 | ||||||
|  | 	RBSet<Key> key_label_pressed; | ||||||
|  | 	RBSet<Key> physical_keys_pressed; | ||||||
|  | 	RBSet<Key> keys_pressed; | ||||||
|  | 	RBSet<JoyButton> joy_buttons_pressed; | ||||||
|  | 	RBMap<JoyAxis, float> _joy_axis; | ||||||
|  | 	//RBMap<StringName,int> custom_action_press;
 | ||||||
|  | 	bool gravity_enabled = false; | ||||||
|  | 	Vector3 gravity; | ||||||
|  | 	bool accelerometer_enabled = false; | ||||||
|  | 	Vector3 accelerometer; | ||||||
|  | 	bool magnetometer_enabled = false; | ||||||
|  | 	Vector3 magnetometer; | ||||||
|  | 	bool gyroscope_enabled = false; | ||||||
|  | 	Vector3 gyroscope; | ||||||
|  | 	Vector2 mouse_pos; | ||||||
|  | 	int64_t mouse_window = 0; | ||||||
|  | 	bool legacy_just_pressed_behavior = false; | ||||||
|  | 	bool disable_input = false; | ||||||
|  | 
 | ||||||
|  | 	struct ActionState { | ||||||
|  | 		uint64_t pressed_physics_frame = UINT64_MAX; | ||||||
|  | 		uint64_t pressed_process_frame = UINT64_MAX; | ||||||
|  | 		uint64_t released_physics_frame = UINT64_MAX; | ||||||
|  | 		uint64_t released_process_frame = UINT64_MAX; | ||||||
|  | 		bool exact = true; | ||||||
|  | 
 | ||||||
|  | 		struct DeviceState { | ||||||
|  | 			bool pressed[MAX_EVENT] = { false }; | ||||||
|  | 			float strength[MAX_EVENT] = { 0.0 }; | ||||||
|  | 			float raw_strength[MAX_EVENT] = { 0.0 }; | ||||||
|  | 		}; | ||||||
|  | 		bool api_pressed = false; | ||||||
|  | 		float api_strength = 0.0; | ||||||
|  | 		HashMap<int, DeviceState> device_states; | ||||||
|  | 
 | ||||||
|  | 		// Cache.
 | ||||||
|  | 		struct ActionStateCache { | ||||||
|  | 			bool pressed = false; | ||||||
|  | 			float strength = false; | ||||||
|  | 			float raw_strength = false; | ||||||
|  | 		} cache; | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	HashMap<StringName, ActionState> action_states; | ||||||
|  | 
 | ||||||
|  | 	bool emulate_touch_from_mouse = false; | ||||||
|  | 	bool emulate_mouse_from_touch = false; | ||||||
|  | 	bool agile_input_event_flushing = false; | ||||||
|  | 	bool use_accumulated_input = true; | ||||||
|  | 
 | ||||||
|  | 	int mouse_from_touch_index = -1; | ||||||
|  | 
 | ||||||
|  | 	struct VibrationInfo { | ||||||
|  | 		float weak_magnitude; | ||||||
|  | 		float strong_magnitude; | ||||||
|  | 		float duration; // Duration in seconds
 | ||||||
|  | 		uint64_t timestamp; | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	HashMap<int, VibrationInfo> joy_vibration; | ||||||
|  | 
 | ||||||
|  | 	struct VelocityTrack { | ||||||
|  | 		uint64_t last_tick = 0; | ||||||
|  | 		Vector2 velocity; | ||||||
|  | 		Vector2 screen_velocity; | ||||||
|  | 		Vector2 accum; | ||||||
|  | 		Vector2 screen_accum; | ||||||
|  | 		float accum_t = 0.0f; | ||||||
|  | 		float min_ref_frame; | ||||||
|  | 		float max_ref_frame; | ||||||
|  | 
 | ||||||
|  | 		void update(const Vector2 &p_delta_p, const Vector2 &p_screen_delta_p); | ||||||
|  | 		void reset(); | ||||||
|  | 		VelocityTrack(); | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	struct Joypad { | ||||||
|  | 		StringName name; | ||||||
|  | 		StringName uid; | ||||||
|  | 		bool connected = false; | ||||||
|  | 		bool last_buttons[(size_t)JoyButton::MAX] = { false }; | ||||||
|  | 		float last_axis[(size_t)JoyAxis::MAX] = { 0.0f }; | ||||||
|  | 		HatMask last_hat = HatMask::CENTER; | ||||||
|  | 		int mapping = -1; | ||||||
|  | 		int hat_current = 0; | ||||||
|  | 		Dictionary info; | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	VelocityTrack mouse_velocity_track; | ||||||
|  | 	HashMap<int, VelocityTrack> touch_velocity_track; | ||||||
|  | 	HashMap<int, Joypad> joy_names; | ||||||
|  | 
 | ||||||
|  | 	HashSet<uint32_t> ignored_device_ids; | ||||||
|  | 
 | ||||||
|  | 	int fallback_mapping = -1; // Index of the guid in map_db.
 | ||||||
|  | 
 | ||||||
|  | 	CursorShape default_shape = CURSOR_ARROW; | ||||||
|  | 
 | ||||||
|  | 	enum JoyType { | ||||||
|  | 		TYPE_BUTTON, | ||||||
|  | 		TYPE_AXIS, | ||||||
|  | 		TYPE_HAT, | ||||||
|  | 		TYPE_MAX, | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	enum JoyAxisRange { | ||||||
|  | 		NEGATIVE_HALF_AXIS = -1, | ||||||
|  | 		FULL_AXIS = 0, | ||||||
|  | 		POSITIVE_HALF_AXIS = 1 | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	struct JoyEvent { | ||||||
|  | 		int type = TYPE_MAX; | ||||||
|  | 		int index = -1; // Can be either JoyAxis or JoyButton.
 | ||||||
|  | 		float value = 0.f; | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	struct JoyBinding { | ||||||
|  | 		JoyType inputType; | ||||||
|  | 		union { | ||||||
|  | 			JoyButton button; | ||||||
|  | 
 | ||||||
|  | 			struct { | ||||||
|  | 				JoyAxis axis; | ||||||
|  | 				JoyAxisRange range; | ||||||
|  | 				bool invert; | ||||||
|  | 			} axis; | ||||||
|  | 
 | ||||||
|  | 			struct { | ||||||
|  | 				HatDir hat; | ||||||
|  | 				HatMask hat_mask; | ||||||
|  | 			} hat; | ||||||
|  | 
 | ||||||
|  | 		} input; | ||||||
|  | 
 | ||||||
|  | 		JoyType outputType; | ||||||
|  | 		union { | ||||||
|  | 			JoyButton button; | ||||||
|  | 
 | ||||||
|  | 			struct { | ||||||
|  | 				JoyAxis axis; | ||||||
|  | 				JoyAxisRange range; | ||||||
|  | 			} axis; | ||||||
|  | 
 | ||||||
|  | 		} output; | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	struct JoyDeviceMapping { | ||||||
|  | 		String uid; | ||||||
|  | 		String name; | ||||||
|  | 		Vector<JoyBinding> bindings; | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	Vector<JoyDeviceMapping> map_db; | ||||||
|  | 
 | ||||||
|  | 	void _set_joypad_mapping(Joypad &p_js, int p_map_index); | ||||||
|  | 
 | ||||||
|  | 	JoyEvent _get_mapped_button_event(const JoyDeviceMapping &mapping, JoyButton p_button); | ||||||
|  | 	JoyEvent _get_mapped_axis_event(const JoyDeviceMapping &mapping, JoyAxis p_axis, float p_value, JoyAxisRange &r_range); | ||||||
|  | 	void _get_mapped_hat_events(const JoyDeviceMapping &mapping, HatDir p_hat, JoyEvent r_events[(size_t)HatDir::MAX]); | ||||||
|  | 	JoyButton _get_output_button(const String &output); | ||||||
|  | 	JoyAxis _get_output_axis(const String &output); | ||||||
|  | 	void _button_event(int p_device, JoyButton p_index, bool p_pressed); | ||||||
|  | 	void _axis_event(int p_device, JoyAxis p_axis, float p_value); | ||||||
|  | 	void _update_action_cache(const StringName &p_action_name, ActionState &r_action_state); | ||||||
|  | 
 | ||||||
|  | 	void _parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_emulated); | ||||||
|  | 
 | ||||||
|  | 	List<Ref<InputEvent>> buffered_events; | ||||||
|  | #ifdef DEBUG_ENABLED | ||||||
|  | 	HashSet<Ref<InputEvent>> frame_parsed_events; | ||||||
|  | 	uint64_t last_parsed_frame = UINT64_MAX; | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 	friend class DisplayServer; | ||||||
|  | 
 | ||||||
|  | 	static void (*set_mouse_mode_func)(MouseMode); | ||||||
|  | 	static MouseMode (*get_mouse_mode_func)(); | ||||||
|  | 	static void (*set_mouse_mode_override_func)(MouseMode); | ||||||
|  | 	static MouseMode (*get_mouse_mode_override_func)(); | ||||||
|  | 	static void (*set_mouse_mode_override_enabled_func)(bool); | ||||||
|  | 	static bool (*is_mouse_mode_override_enabled_func)(); | ||||||
|  | 	static void (*warp_mouse_func)(const Vector2 &p_position); | ||||||
|  | 
 | ||||||
|  | 	static CursorShape (*get_current_cursor_shape_func)(); | ||||||
|  | 	static void (*set_custom_mouse_cursor_func)(const Ref<Resource> &, CursorShape, const Vector2 &); | ||||||
|  | 
 | ||||||
|  | 	EventDispatchFunc event_dispatch_function = nullptr; | ||||||
|  | 
 | ||||||
|  | #ifndef DISABLE_DEPRECATED | ||||||
|  | 	void _vibrate_handheld_bind_compat_91143(int p_duration_ms = 500); | ||||||
|  | 	static void _bind_compatibility_methods(); | ||||||
|  | #endif // DISABLE_DEPRECATED
 | ||||||
|  | 
 | ||||||
|  | protected: | ||||||
|  | 	static void _bind_methods(); | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  | 	void set_mouse_mode(MouseMode p_mode); | ||||||
|  | 	MouseMode get_mouse_mode() const; | ||||||
|  | 	void set_mouse_mode_override(MouseMode p_mode); | ||||||
|  | 	MouseMode get_mouse_mode_override() const; | ||||||
|  | 	void set_mouse_mode_override_enabled(bool p_override_enabled); | ||||||
|  | 	bool is_mouse_mode_override_enabled(); | ||||||
|  | 
 | ||||||
|  | #ifdef TOOLS_ENABLED | ||||||
|  | 	void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override; | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 	static Input *get_singleton(); | ||||||
|  | 
 | ||||||
|  | 	bool is_anything_pressed() const; | ||||||
|  | 	bool is_anything_pressed_except_mouse() const; | ||||||
|  | 	bool is_key_pressed(Key p_keycode) const; | ||||||
|  | 	bool is_physical_key_pressed(Key p_keycode) const; | ||||||
|  | 	bool is_key_label_pressed(Key p_keycode) const; | ||||||
|  | 	bool is_mouse_button_pressed(MouseButton p_button) const; | ||||||
|  | 	bool is_joy_button_pressed(int p_device, JoyButton p_button) const; | ||||||
|  | 	bool is_action_pressed(const StringName &p_action, bool p_exact = false) const; | ||||||
|  | 	bool is_action_just_pressed(const StringName &p_action, bool p_exact = false) const; | ||||||
|  | 	bool is_action_just_released(const StringName &p_action, bool p_exact = false) const; | ||||||
|  | 	float get_action_strength(const StringName &p_action, bool p_exact = false) const; | ||||||
|  | 	float get_action_raw_strength(const StringName &p_action, bool p_exact = false) const; | ||||||
|  | 
 | ||||||
|  | 	float get_axis(const StringName &p_negative_action, const StringName &p_positive_action) const; | ||||||
|  | 	Vector2 get_vector(const StringName &p_negative_x, const StringName &p_positive_x, const StringName &p_negative_y, const StringName &p_positive_y, float p_deadzone = -1.0f) const; | ||||||
|  | 
 | ||||||
|  | 	float get_joy_axis(int p_device, JoyAxis p_axis) const; | ||||||
|  | 	String get_joy_name(int p_idx); | ||||||
|  | 	TypedArray<int> get_connected_joypads(); | ||||||
|  | 	Vector2 get_joy_vibration_strength(int p_device); | ||||||
|  | 	float get_joy_vibration_duration(int p_device); | ||||||
|  | 	uint64_t get_joy_vibration_timestamp(int p_device); | ||||||
|  | 	void joy_connection_changed(int p_idx, bool p_connected, const String &p_name, const String &p_guid = "", const Dictionary &p_joypad_info = Dictionary()); | ||||||
|  | 
 | ||||||
|  | 	Vector3 get_gravity() const; | ||||||
|  | 	Vector3 get_accelerometer() const; | ||||||
|  | 	Vector3 get_magnetometer() const; | ||||||
|  | 	Vector3 get_gyroscope() const; | ||||||
|  | 
 | ||||||
|  | 	Point2 get_mouse_position() const; | ||||||
|  | 	Vector2 get_last_mouse_velocity(); | ||||||
|  | 	Vector2 get_last_mouse_screen_velocity(); | ||||||
|  | 	BitField<MouseButtonMask> get_mouse_button_mask() const; | ||||||
|  | 
 | ||||||
|  | 	void warp_mouse(const Vector2 &p_position); | ||||||
|  | 	Point2 warp_mouse_motion(const Ref<InputEventMouseMotion> &p_motion, const Rect2 &p_rect); | ||||||
|  | 
 | ||||||
|  | 	void parse_input_event(const Ref<InputEvent> &p_event); | ||||||
|  | 
 | ||||||
|  | 	void set_gravity(const Vector3 &p_gravity); | ||||||
|  | 	void set_accelerometer(const Vector3 &p_accel); | ||||||
|  | 	void set_magnetometer(const Vector3 &p_magnetometer); | ||||||
|  | 	void set_gyroscope(const Vector3 &p_gyroscope); | ||||||
|  | 	void set_joy_axis(int p_device, JoyAxis p_axis, float p_value); | ||||||
|  | 
 | ||||||
|  | 	void start_joy_vibration(int p_device, float p_weak_magnitude, float p_strong_magnitude, float p_duration = 0); | ||||||
|  | 	void stop_joy_vibration(int p_device); | ||||||
|  | 	void vibrate_handheld(int p_duration_ms = 500, float p_amplitude = -1.0); | ||||||
|  | 
 | ||||||
|  | 	void set_mouse_position(const Point2 &p_posf); | ||||||
|  | 
 | ||||||
|  | 	void action_press(const StringName &p_action, float p_strength = 1.f); | ||||||
|  | 	void action_release(const StringName &p_action); | ||||||
|  | 
 | ||||||
|  | 	void set_emulate_touch_from_mouse(bool p_emulate); | ||||||
|  | 	bool is_emulating_touch_from_mouse() const; | ||||||
|  | 	void ensure_touch_mouse_raised(); | ||||||
|  | 
 | ||||||
|  | 	void set_emulate_mouse_from_touch(bool p_emulate); | ||||||
|  | 	bool is_emulating_mouse_from_touch() const; | ||||||
|  | 
 | ||||||
|  | 	CursorShape get_default_cursor_shape() const; | ||||||
|  | 	void set_default_cursor_shape(CursorShape p_shape); | ||||||
|  | 	CursorShape get_current_cursor_shape() const; | ||||||
|  | 	void set_custom_mouse_cursor(const Ref<Resource> &p_cursor, CursorShape p_shape = Input::CURSOR_ARROW, const Vector2 &p_hotspot = Vector2()); | ||||||
|  | 
 | ||||||
|  | 	void parse_mapping(const String &p_mapping); | ||||||
|  | 	void joy_button(int p_device, JoyButton p_button, bool p_pressed); | ||||||
|  | 	void joy_axis(int p_device, JoyAxis p_axis, float p_value); | ||||||
|  | 	void joy_hat(int p_device, BitField<HatMask> p_val); | ||||||
|  | 
 | ||||||
|  | 	void add_joy_mapping(const String &p_mapping, bool p_update_existing = false); | ||||||
|  | 	void remove_joy_mapping(const String &p_guid); | ||||||
|  | 
 | ||||||
|  | 	int get_unused_joy_id(); | ||||||
|  | 
 | ||||||
|  | 	bool is_joy_known(int p_device); | ||||||
|  | 	String get_joy_guid(int p_device) const; | ||||||
|  | 	bool should_ignore_device(int p_vendor_id, int p_product_id) const; | ||||||
|  | 	Dictionary get_joy_info(int p_device) const; | ||||||
|  | 	void set_fallback_mapping(const String &p_guid); | ||||||
|  | 
 | ||||||
|  | #ifdef DEBUG_ENABLED | ||||||
|  | 	void flush_frame_parsed_events(); | ||||||
|  | #endif | ||||||
|  | 	void flush_buffered_events(); | ||||||
|  | 	bool is_agile_input_event_flushing(); | ||||||
|  | 	void set_agile_input_event_flushing(bool p_enable); | ||||||
|  | 	void set_use_accumulated_input(bool p_enable); | ||||||
|  | 	bool is_using_accumulated_input(); | ||||||
|  | 
 | ||||||
|  | 	void release_pressed_events(); | ||||||
|  | 
 | ||||||
|  | 	void set_event_dispatch_function(EventDispatchFunc p_function); | ||||||
|  | 
 | ||||||
|  | 	void set_disable_input(bool p_disable); | ||||||
|  | 	bool is_input_disabled() const; | ||||||
|  | 
 | ||||||
|  | 	Input(); | ||||||
|  | 	~Input(); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | VARIANT_ENUM_CAST(Input::MouseMode); | ||||||
|  | VARIANT_ENUM_CAST(Input::CursorShape); | ||||||
							
								
								
									
										63
									
								
								engine/core/input/input_builders.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								engine/core/input/input_builders.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,63 @@ | ||||||
|  | """Functions used to generate source files during build time""" | ||||||
|  | 
 | ||||||
|  | from collections import OrderedDict | ||||||
|  | 
 | ||||||
|  | import methods | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def make_default_controller_mappings(target, source, env): | ||||||
|  |     with methods.generated_wrapper(str(target[0])) as file: | ||||||
|  |         file.write("""\ | ||||||
|  | #include "core/input/default_controller_mappings.h" | ||||||
|  | 
 | ||||||
|  | #include "core/typedefs.h" | ||||||
|  | 
 | ||||||
|  | """) | ||||||
|  | 
 | ||||||
|  |         # ensure mappings have a consistent order | ||||||
|  |         platform_mappings = OrderedDict() | ||||||
|  |         for src_path in map(str, source): | ||||||
|  |             with open(src_path, "r", encoding="utf-8") as f: | ||||||
|  |                 # read mapping file and skip header | ||||||
|  |                 mapping_file_lines = f.readlines()[2:] | ||||||
|  | 
 | ||||||
|  |             current_platform = None | ||||||
|  |             for line in mapping_file_lines: | ||||||
|  |                 if not line: | ||||||
|  |                     continue | ||||||
|  |                 line = line.strip() | ||||||
|  |                 if len(line) == 0: | ||||||
|  |                     continue | ||||||
|  |                 if line[0] == "#": | ||||||
|  |                     current_platform = line[1:].strip() | ||||||
|  |                     if current_platform not in platform_mappings: | ||||||
|  |                         platform_mappings[current_platform] = {} | ||||||
|  |                 elif current_platform: | ||||||
|  |                     line_parts = line.split(",") | ||||||
|  |                     guid = line_parts[0] | ||||||
|  |                     if guid in platform_mappings[current_platform]: | ||||||
|  |                         file.write( | ||||||
|  |                             "// WARNING: DATABASE {} OVERWROTE PRIOR MAPPING: {} {}\n".format( | ||||||
|  |                                 src_path, current_platform, platform_mappings[current_platform][guid] | ||||||
|  |                             ) | ||||||
|  |                         ) | ||||||
|  |                     platform_mappings[current_platform][guid] = line | ||||||
|  | 
 | ||||||
|  |         PLATFORM_VARIABLES = { | ||||||
|  |             "Linux": "LINUXBSD", | ||||||
|  |             "Windows": "WINDOWS", | ||||||
|  |             "Mac OS X": "MACOS", | ||||||
|  |             "Android": "ANDROID", | ||||||
|  |             "iOS": "IOS", | ||||||
|  |             "Web": "WEB", | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         file.write("const char *DefaultControllerMappings::mappings[] = {\n") | ||||||
|  |         for platform, mappings in platform_mappings.items(): | ||||||
|  |             variable = PLATFORM_VARIABLES[platform] | ||||||
|  |             file.write(f"#ifdef {variable}_ENABLED\n") | ||||||
|  |             for mapping in mappings.values(): | ||||||
|  |                 file.write(f'\t"{mapping}",\n') | ||||||
|  |             file.write(f"#endif // {variable}_ENABLED\n") | ||||||
|  | 
 | ||||||
|  |         file.write("\tnullptr\n};\n") | ||||||
							
								
								
									
										146
									
								
								engine/core/input/input_enums.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										146
									
								
								engine/core/input/input_enums.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,146 @@ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*  input_enums.h                                                         */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*                         This file is part of:                          */ | ||||||
|  | /*                             GODOT ENGINE                               */ | ||||||
|  | /*                        https://godotengine.org                         */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ | ||||||
|  | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */ | ||||||
|  | /*                                                                        */ | ||||||
|  | /* 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.                 */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include "core/error/error_macros.h" | ||||||
|  | 
 | ||||||
|  | enum class HatDir { | ||||||
|  | 	UP = 0, | ||||||
|  | 	RIGHT = 1, | ||||||
|  | 	DOWN = 2, | ||||||
|  | 	LEFT = 3, | ||||||
|  | 	MAX = 4, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | enum class HatMask { | ||||||
|  | 	CENTER = 0, | ||||||
|  | 	UP = 1, | ||||||
|  | 	RIGHT = 2, | ||||||
|  | 	DOWN = 4, | ||||||
|  | 	LEFT = 8, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | enum class JoyAxis { | ||||||
|  | 	INVALID = -1, | ||||||
|  | 	LEFT_X = 0, | ||||||
|  | 	LEFT_Y = 1, | ||||||
|  | 	RIGHT_X = 2, | ||||||
|  | 	RIGHT_Y = 3, | ||||||
|  | 	TRIGGER_LEFT = 4, | ||||||
|  | 	TRIGGER_RIGHT = 5, | ||||||
|  | 	SDL_MAX = 6, | ||||||
|  | 	MAX = 10, // OpenVR supports up to 5 Joysticks making a total of 10 axes.
 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | enum class JoyButton { | ||||||
|  | 	INVALID = -1, | ||||||
|  | 	A = 0, | ||||||
|  | 	B = 1, | ||||||
|  | 	X = 2, | ||||||
|  | 	Y = 3, | ||||||
|  | 	BACK = 4, | ||||||
|  | 	GUIDE = 5, | ||||||
|  | 	START = 6, | ||||||
|  | 	LEFT_STICK = 7, | ||||||
|  | 	RIGHT_STICK = 8, | ||||||
|  | 	LEFT_SHOULDER = 9, | ||||||
|  | 	RIGHT_SHOULDER = 10, | ||||||
|  | 	DPAD_UP = 11, | ||||||
|  | 	DPAD_DOWN = 12, | ||||||
|  | 	DPAD_LEFT = 13, | ||||||
|  | 	DPAD_RIGHT = 14, | ||||||
|  | 	MISC1 = 15, | ||||||
|  | 	PADDLE1 = 16, | ||||||
|  | 	PADDLE2 = 17, | ||||||
|  | 	PADDLE3 = 18, | ||||||
|  | 	PADDLE4 = 19, | ||||||
|  | 	TOUCHPAD = 20, | ||||||
|  | 	SDL_MAX = 21, | ||||||
|  | 	MAX = 128, // Android supports up to 36 buttons. DirectInput supports up to 128 buttons.
 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | enum class MIDIMessage { | ||||||
|  | 	NONE = 0, | ||||||
|  | 	NOTE_OFF = 0x8, | ||||||
|  | 	NOTE_ON = 0x9, | ||||||
|  | 	AFTERTOUCH = 0xA, | ||||||
|  | 	CONTROL_CHANGE = 0xB, | ||||||
|  | 	PROGRAM_CHANGE = 0xC, | ||||||
|  | 	CHANNEL_PRESSURE = 0xD, | ||||||
|  | 	PITCH_BEND = 0xE, | ||||||
|  | 	SYSTEM_EXCLUSIVE = 0xF0, | ||||||
|  | 	QUARTER_FRAME = 0xF1, | ||||||
|  | 	SONG_POSITION_POINTER = 0xF2, | ||||||
|  | 	SONG_SELECT = 0xF3, | ||||||
|  | 	TUNE_REQUEST = 0xF6, | ||||||
|  | 	TIMING_CLOCK = 0xF8, | ||||||
|  | 	START = 0xFA, | ||||||
|  | 	CONTINUE = 0xFB, | ||||||
|  | 	STOP = 0xFC, | ||||||
|  | 	ACTIVE_SENSING = 0xFE, | ||||||
|  | 	SYSTEM_RESET = 0xFF, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | enum class MouseButton { | ||||||
|  | 	NONE = 0, | ||||||
|  | 	LEFT = 1, | ||||||
|  | 	RIGHT = 2, | ||||||
|  | 	MIDDLE = 3, | ||||||
|  | 	WHEEL_UP = 4, | ||||||
|  | 	WHEEL_DOWN = 5, | ||||||
|  | 	WHEEL_LEFT = 6, | ||||||
|  | 	WHEEL_RIGHT = 7, | ||||||
|  | 	MB_XBUTTON1 = 8, // "XBUTTON1" is a reserved word on Windows.
 | ||||||
|  | 	MB_XBUTTON2 = 9, // "XBUTTON2" is a reserved word on Windows.
 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | enum class MouseButtonMask { | ||||||
|  | 	NONE = 0, | ||||||
|  | 	LEFT = (1 << (int(MouseButton::LEFT) - 1)), | ||||||
|  | 	RIGHT = (1 << (int(MouseButton::RIGHT) - 1)), | ||||||
|  | 	MIDDLE = (1 << (int(MouseButton::MIDDLE) - 1)), | ||||||
|  | 	MB_XBUTTON1 = (1 << (int(MouseButton::MB_XBUTTON1) - 1)), | ||||||
|  | 	MB_XBUTTON2 = (1 << (int(MouseButton::MB_XBUTTON2) - 1)), | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | inline MouseButtonMask mouse_button_to_mask(MouseButton button) { | ||||||
|  | 	ERR_FAIL_COND_V(button == MouseButton::NONE, MouseButtonMask::NONE); | ||||||
|  | 
 | ||||||
|  | 	return MouseButtonMask(1 << ((int)button - 1)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | constexpr MouseButtonMask operator|(MouseButtonMask p_a, MouseButtonMask p_b) { | ||||||
|  | 	return static_cast<MouseButtonMask>(static_cast<int>(p_a) | static_cast<int>(p_b)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | constexpr MouseButtonMask &operator|=(MouseButtonMask &p_a, MouseButtonMask p_b) { | ||||||
|  | 	return p_a = p_a | p_b; | ||||||
|  | } | ||||||
							
								
								
									
										1932
									
								
								engine/core/input/input_event.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1932
									
								
								engine/core/input/input_event.cpp
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										596
									
								
								engine/core/input/input_event.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										596
									
								
								engine/core/input/input_event.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,596 @@ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*  input_event.h                                                         */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*                         This file is part of:                          */ | ||||||
|  | /*                             GODOT ENGINE                               */ | ||||||
|  | /*                        https://godotengine.org                         */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ | ||||||
|  | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */ | ||||||
|  | /*                                                                        */ | ||||||
|  | /* 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.                 */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include "core/input/input_enums.h" | ||||||
|  | #include "core/io/resource.h" | ||||||
|  | #include "core/math/transform_2d.h" | ||||||
|  | #include "core/os/keyboard.h" | ||||||
|  | #include "core/string/ustring.h" | ||||||
|  | #include "core/typedefs.h" | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Input Event classes. These are used in the main loop. | ||||||
|  |  * The events are pretty obvious. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | class Shortcut; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Input Modifier Status | ||||||
|  |  * for keyboard/mouse events. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | class InputEvent : public Resource { | ||||||
|  | 	GDCLASS(InputEvent, Resource); | ||||||
|  | 
 | ||||||
|  | 	int device = 0; | ||||||
|  | 
 | ||||||
|  | protected: | ||||||
|  | 	bool canceled = false; | ||||||
|  | 	bool pressed = false; | ||||||
|  | 
 | ||||||
|  | 	static void _bind_methods(); | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  | 	static const int DEVICE_ID_EMULATION; | ||||||
|  | 	static const int DEVICE_ID_INTERNAL; | ||||||
|  | 
 | ||||||
|  | 	void set_device(int p_device); | ||||||
|  | 	int get_device() const; | ||||||
|  | 
 | ||||||
|  | 	bool is_action(const StringName &p_action, bool p_exact_match = false) const; | ||||||
|  | 	bool is_action_pressed(const StringName &p_action, bool p_allow_echo = false, bool p_exact_match = false) const; | ||||||
|  | 	bool is_action_released(const StringName &p_action, bool p_exact_match = false) const; | ||||||
|  | 	float get_action_strength(const StringName &p_action, bool p_exact_match = false) const; | ||||||
|  | 	float get_action_raw_strength(const StringName &p_action, bool p_exact_match = false) const; | ||||||
|  | 
 | ||||||
|  | 	bool is_canceled() const; | ||||||
|  | 	bool is_pressed() const; | ||||||
|  | 	bool is_released() const; | ||||||
|  | 	virtual bool is_echo() const; | ||||||
|  | 
 | ||||||
|  | 	virtual String as_text() const = 0; | ||||||
|  | 
 | ||||||
|  | 	virtual Ref<InputEvent> xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs = Vector2()) const; | ||||||
|  | 
 | ||||||
|  | 	virtual bool action_match(const Ref<InputEvent> &p_event, bool p_exact_match, float p_deadzone, bool *r_pressed, float *r_strength, float *r_raw_strength) const; | ||||||
|  | 	virtual bool is_match(const Ref<InputEvent> &p_event, bool p_exact_match = true) const; | ||||||
|  | 
 | ||||||
|  | 	virtual bool is_action_type() const; | ||||||
|  | 
 | ||||||
|  | 	virtual bool accumulate(const Ref<InputEvent> &p_event) { return false; } | ||||||
|  | 
 | ||||||
|  | 	InputEvent() {} | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class InputEventFromWindow : public InputEvent { | ||||||
|  | 	GDCLASS(InputEventFromWindow, InputEvent); | ||||||
|  | 
 | ||||||
|  | 	int64_t window_id = 0; | ||||||
|  | 
 | ||||||
|  | protected: | ||||||
|  | 	static void _bind_methods(); | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  | 	void set_window_id(int64_t p_id); | ||||||
|  | 	int64_t get_window_id() const; | ||||||
|  | 
 | ||||||
|  | 	InputEventFromWindow() {} | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class InputEventWithModifiers : public InputEventFromWindow { | ||||||
|  | 	GDCLASS(InputEventWithModifiers, InputEventFromWindow); | ||||||
|  | 
 | ||||||
|  | 	bool command_or_control_autoremap = false; | ||||||
|  | 
 | ||||||
|  | 	bool shift_pressed = false; | ||||||
|  | 	bool alt_pressed = false; | ||||||
|  | 	bool meta_pressed = false; // "Command" on macOS, "Meta/Win" key on other platforms.
 | ||||||
|  | 	bool ctrl_pressed = false; | ||||||
|  | 
 | ||||||
|  | protected: | ||||||
|  | 	static void _bind_methods(); | ||||||
|  | 	void _validate_property(PropertyInfo &p_property) const; | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  | 	void set_command_or_control_autoremap(bool p_enabled); | ||||||
|  | 	bool is_command_or_control_autoremap() const; | ||||||
|  | 
 | ||||||
|  | 	bool is_command_or_control_pressed() const; | ||||||
|  | 
 | ||||||
|  | 	void set_shift_pressed(bool p_pressed); | ||||||
|  | 	bool is_shift_pressed() const; | ||||||
|  | 
 | ||||||
|  | 	void set_alt_pressed(bool p_pressed); | ||||||
|  | 	bool is_alt_pressed() const; | ||||||
|  | 
 | ||||||
|  | 	void set_ctrl_pressed(bool p_pressed); | ||||||
|  | 	bool is_ctrl_pressed() const; | ||||||
|  | 
 | ||||||
|  | 	void set_meta_pressed(bool p_pressed); | ||||||
|  | 	bool is_meta_pressed() const; | ||||||
|  | 
 | ||||||
|  | 	void set_modifiers_from_event(const InputEventWithModifiers *event); | ||||||
|  | 
 | ||||||
|  | 	BitField<KeyModifierMask> get_modifiers_mask() const; | ||||||
|  | 
 | ||||||
|  | 	virtual String as_text() const override; | ||||||
|  | 	virtual String to_string() override; | ||||||
|  | 
 | ||||||
|  | 	InputEventWithModifiers() {} | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class InputEventKey : public InputEventWithModifiers { | ||||||
|  | 	GDCLASS(InputEventKey, InputEventWithModifiers); | ||||||
|  | 
 | ||||||
|  | 	Key keycode = Key::NONE; // Key enum, without modifier masks.
 | ||||||
|  | 	Key physical_keycode = Key::NONE; | ||||||
|  | 	Key key_label = Key::NONE; | ||||||
|  | 	uint32_t unicode = 0; ///unicode
 | ||||||
|  | 	KeyLocation location = KeyLocation::UNSPECIFIED; | ||||||
|  | 
 | ||||||
|  | 	bool echo = false; /// true if this is an echo key
 | ||||||
|  | 
 | ||||||
|  | protected: | ||||||
|  | 	static void _bind_methods(); | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  | 	void set_pressed(bool p_pressed); | ||||||
|  | 
 | ||||||
|  | 	void set_keycode(Key p_keycode); | ||||||
|  | 	Key get_keycode() const; | ||||||
|  | 
 | ||||||
|  | 	void set_physical_keycode(Key p_keycode); | ||||||
|  | 	Key get_physical_keycode() const; | ||||||
|  | 
 | ||||||
|  | 	void set_key_label(Key p_key_label); | ||||||
|  | 	Key get_key_label() const; | ||||||
|  | 
 | ||||||
|  | 	void set_unicode(char32_t p_unicode); | ||||||
|  | 	char32_t get_unicode() const; | ||||||
|  | 
 | ||||||
|  | 	void set_location(KeyLocation p_key_location); | ||||||
|  | 	KeyLocation get_location() const; | ||||||
|  | 
 | ||||||
|  | 	void set_echo(bool p_enable); | ||||||
|  | 	virtual bool is_echo() const override; | ||||||
|  | 
 | ||||||
|  | 	Key get_keycode_with_modifiers() const; | ||||||
|  | 	Key get_physical_keycode_with_modifiers() const; | ||||||
|  | 	Key get_key_label_with_modifiers() const; | ||||||
|  | 
 | ||||||
|  | 	virtual bool action_match(const Ref<InputEvent> &p_event, bool p_exact_match, float p_deadzone, bool *r_pressed, float *r_strength, float *r_raw_strength) const override; | ||||||
|  | 	virtual bool is_match(const Ref<InputEvent> &p_event, bool p_exact_match = true) const override; | ||||||
|  | 
 | ||||||
|  | 	virtual bool is_action_type() const override { return true; } | ||||||
|  | 
 | ||||||
|  | 	virtual String as_text_physical_keycode() const; | ||||||
|  | 	virtual String as_text_keycode() const; | ||||||
|  | 	virtual String as_text_key_label() const; | ||||||
|  | 	virtual String as_text_location() const; | ||||||
|  | 	virtual String as_text() const override; | ||||||
|  | 	virtual String to_string() override; | ||||||
|  | 
 | ||||||
|  | 	static Ref<InputEventKey> create_reference(Key p_keycode_with_modifier_masks, bool p_physical = false); | ||||||
|  | 
 | ||||||
|  | 	InputEventKey() {} | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class InputEventMouse : public InputEventWithModifiers { | ||||||
|  | 	GDCLASS(InputEventMouse, InputEventWithModifiers); | ||||||
|  | 
 | ||||||
|  | 	BitField<MouseButtonMask> button_mask = MouseButtonMask::NONE; | ||||||
|  | 
 | ||||||
|  | 	Vector2 pos; | ||||||
|  | 	Vector2 global_pos; | ||||||
|  | 
 | ||||||
|  | protected: | ||||||
|  | 	static void _bind_methods(); | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  | 	void set_button_mask(BitField<MouseButtonMask> p_mask); | ||||||
|  | 	BitField<MouseButtonMask> get_button_mask() const; | ||||||
|  | 
 | ||||||
|  | 	void set_position(const Vector2 &p_pos); | ||||||
|  | 	Vector2 get_position() const; | ||||||
|  | 
 | ||||||
|  | 	void set_global_position(const Vector2 &p_global_pos); | ||||||
|  | 	Vector2 get_global_position() const; | ||||||
|  | 
 | ||||||
|  | 	InputEventMouse() {} | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class InputEventMouseButton : public InputEventMouse { | ||||||
|  | 	GDCLASS(InputEventMouseButton, InputEventMouse); | ||||||
|  | 
 | ||||||
|  | 	float factor = 1; | ||||||
|  | 	MouseButton button_index = MouseButton::NONE; | ||||||
|  | 	bool double_click = false; //last even less than double click time
 | ||||||
|  | 
 | ||||||
|  | protected: | ||||||
|  | 	static void _bind_methods(); | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  | 	void set_factor(float p_factor); | ||||||
|  | 	float get_factor() const; | ||||||
|  | 
 | ||||||
|  | 	void set_button_index(MouseButton p_index); | ||||||
|  | 	MouseButton get_button_index() const; | ||||||
|  | 
 | ||||||
|  | 	void set_pressed(bool p_pressed); | ||||||
|  | 	void set_canceled(bool p_canceled); | ||||||
|  | 
 | ||||||
|  | 	void set_double_click(bool p_double_click); | ||||||
|  | 	bool is_double_click() const; | ||||||
|  | 
 | ||||||
|  | 	virtual Ref<InputEvent> xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs = Vector2()) const override; | ||||||
|  | 
 | ||||||
|  | 	virtual bool action_match(const Ref<InputEvent> &p_event, bool p_exact_match, float p_deadzone, bool *r_pressed, float *r_strength, float *r_raw_strength) const override; | ||||||
|  | 	virtual bool is_match(const Ref<InputEvent> &p_event, bool p_exact_match = true) const override; | ||||||
|  | 
 | ||||||
|  | 	virtual bool is_action_type() const override { return true; } | ||||||
|  | 	virtual String as_text() const override; | ||||||
|  | 	virtual String to_string() override; | ||||||
|  | 
 | ||||||
|  | 	InputEventMouseButton() {} | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class InputEventMouseMotion : public InputEventMouse { | ||||||
|  | 	GDCLASS(InputEventMouseMotion, InputEventMouse); | ||||||
|  | 
 | ||||||
|  | 	Vector2 tilt; | ||||||
|  | 	float pressure = 0; | ||||||
|  | 	Vector2 relative; | ||||||
|  | 	Vector2 screen_relative; | ||||||
|  | 	Vector2 velocity; | ||||||
|  | 	Vector2 screen_velocity; | ||||||
|  | 	bool pen_inverted = false; | ||||||
|  | 
 | ||||||
|  | protected: | ||||||
|  | 	static void _bind_methods(); | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  | 	void set_tilt(const Vector2 &p_tilt); | ||||||
|  | 	Vector2 get_tilt() const; | ||||||
|  | 
 | ||||||
|  | 	void set_pressure(float p_pressure); | ||||||
|  | 	float get_pressure() const; | ||||||
|  | 
 | ||||||
|  | 	void set_pen_inverted(bool p_inverted); | ||||||
|  | 	bool get_pen_inverted() const; | ||||||
|  | 
 | ||||||
|  | 	void set_relative(const Vector2 &p_relative); | ||||||
|  | 	Vector2 get_relative() const; | ||||||
|  | 
 | ||||||
|  | 	void set_relative_screen_position(const Vector2 &p_relative); | ||||||
|  | 	Vector2 get_relative_screen_position() const; | ||||||
|  | 
 | ||||||
|  | 	void set_velocity(const Vector2 &p_velocity); | ||||||
|  | 	Vector2 get_velocity() const; | ||||||
|  | 
 | ||||||
|  | 	void set_screen_velocity(const Vector2 &p_velocity); | ||||||
|  | 	Vector2 get_screen_velocity() const; | ||||||
|  | 
 | ||||||
|  | 	virtual Ref<InputEvent> xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs = Vector2()) const override; | ||||||
|  | 	virtual String as_text() const override; | ||||||
|  | 	virtual String to_string() override; | ||||||
|  | 
 | ||||||
|  | 	virtual bool accumulate(const Ref<InputEvent> &p_event) override; | ||||||
|  | 
 | ||||||
|  | 	InputEventMouseMotion() {} | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class InputEventJoypadMotion : public InputEvent { | ||||||
|  | 	GDCLASS(InputEventJoypadMotion, InputEvent); | ||||||
|  | 	JoyAxis axis = (JoyAxis)0; ///< Joypad axis
 | ||||||
|  | 	float axis_value = 0; ///< -1 to 1
 | ||||||
|  | 
 | ||||||
|  | protected: | ||||||
|  | 	static void _bind_methods(); | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  | 	void set_axis(JoyAxis p_axis); | ||||||
|  | 	JoyAxis get_axis() const; | ||||||
|  | 
 | ||||||
|  | 	void set_axis_value(float p_value); | ||||||
|  | 	float get_axis_value() const; | ||||||
|  | 
 | ||||||
|  | 	virtual bool action_match(const Ref<InputEvent> &p_event, bool p_exact_match, float p_deadzone, bool *r_pressed, float *r_strength, float *r_raw_strength) const override; | ||||||
|  | 	virtual bool is_match(const Ref<InputEvent> &p_event, bool p_exact_match = true) const override; | ||||||
|  | 
 | ||||||
|  | 	virtual bool is_action_type() const override { return true; } | ||||||
|  | 	virtual String as_text() const override; | ||||||
|  | 	virtual String to_string() override; | ||||||
|  | 
 | ||||||
|  | 	static Ref<InputEventJoypadMotion> create_reference(JoyAxis p_axis, float p_value); | ||||||
|  | 
 | ||||||
|  | 	InputEventJoypadMotion() {} | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class InputEventJoypadButton : public InputEvent { | ||||||
|  | 	GDCLASS(InputEventJoypadButton, InputEvent); | ||||||
|  | 
 | ||||||
|  | 	JoyButton button_index = (JoyButton)0; | ||||||
|  | 	float pressure = 0; //0 to 1
 | ||||||
|  | protected: | ||||||
|  | 	static void _bind_methods(); | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  | 	void set_button_index(JoyButton p_index); | ||||||
|  | 	JoyButton get_button_index() const; | ||||||
|  | 
 | ||||||
|  | 	void set_pressed(bool p_pressed); | ||||||
|  | 
 | ||||||
|  | 	void set_pressure(float p_pressure); | ||||||
|  | 	float get_pressure() const; | ||||||
|  | 
 | ||||||
|  | 	virtual bool action_match(const Ref<InputEvent> &p_event, bool p_exact_match, float p_deadzone, bool *r_pressed, float *r_strength, float *r_raw_strength) const override; | ||||||
|  | 	virtual bool is_match(const Ref<InputEvent> &p_event, bool p_exact_match = true) const override; | ||||||
|  | 
 | ||||||
|  | 	virtual bool is_action_type() const override { return true; } | ||||||
|  | 
 | ||||||
|  | 	virtual String as_text() const override; | ||||||
|  | 	virtual String to_string() override; | ||||||
|  | 
 | ||||||
|  | 	static Ref<InputEventJoypadButton> create_reference(JoyButton p_btn_index); | ||||||
|  | 
 | ||||||
|  | 	InputEventJoypadButton() {} | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class InputEventScreenTouch : public InputEventFromWindow { | ||||||
|  | 	GDCLASS(InputEventScreenTouch, InputEventFromWindow); | ||||||
|  | 	int index = 0; | ||||||
|  | 	Vector2 pos; | ||||||
|  | 	bool double_tap = false; | ||||||
|  | 
 | ||||||
|  | protected: | ||||||
|  | 	static void _bind_methods(); | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  | 	void set_index(int p_index); | ||||||
|  | 	int get_index() const; | ||||||
|  | 
 | ||||||
|  | 	void set_position(const Vector2 &p_pos); | ||||||
|  | 	Vector2 get_position() const; | ||||||
|  | 
 | ||||||
|  | 	void set_pressed(bool p_pressed); | ||||||
|  | 	void set_canceled(bool p_canceled); | ||||||
|  | 
 | ||||||
|  | 	void set_double_tap(bool p_double_tap); | ||||||
|  | 	bool is_double_tap() const; | ||||||
|  | 
 | ||||||
|  | 	virtual Ref<InputEvent> xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs = Vector2()) const override; | ||||||
|  | 	virtual String as_text() const override; | ||||||
|  | 	virtual String to_string() override; | ||||||
|  | 
 | ||||||
|  | 	InputEventScreenTouch() {} | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class InputEventScreenDrag : public InputEventFromWindow { | ||||||
|  | 	GDCLASS(InputEventScreenDrag, InputEventFromWindow); | ||||||
|  | 	int index = 0; | ||||||
|  | 	Vector2 pos; | ||||||
|  | 	Vector2 relative; | ||||||
|  | 	Vector2 screen_relative; | ||||||
|  | 	Vector2 velocity; | ||||||
|  | 	Vector2 screen_velocity; | ||||||
|  | 	Vector2 tilt; | ||||||
|  | 	float pressure = 0; | ||||||
|  | 	bool pen_inverted = false; | ||||||
|  | 
 | ||||||
|  | protected: | ||||||
|  | 	static void _bind_methods(); | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  | 	void set_index(int p_index); | ||||||
|  | 	int get_index() const; | ||||||
|  | 
 | ||||||
|  | 	void set_tilt(const Vector2 &p_tilt); | ||||||
|  | 	Vector2 get_tilt() const; | ||||||
|  | 
 | ||||||
|  | 	void set_pressure(float p_pressure); | ||||||
|  | 	float get_pressure() const; | ||||||
|  | 
 | ||||||
|  | 	void set_pen_inverted(bool p_inverted); | ||||||
|  | 	bool get_pen_inverted() const; | ||||||
|  | 
 | ||||||
|  | 	void set_position(const Vector2 &p_pos); | ||||||
|  | 	Vector2 get_position() const; | ||||||
|  | 
 | ||||||
|  | 	void set_relative(const Vector2 &p_relative); | ||||||
|  | 	Vector2 get_relative() const; | ||||||
|  | 
 | ||||||
|  | 	void set_relative_screen_position(const Vector2 &p_relative); | ||||||
|  | 	Vector2 get_relative_screen_position() const; | ||||||
|  | 
 | ||||||
|  | 	void set_velocity(const Vector2 &p_velocity); | ||||||
|  | 	Vector2 get_velocity() const; | ||||||
|  | 
 | ||||||
|  | 	void set_screen_velocity(const Vector2 &p_velocity); | ||||||
|  | 	Vector2 get_screen_velocity() const; | ||||||
|  | 
 | ||||||
|  | 	virtual Ref<InputEvent> xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs = Vector2()) const override; | ||||||
|  | 	virtual String as_text() const override; | ||||||
|  | 	virtual String to_string() override; | ||||||
|  | 
 | ||||||
|  | 	virtual bool accumulate(const Ref<InputEvent> &p_event) override; | ||||||
|  | 
 | ||||||
|  | 	InputEventScreenDrag() {} | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class InputEventAction : public InputEvent { | ||||||
|  | 	GDCLASS(InputEventAction, InputEvent); | ||||||
|  | 
 | ||||||
|  | 	StringName action; | ||||||
|  | 	float strength = 1.0f; | ||||||
|  | 	int event_index = -1; | ||||||
|  | 
 | ||||||
|  | protected: | ||||||
|  | 	static void _bind_methods(); | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  | 	void set_action(const StringName &p_action); | ||||||
|  | 	StringName get_action() const; | ||||||
|  | 
 | ||||||
|  | 	void set_pressed(bool p_pressed); | ||||||
|  | 
 | ||||||
|  | 	void set_strength(float p_strength); | ||||||
|  | 	float get_strength() const; | ||||||
|  | 
 | ||||||
|  | 	void set_event_index(int p_index); | ||||||
|  | 	int get_event_index() const; | ||||||
|  | 
 | ||||||
|  | 	virtual bool is_action(const StringName &p_action) const; | ||||||
|  | 
 | ||||||
|  | 	virtual bool action_match(const Ref<InputEvent> &p_event, bool p_exact_match, float p_deadzone, bool *r_pressed, float *r_strength, float *r_raw_strength) const override; | ||||||
|  | 	virtual bool is_match(const Ref<InputEvent> &p_event, bool p_exact_match = true) const override; | ||||||
|  | 
 | ||||||
|  | 	virtual bool is_action_type() const override { return true; } | ||||||
|  | 
 | ||||||
|  | 	virtual String as_text() const override; | ||||||
|  | 	virtual String to_string() override; | ||||||
|  | 
 | ||||||
|  | 	InputEventAction() {} | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class InputEventGesture : public InputEventWithModifiers { | ||||||
|  | 	GDCLASS(InputEventGesture, InputEventWithModifiers); | ||||||
|  | 
 | ||||||
|  | 	Vector2 pos; | ||||||
|  | 
 | ||||||
|  | protected: | ||||||
|  | 	static void _bind_methods(); | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  | 	void set_position(const Vector2 &p_pos); | ||||||
|  | 	Vector2 get_position() const; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class InputEventMagnifyGesture : public InputEventGesture { | ||||||
|  | 	GDCLASS(InputEventMagnifyGesture, InputEventGesture); | ||||||
|  | 	real_t factor = 1.0; | ||||||
|  | 
 | ||||||
|  | protected: | ||||||
|  | 	static void _bind_methods(); | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  | 	void set_factor(real_t p_factor); | ||||||
|  | 	real_t get_factor() const; | ||||||
|  | 
 | ||||||
|  | 	virtual Ref<InputEvent> xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs = Vector2()) const override; | ||||||
|  | 	virtual String as_text() const override; | ||||||
|  | 	virtual String to_string() override; | ||||||
|  | 
 | ||||||
|  | 	InputEventMagnifyGesture() {} | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class InputEventPanGesture : public InputEventGesture { | ||||||
|  | 	GDCLASS(InputEventPanGesture, InputEventGesture); | ||||||
|  | 	Vector2 delta; | ||||||
|  | 
 | ||||||
|  | protected: | ||||||
|  | 	static void _bind_methods(); | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  | 	void set_delta(const Vector2 &p_delta); | ||||||
|  | 	Vector2 get_delta() const; | ||||||
|  | 
 | ||||||
|  | 	virtual Ref<InputEvent> xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs = Vector2()) const override; | ||||||
|  | 	virtual String as_text() const override; | ||||||
|  | 	virtual String to_string() override; | ||||||
|  | 
 | ||||||
|  | 	InputEventPanGesture() {} | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class InputEventMIDI : public InputEvent { | ||||||
|  | 	GDCLASS(InputEventMIDI, InputEvent); | ||||||
|  | 
 | ||||||
|  | 	int channel = 0; | ||||||
|  | 	MIDIMessage message = MIDIMessage::NONE; | ||||||
|  | 	int pitch = 0; | ||||||
|  | 	int velocity = 0; | ||||||
|  | 	int instrument = 0; | ||||||
|  | 	int pressure = 0; | ||||||
|  | 	int controller_number = 0; | ||||||
|  | 	int controller_value = 0; | ||||||
|  | 
 | ||||||
|  | protected: | ||||||
|  | 	static void _bind_methods(); | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  | 	void set_channel(const int p_channel); | ||||||
|  | 	int get_channel() const; | ||||||
|  | 
 | ||||||
|  | 	void set_message(const MIDIMessage p_message); | ||||||
|  | 	MIDIMessage get_message() const; | ||||||
|  | 
 | ||||||
|  | 	void set_pitch(const int p_pitch); | ||||||
|  | 	int get_pitch() const; | ||||||
|  | 
 | ||||||
|  | 	void set_velocity(const int p_velocity); | ||||||
|  | 	int get_velocity() const; | ||||||
|  | 
 | ||||||
|  | 	void set_instrument(const int p_instrument); | ||||||
|  | 	int get_instrument() const; | ||||||
|  | 
 | ||||||
|  | 	void set_pressure(const int p_pressure); | ||||||
|  | 	int get_pressure() const; | ||||||
|  | 
 | ||||||
|  | 	void set_controller_number(const int p_controller_number); | ||||||
|  | 	int get_controller_number() const; | ||||||
|  | 
 | ||||||
|  | 	void set_controller_value(const int p_controller_value); | ||||||
|  | 	int get_controller_value() const; | ||||||
|  | 
 | ||||||
|  | 	virtual String as_text() const override; | ||||||
|  | 	virtual String to_string() override; | ||||||
|  | 
 | ||||||
|  | 	InputEventMIDI() {} | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class InputEventShortcut : public InputEvent { | ||||||
|  | 	GDCLASS(InputEventShortcut, InputEvent); | ||||||
|  | 
 | ||||||
|  | 	Ref<Shortcut> shortcut; | ||||||
|  | 
 | ||||||
|  | protected: | ||||||
|  | 	static void _bind_methods(); | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  | 	void set_shortcut(Ref<Shortcut> p_shortcut); | ||||||
|  | 	Ref<Shortcut> get_shortcut(); | ||||||
|  | 
 | ||||||
|  | 	virtual String as_text() const override; | ||||||
|  | 	virtual String to_string() override; | ||||||
|  | 
 | ||||||
|  | 	InputEventShortcut(); | ||||||
|  | }; | ||||||
							
								
								
									
										41
									
								
								engine/core/input/input_map.compat.inc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								engine/core/input/input_map.compat.inc
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,41 @@ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*  input_map.compat.inc                                                  */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*                         This file is part of:                          */ | ||||||
|  | /*                             GODOT ENGINE                               */ | ||||||
|  | /*                        https://godotengine.org                         */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ | ||||||
|  | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */ | ||||||
|  | /*                                                                        */ | ||||||
|  | /* 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 DISABLE_DEPRECATED | ||||||
|  | 
 | ||||||
|  | void InputMap::_add_action_bind_compat_97281(const StringName &p_action, float p_deadzone) { | ||||||
|  | 	add_action(p_action, p_deadzone); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void InputMap::_bind_compatibility_methods() { | ||||||
|  | 	ClassDB::bind_compatibility_method(D_METHOD("add_action", "action", "deadzone"), &InputMap::_add_action_bind_compat_97281, DEFVAL(0.5f)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #endif // DISABLE_DEPRECATED
 | ||||||
							
								
								
									
										923
									
								
								engine/core/input/input_map.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										923
									
								
								engine/core/input/input_map.cpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,923 @@ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*  input_map.cpp                                                         */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*                         This file is part of:                          */ | ||||||
|  | /*                             GODOT ENGINE                               */ | ||||||
|  | /*                        https://godotengine.org                         */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ | ||||||
|  | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */ | ||||||
|  | /*                                                                        */ | ||||||
|  | /* 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 "input_map.h" | ||||||
|  | #include "input_map.compat.inc" | ||||||
|  | 
 | ||||||
|  | #include "core/config/project_settings.h" | ||||||
|  | #include "core/input/input.h" | ||||||
|  | #include "core/os/keyboard.h" | ||||||
|  | #include "core/os/os.h" | ||||||
|  | #include "core/variant/typed_array.h" | ||||||
|  | 
 | ||||||
|  | InputMap *InputMap::singleton = nullptr; | ||||||
|  | 
 | ||||||
|  | int InputMap::ALL_DEVICES = -1; | ||||||
|  | 
 | ||||||
|  | void InputMap::_bind_methods() { | ||||||
|  | 	ClassDB::bind_method(D_METHOD("has_action", "action"), &InputMap::has_action); | ||||||
|  | 	ClassDB::bind_method(D_METHOD("get_actions"), &InputMap::_get_actions); | ||||||
|  | 	ClassDB::bind_method(D_METHOD("add_action", "action", "deadzone"), &InputMap::add_action, DEFVAL(DEFAULT_DEADZONE)); | ||||||
|  | 	ClassDB::bind_method(D_METHOD("erase_action", "action"), &InputMap::erase_action); | ||||||
|  | 
 | ||||||
|  | 	ClassDB::bind_method(D_METHOD("get_action_description", "action"), &InputMap::get_action_description); | ||||||
|  | 
 | ||||||
|  | 	ClassDB::bind_method(D_METHOD("action_set_deadzone", "action", "deadzone"), &InputMap::action_set_deadzone); | ||||||
|  | 	ClassDB::bind_method(D_METHOD("action_get_deadzone", "action"), &InputMap::action_get_deadzone); | ||||||
|  | 	ClassDB::bind_method(D_METHOD("action_add_event", "action", "event"), &InputMap::action_add_event); | ||||||
|  | 	ClassDB::bind_method(D_METHOD("action_has_event", "action", "event"), &InputMap::action_has_event); | ||||||
|  | 	ClassDB::bind_method(D_METHOD("action_erase_event", "action", "event"), &InputMap::action_erase_event); | ||||||
|  | 	ClassDB::bind_method(D_METHOD("action_erase_events", "action"), &InputMap::action_erase_events); | ||||||
|  | 	ClassDB::bind_method(D_METHOD("action_get_events", "action"), &InputMap::_action_get_events); | ||||||
|  | 	ClassDB::bind_method(D_METHOD("event_is_action", "event", "action", "exact_match"), &InputMap::event_is_action, DEFVAL(false)); | ||||||
|  | 	ClassDB::bind_method(D_METHOD("load_from_project_settings"), &InputMap::load_from_project_settings); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Returns an nonexistent action error message with a suggestion of the closest | ||||||
|  |  * matching action name (if possible). | ||||||
|  |  */ | ||||||
|  | String InputMap::suggest_actions(const StringName &p_action) const { | ||||||
|  | 	List<StringName> actions = get_actions(); | ||||||
|  | 	StringName closest_action; | ||||||
|  | 	float closest_similarity = 0.0; | ||||||
|  | 
 | ||||||
|  | 	// Find the most action with the most similar name.
 | ||||||
|  | 	for (const StringName &action : actions) { | ||||||
|  | 		const float similarity = String(action).similarity(p_action); | ||||||
|  | 
 | ||||||
|  | 		if (similarity > closest_similarity) { | ||||||
|  | 			closest_action = action; | ||||||
|  | 			closest_similarity = similarity; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	String error_message = vformat("The InputMap action \"%s\" doesn't exist.", p_action); | ||||||
|  | 
 | ||||||
|  | 	if (closest_similarity >= 0.4) { | ||||||
|  | 		// Only include a suggestion in the error message if it's similar enough.
 | ||||||
|  | 		error_message += vformat(" Did you mean \"%s\"?", closest_action); | ||||||
|  | 	} | ||||||
|  | 	return error_message; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #ifdef TOOLS_ENABLED | ||||||
|  | void InputMap::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const { | ||||||
|  | 	const String pf = p_function; | ||||||
|  | 	bool first_argument_is_action = false; | ||||||
|  | 	if (p_idx == 0) { | ||||||
|  | 		first_argument_is_action = (pf == "has_action" || pf == "erase_action" || | ||||||
|  | 				pf == "action_set_deadzone" || pf == "action_get_deadzone" || | ||||||
|  | 				pf == "action_has_event" || pf == "action_add_event" || pf == "action_get_events" || | ||||||
|  | 				pf == "action_erase_event" || pf == "action_erase_events"); | ||||||
|  | 	} | ||||||
|  | 	if (first_argument_is_action || (p_idx == 1 && pf == "event_is_action")) { | ||||||
|  | 		// Cannot rely on `get_actions()`, otherwise the actions would be in the context of the Editor (no user-defined actions).
 | ||||||
|  | 		List<PropertyInfo> pinfo; | ||||||
|  | 		ProjectSettings::get_singleton()->get_property_list(&pinfo); | ||||||
|  | 
 | ||||||
|  | 		for (const PropertyInfo &pi : pinfo) { | ||||||
|  | 			if (!pi.name.begins_with("input/")) { | ||||||
|  | 				continue; | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			String name = pi.name.substr(pi.name.find_char('/') + 1); | ||||||
|  | 			r_options->push_back(name.quote()); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	Object::get_argument_options(p_function, p_idx, r_options); | ||||||
|  | } | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | void InputMap::add_action(const StringName &p_action, float p_deadzone) { | ||||||
|  | 	ERR_FAIL_COND_MSG(input_map.has(p_action), vformat("InputMap already has action \"%s\".", String(p_action))); | ||||||
|  | 	input_map[p_action] = Action(); | ||||||
|  | 	static int last_id = 1; | ||||||
|  | 	input_map[p_action].id = last_id; | ||||||
|  | 	input_map[p_action].deadzone = p_deadzone; | ||||||
|  | 	last_id++; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void InputMap::erase_action(const StringName &p_action) { | ||||||
|  | 	ERR_FAIL_COND_MSG(!input_map.has(p_action), suggest_actions(p_action)); | ||||||
|  | 
 | ||||||
|  | 	input_map.erase(p_action); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TypedArray<StringName> InputMap::_get_actions() { | ||||||
|  | 	TypedArray<StringName> ret; | ||||||
|  | 	List<StringName> actions = get_actions(); | ||||||
|  | 	if (actions.is_empty()) { | ||||||
|  | 		return ret; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for (const StringName &E : actions) { | ||||||
|  | 		ret.push_back(E); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | List<StringName> InputMap::get_actions() const { | ||||||
|  | 	List<StringName> actions = List<StringName>(); | ||||||
|  | 	if (input_map.is_empty()) { | ||||||
|  | 		return actions; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for (const KeyValue<StringName, Action> &E : input_map) { | ||||||
|  | 		actions.push_back(E.key); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return actions; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | List<Ref<InputEvent>>::Element *InputMap::_find_event(Action &p_action, const Ref<InputEvent> &p_event, bool p_exact_match, bool *r_pressed, float *r_strength, float *r_raw_strength, int *r_event_index) const { | ||||||
|  | 	ERR_FAIL_COND_V(p_event.is_null(), nullptr); | ||||||
|  | 
 | ||||||
|  | 	int i = 0; | ||||||
|  | 	for (List<Ref<InputEvent>>::Element *E = p_action.inputs.front(); E; E = E->next()) { | ||||||
|  | 		int device = E->get()->get_device(); | ||||||
|  | 		if (device == ALL_DEVICES || device == p_event->get_device()) { | ||||||
|  | 			if (E->get()->action_match(p_event, p_exact_match, p_action.deadzone, r_pressed, r_strength, r_raw_strength)) { | ||||||
|  | 				if (r_event_index) { | ||||||
|  | 					*r_event_index = i; | ||||||
|  | 				} | ||||||
|  | 				return E; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		i++; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return nullptr; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool InputMap::has_action(const StringName &p_action) const { | ||||||
|  | 	return input_map.has(p_action); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | String InputMap::get_action_description(const StringName &p_action) const { | ||||||
|  | 	ERR_FAIL_COND_V_MSG(!input_map.has(p_action), String(), suggest_actions(p_action)); | ||||||
|  | 
 | ||||||
|  | 	String ret; | ||||||
|  | 	const List<Ref<InputEvent>> &inputs = input_map[p_action].inputs; | ||||||
|  | 	for (Ref<InputEventKey> iek : inputs) { | ||||||
|  | 		if (iek.is_valid()) { | ||||||
|  | 			if (!ret.is_empty()) { | ||||||
|  | 				ret += RTR(" or "); | ||||||
|  | 			} | ||||||
|  | 			ret += iek->as_text(); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if (ret.is_empty()) { | ||||||
|  | 		ret = RTR("Action has no bound inputs"); | ||||||
|  | 	} | ||||||
|  | 	return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | float InputMap::action_get_deadzone(const StringName &p_action) { | ||||||
|  | 	ERR_FAIL_COND_V_MSG(!input_map.has(p_action), 0.0f, suggest_actions(p_action)); | ||||||
|  | 
 | ||||||
|  | 	return input_map[p_action].deadzone; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void InputMap::action_set_deadzone(const StringName &p_action, float p_deadzone) { | ||||||
|  | 	ERR_FAIL_COND_MSG(!input_map.has(p_action), suggest_actions(p_action)); | ||||||
|  | 
 | ||||||
|  | 	input_map[p_action].deadzone = p_deadzone; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void InputMap::action_add_event(const StringName &p_action, const Ref<InputEvent> &p_event) { | ||||||
|  | 	ERR_FAIL_COND_MSG(p_event.is_null(), "It's not a reference to a valid InputEvent object."); | ||||||
|  | 	ERR_FAIL_COND_MSG(!input_map.has(p_action), suggest_actions(p_action)); | ||||||
|  | 	if (_find_event(input_map[p_action], p_event, true)) { | ||||||
|  | 		return; // Already added.
 | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	input_map[p_action].inputs.push_back(p_event); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool InputMap::action_has_event(const StringName &p_action, const Ref<InputEvent> &p_event) { | ||||||
|  | 	ERR_FAIL_COND_V_MSG(!input_map.has(p_action), false, suggest_actions(p_action)); | ||||||
|  | 	return (_find_event(input_map[p_action], p_event, true) != nullptr); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void InputMap::action_erase_event(const StringName &p_action, const Ref<InputEvent> &p_event) { | ||||||
|  | 	ERR_FAIL_COND_MSG(!input_map.has(p_action), suggest_actions(p_action)); | ||||||
|  | 
 | ||||||
|  | 	List<Ref<InputEvent>>::Element *E = _find_event(input_map[p_action], p_event, true); | ||||||
|  | 	if (E) { | ||||||
|  | 		input_map[p_action].inputs.erase(E); | ||||||
|  | 
 | ||||||
|  | 		if (Input::get_singleton()->is_action_pressed(p_action)) { | ||||||
|  | 			Input::get_singleton()->action_release(p_action); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void InputMap::action_erase_events(const StringName &p_action) { | ||||||
|  | 	ERR_FAIL_COND_MSG(!input_map.has(p_action), suggest_actions(p_action)); | ||||||
|  | 
 | ||||||
|  | 	input_map[p_action].inputs.clear(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TypedArray<InputEvent> InputMap::_action_get_events(const StringName &p_action) { | ||||||
|  | 	TypedArray<InputEvent> ret; | ||||||
|  | 	const List<Ref<InputEvent>> *al = action_get_events(p_action); | ||||||
|  | 	if (al) { | ||||||
|  | 		for (const List<Ref<InputEvent>>::Element *E = al->front(); E; E = E->next()) { | ||||||
|  | 			ret.push_back(E->get()); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const List<Ref<InputEvent>> *InputMap::action_get_events(const StringName &p_action) { | ||||||
|  | 	HashMap<StringName, Action>::Iterator E = input_map.find(p_action); | ||||||
|  | 	if (!E) { | ||||||
|  | 		return nullptr; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return &E->value.inputs; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool InputMap::event_is_action(const Ref<InputEvent> &p_event, const StringName &p_action, bool p_exact_match) const { | ||||||
|  | 	return event_get_action_status(p_event, p_action, p_exact_match); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int InputMap::event_get_index(const Ref<InputEvent> &p_event, const StringName &p_action, bool p_exact_match) const { | ||||||
|  | 	int index = -1; | ||||||
|  | 	bool valid = event_get_action_status(p_event, p_action, p_exact_match, nullptr, nullptr, nullptr, &index); | ||||||
|  | 	return valid ? index : -1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool InputMap::event_get_action_status(const Ref<InputEvent> &p_event, const StringName &p_action, bool p_exact_match, bool *r_pressed, float *r_strength, float *r_raw_strength, int *r_event_index) const { | ||||||
|  | 	HashMap<StringName, Action>::Iterator E = input_map.find(p_action); | ||||||
|  | 	ERR_FAIL_COND_V_MSG(!E, false, suggest_actions(p_action)); | ||||||
|  | 
 | ||||||
|  | 	Ref<InputEventAction> input_event_action = p_event; | ||||||
|  | 	if (input_event_action.is_valid()) { | ||||||
|  | 		const bool pressed = input_event_action->is_pressed(); | ||||||
|  | 		if (r_pressed != nullptr) { | ||||||
|  | 			*r_pressed = pressed; | ||||||
|  | 		} | ||||||
|  | 		const float strength = pressed ? input_event_action->get_strength() : 0.0f; | ||||||
|  | 		if (r_strength != nullptr) { | ||||||
|  | 			*r_strength = strength; | ||||||
|  | 		} | ||||||
|  | 		if (r_raw_strength != nullptr) { | ||||||
|  | 			*r_raw_strength = strength; | ||||||
|  | 		} | ||||||
|  | 		if (r_event_index) { | ||||||
|  | 			if (input_event_action->get_event_index() >= 0) { | ||||||
|  | 				*r_event_index = input_event_action->get_event_index(); | ||||||
|  | 			} else { | ||||||
|  | 				*r_event_index = E->value.inputs.size(); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		return input_event_action->get_action() == p_action; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	List<Ref<InputEvent>>::Element *event = _find_event(E->value, p_event, p_exact_match, r_pressed, r_strength, r_raw_strength, r_event_index); | ||||||
|  | 	return event != nullptr; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const HashMap<StringName, InputMap::Action> &InputMap::get_action_map() const { | ||||||
|  | 	return input_map; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void InputMap::load_from_project_settings() { | ||||||
|  | 	input_map.clear(); | ||||||
|  | 
 | ||||||
|  | 	List<PropertyInfo> pinfo; | ||||||
|  | 	ProjectSettings::get_singleton()->get_property_list(&pinfo); | ||||||
|  | 
 | ||||||
|  | 	for (const PropertyInfo &pi : pinfo) { | ||||||
|  | 		if (!pi.name.begins_with("input/")) { | ||||||
|  | 			continue; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		String name = pi.name.substr(pi.name.find_char('/') + 1); | ||||||
|  | 
 | ||||||
|  | 		Dictionary action = GLOBAL_GET(pi.name); | ||||||
|  | 		float deadzone = action.has("deadzone") ? (float)action["deadzone"] : DEFAULT_DEADZONE; | ||||||
|  | 		Array events = action["events"]; | ||||||
|  | 
 | ||||||
|  | 		add_action(name, deadzone); | ||||||
|  | 		for (int i = 0; i < events.size(); i++) { | ||||||
|  | 			Ref<InputEvent> event = events[i]; | ||||||
|  | 			if (event.is_null()) { | ||||||
|  | 				continue; | ||||||
|  | 			} | ||||||
|  | 			action_add_event(name, event); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | struct _BuiltinActionDisplayName { | ||||||
|  | 	const char *name; | ||||||
|  | 	const char *display_name; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static const _BuiltinActionDisplayName _builtin_action_display_names[] = { | ||||||
|  | 	/* clang-format off */ | ||||||
|  |     { "ui_accept",                                     TTRC("Accept") }, | ||||||
|  |     { "ui_select",                                     TTRC("Select") }, | ||||||
|  |     { "ui_cancel",                                     TTRC("Cancel") }, | ||||||
|  |     { "ui_focus_next",                                 TTRC("Focus Next") }, | ||||||
|  |     { "ui_focus_prev",                                 TTRC("Focus Prev") }, | ||||||
|  |     { "ui_left",                                       TTRC("Left") }, | ||||||
|  |     { "ui_right",                                      TTRC("Right") }, | ||||||
|  |     { "ui_up",                                         TTRC("Up") }, | ||||||
|  |     { "ui_down",                                       TTRC("Down") }, | ||||||
|  |     { "ui_page_up",                                    TTRC("Page Up") }, | ||||||
|  |     { "ui_page_down",                                  TTRC("Page Down") }, | ||||||
|  |     { "ui_home",                                       TTRC("Home") }, | ||||||
|  |     { "ui_end",                                        TTRC("End") }, | ||||||
|  |     { "ui_cut",                                        TTRC("Cut") }, | ||||||
|  |     { "ui_copy",                                       TTRC("Copy") }, | ||||||
|  |     { "ui_paste",                                      TTRC("Paste") }, | ||||||
|  | 	{ "ui_focus_mode",                                 TTRC("Toggle Tab Focus Mode") }, | ||||||
|  |     { "ui_undo",                                       TTRC("Undo") }, | ||||||
|  |     { "ui_redo",                                       TTRC("Redo") }, | ||||||
|  |     { "ui_text_completion_query",                      TTRC("Completion Query") }, | ||||||
|  |     { "ui_text_newline",                               TTRC("New Line") }, | ||||||
|  |     { "ui_text_newline_blank",                         TTRC("New Blank Line") }, | ||||||
|  |     { "ui_text_newline_above",                         TTRC("New Line Above") }, | ||||||
|  |     { "ui_text_indent",                                TTRC("Indent") }, | ||||||
|  |     { "ui_text_dedent",                                TTRC("Dedent") }, | ||||||
|  |     { "ui_text_backspace",                             TTRC("Backspace") }, | ||||||
|  |     { "ui_text_backspace_word",                        TTRC("Backspace Word") }, | ||||||
|  |     { "ui_text_backspace_word.macos",                  TTRC("Backspace Word") }, | ||||||
|  |     { "ui_text_backspace_all_to_left",                 TTRC("Backspace all to Left") }, | ||||||
|  |     { "ui_text_backspace_all_to_left.macos",           TTRC("Backspace all to Left") }, | ||||||
|  |     { "ui_text_delete",                                TTRC("Delete") }, | ||||||
|  |     { "ui_text_delete_word",                           TTRC("Delete Word") }, | ||||||
|  |     { "ui_text_delete_word.macos",                     TTRC("Delete Word") }, | ||||||
|  |     { "ui_text_delete_all_to_right",                   TTRC("Delete all to Right") }, | ||||||
|  |     { "ui_text_delete_all_to_right.macos",             TTRC("Delete all to Right") }, | ||||||
|  |     { "ui_text_caret_left",                            TTRC("Caret Left") }, | ||||||
|  |     { "ui_text_caret_word_left",                       TTRC("Caret Word Left") }, | ||||||
|  |     { "ui_text_caret_word_left.macos",                 TTRC("Caret Word Left") }, | ||||||
|  |     { "ui_text_caret_right",                           TTRC("Caret Right") }, | ||||||
|  |     { "ui_text_caret_word_right",                      TTRC("Caret Word Right") }, | ||||||
|  |     { "ui_text_caret_word_right.macos",                TTRC("Caret Word Right") }, | ||||||
|  |     { "ui_text_caret_up",                              TTRC("Caret Up") }, | ||||||
|  |     { "ui_text_caret_down",                            TTRC("Caret Down") }, | ||||||
|  |     { "ui_text_caret_line_start",                      TTRC("Caret Line Start") }, | ||||||
|  |     { "ui_text_caret_line_start.macos",                TTRC("Caret Line Start") }, | ||||||
|  |     { "ui_text_caret_line_end",                        TTRC("Caret Line End") }, | ||||||
|  |     { "ui_text_caret_line_end.macos",                  TTRC("Caret Line End") }, | ||||||
|  |     { "ui_text_caret_page_up",                         TTRC("Caret Page Up") }, | ||||||
|  |     { "ui_text_caret_page_down",                       TTRC("Caret Page Down") }, | ||||||
|  |     { "ui_text_caret_document_start",                  TTRC("Caret Document Start") }, | ||||||
|  |     { "ui_text_caret_document_start.macos",            TTRC("Caret Document Start") }, | ||||||
|  |     { "ui_text_caret_document_end",                    TTRC("Caret Document End") }, | ||||||
|  |     { "ui_text_caret_document_end.macos",              TTRC("Caret Document End") }, | ||||||
|  |     { "ui_text_caret_add_below",                       TTRC("Caret Add Below") }, | ||||||
|  |     { "ui_text_caret_add_below.macos",                 TTRC("Caret Add Below") }, | ||||||
|  |     { "ui_text_caret_add_above",                       TTRC("Caret Add Above") }, | ||||||
|  |     { "ui_text_caret_add_above.macos",                 TTRC("Caret Add Above") }, | ||||||
|  |     { "ui_text_scroll_up",                             TTRC("Scroll Up") }, | ||||||
|  |     { "ui_text_scroll_up.macos",                       TTRC("Scroll Up") }, | ||||||
|  |     { "ui_text_scroll_down",                           TTRC("Scroll Down") }, | ||||||
|  |     { "ui_text_scroll_down.macos",                     TTRC("Scroll Down") }, | ||||||
|  |     { "ui_text_select_all",                            TTRC("Select All") }, | ||||||
|  |     { "ui_text_select_word_under_caret",               TTRC("Select Word Under Caret") }, | ||||||
|  |     { "ui_text_add_selection_for_next_occurrence",     TTRC("Add Selection for Next Occurrence") }, | ||||||
|  |     { "ui_text_skip_selection_for_next_occurrence",    TTRC("Skip Selection for Next Occurrence") }, | ||||||
|  |     { "ui_text_clear_carets_and_selection",            TTRC("Clear Carets and Selection") }, | ||||||
|  |     { "ui_text_toggle_insert_mode",                    TTRC("Toggle Insert Mode") }, | ||||||
|  |     { "ui_text_submit",                                TTRC("Submit Text") }, | ||||||
|  |     { "ui_graph_duplicate",                            TTRC("Duplicate Nodes") }, | ||||||
|  |     { "ui_graph_delete",                               TTRC("Delete Nodes") }, | ||||||
|  | 	{ "ui_graph_follow_left",                          TTRC("Follow Input Port Connection") }, | ||||||
|  | 	{ "ui_graph_follow_right",                         TTRC("Follow Output Port Connection") }, | ||||||
|  |     { "ui_filedialog_up_one_level",                    TTRC("Go Up One Level") }, | ||||||
|  |     { "ui_filedialog_refresh",                         TTRC("Refresh") }, | ||||||
|  |     { "ui_filedialog_show_hidden",                     TTRC("Show Hidden") }, | ||||||
|  |     { "ui_swap_input_direction ",                      TTRC("Swap Input Direction") }, | ||||||
|  |     { "ui_unicode_start",                              TTRC("Start Unicode Character Input") }, | ||||||
|  |     { "ui_colorpicker_delete_preset",                  TTRC("Toggle License Notices") }, | ||||||
|  | 	{ "ui_accessibility_drag_and_drop",                TTRC("Accessibility: Keyboard Drag and Drop") }, | ||||||
|  |     { "",                                              ""} | ||||||
|  | 	/* clang-format on */ | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | String InputMap::get_builtin_display_name(const String &p_name) const { | ||||||
|  | 	constexpr int len = std::size(_builtin_action_display_names); | ||||||
|  | 
 | ||||||
|  | 	for (int i = 0; i < len; i++) { | ||||||
|  | 		if (_builtin_action_display_names[i].name == p_name) { | ||||||
|  | 			return RTR(_builtin_action_display_names[i].display_name); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return p_name; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const HashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins() { | ||||||
|  | 	// Return cache if it has already been built.
 | ||||||
|  | 	if (default_builtin_cache.size()) { | ||||||
|  | 		return default_builtin_cache; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	List<Ref<InputEvent>> inputs; | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::ENTER)); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::KP_ENTER)); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::SPACE)); | ||||||
|  | 	default_builtin_cache.insert("ui_accept", inputs); | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventJoypadButton::create_reference(JoyButton::Y)); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::SPACE)); | ||||||
|  | 	default_builtin_cache.insert("ui_select", inputs); | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::ESCAPE)); | ||||||
|  | 	default_builtin_cache.insert("ui_cancel", inputs); | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::TAB)); | ||||||
|  | 	default_builtin_cache.insert("ui_focus_next", inputs); | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::TAB | KeyModifierMask::SHIFT)); | ||||||
|  | 	default_builtin_cache.insert("ui_focus_prev", inputs); | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::LEFT)); | ||||||
|  | 	inputs.push_back(InputEventJoypadButton::create_reference(JoyButton::DPAD_LEFT)); | ||||||
|  | 	inputs.push_back(InputEventJoypadMotion::create_reference(JoyAxis::LEFT_X, -1.0)); | ||||||
|  | 	default_builtin_cache.insert("ui_left", inputs); | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::RIGHT)); | ||||||
|  | 	inputs.push_back(InputEventJoypadButton::create_reference(JoyButton::DPAD_RIGHT)); | ||||||
|  | 	inputs.push_back(InputEventJoypadMotion::create_reference(JoyAxis::LEFT_X, 1.0)); | ||||||
|  | 	default_builtin_cache.insert("ui_right", inputs); | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::UP)); | ||||||
|  | 	inputs.push_back(InputEventJoypadButton::create_reference(JoyButton::DPAD_UP)); | ||||||
|  | 	inputs.push_back(InputEventJoypadMotion::create_reference(JoyAxis::LEFT_Y, -1.0)); | ||||||
|  | 	default_builtin_cache.insert("ui_up", inputs); | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::DOWN)); | ||||||
|  | 	inputs.push_back(InputEventJoypadButton::create_reference(JoyButton::DPAD_DOWN)); | ||||||
|  | 	inputs.push_back(InputEventJoypadMotion::create_reference(JoyAxis::LEFT_Y, 1.0)); | ||||||
|  | 	default_builtin_cache.insert("ui_down", inputs); | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::PAGEUP)); | ||||||
|  | 	default_builtin_cache.insert("ui_page_up", inputs); | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::PAGEDOWN)); | ||||||
|  | 	default_builtin_cache.insert("ui_page_down", inputs); | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::HOME)); | ||||||
|  | 	default_builtin_cache.insert("ui_home", inputs); | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::END)); | ||||||
|  | 	default_builtin_cache.insert("ui_end", inputs); | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	default_builtin_cache.insert("ui_accessibility_drag_and_drop", inputs); | ||||||
|  | 
 | ||||||
|  | 	// ///// UI basic Shortcuts /////
 | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::X | KeyModifierMask::CMD_OR_CTRL)); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::KEY_DELETE | KeyModifierMask::SHIFT)); | ||||||
|  | 	default_builtin_cache.insert("ui_cut", inputs); | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::C | KeyModifierMask::CMD_OR_CTRL)); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::INSERT | KeyModifierMask::CMD_OR_CTRL)); | ||||||
|  | 	default_builtin_cache.insert("ui_copy", inputs); | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::M | KeyModifierMask::CTRL)); | ||||||
|  | 	default_builtin_cache.insert("ui_focus_mode", inputs); | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::V | KeyModifierMask::CMD_OR_CTRL)); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::INSERT | KeyModifierMask::SHIFT)); | ||||||
|  | 	default_builtin_cache.insert("ui_paste", inputs); | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::Z | KeyModifierMask::CMD_OR_CTRL)); | ||||||
|  | 	default_builtin_cache.insert("ui_undo", inputs); | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::Z | KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT)); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::Y | KeyModifierMask::CMD_OR_CTRL)); | ||||||
|  | 	default_builtin_cache.insert("ui_redo", inputs); | ||||||
|  | 
 | ||||||
|  | 	// ///// UI Text Input Shortcuts /////
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::SPACE | KeyModifierMask::CTRL)); | ||||||
|  | 	default_builtin_cache.insert("ui_text_completion_query", inputs); | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(KeyModifierMask::SHIFT | Key::TAB)); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(KeyModifierMask::SHIFT | Key::ENTER)); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(KeyModifierMask::SHIFT | Key::KP_ENTER)); | ||||||
|  | 	default_builtin_cache.insert("ui_text_completion_accept", inputs); | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::TAB)); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::ENTER)); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::KP_ENTER)); | ||||||
|  | 	default_builtin_cache.insert("ui_text_completion_replace", inputs); | ||||||
|  | 
 | ||||||
|  | 	// Newlines
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::ENTER)); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::KP_ENTER)); | ||||||
|  | 	default_builtin_cache.insert("ui_text_newline", inputs); | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::ENTER | KeyModifierMask::CMD_OR_CTRL)); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::KP_ENTER | KeyModifierMask::CMD_OR_CTRL)); | ||||||
|  | 	default_builtin_cache.insert("ui_text_newline_blank", inputs); | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::ENTER | KeyModifierMask::SHIFT | KeyModifierMask::CMD_OR_CTRL)); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::KP_ENTER | KeyModifierMask::SHIFT | KeyModifierMask::CMD_OR_CTRL)); | ||||||
|  | 	default_builtin_cache.insert("ui_text_newline_above", inputs); | ||||||
|  | 
 | ||||||
|  | 	// Indentation
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::TAB)); | ||||||
|  | 	default_builtin_cache.insert("ui_text_indent", inputs); | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::TAB | KeyModifierMask::SHIFT)); | ||||||
|  | 	default_builtin_cache.insert("ui_text_dedent", inputs); | ||||||
|  | 
 | ||||||
|  | 	// Text Backspace and Delete
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::BACKSPACE)); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::BACKSPACE | KeyModifierMask::SHIFT)); | ||||||
|  | 	default_builtin_cache.insert("ui_text_backspace", inputs); | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::BACKSPACE | KeyModifierMask::CMD_OR_CTRL)); | ||||||
|  | 	default_builtin_cache.insert("ui_text_backspace_word", inputs); | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::BACKSPACE | KeyModifierMask::ALT)); | ||||||
|  | 	default_builtin_cache.insert("ui_text_backspace_word.macos", inputs); | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	default_builtin_cache.insert("ui_text_backspace_all_to_left", inputs); | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::BACKSPACE | KeyModifierMask::CMD_OR_CTRL)); | ||||||
|  | 	default_builtin_cache.insert("ui_text_backspace_all_to_left.macos", inputs); | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::KEY_DELETE)); | ||||||
|  | 	default_builtin_cache.insert("ui_text_delete", inputs); | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::KEY_DELETE | KeyModifierMask::CMD_OR_CTRL)); | ||||||
|  | 	default_builtin_cache.insert("ui_text_delete_word", inputs); | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::KEY_DELETE | KeyModifierMask::ALT)); | ||||||
|  | 	default_builtin_cache.insert("ui_text_delete_word.macos", inputs); | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	default_builtin_cache.insert("ui_text_delete_all_to_right", inputs); | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::KEY_DELETE | KeyModifierMask::CMD_OR_CTRL)); | ||||||
|  | 	default_builtin_cache.insert("ui_text_delete_all_to_right.macos", inputs); | ||||||
|  | 
 | ||||||
|  | 	// Text Caret Movement Left/Right
 | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::LEFT)); | ||||||
|  | 	default_builtin_cache.insert("ui_text_caret_left", inputs); | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::LEFT | KeyModifierMask::CMD_OR_CTRL)); | ||||||
|  | 	default_builtin_cache.insert("ui_text_caret_word_left", inputs); | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::LEFT | KeyModifierMask::ALT)); | ||||||
|  | 	default_builtin_cache.insert("ui_text_caret_word_left.macos", inputs); | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::RIGHT)); | ||||||
|  | 	default_builtin_cache.insert("ui_text_caret_right", inputs); | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::RIGHT | KeyModifierMask::CMD_OR_CTRL)); | ||||||
|  | 	default_builtin_cache.insert("ui_text_caret_word_right", inputs); | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::RIGHT | KeyModifierMask::ALT)); | ||||||
|  | 	default_builtin_cache.insert("ui_text_caret_word_right.macos", inputs); | ||||||
|  | 
 | ||||||
|  | 	// Text Caret Movement Up/Down
 | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::UP)); | ||||||
|  | 	default_builtin_cache.insert("ui_text_caret_up", inputs); | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::DOWN)); | ||||||
|  | 	default_builtin_cache.insert("ui_text_caret_down", inputs); | ||||||
|  | 
 | ||||||
|  | 	// Text Caret Movement Line Start/End
 | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::HOME)); | ||||||
|  | 	default_builtin_cache.insert("ui_text_caret_line_start", inputs); | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::A | KeyModifierMask::CTRL)); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::LEFT | KeyModifierMask::CMD_OR_CTRL)); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::HOME)); | ||||||
|  | 	default_builtin_cache.insert("ui_text_caret_line_start.macos", inputs); | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::END)); | ||||||
|  | 	default_builtin_cache.insert("ui_text_caret_line_end", inputs); | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::E | KeyModifierMask::CTRL)); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::RIGHT | KeyModifierMask::CMD_OR_CTRL)); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::END)); | ||||||
|  | 	default_builtin_cache.insert("ui_text_caret_line_end.macos", inputs); | ||||||
|  | 
 | ||||||
|  | 	// Text Caret Movement Page Up/Down
 | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::PAGEUP)); | ||||||
|  | 	default_builtin_cache.insert("ui_text_caret_page_up", inputs); | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::PAGEDOWN)); | ||||||
|  | 	default_builtin_cache.insert("ui_text_caret_page_down", inputs); | ||||||
|  | 
 | ||||||
|  | 	// Text Caret Movement Document Start/End
 | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::HOME | KeyModifierMask::CMD_OR_CTRL)); | ||||||
|  | 	default_builtin_cache.insert("ui_text_caret_document_start", inputs); | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::UP | KeyModifierMask::CMD_OR_CTRL)); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::HOME | KeyModifierMask::CMD_OR_CTRL)); | ||||||
|  | 	default_builtin_cache.insert("ui_text_caret_document_start.macos", inputs); | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::END | KeyModifierMask::CMD_OR_CTRL)); | ||||||
|  | 	default_builtin_cache.insert("ui_text_caret_document_end", inputs); | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::DOWN | KeyModifierMask::CMD_OR_CTRL)); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::END | KeyModifierMask::CMD_OR_CTRL)); | ||||||
|  | 	default_builtin_cache.insert("ui_text_caret_document_end.macos", inputs); | ||||||
|  | 
 | ||||||
|  | 	// Text Caret Addition Below/Above
 | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::DOWN | KeyModifierMask::SHIFT | KeyModifierMask::CMD_OR_CTRL)); | ||||||
|  | 	default_builtin_cache.insert("ui_text_caret_add_below", inputs); | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::L | KeyModifierMask::SHIFT | KeyModifierMask::CMD_OR_CTRL)); | ||||||
|  | 	default_builtin_cache.insert("ui_text_caret_add_below.macos", inputs); | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::UP | KeyModifierMask::SHIFT | KeyModifierMask::CMD_OR_CTRL)); | ||||||
|  | 	default_builtin_cache.insert("ui_text_caret_add_above", inputs); | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::O | KeyModifierMask::SHIFT | KeyModifierMask::CMD_OR_CTRL)); | ||||||
|  | 	default_builtin_cache.insert("ui_text_caret_add_above.macos", inputs); | ||||||
|  | 
 | ||||||
|  | 	// Text Scrolling
 | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::UP | KeyModifierMask::CMD_OR_CTRL)); | ||||||
|  | 	default_builtin_cache.insert("ui_text_scroll_up", inputs); | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::UP | KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::ALT)); | ||||||
|  | 	default_builtin_cache.insert("ui_text_scroll_up.macos", inputs); | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::DOWN | KeyModifierMask::CMD_OR_CTRL)); | ||||||
|  | 	default_builtin_cache.insert("ui_text_scroll_down", inputs); | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::DOWN | KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::ALT)); | ||||||
|  | 	default_builtin_cache.insert("ui_text_scroll_down.macos", inputs); | ||||||
|  | 
 | ||||||
|  | 	// Text Misc
 | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::A | KeyModifierMask::CMD_OR_CTRL)); | ||||||
|  | 	default_builtin_cache.insert("ui_text_select_all", inputs); | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::G | KeyModifierMask::ALT)); | ||||||
|  | 	default_builtin_cache.insert("ui_text_select_word_under_caret", inputs); | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::G | KeyModifierMask::CTRL | KeyModifierMask::META)); | ||||||
|  | 	default_builtin_cache.insert("ui_text_select_word_under_caret.macos", inputs); | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::D | KeyModifierMask::CMD_OR_CTRL)); | ||||||
|  | 	default_builtin_cache.insert("ui_text_add_selection_for_next_occurrence", inputs); | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::D | KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::ALT)); | ||||||
|  | 	default_builtin_cache.insert("ui_text_skip_selection_for_next_occurrence", inputs); | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::ESCAPE)); | ||||||
|  | 	default_builtin_cache.insert("ui_text_clear_carets_and_selection", inputs); | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::INSERT)); | ||||||
|  | 	default_builtin_cache.insert("ui_text_toggle_insert_mode", inputs); | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::MENU)); | ||||||
|  | 	default_builtin_cache.insert("ui_menu", inputs); | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::ENTER)); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::KP_ENTER)); | ||||||
|  | 	default_builtin_cache.insert("ui_text_submit", inputs); | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::U | KeyModifierMask::CTRL | KeyModifierMask::SHIFT)); | ||||||
|  | 	default_builtin_cache.insert("ui_unicode_start", inputs); | ||||||
|  | 
 | ||||||
|  | 	// ///// UI Graph Shortcuts /////
 | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::D | KeyModifierMask::CMD_OR_CTRL)); | ||||||
|  | 	default_builtin_cache.insert("ui_graph_duplicate", inputs); | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::KEY_DELETE)); | ||||||
|  | 	default_builtin_cache.insert("ui_graph_delete", inputs); | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::LEFT | KeyModifierMask::CMD_OR_CTRL)); | ||||||
|  | 	default_builtin_cache.insert("ui_graph_follow_left", inputs); | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::LEFT | KeyModifierMask::ALT)); | ||||||
|  | 	default_builtin_cache.insert("ui_graph_follow_left.macos", inputs); | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::RIGHT | KeyModifierMask::CMD_OR_CTRL)); | ||||||
|  | 	default_builtin_cache.insert("ui_graph_follow_right", inputs); | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::RIGHT | KeyModifierMask::ALT)); | ||||||
|  | 	default_builtin_cache.insert("ui_graph_follow_right.macos", inputs); | ||||||
|  | 
 | ||||||
|  | 	// ///// UI File Dialog Shortcuts /////
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::BACKSPACE)); | ||||||
|  | 	default_builtin_cache.insert("ui_filedialog_up_one_level", inputs); | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::F5)); | ||||||
|  | 	default_builtin_cache.insert("ui_filedialog_refresh", inputs); | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::H)); | ||||||
|  | 	default_builtin_cache.insert("ui_filedialog_show_hidden", inputs); | ||||||
|  | 
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::QUOTELEFT | KeyModifierMask::CMD_OR_CTRL)); | ||||||
|  | 	default_builtin_cache.insert("ui_swap_input_direction", inputs); | ||||||
|  | 
 | ||||||
|  | 	// ///// UI ColorPicker Shortcuts /////
 | ||||||
|  | 	inputs = List<Ref<InputEvent>>(); | ||||||
|  | 	inputs.push_back(InputEventJoypadButton::create_reference(JoyButton::X)); | ||||||
|  | 	inputs.push_back(InputEventKey::create_reference(Key::KEY_DELETE)); | ||||||
|  | 	default_builtin_cache.insert("ui_colorpicker_delete_preset", inputs); | ||||||
|  | 
 | ||||||
|  | 	return default_builtin_cache; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const HashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins_with_feature_overrides_applied() { | ||||||
|  | 	if (default_builtin_with_overrides_cache.size() > 0) { | ||||||
|  | 		return default_builtin_with_overrides_cache; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	const HashMap<String, List<Ref<InputEvent>>> &builtins = get_builtins(); | ||||||
|  | 
 | ||||||
|  | 	// Get a list of all built in inputs which are valid overrides for the OS
 | ||||||
|  | 	// Key = builtin name (e.g. ui_accept)
 | ||||||
|  | 	// Value = override/feature names (e.g. macos, if it was defined as "ui_accept.macos" and the platform supports that feature)
 | ||||||
|  | 	HashMap<String, Vector<String>> builtins_with_overrides; | ||||||
|  | 	for (const KeyValue<String, List<Ref<InputEvent>>> &E : builtins) { | ||||||
|  | 		String fullname = E.key; | ||||||
|  | 
 | ||||||
|  | 		Vector<String> split = fullname.split("."); | ||||||
|  | 		const String &name = split[0]; | ||||||
|  | 		String override_for = split.size() > 1 ? split[1] : String(); | ||||||
|  | 
 | ||||||
|  | 		if (!override_for.is_empty() && OS::get_singleton()->has_feature(override_for)) { | ||||||
|  | 			builtins_with_overrides[name].push_back(override_for); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for (const KeyValue<String, List<Ref<InputEvent>>> &E : builtins) { | ||||||
|  | 		String fullname = E.key; | ||||||
|  | 
 | ||||||
|  | 		Vector<String> split = fullname.split("."); | ||||||
|  | 		const String &name = split[0]; | ||||||
|  | 		String override_for = split.size() > 1 ? split[1] : String(); | ||||||
|  | 
 | ||||||
|  | 		if (builtins_with_overrides.has(name) && override_for.is_empty()) { | ||||||
|  | 			// Builtin has an override but this particular one is not an override, so skip.
 | ||||||
|  | 			continue; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if (!override_for.is_empty() && !OS::get_singleton()->has_feature(override_for)) { | ||||||
|  | 			// OS does not support this override - skip.
 | ||||||
|  | 			continue; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		default_builtin_with_overrides_cache.insert(name, E.value); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return default_builtin_with_overrides_cache; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void InputMap::load_default() { | ||||||
|  | 	HashMap<String, List<Ref<InputEvent>>> builtins = get_builtins_with_feature_overrides_applied(); | ||||||
|  | 
 | ||||||
|  | 	for (const KeyValue<String, List<Ref<InputEvent>>> &E : builtins) { | ||||||
|  | 		String name = E.key; | ||||||
|  | 
 | ||||||
|  | 		add_action(name); | ||||||
|  | 
 | ||||||
|  | 		const List<Ref<InputEvent>> &inputs = E.value; | ||||||
|  | 		for (const List<Ref<InputEvent>>::Element *I = inputs.front(); I; I = I->next()) { | ||||||
|  | 			Ref<InputEventKey> iek = I->get(); | ||||||
|  | 
 | ||||||
|  | 			// For the editor, only add keyboard actions.
 | ||||||
|  | 			if (iek.is_valid()) { | ||||||
|  | 				action_add_event(name, I->get()); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | InputMap::InputMap() { | ||||||
|  | 	ERR_FAIL_COND_MSG(singleton, "Singleton in InputMap already exist."); | ||||||
|  | 	singleton = this; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | InputMap::~InputMap() { | ||||||
|  | 	singleton = nullptr; | ||||||
|  | } | ||||||
							
								
								
									
										119
									
								
								engine/core/input/input_map.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										119
									
								
								engine/core/input/input_map.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,119 @@ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*  input_map.h                                                           */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*                         This file is part of:                          */ | ||||||
|  | /*                             GODOT ENGINE                               */ | ||||||
|  | /*                        https://godotengine.org                         */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ | ||||||
|  | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */ | ||||||
|  | /*                                                                        */ | ||||||
|  | /* 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.                 */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include "core/input/input_event.h" | ||||||
|  | #include "core/object/class_db.h" | ||||||
|  | #include "core/object/object.h" | ||||||
|  | #include "core/templates/hash_map.h" | ||||||
|  | 
 | ||||||
|  | template <typename T> | ||||||
|  | class TypedArray; | ||||||
|  | 
 | ||||||
|  | class InputMap : public Object { | ||||||
|  | 	GDCLASS(InputMap, Object); | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  | 	/**
 | ||||||
|  | 	 * A special value used to signify that a given Action can be triggered by any device | ||||||
|  | 	 */ | ||||||
|  | 	static int ALL_DEVICES; | ||||||
|  | 
 | ||||||
|  | 	struct Action { | ||||||
|  | 		int id; | ||||||
|  | 		float deadzone; | ||||||
|  | 		List<Ref<InputEvent>> inputs; | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	static constexpr float DEFAULT_DEADZONE = 0.2f; | ||||||
|  | 	// Keep bigger deadzone for toggle actions (default `ui_*` actions, axis `pressed`) (GH-103360).
 | ||||||
|  | 	static constexpr float DEFAULT_TOGGLE_DEADZONE = 0.5f; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  | 	static InputMap *singleton; | ||||||
|  | 
 | ||||||
|  | 	mutable HashMap<StringName, Action> input_map; | ||||||
|  | 	HashMap<String, List<Ref<InputEvent>>> default_builtin_cache; | ||||||
|  | 	HashMap<String, List<Ref<InputEvent>>> default_builtin_with_overrides_cache; | ||||||
|  | 
 | ||||||
|  | 	List<Ref<InputEvent>>::Element *_find_event(Action &p_action, const Ref<InputEvent> &p_event, bool p_exact_match = false, bool *r_pressed = nullptr, float *r_strength = nullptr, float *r_raw_strength = nullptr, int *r_event_index = nullptr) const; | ||||||
|  | 
 | ||||||
|  | 	TypedArray<InputEvent> _action_get_events(const StringName &p_action); | ||||||
|  | 	TypedArray<StringName> _get_actions(); | ||||||
|  | 
 | ||||||
|  | protected: | ||||||
|  | 	static void _bind_methods(); | ||||||
|  | 
 | ||||||
|  | #ifndef DISABLE_DEPRECATED | ||||||
|  | 	void _add_action_bind_compat_97281(const StringName &p_action, float p_deadzone = 0.5); | ||||||
|  | 	static void _bind_compatibility_methods(); | ||||||
|  | #endif // DISABLE_DEPRECATED
 | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  | 	static _FORCE_INLINE_ InputMap *get_singleton() { return singleton; } | ||||||
|  | 
 | ||||||
|  | 	bool has_action(const StringName &p_action) const; | ||||||
|  | 	List<StringName> get_actions() const; | ||||||
|  | 	void add_action(const StringName &p_action, float p_deadzone = DEFAULT_DEADZONE); | ||||||
|  | 	void erase_action(const StringName &p_action); | ||||||
|  | 
 | ||||||
|  | 	String get_action_description(const StringName &p_action) const; | ||||||
|  | 
 | ||||||
|  | 	float action_get_deadzone(const StringName &p_action); | ||||||
|  | 	void action_set_deadzone(const StringName &p_action, float p_deadzone); | ||||||
|  | 	void action_add_event(const StringName &p_action, const Ref<InputEvent> &p_event); | ||||||
|  | 	bool action_has_event(const StringName &p_action, const Ref<InputEvent> &p_event); | ||||||
|  | 	void action_erase_event(const StringName &p_action, const Ref<InputEvent> &p_event); | ||||||
|  | 	void action_erase_events(const StringName &p_action); | ||||||
|  | 
 | ||||||
|  | 	const List<Ref<InputEvent>> *action_get_events(const StringName &p_action); | ||||||
|  | 	bool event_is_action(const Ref<InputEvent> &p_event, const StringName &p_action, bool p_exact_match = false) const; | ||||||
|  | 	int event_get_index(const Ref<InputEvent> &p_event, const StringName &p_action, bool p_exact_match = false) const; | ||||||
|  | 	bool event_get_action_status(const Ref<InputEvent> &p_event, const StringName &p_action, bool p_exact_match = false, bool *r_pressed = nullptr, float *r_strength = nullptr, float *r_raw_strength = nullptr, int *r_event_index = nullptr) const; | ||||||
|  | 
 | ||||||
|  | 	const HashMap<StringName, Action> &get_action_map() const; | ||||||
|  | 	void load_from_project_settings(); | ||||||
|  | 	void load_default(); | ||||||
|  | 
 | ||||||
|  | 	String suggest_actions(const StringName &p_action) const; | ||||||
|  | 
 | ||||||
|  | #ifdef TOOLS_ENABLED | ||||||
|  | 	virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override; | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 	String get_builtin_display_name(const String &p_name) const; | ||||||
|  | 	// Use an Ordered Map so insertion order is preserved. We want the elements to be 'grouped' somewhat.
 | ||||||
|  | 	const HashMap<String, List<Ref<InputEvent>>> &get_builtins(); | ||||||
|  | 	const HashMap<String, List<Ref<InputEvent>>> &get_builtins_with_feature_overrides_applied(); | ||||||
|  | 
 | ||||||
|  | 	InputMap(); | ||||||
|  | 	~InputMap(); | ||||||
|  | }; | ||||||
							
								
								
									
										131
									
								
								engine/core/input/shortcut.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										131
									
								
								engine/core/input/shortcut.cpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,131 @@ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*  shortcut.cpp                                                          */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*                         This file is part of:                          */ | ||||||
|  | /*                             GODOT ENGINE                               */ | ||||||
|  | /*                        https://godotengine.org                         */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ | ||||||
|  | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */ | ||||||
|  | /*                                                                        */ | ||||||
|  | /* 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 "shortcut.h" | ||||||
|  | 
 | ||||||
|  | void Shortcut::set_events(const Array &p_events) { | ||||||
|  | 	for (int i = 0; i < p_events.size(); i++) { | ||||||
|  | 		Ref<InputEventShortcut> ies = p_events[i]; | ||||||
|  | 		ERR_FAIL_COND_MSG(ies.is_valid(), "Cannot set a shortcut event to an instance of InputEventShortcut."); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	events = p_events; | ||||||
|  | 	emit_changed(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Shortcut::set_events_list(const List<Ref<InputEvent>> *p_events) { | ||||||
|  | 	events.clear(); | ||||||
|  | 
 | ||||||
|  | 	for (const Ref<InputEvent> &ie : *p_events) { | ||||||
|  | 		events.push_back(ie); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Array Shortcut::get_events() const { | ||||||
|  | 	return events; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool Shortcut::matches_event(const Ref<InputEvent> &p_event) const { | ||||||
|  | 	Ref<InputEventShortcut> ies = p_event; | ||||||
|  | 	if (ies.is_valid()) { | ||||||
|  | 		if (ies->get_shortcut().ptr() == this) { | ||||||
|  | 			return true; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for (int i = 0; i < events.size(); i++) { | ||||||
|  | 		Ref<InputEvent> ie = events[i]; | ||||||
|  | 		bool valid = ie.is_valid() && ie->is_match(p_event); | ||||||
|  | 
 | ||||||
|  | 		// Stop on first valid event - don't need to check further.
 | ||||||
|  | 		if (valid) { | ||||||
|  | 			return true; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | String Shortcut::get_as_text() const { | ||||||
|  | 	for (int i = 0; i < events.size(); i++) { | ||||||
|  | 		Ref<InputEvent> ie = events[i]; | ||||||
|  | 		// Return first shortcut which is valid
 | ||||||
|  | 		if (ie.is_valid()) { | ||||||
|  | 			return ie->as_text(); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return "None"; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool Shortcut::has_valid_event() const { | ||||||
|  | 	// Tests if there is ANY input event which is valid.
 | ||||||
|  | 	for (int i = 0; i < events.size(); i++) { | ||||||
|  | 		Ref<InputEvent> ie = events[i]; | ||||||
|  | 		if (ie.is_valid()) { | ||||||
|  | 			return true; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Shortcut::_bind_methods() { | ||||||
|  | 	ClassDB::bind_method(D_METHOD("set_events", "events"), &Shortcut::set_events); | ||||||
|  | 	ClassDB::bind_method(D_METHOD("get_events"), &Shortcut::get_events); | ||||||
|  | 
 | ||||||
|  | 	ClassDB::bind_method(D_METHOD("has_valid_event"), &Shortcut::has_valid_event); | ||||||
|  | 
 | ||||||
|  | 	ClassDB::bind_method(D_METHOD("matches_event", "event"), &Shortcut::matches_event); | ||||||
|  | 	ClassDB::bind_method(D_METHOD("get_as_text"), &Shortcut::get_as_text); | ||||||
|  | 
 | ||||||
|  | 	ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "events", PROPERTY_HINT_ARRAY_TYPE, MAKE_RESOURCE_TYPE_HINT("InputEvent")), "set_events", "get_events"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool Shortcut::is_event_array_equal(const Array &p_event_array1, const Array &p_event_array2) { | ||||||
|  | 	if (p_event_array1.size() != p_event_array2.size()) { | ||||||
|  | 		return false; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	bool is_same = true; | ||||||
|  | 	for (int i = 0; i < p_event_array1.size(); i++) { | ||||||
|  | 		Ref<InputEvent> ie_1 = p_event_array1[i]; | ||||||
|  | 		Ref<InputEvent> ie_2 = p_event_array2[i]; | ||||||
|  | 
 | ||||||
|  | 		is_same = ie_1->is_match(ie_2); | ||||||
|  | 
 | ||||||
|  | 		// Break on the first that doesn't match - don't need to check further.
 | ||||||
|  | 		if (!is_same) { | ||||||
|  | 			break; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return is_same; | ||||||
|  | } | ||||||
							
								
								
									
										56
									
								
								engine/core/input/shortcut.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								engine/core/input/shortcut.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,56 @@ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*  shortcut.h                                                            */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /*                         This file is part of:                          */ | ||||||
|  | /*                             GODOT ENGINE                               */ | ||||||
|  | /*                        https://godotengine.org                         */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ | ||||||
|  | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */ | ||||||
|  | /*                                                                        */ | ||||||
|  | /* 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.                 */ | ||||||
|  | /**************************************************************************/ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include "core/input/input_event.h" | ||||||
|  | #include "core/io/resource.h" | ||||||
|  | 
 | ||||||
|  | class Shortcut : public Resource { | ||||||
|  | 	GDCLASS(Shortcut, Resource); | ||||||
|  | 
 | ||||||
|  | 	Array events; | ||||||
|  | 
 | ||||||
|  | protected: | ||||||
|  | 	static void _bind_methods(); | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  | 	void set_events(const Array &p_events); | ||||||
|  | 	Array get_events() const; | ||||||
|  | 
 | ||||||
|  | 	void set_events_list(const List<Ref<InputEvent>> *p_events); | ||||||
|  | 
 | ||||||
|  | 	bool matches_event(const Ref<InputEvent> &p_event) const; | ||||||
|  | 	bool has_valid_event() const; | ||||||
|  | 
 | ||||||
|  | 	String get_as_text() const; | ||||||
|  | 
 | ||||||
|  | 	static bool is_event_array_equal(const Array &p_event_array1, const Array &p_event_array2); | ||||||
|  | }; | ||||||
							
								
								
									
										6
									
								
								engine/core/io/SCsub
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								engine/core/io/SCsub
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,6 @@ | ||||||
|  | #!/usr/bin/env python | ||||||
|  | from misc.utility.scons_hints import * | ||||||
|  | 
 | ||||||
|  | Import("env") | ||||||
|  | 
 | ||||||
|  | env.add_source_files(env.core_sources, "*.cpp") | ||||||
Some files were not shown because too many files have changed in this diff Show more
		Loading…
	
		Reference in a new issue