forked from hertog/godot-module-template
		
	feat: godot-engine-source-4.3-stable
This commit is contained in:
		
							parent
							
								
									c59a7dcade
								
							
						
					
					
						commit
						7125d019b5
					
				
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							|  | @ -6,3 +6,4 @@ | |||
| config.log | ||||
| .sconf_temp | ||||
| 
 | ||||
| engine/.github | ||||
|  |  | |||
							
								
								
									
										199
									
								
								engine/.clang-format
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										199
									
								
								engine/.clang-format
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,199 @@ | |||
| # Commented out parameters are those with the same value as base LLVM style. | ||||
| # We can uncomment them if we want to change their value, or enforce the | ||||
| # chosen value in case the base style changes (last sync: Clang 14.0). | ||||
| --- | ||||
| ### General config, applies to all languages ### | ||||
| BasedOnStyle:  LLVM | ||||
| AccessModifierOffset: -4 | ||||
| AlignAfterOpenBracket: DontAlign | ||||
| # AlignArrayOfStructures: None | ||||
| # AlignConsecutiveMacros: None | ||||
| # AlignConsecutiveAssignments: None | ||||
| # AlignConsecutiveBitFields: None | ||||
| # AlignConsecutiveDeclarations: None | ||||
| # AlignEscapedNewlines: Right | ||||
| AlignOperands:   DontAlign | ||||
| AlignTrailingComments: false | ||||
| # AllowAllArgumentsOnNextLine: true | ||||
| AllowAllParametersOfDeclarationOnNextLine: false | ||||
| # AllowShortEnumsOnASingleLine: true | ||||
| # AllowShortBlocksOnASingleLine: Never | ||||
| # AllowShortCaseLabelsOnASingleLine: false | ||||
| # AllowShortFunctionsOnASingleLine: All | ||||
| # AllowShortLambdasOnASingleLine: All | ||||
| # AllowShortIfStatementsOnASingleLine: Never | ||||
| # AllowShortLoopsOnASingleLine: false | ||||
| # AlwaysBreakAfterDefinitionReturnType: None | ||||
| # AlwaysBreakAfterReturnType: None | ||||
| # AlwaysBreakBeforeMultilineStrings: false | ||||
| # AlwaysBreakTemplateDeclarations: MultiLine | ||||
| # AttributeMacros: | ||||
| #   - __capability | ||||
| # BinPackArguments: true | ||||
| # BinPackParameters: true | ||||
| # BraceWrapping: | ||||
| #   AfterCaseLabel:  false | ||||
| #   AfterClass:      false | ||||
| #   AfterControlStatement: Never | ||||
| #   AfterEnum:       false | ||||
| #   AfterFunction:   false | ||||
| #   AfterNamespace:  false | ||||
| #   AfterObjCDeclaration: false | ||||
| #   AfterStruct:     false | ||||
| #   AfterUnion:      false | ||||
| #   AfterExternBlock: false | ||||
| #   BeforeCatch:     false | ||||
| #   BeforeElse:      false | ||||
| #   BeforeLambdaBody: false | ||||
| #   BeforeWhile:     false | ||||
| #   IndentBraces:    false | ||||
| #   SplitEmptyFunction: true | ||||
| #   SplitEmptyRecord: true | ||||
| #   SplitEmptyNamespace: true | ||||
| # BreakBeforeBinaryOperators: None | ||||
| # BreakBeforeConceptDeclarations: true | ||||
| # BreakBeforeBraces: Attach | ||||
| # BreakBeforeInheritanceComma: false | ||||
| # BreakInheritanceList: BeforeColon | ||||
| # BreakBeforeTernaryOperators: true | ||||
| # BreakConstructorInitializersBeforeComma: false | ||||
| BreakConstructorInitializers: AfterColon | ||||
| # BreakStringLiterals: true | ||||
| ColumnLimit:     0 | ||||
| # CommentPragmas:  '^ IWYU pragma:' | ||||
| # QualifierAlignment: Leave | ||||
| # CompactNamespaces: false | ||||
| ConstructorInitializerIndentWidth: 8 | ||||
| ContinuationIndentWidth: 8 | ||||
| Cpp11BracedListStyle: false | ||||
| # DeriveLineEnding: true | ||||
| # DerivePointerAlignment: false | ||||
| # DisableFormat:   false | ||||
| # EmptyLineAfterAccessModifier: Never | ||||
| # EmptyLineBeforeAccessModifier: LogicalBlock | ||||
| # ExperimentalAutoDetectBinPacking: false | ||||
| # PackConstructorInitializers: BinPack | ||||
| ConstructorInitializerAllOnOneLineOrOnePerLine: true | ||||
| # AllowAllConstructorInitializersOnNextLine: true | ||||
| # FixNamespaceComments: true | ||||
| # ForEachMacros: | ||||
| #   - foreach | ||||
| #   - Q_FOREACH | ||||
| #   - BOOST_FOREACH | ||||
| # IfMacros: | ||||
| #   - KJ_IF_MAYBE | ||||
| # IncludeBlocks:   Preserve | ||||
| IncludeCategories: | ||||
|   - Regex:           '".*"' | ||||
|     Priority:        1 | ||||
|   - Regex:           '^<.*\.h>' | ||||
|     Priority:        2 | ||||
|   - Regex:           '^<.*' | ||||
|     Priority:        3 | ||||
| # IncludeIsMainRegex: '(Test)?$' | ||||
| # IncludeIsMainSourceRegex: '' | ||||
| # IndentAccessModifiers: false | ||||
| IndentCaseLabels: true | ||||
| # IndentCaseBlocks: false | ||||
| # IndentGotoLabels: true | ||||
| # IndentPPDirectives: None | ||||
| # IndentExternBlock: AfterExternBlock | ||||
| # IndentRequires:  false | ||||
| IndentWidth:     4 | ||||
| # IndentWrappedFunctionNames: false | ||||
| # InsertTrailingCommas: None | ||||
| # JavaScriptQuotes: Leave | ||||
| # JavaScriptWrapImports: true | ||||
| KeepEmptyLinesAtTheStartOfBlocks: false | ||||
| # LambdaBodyIndentation: Signature | ||||
| # MacroBlockBegin: '' | ||||
| # MacroBlockEnd:   '' | ||||
| # MaxEmptyLinesToKeep: 1 | ||||
| # NamespaceIndentation: None | ||||
| # PenaltyBreakAssignment: 2 | ||||
| # PenaltyBreakBeforeFirstCallParameter: 19 | ||||
| # PenaltyBreakComment: 300 | ||||
| # PenaltyBreakFirstLessLess: 120 | ||||
| # PenaltyBreakOpenParenthesis: 0 | ||||
| # PenaltyBreakString: 1000 | ||||
| # PenaltyBreakTemplateDeclaration: 10 | ||||
| # PenaltyExcessCharacter: 1000000 | ||||
| # PenaltyReturnTypeOnItsOwnLine: 60 | ||||
| # PenaltyIndentedWhitespace: 0 | ||||
| # PointerAlignment: Right | ||||
| # PPIndentWidth:   -1 | ||||
| # ReferenceAlignment: Pointer | ||||
| # ReflowComments:  true | ||||
| # RemoveBracesLLVM: false | ||||
| # SeparateDefinitionBlocks: Leave | ||||
| # ShortNamespaceLines: 1 | ||||
| # SortIncludes:    CaseSensitive | ||||
| # SortJavaStaticImport: Before | ||||
| # SortUsingDeclarations: true | ||||
| # SpaceAfterCStyleCast: false | ||||
| # SpaceAfterLogicalNot: false | ||||
| # SpaceAfterTemplateKeyword: true | ||||
| # SpaceBeforeAssignmentOperators: true | ||||
| # SpaceBeforeCaseColon: false | ||||
| # SpaceBeforeCpp11BracedList: false | ||||
| # SpaceBeforeCtorInitializerColon: true | ||||
| # SpaceBeforeInheritanceColon: true | ||||
| # SpaceBeforeParens: ControlStatements | ||||
| # SpaceBeforeParensOptions: | ||||
| #   AfterControlStatements: true | ||||
| #   AfterForeachMacros: true | ||||
| #   AfterFunctionDefinitionName: false | ||||
| #   AfterFunctionDeclarationName: false | ||||
| #   AfterIfMacros:   true | ||||
| #   AfterOverloadedOperator: false | ||||
| #   BeforeNonEmptyParentheses: false | ||||
| # SpaceAroundPointerQualifiers: Default | ||||
| # SpaceBeforeRangeBasedForLoopColon: true | ||||
| # SpaceInEmptyBlock: false | ||||
| # SpaceInEmptyParentheses: false | ||||
| # SpacesBeforeTrailingComments: 1 | ||||
| # SpacesInAngles:  Never | ||||
| # SpacesInConditionalStatement: false | ||||
| # SpacesInContainerLiterals: true | ||||
| # SpacesInCStyleCastParentheses: false | ||||
| ## Godot TODO: We'll want to use a min of 1, but we need to see how to fix | ||||
| ## our comment capitalization at the same time. | ||||
| SpacesInLineCommentPrefix: | ||||
|   Minimum:         0 | ||||
|   Maximum:         -1 | ||||
| # SpacesInParentheses: false | ||||
| # SpacesInSquareBrackets: false | ||||
| # SpaceBeforeSquareBrackets: false | ||||
| # BitFieldColonSpacing: Both | ||||
| # StatementAttributeLikeMacros: | ||||
| #   - Q_EMIT | ||||
| # StatementMacros: | ||||
| #   - Q_UNUSED | ||||
| #   - QT_REQUIRE_VERSION | ||||
| TabWidth:        4 | ||||
| # UseCRLF:         false | ||||
| UseTab:          Always | ||||
| # WhitespaceSensitiveMacros: | ||||
| #   - STRINGIZE | ||||
| #   - PP_STRINGIZE | ||||
| #   - BOOST_PP_STRINGIZE | ||||
| #   - NS_SWIFT_NAME | ||||
| #   - CF_SWIFT_NAME | ||||
| --- | ||||
| ### C++ specific config ### | ||||
| Language:        Cpp | ||||
| Standard:        c++17 | ||||
| --- | ||||
| ### ObjC specific config ### | ||||
| Language:        ObjC | ||||
| # ObjCBinPackProtocolList: Auto | ||||
| ObjCBlockIndentWidth: 4 | ||||
| # ObjCBreakBeforeNestedBlockParam: true | ||||
| # ObjCSpaceAfterProperty: false | ||||
| # ObjCSpaceBeforeProtocolList: true | ||||
| --- | ||||
| ### Java specific config ### | ||||
| Language:        Java | ||||
| # BreakAfterJavaFieldAnnotations: false | ||||
| JavaImportGroups: ['org.godotengine', 'android', 'androidx', 'com.android', 'com.google', 'java', 'javax'] | ||||
| ... | ||||
							
								
								
									
										22
									
								
								engine/.clang-tidy
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								engine/.clang-tidy
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,22 @@ | |||
| --- | ||||
| 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 | ||||
| WarningsAsErrors: '' | ||||
| 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 | ||||
| ... | ||||
							
								
								
									
										26
									
								
								engine/.editorconfig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								engine/.editorconfig
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,26 @@ | |||
| root = true | ||||
| 
 | ||||
| [*] | ||||
| charset = utf-8 | ||||
| end_of_line = lf | ||||
| indent_style = tab | ||||
| insert_final_newline = true | ||||
| 
 | ||||
| [*.{cpp,hpp,c,h,mm}] | ||||
| trim_trailing_whitespace = true | ||||
| 
 | ||||
| [{*.gradle,AndroidManifest.xml}] | ||||
| indent_style = space | ||||
| indent_size = 4 | ||||
| 
 | ||||
| [{*.py,SConstruct,SCsub}] | ||||
| indent_style = space | ||||
| indent_size = 4 | ||||
| 
 | ||||
| # YAML requires indentation with spaces instead of tabs. | ||||
| [*.{yml,yaml}] | ||||
| indent_style = space | ||||
| indent_size = 2 | ||||
| 
 | ||||
| [*.svg] | ||||
| insert_final_newline = false | ||||
							
								
								
									
										56
									
								
								engine/.git-blame-ignore-revs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								engine/.git-blame-ignore-revs
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,56 @@ | |||
| # 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 | ||||
							
								
								
									
										23
									
								
								engine/.gitattributes
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								engine/.gitattributes
									
									
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,23 @@ | |||
| # Properly detect languages on Github | ||||
| *.h linguist-language=cpp | ||||
| *.inc linguist-language=cpp | ||||
| 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 | ||||
							
								
								
									
										381
									
								
								engine/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										381
									
								
								engine/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,381 @@ | |||
| # 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 | ||||
| build.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 | ||||
| 
 | ||||
| # 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 | ||||
| 
 | ||||
| # Mypy | ||||
| .mypy_cache | ||||
| 
 | ||||
| # 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/* | ||||
| 
 | ||||
| ############################## | ||||
| ### 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/ | ||||
| 
 | ||||
| # 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 | ||||
| 
 | ||||
| # 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 | ||||
							
								
								
									
										182
									
								
								engine/.mailmap
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										182
									
								
								engine/.mailmap
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,182 @@ | |||
| Aaron Record <aaronjrecord@gmail.com> | ||||
| ajreckof <66184050+ajreckof@users.noreply.github.com> <tbonhoure@ymail.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> <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> | ||||
| 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> | ||||
| 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> | ||||
| 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> | ||||
| Kelly Thomas <kelly.thomas@hotmail.com.au> | ||||
| Kongfa Waroros <gongpha@hotmail.com> | ||||
| K. S. Ernest (iFire) Lee <ernest.lee@chibifire.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> | ||||
| 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> | ||||
| 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> | ||||
| 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> | ||||
| 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> | ||||
| 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> | ||||
| 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> | ||||
							
								
								
									
										184
									
								
								engine/.pre-commit-config.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										184
									
								
								engine/.pre-commit-config.yaml
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,184 @@ | |||
| default_language_version: | ||||
|   python: python3 | ||||
| 
 | ||||
| exclude: | | ||||
|   (?x)^( | ||||
|     .*thirdparty/.*| | ||||
|     .*-so_wrap\.(h|c)$ | ||||
|   ) | ||||
| 
 | ||||
| repos: | ||||
|   - repo: https://github.com/pre-commit/mirrors-clang-format | ||||
|     rev: v17.0.6 | ||||
|     hooks: | ||||
|       - id: clang-format | ||||
|         files: \.(c|h|cpp|hpp|cc|hh|cxx|hxx|m|mm|inc|java|glsl)$ | ||||
|         types_or: [text] | ||||
|         exclude: | | ||||
|           (?x)^( | ||||
|             tests/python_build/.*| | ||||
|             platform/android/java/lib/src/com/.* | ||||
|           ) | ||||
| 
 | ||||
|   - 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] | ||||
|         exclude: | | ||||
|           (?x)^( | ||||
|             tests/python_build/.*| | ||||
|             platform/android/java/lib/src/com/.* | ||||
|           ) | ||||
|         additional_dependencies: [clang-tidy==18.1.1] | ||||
|         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.4.4 | ||||
|     hooks: | ||||
|       - id: ruff | ||||
|         args: [--fix] | ||||
|       - id: ruff-format | ||||
| 
 | ||||
|   - repo: https://github.com/pre-commit/mirrors-mypy | ||||
|     rev: v0.971 | ||||
|     hooks: | ||||
|       - id: mypy | ||||
|         files: \.py$ | ||||
|         types_or: [text] | ||||
| 
 | ||||
|   - repo: https://github.com/codespell-project/codespell | ||||
|     rev: v2.3.0 | ||||
|     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] | ||||
|         pass_filenames: false | ||||
|         files: ^(doc/classes|.*/doc_classes)/.*\.xml$ | ||||
| 
 | ||||
|       - 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/com/.*| | ||||
|             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)$ | ||||
|         exclude: ^.*/(thread|platform_config|platform_gl)\.h$ | ||||
| 
 | ||||
|       - 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\.notest\.gd$| | ||||
|             modules/gdscript/tests/scripts/parser/warnings/empty_file_newline\.notest\.gd$| | ||||
|             platform/android/java/lib/src/com/google/.* | ||||
|           ) | ||||
| 
 | ||||
|       - 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 | ||||
							
								
								
									
										321
									
								
								engine/AUTHORS.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										321
									
								
								engine/AUTHORS.md
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,321 @@ | |||
| # Godot Engine authors | ||||
| 
 | ||||
| Godot Engine is developed by a community of voluntary contributors who | ||||
| contribute code, bug reports, documentation, artwork, support, etc. | ||||
| 
 | ||||
| It is impossible to list them all; nevertheless, this file aims at listing | ||||
| the developers who contributed significant patches to this MIT licensed | ||||
| source code. "Significant" is arbitrarily decided, but should be fair :) | ||||
| 
 | ||||
| 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 | ||||
| 
 | ||||
| (in alphabetical order, with over 10 commits excluding merges) | ||||
| 
 | ||||
|     Aaron Franke (aaronfranke) | ||||
|     Aaron Pagano (aaronp64) | ||||
|     Aaron Record (LightningAA) | ||||
|     Adam Scott (adamscott) | ||||
|     Alexander Hartmann (Alex2782) | ||||
|     Alexander Holland (AlexHolly) | ||||
|     Alex Drozd (brno32) | ||||
|     Alexey Khoroshavin (allkhor) | ||||
|     Alfred Reinold Baudisch (alfredbaudisch) | ||||
|     Alistair Leslie-Hughes (alesliehughes) | ||||
|     Alket Rexhepi (alketii) | ||||
|     Andrea Catania (AndreaCatania) | ||||
|     Andreia Gaita (shana) | ||||
|     Andrii Doroshenko (Xrayez) | ||||
|     Andy Maloney (asmaloney) | ||||
|     Andy Moss (MillionOstrich) | ||||
|     Angad Kambli (angad-k) | ||||
|     Anilforextra (AnilBK) | ||||
|     Anish Bhobe (KidRigger) | ||||
|     Anton Yabchinskiy (a12n) | ||||
|     Anutrix | ||||
|     Aren Villanueva (kurikaesu) | ||||
|     Ariel Manzur (punto-) | ||||
|     Arman Elgudzhyan (puchik) | ||||
|     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) | ||||
|     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) | ||||
|     Dharkael (lupoDharkael) | ||||
|     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) | ||||
|     Florian Kothmeier (Dragoncraft89) | ||||
|     follower | ||||
|     foxydevloper | ||||
|     François Belair (Razoric480) | ||||
|     Franklin Sobrinho (TheHX) | ||||
|     Fredia Huya-Kouadio (m4gr3d) | ||||
|     Geequlim | ||||
|     George Marques (vnen) | ||||
|     Gerrit Großkopf (Grosskopf) | ||||
|     Gilles Roudiere (groud) | ||||
|     Gordon MacPherson (RevoluPowered) | ||||
|     Guilherme Felipe de C. G. da Silva (guilhermefelipecgs) | ||||
|     Hakim Rouatbi (hakro) | ||||
|     Hanif Bin Ariffin (hbina) | ||||
|     Haoyu Qiu (timothyqiu) | ||||
|     Hein-Pieter van Braam-Stewart (hpvb) | ||||
|     Hendrik Brucker (Geometror) | ||||
|     Hilderin | ||||
|     hilfazer | ||||
|     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 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 | ||||
|     Kelly Thomas (KellyThomas) | ||||
|     kleonc | ||||
|     Kongfa Waroros (gongpha) | ||||
|     Kostadin Damyanov (Max-Might) | ||||
|     K. S. Ernest (iFire) Lee (fire) | ||||
|     lawnjelly | ||||
|     Leon Krause (leonkrause) | ||||
|     Liz Haas (27thLiz) | ||||
|     Lucien Menassol (Kanabenki) | ||||
|     Lyuma | ||||
|     Maganty Rushyendra (mrushyendra) | ||||
|     Magian (magian1127) | ||||
|     Mai Lavelle (maiself) | ||||
|     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 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 (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) | ||||
|     MinusKube | ||||
|     MJacred | ||||
|     Morris "Tabor" Arroad (mortarroad) | ||||
|     mrezai | ||||
|     Muhammad Huri (CakHuri) | ||||
|     muiroc | ||||
|     myaaaaaaaaa | ||||
|     Nathalie Galla (MurderVeggie) | ||||
|     Nathan Franke (nathanfranke) | ||||
|     Nathan Lovato (NathanLovato) | ||||
|     Nathan Warden (NathanWarden) | ||||
|     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 | ||||
|     Pascal Richter (ShyRed) | ||||
|     passivestar | ||||
|     Patrick Dawson (pkdawson) | ||||
|     Patrick Exner (FlameLizard) | ||||
|     Patrick (firefly2442) | ||||
|     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) | ||||
|     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 | ||||
|     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 | ||||
|     Swarnim Arun (minraws) | ||||
|     TC (floppyhammer) | ||||
|     TechnoPorg | ||||
|     Thaddeus Crews (Repiteo) | ||||
|     Thakee Nathees (ThakeeNathees) | ||||
|     thebestnom | ||||
|     Theo Hallenius (TheoXD) | ||||
|     Timo Schwarzer (timoschwarzer) | ||||
|     Timothé Bonhoure (ajreckof) | ||||
|     Timo (toger5) | ||||
|     Tomasz Chabora (KoBeWi) | ||||
|     trollodel | ||||
|     Twarit Waikar (IronicallySerious) | ||||
|     Umang Kalra (theoway) | ||||
|     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) | ||||
|     yg2f (SuperUserNameMan) | ||||
|     Yordan Dolchinkov (Jordyfel) | ||||
|     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) | ||||
							
								
								
									
										3525
									
								
								engine/CHANGELOG.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3525
									
								
								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 | ||||
							
								
								
									
										1984
									
								
								engine/COPYRIGHT.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1984
									
								
								engine/COPYRIGHT.txt
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										516
									
								
								engine/DONORS.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										516
									
								
								engine/DONORS.md
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,516 @@ | |||
| # 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 | ||||
| 
 | ||||
|     OSS Capital <https://oss.capital/> | ||||
|     Re-Logic <https://re-logic.com/> | ||||
| 
 | ||||
| ## Platinum sponsors | ||||
| 
 | ||||
|     Google Play <https://play.google.com/> | ||||
|     Ramatak <https://ramatak.com/> | ||||
|     V-Sekai <https://github.com/V-Sekai> | ||||
|     W4 Games <https://w4games.com/> | ||||
| 
 | ||||
| ## Gold sponsors | ||||
| 
 | ||||
|     Mega Crit <https://www.megacrit.com/> | ||||
|     Pirate Software <https://gopiratesoftware.com/> | ||||
|     Prehensile Tales <https://prehensile-tales.com/> | ||||
|     Robot Gentleman <http://robotgentleman.com/> | ||||
| 
 | ||||
| ## Silver sponsors | ||||
| 
 | ||||
|     Broken Rules <https://brokenrul.es/> | ||||
|     Chasing Carrots <https://www.chasing-carrots.com/> | ||||
|     Copia Wealth Studios <https://copiawealthstudios.com/> | ||||
|     Indoor Astronaut <https://indoorastronaut.ch/> | ||||
|     Load Complete <https://loadcomplete.com/> | ||||
|     Null <https://null.com/> | ||||
|     Orbital Knight <https://www.orbitalknight.com/> | ||||
|     Playful Studios <https://playfulstudios.com/> | ||||
| 
 | ||||
| ## Diamond members | ||||
| 
 | ||||
|     Bippinbits <http://domekeepergame.com/> | ||||
|     Sealow | ||||
|     And 5 anonymous donors | ||||
| 
 | ||||
| ## Titanium members | ||||
| 
 | ||||
|     Adriaan de Jongh <https://adriaan.games/> | ||||
|     Anitya Space <https://www.anitya.space/> | ||||
|     Basically Games | ||||
|     FDG Entertainment <https://www.fdg-entertainment.com/> | ||||
|     Game Dev Artisan <https://gamedevartisan.com/> | ||||
|     Garry Newman | ||||
|     Isaiah Smith <https://www.isaiahsmith.dev/> | ||||
|     Libretrend <https://libretrend.com/> | ||||
|     Life Art Studios <https://lifeartstudios.net/> | ||||
|     Lucid Silence Games | ||||
|     Matthew Campbell | ||||
|     PolyMars <https://polymars.dev/> | ||||
|     RPG in a Box <https://www.rpginabox.com/> | ||||
|     Razenpok <https://www.youtube.com/watch?v=-QxI-RP6-HM> | ||||
|     Smirk Software <https://smirk.gg/> | ||||
|     粟二华 (Su Erhua) | ||||
|     And 6 anonymous donors | ||||
| 
 | ||||
| ## Platinum members | ||||
| 
 | ||||
|     Andy Touch | ||||
|     BlockImperiumGames (BIG) | ||||
|     Christoph Woinke | ||||
|     Christopher Shifflett | ||||
|     Cody Bentley | ||||
|     Darrin Massena | ||||
|     Edward Flick | ||||
|     GetIntoGameDev | ||||
|     HP van Braam | ||||
|     iCommitGames | ||||
|     Jonah Stich | ||||
|     katnamag | ||||
|     Marek Belski | ||||
|     Matthew Ekenstedt | ||||
|     Memories in 8Bit | ||||
|     Mike King | ||||
|     Neal Gompa (Conan Kudo) | ||||
|     Radivarig | ||||
|     Ronnie Cheng | ||||
|     Ryan Heath | ||||
|     Scott Pezza | ||||
|     ShikadiGum | ||||
|     Silver Creek Entertainment | ||||
|     SolarLabyrinth | ||||
|     Stephan Kessler | ||||
|     Stephan Lanfermann | ||||
|     TigerJ | ||||
|     Violin Iliev | ||||
|     Vladimír Chvátil | ||||
|     And 16 anonymous donors | ||||
| 
 | ||||
| ## Gold members | ||||
| 
 | ||||
|     80px | ||||
|     afreytes | ||||
|     alMoo Games | ||||
|     Alva Majo | ||||
|     Antti Vesanen | ||||
|     Asher Glick | ||||
|     Axthelm | ||||
|     Bellbird Studio | ||||
|     Benito | ||||
|     Benjamin Sarsgard | ||||
|     Ben Rog-Wilhelm | ||||
|     Bernd Barsuhn | ||||
|     BetaTester704 | ||||
|     Brian Levinsen | ||||
|     Brut | ||||
|     Bryce Dixon | ||||
|     c64cosmin | ||||
|     Carlo del Mundo | ||||
|     Cindy Trieu | ||||
|     ClarkThyLord | ||||
|     Codex404 | ||||
|     cora | ||||
|     Daniel Eichler | ||||
|     Daniel Krafft | ||||
|     David Chen Zhen | ||||
|     David Coles | ||||
|     David Hubber | ||||
|     David Snopek | ||||
|     Deakcor | ||||
|     Delton Ding | ||||
|     dfseifert | ||||
|     dgehrig | ||||
|     dhanielk | ||||
|     Distorted Realities | ||||
|     Dono | ||||
|     Don't You Know Who I Am? Inc. | ||||
|     Dustuu | ||||
|     Edelweiss | ||||
|     Ends | ||||
|     Eren Ogrul | ||||
|     Eric Brand | ||||
|     Eric Phy | ||||
|     Faisal Al-Kubaisi (QatariGameDev) | ||||
|     FeralBytes | ||||
|     Festzeltgaming.de | ||||
|     Gaudipern | ||||
|     GlassBrick | ||||
|     Grau | ||||
|     Guangzhou Lingchan | ||||
|     Hayden Oliver | ||||
|     hiulit | ||||
|     Illyan | ||||
|     Ivan Tabashki | ||||
|     Jacob (HACKhalo2 Studios) | ||||
|     Jam | ||||
|     Jason Cawood | ||||
|     Javier Roman | ||||
|     Jeff Hungerford | ||||
|     Jeronimo Schreyer | ||||
|     Joel Martinez | ||||
|     Johannes Wuensch | ||||
|     John Gabriel | ||||
|     Jonas Yamazaki | ||||
|     José Canepa | ||||
|     Joshua Stelly | ||||
|     Justin Sasso | ||||
|     Kalydi Balázs | ||||
|     KAR Games | ||||
|     Kiri "ExpiredPopsicle" Artemis | ||||
|     KOGA Mitsuhiro (@shiena) | ||||
|     korinVR | ||||
|     Kristian Kriehl | ||||
|     Lars Thießen | ||||
|     Lisandro Lorea (Red Mage Games) | ||||
|     Logan Apple | ||||
|     Luca Junge | ||||
|     LyaaaaaGames | ||||
|     m1n1ster | ||||
|     Manuel Requena | ||||
|     Mara Huldra | ||||
|     Martin Šenkeřík | ||||
|     Michael Gooch | ||||
|     Modus Ponens | ||||
|     Moshe Harris | ||||
|     Moth | ||||
|     Mr. Byte | ||||
|     Nassor Paulino da Silva | ||||
|     nezticle | ||||
|     Niklas Wahrman | ||||
|     Niwl Games | ||||
|     NotNet | ||||
|     Oathbringer | ||||
|     Officine Pixel | ||||
|     ohanaya3 | ||||
|     Okatima AB | ||||
|     Oleksii Nosov | ||||
|     Osirisa | ||||
|     Patrick Traynor | ||||
|     Petr Malac | ||||
|     pirey | ||||
|     Rafa Laguna | ||||
|     @reilaos | ||||
|     Request | ||||
|     re:thinc | ||||
|     Richard Ivánek | ||||
|     Rudi P | ||||
|     Samuel Judd | ||||
|     ScoreSpace | ||||
|     Shiny Shinken | ||||
|     Silverclad Studios | ||||
|     Sofox | ||||
|     Space Kraken Studios | ||||
|     spacesloth | ||||
|     Spoony Panda | ||||
|     TANAKA Yu | ||||
|     TaraSophieDev (pls fix #43093) | ||||
|     Thad Guidry | ||||
|     ThatGamer | ||||
|     The Polyglot Programmer | ||||
|     TheRiverNyx | ||||
|     Thomas Lobig | ||||
|     Tim Nedvyga | ||||
|     Tom Langwaldt | ||||
|     Trevor Slocum | ||||
|     tukon | ||||
|     Tyler C | ||||
|     Vagastella | ||||
|     Vincent Foulon | ||||
|     Vojtech Lacina | ||||
|     Watchinofoye | ||||
|     Weasel Games | ||||
|     Wilson Birney | ||||
|     Wolfram | ||||
|     WuotanStudios.com | ||||
|     Zhu Li | ||||
|     zikes | ||||
|     嗯大爷 | ||||
| 
 | ||||
|     Alex Khayrullin | ||||
|     Algebrute | ||||
|     Andriy | ||||
|     Antanas Paskauskas | ||||
|     anti666 | ||||
|     Ari | ||||
|     Arisaka Mayuki | ||||
|     Arthur S. Muszynski | ||||
|     Cameron Connolly | ||||
|     Charlie Whitfield | ||||
|     Craig Ostrin | ||||
|     Craig Swain | ||||
|     CzechBlueBear | ||||
|     Dennis Belfrage | ||||
|     Emily A. Bellows | ||||
|     Felix Winterhalter | ||||
|     Fransiska | ||||
|     Harry Tumber | ||||
|     James Couzens | ||||
|     Jared White | ||||
|     Jesús Chicharro | ||||
|     Joel Fivat | ||||
|     Johnathan Kupferer | ||||
|     Josef Stumpfegger | ||||
|     Joshua Lesperance | ||||
|     Kelteseth | ||||
|     kickmaniac | ||||
|     Liam Smyth | ||||
|     LoparPanda | ||||
|     Martin Gulliksson | ||||
|     Martin Soucek | ||||
|     Michael Dürwald | ||||
|     Michael Policastro | ||||
|     n00sh | ||||
|     Nicolás Monner Sans | ||||
|     Nikita Rotskov | ||||
|     Nikola Whallon | ||||
|     Oliver Dick | ||||
|     Patrick Wuttke | ||||
|     Pete Goodwin | ||||
|     Philip Woods | ||||
|     Reilt | ||||
|     Rickard Hermanson | ||||
|     Rob | ||||
|     Rob McInroy | ||||
|     RodZilla | ||||
|     Ruzgud | ||||
|     Ryan Breaker | ||||
|     "Sage Automatic Systems, LLC" | ||||
|     spacechase0 | ||||
|     sus | ||||
|     Thomas Kurz | ||||
|     Tobias Bocanegra | ||||
|     Torbulous | ||||
|     toto bibi | ||||
|     Valryia | ||||
|     VoidPointer | ||||
|     Yifan Lai | ||||
| 
 | ||||
|     Aaron Mayfield | ||||
|     Adam Carr | ||||
|     Adam Smeltzer | ||||
|     Adisibio | ||||
|     Aidan Marwick | ||||
|     Aidan O'Flannagain | ||||
|     AJWolbers | ||||
|     Alan Beauchamp | ||||
|     Alexander Erlemann | ||||
|     Alex Clavelle | ||||
|     alex raeside | ||||
|     Andre Altmueller | ||||
|     Andreas Østergaard Nielsen | ||||
|     Andrew | ||||
|     Ano Nim | ||||
|     Arch Toasty | ||||
|     Arda Erol | ||||
|     A Really Tall Horse | ||||
|     Arturo Rosales | ||||
|     Ash K | ||||
|     Aubrey Falconer | ||||
|     Austin Miller | ||||
|     AzulCrescent | ||||
|     Beau Seymour | ||||
|     Benedikt | ||||
|     Bread | ||||
|     Brian Ford | ||||
|     Caleb Makela | ||||
|     Cameron Meyer | ||||
|     Carl van der Geest | ||||
|     Checkpoint Charlie | ||||
|     Chris Cavalluzzi | ||||
|     Chris Jagusch | ||||
|     Chris Lee | ||||
|     Christian Mauduit | ||||
|     Christian Ringshofer | ||||
|     Christoph Czurda | ||||
|     Christophe Gagnier | ||||
|     Cody Parker | ||||
|     Conall O | ||||
|     Corchari | ||||
|     Corey W | ||||
|     Dakota Watkins | ||||
|     Daniele Tolomelli | ||||
|     Daniel Ramos | ||||
|     Dave Jansen | ||||
|     Davesnothere | ||||
|     David Baker | ||||
|     David Bôle | ||||
|     David May | ||||
|     David Maziarka | ||||
|     Devin Carraway | ||||
|     Devin R | ||||
|     Dimitri Roche | ||||
|     Donovan Hutcheon | ||||
|     Ducky | ||||
|     Duodecimal | ||||
|     Egon Elbre | ||||
|     Elijah Anderson | ||||
|     Eric Persson | ||||
|     Eric Stokes | ||||
|     Eric Williams | ||||
|     Erkki Seppälä | ||||
|     Ewan Holmes | ||||
|     Felix Adam | ||||
|     Frank | ||||
|     Frying☆Pan | ||||
|     Game Endeavor | ||||
|     gamerminstrel | ||||
|     Gary Thomas | ||||
|     gebba | ||||
|     Greyson Richey | ||||
|     Guo Hongci | ||||
|     Haplo | ||||
|     Helge Maus | ||||
|     Heribert Hirth | ||||
|     Ian Richard Kunert | ||||
|     Ian Williams | ||||
|     itsybitesyspider | ||||
|     iveks | ||||
|     Jacob Wallace | ||||
|     Jako Danar | ||||
|     James Gary | ||||
|     James Hulsizer | ||||
|     Jamie Massey | ||||
|     JARKKO PARVIAINEN | ||||
|     Jason Evans | ||||
|     Joakim Askenbäck | ||||
|     Jonas | ||||
|     Jonas Arndt | ||||
|     Jonas Yamazaki | ||||
|     Jonathan Bieber | ||||
|     Jon Sully | ||||
|     Joseph Catrambone | ||||
|     Josh Taylor | ||||
|     Juanfran | ||||
|     Julian le Roux | ||||
|     Justin Spedding | ||||
|     Keith Bradner | ||||
|     kindzadza | ||||
|     KsyTek Games | ||||
|     Kyle Burnett | ||||
|     Kyle Haltermann | ||||
|     Kyle Jacobs | ||||
|     Leland Vakarian | ||||
|     Levi Berciu | ||||
|     liberodark | ||||
|     Linus Lind Lundgren | ||||
|     Ludovic DELVAL | ||||
|     Luigi Renna | ||||
|     Luis Morao | ||||
|     Lukas Komischke | ||||
|     Luke Diasio | ||||
|     Major Haul | ||||
|     Malcolm | ||||
|     Manuele Finocchiaro | ||||
|     Marcos Heitor Carvalho | ||||
|     Markie Music | ||||
|     Mark Tyler | ||||
|     Markus Michael Egger | ||||
|     Martin Holas | ||||
|     Martin Liška | ||||
|     Martin Trbola | ||||
|     Matěj Drábek | ||||
|     Mathieu | ||||
|     Matt Edwards | ||||
|     Maverick | ||||
|     Maxime Blade | ||||
|     Maxwell | ||||
|     Melissa Mears | ||||
|     Metal Demon 2000 | ||||
|     Michael Morrison | ||||
|     Mike Copley | ||||
|     Molly Jameson | ||||
|     Moritz Weissenberger | ||||
|     Mrjemandem | ||||
|     naonya3 | ||||
|     Nathaniel | ||||
|     neighty | ||||
|     Neil Blakey-Milner | ||||
|     Neofytos Chimonas | ||||
|     Nerdforge | ||||
|     Nerdyninja | ||||
|     Nick Eldrenkamp | ||||
|     Nik Rudenko | ||||
|     Noel Billig | ||||
|     ozrk | ||||
|     Patrick Horn | ||||
|     Patrickm | ||||
|     Patrick Nafarrete | ||||
|     Paul Black | ||||
|     Paul Gieske | ||||
|     Paul Mozet | ||||
|     Pete | ||||
|     Phoenix Jauregui | ||||
|     Pierre Caye | ||||
|     Pixel Archipel | ||||
|     Point08 | ||||
|     PsycHead | ||||
|     Quincy Quincy | ||||
|     Quinn Morrison | ||||
|     Raghava Kovvali | ||||
|     Ragnar Pettersson | ||||
|     Rammeow | ||||
|     Rebecca H | ||||
|     Richard Hayes | ||||
|     Riley | ||||
|     RobotCritter | ||||
|     Roland Rząsa | ||||
|     Russ | ||||
|     Ryan Groom | ||||
|     Sammy Fischer | ||||
|     Satnam Singh | ||||
|     Sebastian Michailidis | ||||
|     SeongWan Kim | ||||
|     Shane Lillie | ||||
|     Shane Spoor | ||||
|     Shaun Kohanowski | ||||
|     Simon Jonas Larsen | ||||
|     Simon Schoenenberger | ||||
|     Sina Yeganeh | ||||
|     Skalli | ||||
|     slavfox | ||||
|     smo1704 | ||||
|     SpicyCactuar | ||||
|     Stephen Rice | ||||
|     Stephen Schlie | ||||
|     Sven Walter | ||||
|     SxP | ||||
|     tadashi endo | ||||
|     Tarch | ||||
|     TheVoiceInMyHead | ||||
|     Thibaut DECROMBECQUE | ||||
|     thomas | ||||
|     Thomas Pickett | ||||
|     Tim Drumheller | ||||
|     Tim Klein | ||||
|     Tom Webster | ||||
|     Trent Skinner | ||||
|     Tyler Stepke | ||||
|     Uther | ||||
|     Vaughan Ling | ||||
|     vlnx | ||||
|     Wapiti . | ||||
|     Wiley Thompson | ||||
|     Xatonym | ||||
|     Zekim | ||||
|     ケルベロス | ||||
|     貴宏 小松 | ||||
| 
 | ||||
|     And 181 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) | ||||
							
								
								
									
										1104
									
								
								engine/SConstruct
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1104
									
								
								engine/SConstruct
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										298
									
								
								engine/core/SCsub
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										298
									
								
								engine/core/SCsub
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,298 @@ | |||
| #!/usr/bin/env python | ||||
| 
 | ||||
| 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(CPPPATH=[thirdparty_brotli_dir + "include"]) | ||||
|     env.Prepend(CPPPATH=[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(CPPPATH=[thirdparty_clipper_dir + "include"]) | ||||
|     env.Prepend(CPPPATH=[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(CPPPATH=[thirdparty_zlib_dir]) | ||||
|     # Needs to be available in main env too | ||||
|     env.Prepend(CPPPATH=[thirdparty_zlib_dir]) | ||||
|     if env.dev_build: | ||||
|         env_thirdparty.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"]: | ||||
|         # 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(CPPPATH=[thirdparty_zstd_dir, thirdparty_zstd_dir + "common"]) | ||||
|     env_thirdparty.Append(CPPDEFINES=["ZSTD_STATIC_LINKING_ONLY"]) | ||||
|     env.Prepend(CPPPATH=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(target) as file: | ||||
|         for c in source[0].read(): | ||||
|             cs = c.strip() | ||||
|             if cs != "": | ||||
|                 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(target) as file: | ||||
|         file.write( | ||||
|             """\ | ||||
| #define VERSION_SHORT_NAME "{short_name}" | ||||
| #define VERSION_NAME "{name}" | ||||
| #define VERSION_MAJOR {major} | ||||
| #define VERSION_MINOR {minor} | ||||
| #define VERSION_PATCH {patch} | ||||
| #define VERSION_STATUS "{status}" | ||||
| #define VERSION_BUILD "{build}" | ||||
| #define VERSION_MODULE_CONFIG "{module_config}" | ||||
| #define VERSION_WEBSITE "{website}" | ||||
| #define VERSION_DOCS_BRANCH "{docs_branch}" | ||||
| #define VERSION_DOCS_URL "https://docs.godotengine.org/en/" VERSION_DOCS_BRANCH | ||||
| """.format(**env.version_info) | ||||
|         ) | ||||
| 
 | ||||
| 
 | ||||
| env.CommandNoCache("version_generated.gen.h", env.Value(env.version_info), env.Run(version_info_builder)) | ||||
| 
 | ||||
| 
 | ||||
| # Generate version hash | ||||
| def version_hash_builder(target, source, env): | ||||
|     with methods.generated_wrapper(target) as file: | ||||
|         file.write( | ||||
|             """\ | ||||
| #include "core/version.h" | ||||
| 
 | ||||
| const char *const VERSION_HASH = "{git_hash}"; | ||||
| const uint64_t VERSION_TIMESTAMP = {git_timestamp}; | ||||
| """.format(**env.version_info) | ||||
|         ) | ||||
| 
 | ||||
| 
 | ||||
| gen_hash = env.CommandNoCache( | ||||
|     "version_hash.gen.cpp", env.Value(env.version_info["git_hash"]), 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(target) 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.Depends( | ||||
|     "#core/io/certs_compressed.gen.h", | ||||
|     ["#thirdparty/certs/ca-certificates.crt", env.Value(env["builtin_certs"]), env.Value(env["system_certs_path"])], | ||||
| ) | ||||
| env.CommandNoCache( | ||||
|     "#core/io/certs_compressed.gen.h", | ||||
|     "#thirdparty/certs/ca-certificates.crt", | ||||
|     env.Run(core_builders.make_certs_header), | ||||
| ) | ||||
| 
 | ||||
| # Authors | ||||
| env.Depends("#core/authors.gen.h", "../AUTHORS.md") | ||||
| env.CommandNoCache("#core/authors.gen.h", "../AUTHORS.md", env.Run(core_builders.make_authors_header)) | ||||
| 
 | ||||
| # Donors | ||||
| env.Depends("#core/donors.gen.h", "../DONORS.md") | ||||
| env.CommandNoCache("#core/donors.gen.h", "../DONORS.md", env.Run(core_builders.make_donors_header)) | ||||
| 
 | ||||
| # License | ||||
| env.Depends("#core/license.gen.h", ["../COPYRIGHT.txt", "../LICENSE.txt"]) | ||||
| 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) | ||||
							
								
								
									
										7
									
								
								engine/core/config/SCsub
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								engine/core/config/SCsub
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,7 @@ | |||
| #!/usr/bin/env python | ||||
| 
 | ||||
| Import("env") | ||||
| 
 | ||||
| env_config = env.Clone() | ||||
| 
 | ||||
| env_config.add_source_files(env.core_sources, "*.cpp") | ||||
							
								
								
									
										403
									
								
								engine/core/config/engine.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										403
									
								
								engine/core/config/engine.cpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,403 @@ | |||
| /**************************************************************************/ | ||||
| /*  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" | ||||
| 
 | ||||
| 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; | ||||
| } | ||||
| 
 | ||||
| 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 _time_scale; | ||||
| } | ||||
| 
 | ||||
| Dictionary Engine::get_version_info() const { | ||||
| 	Dictionary dict; | ||||
| 	dict["major"] = VERSION_MAJOR; | ||||
| 	dict["minor"] = VERSION_MINOR; | ||||
| 	dict["patch"] = VERSION_PATCH; | ||||
| 	dict["hex"] = VERSION_HEX; | ||||
| 	dict["status"] = VERSION_STATUS; | ||||
| 	dict["build"] = VERSION_BUILD; | ||||
| 
 | ||||
| 	String hash = String(VERSION_HASH); | ||||
| 	dict["hash"] = hash.is_empty() ? String("unknown") : hash; | ||||
| 
 | ||||
| 	dict["timestamp"] = 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(__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; | ||||
| } | ||||
| 
 | ||||
| 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; | ||||
| } | ||||
| 
 | ||||
| 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 | ||||
| } | ||||
							
								
								
									
										193
									
								
								engine/core/config/engine.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										193
									
								
								engine/core/config/engine.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,193 @@ | |||
| /**************************************************************************/ | ||||
| /*  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.                 */ | ||||
| /**************************************************************************/ | ||||
| 
 | ||||
| #ifndef ENGINE_H | ||||
| #define ENGINE_H | ||||
| 
 | ||||
| #include "core/os/main_loop.h" | ||||
| #include "core/string/ustring.h" | ||||
| #include "core/templates/list.h" | ||||
| #include "core/templates/vector.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; | ||||
| 	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 _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; | ||||
| 
 | ||||
| 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; | ||||
| 
 | ||||
| 	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; } | ||||
| #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; } | ||||
| #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; | ||||
| 	int32_t get_gpu_index() const; | ||||
| 
 | ||||
| 	void increment_frames_drawn(); | ||||
| 	bool notify_frame_server_synced(); | ||||
| 
 | ||||
| 	Engine(); | ||||
| 	virtual ~Engine(); | ||||
| }; | ||||
| 
 | ||||
| #endif // ENGINE_H
 | ||||
							
								
								
									
										1593
									
								
								engine/core/config/project_settings.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1593
									
								
								engine/core/config/project_settings.cpp
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										248
									
								
								engine/core/config/project_settings.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										248
									
								
								engine/core/config/project_settings.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,248 @@ | |||
| /**************************************************************************/ | ||||
| /*  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.                 */ | ||||
| /**************************************************************************/ | ||||
| 
 | ||||
| #ifndef PROJECT_SETTINGS_H | ||||
| #define PROJECT_SETTINGS_H | ||||
| 
 | ||||
| #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; | ||||
| 
 | ||||
| 	enum { | ||||
| 		// Properties that are not for built in values begin from this value, so builtin ones are displayed first.
 | ||||
| 		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 = true, int p_offset = 0); | ||||
| 
 | ||||
| 	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; | ||||
| 
 | ||||
| 	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) | ||||
| 
 | ||||
| #endif // PROJECT_SETTINGS_H
 | ||||
							
								
								
									
										2057
									
								
								engine/core/core_bind.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2057
									
								
								engine/core/core_bind.cpp
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										621
									
								
								engine/core/core_bind.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										621
									
								
								engine/core/core_bind.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,621 @@ | |||
| /**************************************************************************/ | ||||
| /*  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.                 */ | ||||
| /**************************************************************************/ | ||||
| 
 | ||||
| #ifndef CORE_BIND_H | ||||
| #define CORE_BIND_H | ||||
| 
 | ||||
| #include "core/debugger/engine_profiler.h" | ||||
| #include "core/io/image.h" | ||||
| #include "core/io/resource_loader.h" | ||||
| #include "core/io/resource_saver.h" | ||||
| #include "core/object/script_language.h" | ||||
| #include "core/os/os.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 core_bind { | ||||
| 
 | ||||
| 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 = Array()); | ||||
| 	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); | ||||
| 	bool exists(const String &p_path, const String &p_type_hint = ""); | ||||
| 	ResourceUID::ID get_resource_uid(const String &p_path); | ||||
| 
 | ||||
| 	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); | ||||
| 
 | ||||
| 	ResourceSaver() { singleton = this; } | ||||
| }; | ||||
| 
 | ||||
| class OS : public Object { | ||||
| 	GDCLASS(OS, Object); | ||||
| 
 | ||||
| 	mutable HashMap<String, bool> feature_cache; | ||||
| 
 | ||||
| protected: | ||||
| 	static void _bind_methods(); | ||||
| 	static OS *singleton; | ||||
| 
 | ||||
| public: | ||||
| 	enum RenderingDriver { | ||||
| 		RENDERING_DRIVER_VULKAN, | ||||
| 		RENDERING_DRIVER_OPENGL3, | ||||
| 		RENDERING_DRIVER_D3D12, | ||||
| 	}; | ||||
| 
 | ||||
| 	PackedByteArray get_entropy(int p_bytes); | ||||
| 	String get_system_ca_certificates(); | ||||
| 
 | ||||
| 	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(); | ||||
| 	int execute(const String &p_path, const Vector<String> &p_arguments, Array r_output = Array(), bool p_read_stderr = false, bool p_open_console = false); | ||||
| 	Dictionary execute_with_pipe(const String &p_path, const Vector<String> &p_arguments); | ||||
| 	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; | ||||
| 	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; | ||||
| 
 | ||||
| 	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); | ||||
| 
 | ||||
| 	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; | ||||
| 
 | ||||
| 	static void _bind_methods(); | ||||
| 
 | ||||
| public: | ||||
| 	void wait(); | ||||
| 	bool try_wait(); | ||||
| 	void post(); | ||||
| }; | ||||
| 
 | ||||
| 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: | ||||
| 	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; | ||||
| 
 | ||||
| 	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; | ||||
| 	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; | ||||
| 
 | ||||
| 	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; | ||||
| 
 | ||||
| 	// `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_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 core_bind
 | ||||
| 
 | ||||
| VARIANT_ENUM_CAST(core_bind::ResourceLoader::ThreadLoadStatus); | ||||
| VARIANT_ENUM_CAST(core_bind::ResourceLoader::CacheMode); | ||||
| 
 | ||||
| VARIANT_BITFIELD_CAST(core_bind::ResourceSaver::SaverFlags); | ||||
| 
 | ||||
| VARIANT_ENUM_CAST(core_bind::OS::RenderingDriver); | ||||
| VARIANT_ENUM_CAST(core_bind::OS::SystemDir); | ||||
| 
 | ||||
| VARIANT_ENUM_CAST(core_bind::Geometry2D::PolyBooleanOperation); | ||||
| VARIANT_ENUM_CAST(core_bind::Geometry2D::PolyJoinType); | ||||
| VARIANT_ENUM_CAST(core_bind::Geometry2D::PolyEndType); | ||||
| 
 | ||||
| VARIANT_ENUM_CAST(core_bind::Thread::Priority); | ||||
| 
 | ||||
| #endif // CORE_BIND_H
 | ||||
							
								
								
									
										315
									
								
								engine/core/core_builders.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										315
									
								
								engine/core/core_builders.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,315 @@ | |||
| """Functions used to generate source files during build time""" | ||||
| 
 | ||||
| import zlib | ||||
| 
 | ||||
| 
 | ||||
| def escape_string(s): | ||||
|     def charcode_to_c_escapes(c): | ||||
|         rev_result = [] | ||||
|         while c >= 256: | ||||
|             c, low = (c // 256, c % 256) | ||||
|             rev_result.append("\\%03o" % low) | ||||
|         rev_result.append("\\%03o" % c) | ||||
|         return "".join(reversed(rev_result)) | ||||
| 
 | ||||
|     result = "" | ||||
|     if isinstance(s, str): | ||||
|         s = s.encode("utf-8") | ||||
|     for c in s: | ||||
|         if not (32 <= c < 127) or c in (ord("\\"), ord('"')): | ||||
|             result += charcode_to_c_escapes(c) | ||||
|         else: | ||||
|             result += chr(c) | ||||
|     return result | ||||
| 
 | ||||
| 
 | ||||
| def make_certs_header(target, source, env): | ||||
|     src = str(source[0]) | ||||
|     dst = str(target[0]) | ||||
|     with open(src, "rb") as f, open(dst, "w", encoding="utf-8", newline="\n") as g: | ||||
|         buf = f.read() | ||||
|         decomp_size = len(buf) | ||||
| 
 | ||||
|         # Use maximum zlib compression level to further reduce file size | ||||
|         # (at the cost of initial build times). | ||||
|         buf = zlib.compress(buf, zlib.Z_BEST_COMPRESSION) | ||||
| 
 | ||||
|         g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n") | ||||
|         g.write("#ifndef CERTS_COMPRESSED_GEN_H\n") | ||||
|         g.write("#define CERTS_COMPRESSED_GEN_H\n") | ||||
| 
 | ||||
|         # System certs path. Editor will use them if defined. (for package maintainers) | ||||
|         path = env["system_certs_path"] | ||||
|         g.write('#define _SYSTEM_CERTS_PATH "%s"\n' % str(path)) | ||||
|         if env["builtin_certs"]: | ||||
|             # Defined here and not in env so changing it does not trigger a full rebuild. | ||||
|             g.write("#define BUILTIN_CERTS_ENABLED\n") | ||||
|             g.write("static const int _certs_compressed_size = " + str(len(buf)) + ";\n") | ||||
|             g.write("static const int _certs_uncompressed_size = " + str(decomp_size) + ";\n") | ||||
|             g.write("static const unsigned char _certs_compressed[] = {\n") | ||||
|             for i in range(len(buf)): | ||||
|                 g.write("\t" + str(buf[i]) + ",\n") | ||||
|             g.write("};\n") | ||||
|         g.write("#endif // CERTS_COMPRESSED_GEN_H") | ||||
| 
 | ||||
| 
 | ||||
| def make_authors_header(target, source, env): | ||||
|     sections = [ | ||||
|         "Project Founders", | ||||
|         "Lead Developer", | ||||
|         "Project Manager", | ||||
|         "Developers", | ||||
|     ] | ||||
|     sections_id = [ | ||||
|         "AUTHORS_FOUNDERS", | ||||
|         "AUTHORS_LEAD_DEVELOPERS", | ||||
|         "AUTHORS_PROJECT_MANAGERS", | ||||
|         "AUTHORS_DEVELOPERS", | ||||
|     ] | ||||
| 
 | ||||
|     src = str(source[0]) | ||||
|     dst = str(target[0]) | ||||
|     with open(src, "r", encoding="utf-8") as f, open(dst, "w", encoding="utf-8", newline="\n") as g: | ||||
|         g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n") | ||||
|         g.write("#ifndef AUTHORS_GEN_H\n") | ||||
|         g.write("#define AUTHORS_GEN_H\n") | ||||
| 
 | ||||
|         reading = False | ||||
| 
 | ||||
|         def close_section(): | ||||
|             g.write("\t0\n") | ||||
|             g.write("};\n") | ||||
| 
 | ||||
|         for line in f: | ||||
|             if reading: | ||||
|                 if line.startswith("    "): | ||||
|                     g.write('\t"' + escape_string(line.strip()) + '",\n') | ||||
|                     continue | ||||
|             if line.startswith("## "): | ||||
|                 if reading: | ||||
|                     close_section() | ||||
|                     reading = False | ||||
|                 for section, section_id in zip(sections, sections_id): | ||||
|                     if line.strip().endswith(section): | ||||
|                         current_section = escape_string(section_id) | ||||
|                         reading = True | ||||
|                         g.write("const char *const " + current_section + "[] = {\n") | ||||
|                         break | ||||
| 
 | ||||
|         if reading: | ||||
|             close_section() | ||||
| 
 | ||||
|         g.write("#endif // AUTHORS_GEN_H\n") | ||||
| 
 | ||||
| 
 | ||||
| def make_donors_header(target, source, env): | ||||
|     sections = [ | ||||
|         "Patrons", | ||||
|         "Platinum sponsors", | ||||
|         "Gold sponsors", | ||||
|         "Silver sponsors", | ||||
|         "Diamond members", | ||||
|         "Titanium members", | ||||
|         "Platinum members", | ||||
|         "Gold members", | ||||
|     ] | ||||
|     sections_id = [ | ||||
|         "DONORS_PATRONS", | ||||
|         "DONORS_SPONSORS_PLATINUM", | ||||
|         "DONORS_SPONSORS_GOLD", | ||||
|         "DONORS_SPONSORS_SILVER", | ||||
|         "DONORS_MEMBERS_DIAMOND", | ||||
|         "DONORS_MEMBERS_TITANIUM", | ||||
|         "DONORS_MEMBERS_PLATINUM", | ||||
|         "DONORS_MEMBERS_GOLD", | ||||
|     ] | ||||
| 
 | ||||
|     src = str(source[0]) | ||||
|     dst = str(target[0]) | ||||
|     with open(src, "r", encoding="utf-8") as f, open(dst, "w", encoding="utf-8", newline="\n") as g: | ||||
|         g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n") | ||||
|         g.write("#ifndef DONORS_GEN_H\n") | ||||
|         g.write("#define DONORS_GEN_H\n") | ||||
| 
 | ||||
|         reading = False | ||||
| 
 | ||||
|         def close_section(): | ||||
|             g.write("\t0\n") | ||||
|             g.write("};\n") | ||||
| 
 | ||||
|         for line in f: | ||||
|             if reading >= 0: | ||||
|                 if line.startswith("    "): | ||||
|                     g.write('\t"' + escape_string(line.strip()) + '",\n') | ||||
|                     continue | ||||
|             if line.startswith("## "): | ||||
|                 if reading: | ||||
|                     close_section() | ||||
|                     reading = False | ||||
|                 for section, section_id in zip(sections, sections_id): | ||||
|                     if line.strip().endswith(section): | ||||
|                         current_section = escape_string(section_id) | ||||
|                         reading = True | ||||
|                         g.write("const char *const " + current_section + "[] = {\n") | ||||
|                         break | ||||
| 
 | ||||
|         if reading: | ||||
|             close_section() | ||||
| 
 | ||||
|         g.write("#endif // DONORS_GEN_H\n") | ||||
| 
 | ||||
| 
 | ||||
| def make_license_header(target, source, env): | ||||
|     src_copyright = str(source[0]) | ||||
|     src_license = str(source[1]) | ||||
|     dst = str(target[0]) | ||||
| 
 | ||||
|     class LicenseReader: | ||||
|         def __init__(self, license_file): | ||||
|             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) | ||||
| 
 | ||||
|     from collections import OrderedDict | ||||
| 
 | ||||
|     projects: dict = 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": | ||||
|                 # attach 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: 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(dst, "w", encoding="utf-8", newline="\n") as f: | ||||
|         f.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n") | ||||
|         f.write("#ifndef LICENSE_GEN_H\n") | ||||
|         f.write("#define LICENSE_GEN_H\n") | ||||
|         f.write("const char *const GODOT_LICENSE_TEXT =") | ||||
| 
 | ||||
|         with open(src_license, "r", encoding="utf-8") as license_file: | ||||
|             for line in license_file: | ||||
|                 escaped_string = escape_string(line.strip()) | ||||
|                 f.write('\n\t\t"' + escaped_string + '\\n"') | ||||
|         f.write(";\n\n") | ||||
| 
 | ||||
|         f.write( | ||||
|             "struct ComponentCopyrightPart {\n" | ||||
|             "\tconst char *license;\n" | ||||
|             "\tconst char *const *files;\n" | ||||
|             "\tconst char *const *copyright_statements;\n" | ||||
|             "\tint file_count;\n" | ||||
|             "\tint copyright_count;\n" | ||||
|             "};\n\n" | ||||
|         ) | ||||
| 
 | ||||
|         f.write( | ||||
|             "struct ComponentCopyright {\n" | ||||
|             "\tconst char *name;\n" | ||||
|             "\tconst ComponentCopyrightPart *parts;\n" | ||||
|             "\tint part_count;\n" | ||||
|             "};\n\n" | ||||
|         ) | ||||
| 
 | ||||
|         f.write("const char *const COPYRIGHT_INFO_DATA[] = {\n") | ||||
|         for line in data_list: | ||||
|             f.write('\t"' + escape_string(line) + '",\n') | ||||
|         f.write("};\n\n") | ||||
| 
 | ||||
|         f.write("const 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: | ||||
|                 f.write( | ||||
|                     '\t{ "' | ||||
|                     + escape_string(part["License"][0]) | ||||
|                     + '", ' | ||||
|                     + "©RIGHT_INFO_DATA[" | ||||
|                     + str(part["file_index"]) | ||||
|                     + "], " | ||||
|                     + "©RIGHT_INFO_DATA[" | ||||
|                     + str(part["copyright_index"]) | ||||
|                     + "], " | ||||
|                     + str(len(part["Files"])) | ||||
|                     + ", " | ||||
|                     + str(len(part["Copyright"])) | ||||
|                     + " },\n" | ||||
|                 ) | ||||
|                 part_index += 1 | ||||
|         f.write("};\n\n") | ||||
| 
 | ||||
|         f.write("const int COPYRIGHT_INFO_COUNT = " + str(len(projects)) + ";\n") | ||||
| 
 | ||||
|         f.write("const ComponentCopyright COPYRIGHT_INFO[] = {\n") | ||||
|         for project_name, project in iter(projects.items()): | ||||
|             f.write( | ||||
|                 '\t{ "' | ||||
|                 + escape_string(project_name) | ||||
|                 + '", ' | ||||
|                 + "©RIGHT_PROJECT_PARTS[" | ||||
|                 + str(part_indexes[project_name]) | ||||
|                 + "], " | ||||
|                 + str(len(project)) | ||||
|                 + " },\n" | ||||
|             ) | ||||
|         f.write("};\n\n") | ||||
| 
 | ||||
|         f.write("const int LICENSE_COUNT = " + str(len(license_list)) + ";\n") | ||||
| 
 | ||||
|         f.write("const char *const LICENSE_NAMES[] = {\n") | ||||
|         for license in license_list: | ||||
|             f.write('\t"' + escape_string(license[0]) + '",\n') | ||||
|         f.write("};\n\n") | ||||
| 
 | ||||
|         f.write("const char *const LICENSE_BODIES[] = {\n\n") | ||||
|         for license in license_list: | ||||
|             for line in license[1:]: | ||||
|                 if line == ".": | ||||
|                     f.write('\t"\\n"\n') | ||||
|                 else: | ||||
|                     f.write('\t"' + escape_string(line) + '\\n"\n') | ||||
|             f.write('\t"",\n\n') | ||||
|         f.write("};\n\n") | ||||
| 
 | ||||
|         f.write("#endif // LICENSE_GEN_H\n") | ||||
							
								
								
									
										859
									
								
								engine/core/core_constants.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										859
									
								
								engine/core/core_constants.cpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,859 @@ | |||
| /**************************************************************************/ | ||||
| /*  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/templates/hash_set.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_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_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_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; | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										51
									
								
								engine/core/core_constants.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								engine/core/core_constants.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,51 @@ | |||
| /**************************************************************************/ | ||||
| /*  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.                 */ | ||||
| /**************************************************************************/ | ||||
| 
 | ||||
| #ifndef CORE_CONSTANTS_H | ||||
| #define CORE_CONSTANTS_H | ||||
| 
 | ||||
| #include "core/string/string_name.h" | ||||
| #include "core/templates/hash_set.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); | ||||
| }; | ||||
| 
 | ||||
| #endif // CORE_CONSTANTS_H
 | ||||
							
								
								
									
										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; | ||||
							
								
								
									
										44
									
								
								engine/core/core_globals.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								engine/core/core_globals.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,44 @@ | |||
| /**************************************************************************/ | ||||
| /*  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.                 */ | ||||
| /**************************************************************************/ | ||||
| 
 | ||||
| #ifndef CORE_GLOBALS_H | ||||
| #define CORE_GLOBALS_H | ||||
| 
 | ||||
| // 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; | ||||
| }; | ||||
| 
 | ||||
| #endif // CORE_GLOBALS_H
 | ||||
							
								
								
									
										75
									
								
								engine/core/core_string_names.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								engine/core/core_string_names.cpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,75 @@ | |||
| /**************************************************************************/ | ||||
| /*  core_string_names.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_string_names.h" | ||||
| 
 | ||||
| CoreStringNames *CoreStringNames::singleton = nullptr; | ||||
| 
 | ||||
| CoreStringNames::CoreStringNames() : | ||||
| 		free_(StaticCString::create("free")), | ||||
| 		changed(StaticCString::create("changed")), | ||||
| 		script(StaticCString::create("script")), | ||||
| 		script_changed(StaticCString::create("script_changed")), | ||||
| 		_iter_init(StaticCString::create("_iter_init")), | ||||
| 		_iter_next(StaticCString::create("_iter_next")), | ||||
| 		_iter_get(StaticCString::create("_iter_get")), | ||||
| 		get_rid(StaticCString::create("get_rid")), | ||||
| 		_to_string(StaticCString::create("_to_string")), | ||||
| 		_custom_features(StaticCString::create("_custom_features")), | ||||
| 
 | ||||
| 		x(StaticCString::create("x")), | ||||
| 		y(StaticCString::create("y")), | ||||
| 		z(StaticCString::create("z")), | ||||
| 		w(StaticCString::create("w")), | ||||
| 		r(StaticCString::create("r")), | ||||
| 		g(StaticCString::create("g")), | ||||
| 		b(StaticCString::create("b")), | ||||
| 		a(StaticCString::create("a")), | ||||
| 		position(StaticCString::create("position")), | ||||
| 		size(StaticCString::create("size")), | ||||
| 		end(StaticCString::create("end")), | ||||
| 		basis(StaticCString::create("basis")), | ||||
| 		origin(StaticCString::create("origin")), | ||||
| 		normal(StaticCString::create("normal")), | ||||
| 		d(StaticCString::create("d")), | ||||
| 		h(StaticCString::create("h")), | ||||
| 		s(StaticCString::create("s")), | ||||
| 		v(StaticCString::create("v")), | ||||
| 		r8(StaticCString::create("r8")), | ||||
| 		g8(StaticCString::create("g8")), | ||||
| 		b8(StaticCString::create("b8")), | ||||
| 		a8(StaticCString::create("a8")), | ||||
| 
 | ||||
| 		call(StaticCString::create("call")), | ||||
| 		call_deferred(StaticCString::create("call_deferred")), | ||||
| 		bind(StaticCString::create("bind")), | ||||
| 		notification(StaticCString::create("notification")), | ||||
| 		property_list_changed(StaticCString::create("property_list_changed")) { | ||||
| } | ||||
							
								
								
									
										96
									
								
								engine/core/core_string_names.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								engine/core/core_string_names.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,96 @@ | |||
| /**************************************************************************/ | ||||
| /*  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.                 */ | ||||
| /**************************************************************************/ | ||||
| 
 | ||||
| #ifndef CORE_STRING_NAMES_H | ||||
| #define CORE_STRING_NAMES_H | ||||
| 
 | ||||
| #include "core/string/string_name.h" | ||||
| 
 | ||||
| class CoreStringNames { | ||||
| 	friend void register_core_types(); | ||||
| 	friend void unregister_core_types(); | ||||
| 
 | ||||
| 	static void create() { singleton = memnew(CoreStringNames); } | ||||
| 	static void free() { | ||||
| 		memdelete(singleton); | ||||
| 		singleton = nullptr; | ||||
| 	} | ||||
| 
 | ||||
| 	CoreStringNames(); | ||||
| 
 | ||||
| public: | ||||
| 	_FORCE_INLINE_ static CoreStringNames *get_singleton() { return singleton; } | ||||
| 
 | ||||
| 	static CoreStringNames *singleton; | ||||
| 
 | ||||
| 	StringName free_; // "free", conflict with C++ keyword.
 | ||||
| 	StringName changed; | ||||
| 	StringName script; | ||||
| 	StringName script_changed; | ||||
| 	StringName _iter_init; | ||||
| 	StringName _iter_next; | ||||
| 	StringName _iter_get; | ||||
| 	StringName get_rid; | ||||
| 	StringName _to_string; | ||||
| 	StringName _custom_features; | ||||
| 
 | ||||
| 	StringName x; | ||||
| 	StringName y; | ||||
| 	StringName z; | ||||
| 	StringName w; | ||||
| 	StringName r; | ||||
| 	StringName g; | ||||
| 	StringName b; | ||||
| 	StringName a; | ||||
| 	StringName position; | ||||
| 	StringName size; | ||||
| 	StringName end; | ||||
| 	StringName basis; | ||||
| 	StringName origin; | ||||
| 	StringName normal; | ||||
| 	StringName d; | ||||
| 	StringName h; | ||||
| 	StringName s; | ||||
| 	StringName v; | ||||
| 	StringName r8; | ||||
| 	StringName g8; | ||||
| 	StringName b8; | ||||
| 	StringName a8; | ||||
| 
 | ||||
| 	StringName call; | ||||
| 	StringName call_deferred; | ||||
| 	StringName bind; | ||||
| 	StringName notification; | ||||
| 	StringName property_list_changed; | ||||
| }; | ||||
| 
 | ||||
| #define CoreStringName(m_name) CoreStringNames::get_singleton()->m_name | ||||
| 
 | ||||
| #endif // CORE_STRING_NAMES_H
 | ||||
							
								
								
									
										64
									
								
								engine/core/crypto/SCsub
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								engine/core/crypto/SCsub
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,64 @@ | |||
| #!/usr/bin/env python | ||||
| 
 | ||||
| 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(CPPPATH=["#thirdparty/mbedtls/include"]) | ||||
| 
 | ||||
| # MbedTLS core functions (for CryptoCore). | ||||
| # If the mbedtls module is compiled we don't need to add the .c files with our | ||||
| # custom config since they will be built by the module itself. | ||||
| # Only if the module is not enabled, we must compile here the required sources | ||||
| # to make a "light" build with only the necessary mbedtls files. | ||||
| if not has_module: | ||||
|     # 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() { | ||||
| } | ||||
							
								
								
									
										68
									
								
								engine/core/crypto/aes_context.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								engine/core/crypto/aes_context.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,68 @@ | |||
| /**************************************************************************/ | ||||
| /*  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.                 */ | ||||
| /**************************************************************************/ | ||||
| 
 | ||||
| #ifndef AES_CONTEXT_H | ||||
| #define AES_CONTEXT_H | ||||
| 
 | ||||
| #include "core/crypto/crypto_core.h" | ||||
| #include "core/object/ref_counted.h" | ||||
| 
 | ||||
| class AESContext : public RefCounted { | ||||
| 	GDCLASS(AESContext, RefCounted); | ||||
| 
 | ||||
| public: | ||||
| 	enum Mode { | ||||
| 		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); | ||||
| 
 | ||||
| #endif // AES_CONTEXT_H
 | ||||
							
								
								
									
										263
									
								
								engine/core/crypto/crypto.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										263
									
								
								engine/core/crypto/crypto.cpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,263 @@ | |||
| /**************************************************************************/ | ||||
| /*  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" | ||||
| 
 | ||||
| #include "core/config/engine.h" | ||||
| #include "core/io/certs_compressed.gen.h" | ||||
| #include "core/io/compression.h" | ||||
| 
 | ||||
| /// Resources
 | ||||
| 
 | ||||
| CryptoKey *(*CryptoKey::_create)() = nullptr; | ||||
| CryptoKey *CryptoKey::create() { | ||||
| 	if (_create) { | ||||
| 		return _create(); | ||||
| 	} | ||||
| 	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)() = nullptr; | ||||
| X509Certificate *X509Certificate::create() { | ||||
| 	if (_create) { | ||||
| 		return _create(); | ||||
| 	} | ||||
| 	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)() = nullptr; | ||||
| HMACContext *HMACContext::create() { | ||||
| 	if (_create) { | ||||
| 		return _create(); | ||||
| 	} | ||||
| 	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)() = nullptr; | ||||
| Crypto *Crypto::create() { | ||||
| 	if (_create) { | ||||
| 		return _create(); | ||||
| 	} | ||||
| 	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, "Cannot save Crypto resource to file '" + 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); | ||||
| } | ||||
							
								
								
									
										167
									
								
								engine/core/crypto/crypto.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										167
									
								
								engine/core/crypto/crypto.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,167 @@ | |||
| /**************************************************************************/ | ||||
| /*  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.                 */ | ||||
| /**************************************************************************/ | ||||
| 
 | ||||
| #ifndef CRYPTO_H | ||||
| #define CRYPTO_H | ||||
| 
 | ||||
| #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)(); | ||||
| 
 | ||||
| public: | ||||
| 	static CryptoKey *create(); | ||||
| 	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)(); | ||||
| 
 | ||||
| public: | ||||
| 	static X509Certificate *create(); | ||||
| 	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)(); | ||||
| 
 | ||||
| public: | ||||
| 	static HMACContext *create(); | ||||
| 
 | ||||
| 	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)(); | ||||
| 	static void (*_load_default_certificates)(const String &p_path); | ||||
| 
 | ||||
| public: | ||||
| 	static Crypto *create(); | ||||
| 	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; | ||||
| }; | ||||
| 
 | ||||
| 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; | ||||
| }; | ||||
| 
 | ||||
| #endif // CRYPTO_H
 | ||||
							
								
								
									
										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, " failed\n  ! mbedtls_ctr_drbg_seed returned an error" + itos(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, " failed\n  ! mbedtls_ctr_drbg_seed returned an error" + itos(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, int p_src_len) { | ||||
| 	int 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, int p_dst_len, size_t *r_len, const uint8_t *p_src, int 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, int p_dst_len, size_t *r_len, const uint8_t *p_src, int 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, int 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, int 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, int 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; | ||||
| } | ||||
							
								
								
									
										119
									
								
								engine/core/crypto/crypto_core.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										119
									
								
								engine/core/crypto/crypto_core.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,119 @@ | |||
| /**************************************************************************/ | ||||
| /*  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.                 */ | ||||
| /**************************************************************************/ | ||||
| 
 | ||||
| #ifndef CRYPTO_CORE_H | ||||
| #define CRYPTO_CORE_H | ||||
| 
 | ||||
| #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, int p_src_len); | ||||
| 	static Error b64_encode(uint8_t *r_dst, int p_dst_len, size_t *r_len, const uint8_t *p_src, int p_src_len); | ||||
| 	static Error b64_decode(uint8_t *r_dst, int p_dst_len, size_t *r_len, const uint8_t *p_src, int p_src_len); | ||||
| 
 | ||||
| 	static Error md5(const uint8_t *p_src, int p_src_len, unsigned char r_hash[16]); | ||||
| 	static Error sha1(const uint8_t *p_src, int p_src_len, unsigned char r_hash[20]); | ||||
| 	static Error sha256(const uint8_t *p_src, int p_src_len, unsigned char r_hash[32]); | ||||
| }; | ||||
| 
 | ||||
| #endif // CRYPTO_CORE_H
 | ||||
							
								
								
									
										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(); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										66
									
								
								engine/core/crypto/hashing_context.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								engine/core/crypto/hashing_context.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,66 @@ | |||
| /**************************************************************************/ | ||||
| /*  hashing_context.h                                                     */ | ||||
| /**************************************************************************/ | ||||
| /*                         This file is part of:                          */ | ||||
| /*                             GODOT ENGINE                               */ | ||||
| /*                        https://godotengine.org                         */ | ||||
| /**************************************************************************/ | ||||
| /* Copyright (c) 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 HASHING_CONTEXT_H | ||||
| #define HASHING_CONTEXT_H | ||||
| 
 | ||||
| #include "core/object/ref_counted.h" | ||||
| 
 | ||||
| class HashingContext : public RefCounted { | ||||
| 	GDCLASS(HashingContext, RefCounted); | ||||
| 
 | ||||
| public: | ||||
| 	enum HashType { | ||||
| 		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); | ||||
| 
 | ||||
| #endif // HASHING_CONTEXT_H
 | ||||
							
								
								
									
										5
									
								
								engine/core/debugger/SCsub
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								engine/core/debugger/SCsub
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,5 @@ | |||
| #!/usr/bin/env python | ||||
| 
 | ||||
| Import("env") | ||||
| 
 | ||||
| env.add_source_files(env.core_sources, "*.cpp") | ||||
							
								
								
									
										149
									
								
								engine/core/debugger/debugger_marshalls.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										149
									
								
								engine/core/debugger/debugger_marshalls.cpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,149 @@ | |||
| /**************************************************************************/ | ||||
| /*  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; | ||||
| 	arr.push_back(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; | ||||
| 	arr.push_back(name); | ||||
| 	arr.push_back(type); | ||||
| 	arr.push_back(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() { | ||||
| 	Array arr; | ||||
| 	arr.push_back(hr); | ||||
| 	arr.push_back(min); | ||||
| 	arr.push_back(sec); | ||||
| 	arr.push_back(msec); | ||||
| 	arr.push_back(source_file); | ||||
| 	arr.push_back(source_func); | ||||
| 	arr.push_back(source_line); | ||||
| 	arr.push_back(error); | ||||
| 	arr.push_back(error_descr); | ||||
| 	arr.push_back(warning); | ||||
| 	unsigned int size = callstack.size(); | ||||
| 	const ScriptLanguage::StackInfo *r = callstack.ptr(); | ||||
| 	arr.push_back(size * 3); | ||||
| 	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; | ||||
| } | ||||
							
								
								
									
										73
									
								
								engine/core/debugger/debugger_marshalls.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								engine/core/debugger/debugger_marshalls.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,73 @@ | |||
| /**************************************************************************/ | ||||
| /*  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.                 */ | ||||
| /**************************************************************************/ | ||||
| 
 | ||||
| #ifndef DEBUGGER_MARSHALLS_H | ||||
| #define DEBUGGER_MARSHALLS_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); | ||||
| 	}; | ||||
| }; | ||||
| 
 | ||||
| #endif // DEBUGGER_MARSHALLS_H
 | ||||
							
								
								
									
										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), "Profiler already registered: " + p_name); | ||||
| 	profilers.insert(p_name, p_func); | ||||
| } | ||||
| 
 | ||||
| void EngineDebugger::unregister_profiler(const StringName &p_name) { | ||||
| 	ERR_FAIL_COND_MSG(!profilers.has(p_name), "Profiler not registered: " + 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), "Capture already registered: " + 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), "Capture not registered: " + 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), "Protocol handler already registered: " + 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), "Can't change profiler state, no profiler: " + 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), "Can't add frame data, no profiler: " + 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, "Capture not registered: " + 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, 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; | ||||
| 		msg.push_back(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); | ||||
| 
 | ||||
| 	for (int i = 0; i < p_breakpoints.size(); i++) { | ||||
| 		const String &bp = p_breakpoints[i]; | ||||
| 		int sp = bp.rfind(":"); | ||||
| 		ERR_CONTINUE_MSG(sp == -1, "Invalid breakpoint: '" + bp + "', expected file:line format."); | ||||
| 
 | ||||
| 		singleton_script_debugger->insert_breakpoint(bp.substr(sp + 1, bp.length()).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; | ||||
| } | ||||
							
								
								
									
										145
									
								
								engine/core/debugger/engine_debugger.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										145
									
								
								engine/core/debugger/engine_debugger.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,145 @@ | |||
| /**************************************************************************/ | ||||
| /*  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.                 */ | ||||
| /**************************************************************************/ | ||||
| 
 | ||||
| #ifndef ENGINE_DEBUGGER_H | ||||
| #define ENGINE_DEBUGGER_H | ||||
| 
 | ||||
| #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" | ||||
| #include "core/variant/variant.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, 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(); | ||||
| }; | ||||
| 
 | ||||
| #endif // ENGINE_DEBUGGER_H
 | ||||
							
								
								
									
										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(); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										63
									
								
								engine/core/debugger/engine_profiler.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								engine/core/debugger/engine_profiler.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,63 @@ | |||
| /**************************************************************************/ | ||||
| /*  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.                 */ | ||||
| /**************************************************************************/ | ||||
| 
 | ||||
| #ifndef ENGINE_PROFILER_H | ||||
| #define ENGINE_PROFILER_H | ||||
| 
 | ||||
| #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(); | ||||
| }; | ||||
| 
 | ||||
| #endif // ENGINE_PROFILER_H
 | ||||
							
								
								
									
										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("="); | ||||
| 
 | ||||
| 				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.get_slice_count(" ") <= 1) { | ||||
| 				print_line("Usage: print <expre>"); | ||||
| 			} else { | ||||
| 				String expr = line.get_slicec(' ', 2); | ||||
| 				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.size() == 0) { | ||||
| 					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(":"); | ||||
| 	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); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										59
									
								
								engine/core/debugger/local_debugger.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								engine/core/debugger/local_debugger.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,59 @@ | |||
| /**************************************************************************/ | ||||
| /*  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.                 */ | ||||
| /**************************************************************************/ | ||||
| 
 | ||||
| #ifndef LOCAL_DEBUGGER_H | ||||
| #define LOCAL_DEBUGGER_H | ||||
| 
 | ||||
| #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(); | ||||
| }; | ||||
| 
 | ||||
| #endif // LOCAL_DEBUGGER_H
 | ||||
							
								
								
									
										700
									
								
								engine/core/debugger/remote_debugger.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										700
									
								
								engine/core/debugger/remote_debugger.cpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,700 @@ | |||
| /**************************************************************************/ | ||||
| /*  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/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("Value of custom monitor '" + String(custom_monitor_names[i]) + "' is not a number"); | ||||
| 				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; | ||||
| 	msg.push_back(p_message); | ||||
| 	msg.push_back(Thread::get_caller_id()); | ||||
| 	msg.push_back(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; | ||||
| 		arr.push_back(strings); | ||||
| 		arr.push_back(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(":"); | ||||
| 	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(); | ||||
| 	const String error_str = script_lang ? script_lang->debug_get_error() : ""; | ||||
| 	Array msg; | ||||
| 	msg.push_back(p_can_continue); | ||||
| 	msg.push_back(error_str); | ||||
| 	ERR_FAIL_NULL(script_lang); | ||||
| 	msg.push_back(script_lang->debug_get_stack_level_count() > 0); | ||||
| 	msg.push_back(Thread::get_caller_id() == Thread::get_main_id() ? String(RTR("Main Thread")) : itos(Thread::get_caller_id())); | ||||
| 	if (allow_focus_steal_fn) { | ||||
| 		allow_focus_steal_fn(); | ||||
| 	} | ||||
| 	send_message("debug_enter", msg); | ||||
| 
 | ||||
| 	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; | ||||
| 				var_size.push_back(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 { | ||||
| 				bool captured = false; | ||||
| 				ERR_CONTINUE(_try_capture(command, data, captured) != OK); | ||||
| 				if (!captured) { | ||||
| 					WARN_PRINT("Unknown message received from debugger: " + 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(":"); | ||||
| 		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 == "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); | ||||
| } | ||||
							
								
								
									
										126
									
								
								engine/core/debugger/remote_debugger.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										126
									
								
								engine/core/debugger/remote_debugger.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,126 @@ | |||
| /**************************************************************************/ | ||||
| /*  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.                 */ | ||||
| /**************************************************************************/ | ||||
| 
 | ||||
| #ifndef REMOTE_DEBUGGER_H | ||||
| #define REMOTE_DEBUGGER_H | ||||
| 
 | ||||
| #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(); | ||||
| }; | ||||
| 
 | ||||
| #endif // REMOTE_DEBUGGER_H
 | ||||
							
								
								
									
										244
									
								
								engine/core/debugger/remote_debugger_peer.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										244
									
								
								engine/core/debugger/remote_debugger_peer.cpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,244 @@ | |||
| /**************************************************************************/ | ||||
| /*  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.size() == 0) { | ||||
| 				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."); | ||||
| 			mutex.lock(); | ||||
| 			in_queue.push_back(var); | ||||
| 			mutex.unlock(); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| 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(tcp_client->get_status()) + "', retrying in " + String::num(ms) + " msec."); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if (tcp_client->get_status() != StreamPeerTCP::STATUS_CONNECTED) { | ||||
| 		ERR_PRINT("Remote Debugger: Unable to connect. Status: " + String::num(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(":")) { | ||||
| 		int sep_pos = debug_host.rfind(":"); | ||||
| 		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"); | ||||
| } | ||||
							
								
								
									
										96
									
								
								engine/core/debugger/remote_debugger_peer.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								engine/core/debugger/remote_debugger_peer.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,96 @@ | |||
| /**************************************************************************/ | ||||
| /*  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.                 */ | ||||
| /**************************************************************************/ | ||||
| 
 | ||||
| #ifndef REMOTE_DEBUGGER_PEER_H | ||||
| #define REMOTE_DEBUGGER_PEER_H | ||||
| 
 | ||||
| #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(); | ||||
| }; | ||||
| 
 | ||||
| #endif // REMOTE_DEBUGGER_PEER_H
 | ||||
							
								
								
									
										102
									
								
								engine/core/debugger/script_debugger.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								engine/core/debugger/script_debugger.cpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,102 @@ | |||
| /**************************************************************************/ | ||||
| /*  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].size() == 0) { | ||||
| 		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::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; | ||||
| } | ||||
							
								
								
									
										87
									
								
								engine/core/debugger/script_debugger.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								engine/core/debugger/script_debugger.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,87 @@ | |||
| /**************************************************************************/ | ||||
| /*  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.                 */ | ||||
| /**************************************************************************/ | ||||
| 
 | ||||
| #ifndef SCRIPT_DEBUGGER_H | ||||
| #define SCRIPT_DEBUGGER_H | ||||
| 
 | ||||
| #include "core/object/script_language.h" | ||||
| #include "core/string/string_name.h" | ||||
| #include "core/templates/hash_set.h" | ||||
| #include "core/templates/rb_map.h" | ||||
| #include "core/templates/vector.h" | ||||
| 
 | ||||
| class ScriptDebugger { | ||||
| 	typedef ScriptLanguage::StackInfo StackInfo; | ||||
| 
 | ||||
| 	bool skip_breakpoints = 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 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() {} | ||||
| }; | ||||
| 
 | ||||
| #endif // SCRIPT_DEBUGGER_H
 | ||||
							
								
								
									
										177
									
								
								engine/core/doc_data.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										177
									
								
								engine/core/doc_data.cpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,177 @@ | |||
| /**************************************************************************/ | ||||
| /*  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("\n", " "); | ||||
| 	} else { | ||||
| 		return p_value.get_construct_string().replace("\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_enum.length()); | ||||
| 		} | ||||
| 		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.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.enumeration.length()); | ||||
| 		} | ||||
| 		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.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::property_doc_from_scriptmemberinfo(DocData::PropertyDoc &p_property, const ScriptMemberInfo &p_memberinfo) { | ||||
| 	p_property.name = p_memberinfo.propinfo.name; | ||||
| 	p_property.description = p_memberinfo.doc_string; | ||||
| 
 | ||||
| 	if (p_memberinfo.propinfo.type == Variant::OBJECT) { | ||||
| 		p_property.type = p_memberinfo.propinfo.class_name; | ||||
| 	} else if (p_memberinfo.propinfo.type == Variant::NIL && p_memberinfo.propinfo.usage & PROPERTY_USAGE_NIL_IS_VARIANT) { | ||||
| 		p_property.type = "Variant"; | ||||
| 	} else { | ||||
| 		p_property.type = Variant::get_type_name(p_memberinfo.propinfo.type); | ||||
| 	} | ||||
| 
 | ||||
| 	p_property.setter = p_memberinfo.setter; | ||||
| 	p_property.getter = p_memberinfo.getter; | ||||
| 
 | ||||
| 	if (p_memberinfo.has_default_value && p_memberinfo.default_value.get_type() != Variant::OBJECT) { | ||||
| 		p_property.default_value = get_default_value_string(p_memberinfo.default_value); | ||||
| 	} | ||||
| 
 | ||||
| 	p_property.overridden = false; | ||||
| } | ||||
| 
 | ||||
| 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); | ||||
| 
 | ||||
| 	int i = 0; | ||||
| 	for (List<PropertyInfo>::ConstIterator itr = p_methodinfo.arguments.begin(); itr != p_methodinfo.arguments.end(); ++itr, ++i) { | ||||
| 		DocData::ArgumentDoc argument; | ||||
| 		argument_doc_from_arginfo(argument, *itr); | ||||
| 		int 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); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void DocData::constant_doc_from_variant(DocData::ConstantDoc &p_const, const StringName &p_name, const Variant &p_value, const String &p_desc) { | ||||
| 	p_const.name = p_name; | ||||
| 	p_const.value = p_value; | ||||
| 	p_const.is_value_valid = (p_value.get_type() != Variant::OBJECT); | ||||
| 	p_const.description = p_desc; | ||||
| } | ||||
| 
 | ||||
| void DocData::signal_doc_from_methodinfo(DocData::MethodDoc &p_signal, const MethodInfo &p_methodinfo, const String &p_desc) { | ||||
| 	return method_doc_from_methodinfo(p_signal, p_methodinfo, p_desc); | ||||
| } | ||||
							
								
								
									
										968
									
								
								engine/core/doc_data.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										968
									
								
								engine/core/doc_data.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,968 @@ | |||
| /**************************************************************************/ | ||||
| /*  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.                 */ | ||||
| /**************************************************************************/ | ||||
| 
 | ||||
| #ifndef DOC_DATA_H | ||||
| #define DOC_DATA_H | ||||
| 
 | ||||
| #include "core/io/xml_parser.h" | ||||
| #include "core/variant/variant.h" | ||||
| 
 | ||||
| struct ScriptMemberInfo { | ||||
| 	PropertyInfo propinfo; | ||||
| 	String doc_string; | ||||
| 	StringName setter; | ||||
| 	StringName getter; | ||||
| 
 | ||||
| 	bool has_default_value = false; | ||||
| 	Variant default_value; | ||||
| }; | ||||
| 
 | ||||
| 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.size() == 0) { | ||||
| 							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.size() == 0 || p_method.arguments.size() == 0) { // 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 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("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; | ||||
| 
 | ||||
| 			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; | ||||
| 		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("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.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 (int i = 0; i < enums.size(); i++) { | ||||
| 				doc.enums[enums.get_key_at_index(i)] = EnumDoc::from_dict(enums.get_value_at_index(i)); | ||||
| 			} | ||||
| 
 | ||||
| 			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 property_doc_from_scriptmemberinfo(DocData::PropertyDoc &p_property, const ScriptMemberInfo &p_memberinfo); | ||||
| 	static void method_doc_from_methodinfo(DocData::MethodDoc &p_method, const MethodInfo &p_methodinfo, const String &p_desc); | ||||
| 	static void constant_doc_from_variant(DocData::ConstantDoc &p_const, const StringName &p_name, const Variant &p_value, const String &p_desc); | ||||
| 	static void signal_doc_from_methodinfo(DocData::MethodDoc &p_signal, const MethodInfo &p_methodinfo, const String &p_desc); | ||||
| }; | ||||
| 
 | ||||
| #endif // DOC_DATA_H
 | ||||
							
								
								
									
										7
									
								
								engine/core/error/SCsub
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								engine/core/error/SCsub
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,7 @@ | |||
| #!/usr/bin/env python | ||||
| 
 | ||||
| Import("env") | ||||
| 
 | ||||
| env_error = env.Clone() | ||||
| 
 | ||||
| env_error.add_source_files(env.core_sources, "*.cpp") | ||||
							
								
								
									
										85
									
								
								engine/core/error/error_list.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								engine/core/error/error_list.cpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,85 @@ | |||
| /**************************************************************************/ | ||||
| /*  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" | ||||
| 
 | ||||
| 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(sizeof(error_names) / sizeof(*error_names) == ERR_MAX); | ||||
							
								
								
									
										101
									
								
								engine/core/error/error_list.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								engine/core/error/error_list.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,101 @@ | |||
| /**************************************************************************/ | ||||
| /*  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.                 */ | ||||
| /**************************************************************************/ | ||||
| 
 | ||||
| #ifndef ERROR_LIST_H | ||||
| #define ERROR_LIST_H | ||||
| 
 | ||||
| /** 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 | ||||
|  */ | ||||
| 
 | ||||
| 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[]; | ||||
| 
 | ||||
| #endif // ERROR_LIST_H
 | ||||
							
								
								
									
										130
									
								
								engine/core/error/error_macros.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										130
									
								
								engine/core/error/error_macros.cpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,130 @@ | |||
| /**************************************************************************/ | ||||
| /*  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/os/os.h" | ||||
| #include "core/string/ustring.h" | ||||
| 
 | ||||
| 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(); | ||||
| } | ||||
| 
 | ||||
| // 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); | ||||
| } | ||||
							
								
								
									
										835
									
								
								engine/core/error/error_macros.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										835
									
								
								engine/core/error/error_macros.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,835 @@ | |||
| /**************************************************************************/ | ||||
| /*  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.                 */ | ||||
| /**************************************************************************/ | ||||
| 
 | ||||
| #ifndef ERROR_MACROS_H | ||||
| #define ERROR_MACROS_H | ||||
| 
 | ||||
| #include "core/typedefs.h" | ||||
| 
 | ||||
| #include <atomic> // We'd normally use safe_refcount.h, but that would cause circular includes.
 | ||||
| 
 | ||||
| class String; | ||||
| 
 | ||||
| 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.
 | ||||
| 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); | ||||
| 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); | ||||
| 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); | ||||
| 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); | ||||
| 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); | ||||
| 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_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); | ||||
| 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); | ||||
| void _err_flush_stdout(); | ||||
| 
 | ||||
| #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 | ||||
| 
 | ||||
| #endif // ERROR_MACROS_H
 | ||||
							
								
								
									
										17
									
								
								engine/core/extension/SCsub
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								engine/core/extension/SCsub
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,17 @@ | |||
| #!/usr/bin/env python | ||||
| 
 | ||||
| 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") | ||||
							
								
								
									
										1654
									
								
								engine/core/extension/extension_api_dump.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1654
									
								
								engine/core/extension/extension_api_dump.cpp
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										46
									
								
								engine/core/extension/extension_api_dump.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								engine/core/extension/extension_api_dump.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,46 @@ | |||
| /**************************************************************************/ | ||||
| /*  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.                 */ | ||||
| /**************************************************************************/ | ||||
| 
 | ||||
| #ifndef EXTENSION_API_DUMP_H | ||||
| #define EXTENSION_API_DUMP_H | ||||
| 
 | ||||
| #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 | ||||
| 
 | ||||
| #endif // EXTENSION_API_DUMP_H
 | ||||
							
								
								
									
										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 | ||||
							
								
								
									
										1315
									
								
								engine/core/extension/gdextension.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1315
									
								
								engine/core/extension/gdextension.cpp
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										234
									
								
								engine/core/extension/gdextension.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										234
									
								
								engine/core/extension/gdextension.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,234 @@ | |||
| /**************************************************************************/ | ||||
| /*  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.                 */ | ||||
| /**************************************************************************/ | ||||
| 
 | ||||
| #ifndef GDEXTENSION_H | ||||
| #define GDEXTENSION_H | ||||
| 
 | ||||
| #include <functional> | ||||
| 
 | ||||
| #include "core/extension/gdextension_interface.h" | ||||
| #include "core/io/config_file.h" | ||||
| #include "core/io/resource_loader.h" | ||||
| #include "core/object/ref_counted.h" | ||||
| #include "core/os/shared_object.h" | ||||
| 
 | ||||
| class GDExtensionMethodBind; | ||||
| 
 | ||||
| class GDExtension : public Resource { | ||||
| 	GDCLASS(GDExtension, Resource) | ||||
| 
 | ||||
| 	friend class GDExtensionManager; | ||||
| 
 | ||||
| 	void *library = nullptr; // pointer if valid,
 | ||||
| 	String library_path; | ||||
| 	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; | ||||
| #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); | ||||
| #endif // DISABLE_DEPRECATED
 | ||||
| 	static void _register_extension_class3(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo3 *p_extension_funcs); | ||||
| 	static void _register_extension_class_internal(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo3 *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); | ||||
| 
 | ||||
| 	GDExtensionInitialization initialization; | ||||
| 	int32_t level_initialized = -1; | ||||
| 
 | ||||
| #ifdef TOOLS_ENABLED | ||||
| 	uint64_t resource_last_modified_time = 0; | ||||
| 	uint64_t library_last_modified_time = 0; | ||||
| 	bool is_reloading = false; | ||||
| 	Vector<GDExtensionMethodBind *> invalid_methods; | ||||
| 	Vector<ObjectID> instance_bindings; | ||||
| 
 | ||||
| 	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(); | ||||
| 	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); | ||||
| 
 | ||||
| 	Error open_library(const String &p_path, const String &p_entry_symbol, Vector<SharedObject> *p_dependencies = nullptr); | ||||
| 	void close_library(); | ||||
| 
 | ||||
| 	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: | ||||
| 	bool is_library_open() const; | ||||
| 
 | ||||
| #ifdef TOOLS_ENABLED | ||||
| 	bool is_reloadable() const { return reloadable; } | ||||
| 	void set_reloadable(bool p_reloadable) { reloadable = p_reloadable; } | ||||
| 
 | ||||
| 	bool has_library_changed() const; | ||||
| 	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; | ||||
| 	} | ||||
| 
 | ||||
| 	void track_instance_binding(Object *p_object); | ||||
| 	void untrack_instance_binding(Object *p_object); | ||||
| #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
 | ||||
| 
 | ||||
| #endif // GDEXTENSION_H
 | ||||
							
								
								
									
										971
									
								
								engine/core/extension/gdextension_compat_hashes.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										971
									
								
								engine/core/extension/gdextension_compat_hashes.cpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,971 @@ | |||
| /**************************************************************************/ | ||||
| /*  gdextension_compat_hashes.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_compat_hashes.h" | ||||
| 
 | ||||
| #ifndef DISABLE_DEPRECATED | ||||
| 
 | ||||
| #include "core/object/class_db.h" | ||||
| #include "core/variant/variant.h" | ||||
| 
 | ||||
| HashMap<StringName, LocalVector<GDExtensionCompatHashes::Mapping>> GDExtensionCompatHashes::mappings; | ||||
| 
 | ||||
| bool GDExtensionCompatHashes::lookup_current_hash(const StringName &p_class, const StringName &p_method, uint32_t p_legacy_hash, uint32_t *r_current_hash) { | ||||
| 	LocalVector<Mapping> *methods = mappings.getptr(p_class); | ||||
| 	if (!methods) { | ||||
| 		return false; | ||||
| 	} | ||||
| 
 | ||||
| 	for (const Mapping &mapping : *methods) { | ||||
| 		if (mapping.method == p_method && mapping.legacy_hash == p_legacy_hash) { | ||||
| 			*r_current_hash = mapping.current_hash; | ||||
| 			return true; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return false; | ||||
| } | ||||
| 
 | ||||
| bool GDExtensionCompatHashes::get_legacy_hashes(const StringName &p_class, const StringName &p_method, Array &r_hashes, bool p_check_valid) { | ||||
| 	LocalVector<Mapping> *methods = mappings.getptr(p_class); | ||||
| 	if (!methods) { | ||||
| 		return false; | ||||
| 	} | ||||
| 
 | ||||
| 	bool found = false; | ||||
| 	for (const Mapping &mapping : *methods) { | ||||
| 		if (mapping.method == p_method) { | ||||
| 			if (p_check_valid) { | ||||
| 				MethodBind *mb = ClassDB::get_method_with_compatibility(p_class, p_method, mapping.current_hash); | ||||
| 				if (!mb) { | ||||
| 					WARN_PRINT(vformat("Compatibility hash %d for %s::%s() mapped to non-existent hash %d. Please update gdextension_compat_hashes.cpp.", mapping.legacy_hash, p_class, p_method, mapping.current_hash)); | ||||
| 					continue; | ||||
| 				} | ||||
| 			} | ||||
| 			r_hashes.push_back(mapping.legacy_hash); | ||||
| 			found = true; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return found; | ||||
| } | ||||
| 
 | ||||
| void GDExtensionCompatHashes::initialize() { | ||||
| 	// clang-format off
 | ||||
| 	mappings.insert("AESContext", { | ||||
| 		{ "start", 3167574919, 3122411423 }, | ||||
| 	}); | ||||
| 	mappings.insert("AStar2D", { | ||||
| 		{ "add_point", 3370185124, 4074201818 }, | ||||
| 		{ "set_point_disabled", 4023243586, 972357352 }, | ||||
| 		{ "connect_points", 3785370599, 3710494224 }, | ||||
| 		{ "disconnect_points", 3785370599, 3710494224 }, | ||||
| 		{ "are_points_connected", 4063588998, 2288175859 }, | ||||
| 	}); | ||||
| 	mappings.insert("AStar3D", { | ||||
| 		{ "add_point", 2920922839, 1038703438 }, | ||||
| 		{ "set_point_disabled", 4023243586, 972357352 }, | ||||
| 		{ "connect_points", 3785370599, 3710494224 }, | ||||
| 		{ "disconnect_points", 3785370599, 3710494224 }, | ||||
| 		{ "are_points_connected", 4063588998, 2288175859 }, | ||||
| 	}); | ||||
| 	mappings.insert("AStarGrid2D", { | ||||
| 		{ "set_point_solid", 2825551965, 1765703753 }, | ||||
| 		{ "fill_solid_region", 1152863744, 2261970063 }, | ||||
| 	}); | ||||
| 	mappings.insert("AcceptDialog", { | ||||
| 		{ "add_button", 4158837846, 3328440682 }, | ||||
| 	}); | ||||
| 	mappings.insert("Animation", { | ||||
| 		{ "add_track", 2393815928, 3843682357 }, | ||||
| 		{ "track_insert_key", 1985425300, 808952278 }, | ||||
| 		{ "track_find_key", 3898229885, 3245197284 }, | ||||
| #ifdef REAL_T_IS_DOUBLE | ||||
| 		{ "bezier_track_insert_key", 1057544502, 3767441357 }, | ||||
| #else | ||||
| 		{ "bezier_track_insert_key", 1057544502, 3656773645 }, | ||||
| #endif | ||||
| 		{ "bezier_track_set_key_in_handle", 1028302688, 1719223284 }, | ||||
| 		{ "bezier_track_set_key_out_handle", 1028302688, 1719223284 }, | ||||
| 		{ "audio_track_insert_key", 3489962123, 4021027286 }, | ||||
| 	}); | ||||
| 	mappings.insert("AnimationNode", { | ||||
| 		{ "blend_animation", 11797022, 1630801826 }, | ||||
| 		{ "blend_node", 263389446, 1746075988 }, | ||||
| 		{ "blend_input", 2709059328, 1361527350 }, | ||||
| 	}); | ||||
| 	mappings.insert("AnimationNodeBlendSpace1D", { | ||||
| 		{ "add_blend_point", 4069484420, 285050433 }, | ||||
| 	}); | ||||
| 	mappings.insert("AnimationNodeBlendSpace2D", { | ||||
| 		{ "add_blend_point", 1533588937, 402261981 }, | ||||
| 		{ "add_triangle", 642454959, 753017335 }, | ||||
| 	}); | ||||
| 	mappings.insert("AnimationNodeBlendTree", { | ||||
| #ifdef REAL_T_IS_DOUBLE | ||||
| 		{ "add_node", 2055804584, 1407702499 }, | ||||
| #else | ||||
| 		{ "add_node", 2055804584, 1980270704 }, | ||||
| #endif | ||||
| 	}); | ||||
| 	mappings.insert("AnimationNodeStateMachine", { | ||||
| #ifdef REAL_T_IS_DOUBLE | ||||
| 		{ "add_node", 2055804584, 1407702499 }, | ||||
| #else | ||||
| 		{ "add_node", 2055804584, 1980270704 }, | ||||
| #endif | ||||
| 	}); | ||||
| 	mappings.insert("AnimationNodeStateMachinePlayback", { | ||||
| 		{ "travel", 3683006648, 3823612587 }, | ||||
| 		{ "start", 3683006648, 3823612587 }, | ||||
| 	}); | ||||
| 	mappings.insert("ArrayMesh", { | ||||
| 		{ "add_surface_from_arrays", 172284304, 1796411378 }, | ||||
| 	}); | ||||
| 	mappings.insert("AudioEffectSpectrumAnalyzerInstance", { | ||||
| 		{ "get_magnitude_for_frequency_range", 2693213071, 797993915 }, | ||||
| 	}); | ||||
| 	mappings.insert("AudioServer", { | ||||
| 		{ "add_bus_effect", 4147765248, 4068819785 }, | ||||
| 		{ "get_bus_effect_instance", 2887144608, 1829771234 }, | ||||
| 	}); | ||||
| 	mappings.insert("AudioStreamPlaybackPolyphonic", { | ||||
| 		{ "play_stream", 3792189967, 604492179 }, | ||||
| 	}); | ||||
| 	mappings.insert("AudioStreamRandomizer", { | ||||
| 		{ "add_stream", 3197802065, 1892018854 }, | ||||
| 	}); | ||||
| 	mappings.insert("BitMap", { | ||||
| 		{ "create_from_image_alpha", 505265891, 106271684 }, | ||||
| 		{ "opaque_to_polygons", 876132484, 48478126 }, | ||||
| 	}); | ||||
| 	mappings.insert("CanvasItem", { | ||||
| 		{ "draw_line", 2516941890, 1562330099 }, | ||||
| 		{ "draw_dashed_line", 2175215884, 684651049 }, | ||||
| 		{ "draw_polyline", 4175878946, 3797364428 }, | ||||
| 		{ "draw_polyline_colors", 2239164197, 2311979562 }, | ||||
| 		{ "draw_arc", 3486841771, 4140652635 }, | ||||
| 		{ "draw_multiline", 4230657331, 2239075205 }, | ||||
| 		{ "draw_multiline_colors", 235933050, 4072951537 }, | ||||
| 		{ "draw_rect", 84391229, 2417231121 }, | ||||
| 		{ "draw_texture", 1695860435, 520200117 }, | ||||
| 		{ "draw_texture_rect", 3204081724, 3832805018 }, | ||||
| 		{ "draw_texture_rect_region", 3196597532, 3883821411 }, | ||||
| 		{ "draw_msdf_texture_rect_region", 2672026175, 4219163252 }, | ||||
| 		{ "draw_lcd_texture_rect_region", 169610548, 3212350954 }, | ||||
| 		{ "draw_primitive", 2248678295, 3288481815 }, | ||||
| 		{ "draw_polygon", 2683625537, 974537912 }, | ||||
| 		{ "draw_colored_polygon", 1659099617, 15245644 }, | ||||
| 		{ "draw_string", 2552080639, 728290553 }, | ||||
| 		{ "draw_multiline_string", 4002645436, 1927038192 }, | ||||
| 		{ "draw_string_outline", 850005221, 340562381 }, | ||||
| 		{ "draw_multiline_string_outline", 3717870722, 1912318525 }, | ||||
| 		{ "draw_char", 2329089032, 3339793283 }, | ||||
| 		{ "draw_char_outline", 419453826, 3302344391 }, | ||||
| #ifdef REAL_T_IS_DOUBLE | ||||
| 		{ "draw_mesh", 1634855856, 4036154158 }, | ||||
| 		{ "draw_set_transform", 3283884939, 156553079 }, | ||||
| #else | ||||
| 		{ "draw_mesh", 1634855856, 153818295 }, | ||||
| 		{ "draw_set_transform", 3283884939, 288975085 }, | ||||
| #endif | ||||
| 		{ "draw_animation_slice", 2295343543, 3112831842 }, | ||||
| 	}); | ||||
| 	mappings.insert("CodeEdit", { | ||||
| 		{ "is_in_string", 3294126239, 688195400 }, | ||||
| 		{ "is_in_comment", 3294126239, 688195400 }, | ||||
| 		{ "add_code_completion_option", 1629240608, 947964390 }, | ||||
| 	}); | ||||
| 	mappings.insert("Control", { | ||||
| 		{ "set_offsets_preset", 3651818904, 3724524307 }, | ||||
| 		{ "set_anchors_and_offsets_preset", 3651818904, 3724524307 }, | ||||
| 		{ "set_anchor", 2589937826, 2302782885 }, | ||||
| 		{ "get_theme_icon", 2336455395, 3163973443 }, | ||||
| 		{ "get_theme_stylebox", 2759935355, 604739069 }, | ||||
| 		{ "get_theme_font", 387378635, 2826986490 }, | ||||
| 		{ "get_theme_font_size", 229578101, 1327056374 }, | ||||
| 		{ "get_theme_color", 2377051548, 2798751242 }, | ||||
| 		{ "get_theme_constant", 229578101, 1327056374 }, | ||||
| 		{ "has_theme_icon", 1187511791, 866386512 }, | ||||
| 		{ "has_theme_stylebox", 1187511791, 866386512 }, | ||||
| 		{ "has_theme_font", 1187511791, 866386512 }, | ||||
| 		{ "has_theme_font_size", 1187511791, 866386512 }, | ||||
| 		{ "has_theme_color", 1187511791, 866386512 }, | ||||
| 		{ "has_theme_constant", 1187511791, 866386512 }, | ||||
| 	}); | ||||
| 	mappings.insert("Crypto", { | ||||
| 		{ "generate_self_signed_certificate", 947314696, 492266173 }, | ||||
| 	}); | ||||
| 	mappings.insert("Curve", { | ||||
| 		{ "add_point", 2766148617, 434072736 }, | ||||
| 	}); | ||||
| 	mappings.insert("Curve2D", { | ||||
| #ifdef REAL_T_IS_DOUBLE | ||||
| 		{ "add_point", 529706502, 3343370600 }, | ||||
| #else | ||||
| 		{ "add_point", 2437345566, 4175465202 }, | ||||
| #endif | ||||
| 	}); | ||||
| 	mappings.insert("Curve3D", { | ||||
| #ifdef REAL_T_IS_DOUBLE | ||||
| 		{ "add_point", 3544159631, 917388502 }, | ||||
| #else | ||||
| 		{ "add_point", 3836314258, 2931053748 }, | ||||
| #endif | ||||
| 	}); | ||||
| 	mappings.insert("DirAccess", { | ||||
| 		{ "list_dir_begin", 2018049411, 2610976713 }, | ||||
| 		{ "copy", 198434953, 1063198817 }, | ||||
| 		{ "copy_absolute", 198434953, 1063198817 }, | ||||
| 	}); | ||||
| 	mappings.insert("DisplayServer", { | ||||
| 		{ "global_menu_add_submenu_item", 3806306913, 2828985934 }, | ||||
| 		{ "global_menu_add_item", 3415468211, 3401266716 }, | ||||
| 		{ "global_menu_add_check_item", 3415468211, 3401266716 }, | ||||
| 		{ "global_menu_add_icon_item", 1700867534, 4245856523 }, | ||||
| 		{ "global_menu_add_icon_check_item", 1700867534, 4245856523 }, | ||||
| 		{ "global_menu_add_radio_check_item", 3415468211, 3401266716 }, | ||||
| 		{ "global_menu_add_icon_radio_check_item", 1700867534, 4245856523 }, | ||||
| 		{ "global_menu_add_multistate_item", 635750054, 3431222859 }, | ||||
| 		{ "global_menu_add_separator", 1041533178, 3214812433 }, | ||||
| 		{ "tts_speak", 3741216677, 903992738 }, | ||||
| 		{ "is_touchscreen_available", 4162880507, 3323674545 }, | ||||
| 		{ "screen_set_orientation", 2629526904, 2211511631 }, | ||||
| 		{ "window_get_native_handle", 2709193271, 1096425680 }, | ||||
| 		{ "window_set_title", 3043792800, 441246282 }, | ||||
| 		{ "window_set_mouse_passthrough", 3958815166, 1993637420 }, | ||||
| 		{ "window_set_current_screen", 3023605688, 2230941749 }, | ||||
| 		{ "window_set_position", 3614040015, 2019273902 }, | ||||
| 		{ "window_set_size", 3614040015, 2019273902 }, | ||||
| 		{ "window_set_rect_changed_callback", 3653650673, 1091192925 }, | ||||
| 		{ "window_set_window_event_callback", 3653650673, 1091192925 }, | ||||
| 		{ "window_set_input_event_callback", 3653650673, 1091192925 }, | ||||
| 		{ "window_set_input_text_callback", 3653650673, 1091192925 }, | ||||
| 		{ "window_set_drop_files_callback", 3653650673, 1091192925 }, | ||||
| 		{ "window_set_max_size", 3614040015, 2019273902 }, | ||||
| 		{ "window_set_min_size", 3614040015, 2019273902 }, | ||||
| 		{ "window_set_mode", 2942569511, 1319965401 }, | ||||
| 		{ "window_set_flag", 3971592565, 254894155 }, | ||||
| 		{ "window_get_flag", 2662949986, 802816991 }, | ||||
| 		{ "window_set_window_buttons_offset", 3614040015, 2019273902 }, | ||||
| 		{ "window_set_ime_active", 450484987, 1661950165 }, | ||||
| 		{ "window_set_ime_position", 3614040015, 2019273902 }, | ||||
| 		{ "window_set_vsync_mode", 1708924624, 2179333492 }, | ||||
| #ifdef REAL_T_IS_DOUBLE | ||||
| 		{ "cursor_set_custom_image", 1358907026, 4163678968 }, | ||||
| 		{ "virtual_keyboard_show", 384539973, 1323934605 }, | ||||
| #else | ||||
| 		{ "cursor_set_custom_image", 1358907026, 1816663697 }, | ||||
| 		{ "virtual_keyboard_show", 860410478, 3042891259 }, | ||||
| #endif | ||||
| 	}); | ||||
| 	mappings.insert("ENetConnection", { | ||||
| 		{ "create_host_bound", 866250949, 1515002313 }, | ||||
| 		{ "connect_to_host", 385984708, 2171300490 }, | ||||
| 		{ "dtls_client_setup", 3097527179, 1966198364 }, | ||||
| 	}); | ||||
| 	mappings.insert("ENetMultiplayerPeer", { | ||||
| 		{ "create_server", 1616151701, 2917761309 }, | ||||
| 		{ "create_client", 920217784, 2327163476 }, | ||||
| 	}); | ||||
| 	mappings.insert("EditorCommandPalette", { | ||||
| 		{ "add_command", 3664614892, 864043298 }, | ||||
| 	}); | ||||
| 	mappings.insert("EditorDebuggerSession", { | ||||
| 		{ "send_message", 3780025912, 85656714 }, | ||||
| 		{ "toggle_profiler", 35674246, 1198443697 }, | ||||
| 	}); | ||||
| 	mappings.insert("EditorFileDialog", { | ||||
| 		{ "add_filter", 233059325, 3388804757 }, | ||||
| 	}); | ||||
| 	mappings.insert("EditorImportPlugin", { | ||||
| 		{ "append_import_external_resource", 3645925746, 320493106 }, | ||||
| 	}); | ||||
| 	mappings.insert("EditorInterface", { | ||||
| 		{ "popup_dialog", 2478844058, 2015770942 }, | ||||
| 		{ "popup_dialog_centered", 1723337679, 346557367 }, | ||||
| 		{ "popup_dialog_centered_ratio", 1310934579, 2093669136 }, | ||||
| 		{ "popup_dialog_centered_clamped", 3433759678, 3763385571 }, | ||||
| 		{ "inspect_object", 2564140749, 127962172 }, | ||||
| 		{ "edit_script", 3664508569, 219829402 }, | ||||
| 		{ "save_scene_as", 1168363258, 3647332257 }, | ||||
| 	}); | ||||
| 	mappings.insert("EditorNode3DGizmo", { | ||||
| 		{ "add_lines", 302451090, 2910971437 }, | ||||
| 	#ifdef REAL_T_IS_DOUBLE | ||||
| 		{ "add_mesh", 3332776472, 2161761131 }, | ||||
| 	#else | ||||
| 		{ "add_mesh", 1868867708, 1579955111 }, | ||||
| 	#endif | ||||
| 		{ "add_unscaled_billboard", 3719733075, 520007164 }, | ||||
| 	}); | ||||
| 	mappings.insert("EditorNode3DGizmoPlugin", { | ||||
| 		{ "create_icon_material", 2976007329, 3804976916 }, | ||||
| 		{ "get_material", 3501703615, 974464017 }, | ||||
| 	}); | ||||
| 	mappings.insert("EditorScenePostImportPlugin", { | ||||
| 		{ "add_import_option_advanced", 3774155785, 3674075649 }, | ||||
| 	}); | ||||
| 	mappings.insert("EditorUndoRedoManager", { | ||||
| 		{ "create_action", 3577985681, 2107025470 }, | ||||
| 	}); | ||||
| 	mappings.insert("EngineDebugger", { | ||||
| 		{ "profiler_enable", 438160728, 3192561009 }, | ||||
| 	}); | ||||
| 	mappings.insert("Expression", { | ||||
| 		{ "parse", 3658149758, 3069722906 }, | ||||
| 	}); | ||||
| 	mappings.insert("FileAccess", { | ||||
| 		{ "open_compressed", 2874458257, 3686439335 }, | ||||
| 		{ "store_csv_line", 2217842308, 2173791505 }, | ||||
| 	}); | ||||
| 	mappings.insert("FileDialog", { | ||||
| 		{ "add_filter", 233059325, 3388804757 }, | ||||
| 	}); | ||||
| 	mappings.insert("Font", { | ||||
| 		{ "get_string_size", 3678918099, 1868866121 }, | ||||
| 		{ "get_multiline_string_size", 2427690650, 519636710 }, | ||||
| 		{ "draw_string", 2565402639, 1983721962 }, | ||||
| 		{ "draw_multiline_string", 348869189, 1171506176 }, | ||||
| 		{ "draw_string_outline", 657875837, 623754045 }, | ||||
| 		{ "draw_multiline_string_outline", 1649790182, 3206388178 }, | ||||
| 		{ "draw_char", 1462476057, 3815617597 }, | ||||
| 		{ "draw_char_outline", 4161008124, 209525354 }, | ||||
| 	#ifdef REAL_T_IS_DOUBLE | ||||
| 		{ "find_variation", 625117670, 2196349508 }, | ||||
| 	#else | ||||
| 		{ "find_variation", 1222433716, 3344325384 }, | ||||
| 		// Pre-existing compatibility hash.
 | ||||
| 		{ "find_variation", 1149405976, 1851767612 }, | ||||
| 	#endif | ||||
| 	}); | ||||
| 	mappings.insert("GLTFDocument", { | ||||
| 		{ "append_from_file", 1862991421, 866380864 }, | ||||
| 		{ "append_from_buffer", 2818062664, 1616081266 }, | ||||
| 		{ "append_from_scene", 374125375, 1622574258 }, | ||||
| 		{ "generate_scene", 2770277081, 596118388 }, | ||||
| 	}); | ||||
| 	mappings.insert("Geometry2D", { | ||||
| 		{ "offset_polygon", 3837618924, 1275354010 }, | ||||
| 		{ "offset_polyline", 328033063, 2328231778 }, | ||||
| 	}); | ||||
| 	mappings.insert("Geometry3D", { | ||||
| 		{ "build_cylinder_planes", 3142160516, 449920067 }, | ||||
| 		{ "build_capsule_planes", 410870045, 2113592876 }, | ||||
| 	}); | ||||
| 	mappings.insert("GraphNode", { | ||||
| 		{ "set_slot", 902131739, 2873310869 }, | ||||
| 	}); | ||||
| 	mappings.insert("GridMap", { | ||||
| 		{ "set_cell_item", 4177201334, 3449088946 }, | ||||
| 	}); | ||||
| 	mappings.insert("HTTPClient", { | ||||
| 		{ "connect_to_host", 1970282951, 504540374 }, | ||||
| 		{ "request", 3249905507, 3778990155 }, | ||||
| 	}); | ||||
| 	mappings.insert("HTTPRequest", { | ||||
| 		{ "request", 2720304520, 3215244323 }, | ||||
| 		{ "request_raw", 4282724657, 2714829993 }, | ||||
| 	}); | ||||
| 	mappings.insert("IP", { | ||||
| 		{ "resolve_hostname", 396864159, 4283295457 }, | ||||
| 		{ "resolve_hostname_addresses", 3462780090, 773767525 }, | ||||
| 		{ "resolve_hostname_queue_item", 3936392508, 1749894742 }, | ||||
| 	}); | ||||
| 	mappings.insert("Image", { | ||||
| 		{ "resize", 2461393748, 994498151 }, | ||||
| 		{ "save_jpg", 578836491, 2800019068 }, | ||||
| 		{ "save_webp", 3594949219, 2781156876 }, | ||||
| 		{ "compress", 4094210332, 2975424957 }, | ||||
| 		{ "compress_from_channels", 279105990, 4212890953 }, | ||||
| 		{ "load_svg_from_buffer", 1822513750, 311853421 }, | ||||
| 		{ "load_svg_from_string", 1461766635, 3254053600 }, | ||||
| 	}); | ||||
| 	mappings.insert("ImmediateMesh", { | ||||
| 		{ "surface_begin", 3716480242, 2794442543 }, | ||||
| 	}); | ||||
| 	mappings.insert("ImporterMesh", { | ||||
| 		{ "add_surface", 4122361985, 1740448849 }, | ||||
| 	}); | ||||
| 	mappings.insert("Input", { | ||||
| 		{ "get_vector", 1517139831, 2479607902 }, | ||||
| 		{ "start_joy_vibration", 1890603622, 2576575033 }, | ||||
| 		{ "action_press", 573731101, 1713091165 }, | ||||
| #ifdef REAL_T_IS_DOUBLE | ||||
| 		{ "set_custom_mouse_cursor", 3489634142, 1277868338 }, | ||||
| #else | ||||
| 		{ "set_custom_mouse_cursor", 3489634142, 703945977 }, | ||||
| #endif | ||||
| 	}); | ||||
| 	mappings.insert("InputEvent", { | ||||
| 		{ "is_match", 3392494811, 1754951977 }, | ||||
| #ifdef REAL_T_IS_DOUBLE | ||||
| 		{ "xformed_by", 2747409789, 3242949850 }, | ||||
| #else | ||||
| 		{ "xformed_by", 2747409789, 1282766827 }, | ||||
| #endif | ||||
| 	}); | ||||
| 	mappings.insert("InputMap", { | ||||
| 		{ "add_action", 573731101, 4100757082 }, | ||||
| 	}); | ||||
| 	mappings.insert("ItemList", { | ||||
| 		{ "add_item", 4086250691, 359861678 }, | ||||
| 		{ "add_icon_item", 3332687421, 4256579627 }, | ||||
| 		{ "get_item_rect", 1501513492, 159227807 }, | ||||
| 		{ "select", 4023243586, 972357352 }, | ||||
| 	}); | ||||
| 	mappings.insert("JSON", { | ||||
| 		{ "stringify", 2656701787, 462733549 }, | ||||
| 	}); | ||||
| 	mappings.insert("JavaScriptBridge", { | ||||
| 		{ "download_buffer", 4123979296, 3352272093 }, | ||||
| 	}); | ||||
| 	mappings.insert("Line2D", { | ||||
| 		{ "add_point", 468506575, 2654014372 }, | ||||
| 	}); | ||||
| 	mappings.insert("MultiplayerAPI", { | ||||
| 		{ "rpc", 1833408346, 2077486355 }, | ||||
| 	}); | ||||
| 	mappings.insert("NavigationMeshGenerator", { | ||||
| 		{ "parse_source_geometry_data", 3703028813, 685862123 }, | ||||
| 		{ "bake_from_source_geometry_data", 3669016597, 2469318639 }, | ||||
| 	}); | ||||
| 	mappings.insert("NavigationServer2D", { | ||||
| 		{ "map_get_path", 56240621, 3146466012 }, | ||||
| 	}); | ||||
| 	mappings.insert("NavigationServer3D", { | ||||
| 		{ "map_get_path", 2121045993, 1187418690 }, | ||||
| 		{ "parse_source_geometry_data", 3703028813, 685862123 }, | ||||
| 		{ "bake_from_source_geometry_data", 3669016597, 2469318639 }, | ||||
| 		{ "bake_from_source_geometry_data_async", 3669016597, 2469318639 }, | ||||
| 	}); | ||||
| 	mappings.insert("Node", { | ||||
| 		{ "add_child", 3070154285, 3863233950 }, | ||||
| 		{ "reparent", 2570952461, 3685795103 }, | ||||
| 		{ "find_child", 4253159453, 2008217037 }, | ||||
| 		{ "find_children", 1585018254, 2560337219 }, | ||||
| 		{ "propagate_call", 1667910434, 1871007965 }, | ||||
| 		{ "set_multiplayer_authority", 4023243586, 972357352 }, | ||||
| 	}); | ||||
| 	mappings.insert("Node3D", { | ||||
| #ifdef REAL_T_IS_DOUBLE | ||||
| 		{ "look_at", 136915519, 819337406 }, | ||||
| 		{ "look_at_from_position", 4067663783, 1809580162 }, | ||||
| #else | ||||
| 		{ "look_at", 3123400617, 2882425029 }, | ||||
| 		{ "look_at_from_position", 4067663783, 2086826090 }, | ||||
| #endif | ||||
| 	}); | ||||
| 	mappings.insert("Noise", { | ||||
| 		{ "get_image", 2569233413, 3180683109 }, | ||||
| 		{ "get_seamless_image", 2210827790, 2770743602 }, | ||||
| 		{ "get_image_3d", 2358868431, 3977814329 }, | ||||
| 		{ "get_seamless_image_3d", 3328694319, 451006340 }, | ||||
| 	}); | ||||
| 	mappings.insert("OS", { | ||||
| 		{ "alert", 233059325, 1783970740 }, | ||||
| 		{ "get_system_font_path", 2262142305, 626580860 }, | ||||
| 		{ "get_system_font_path_for_text", 3824042574, 197317981 }, | ||||
| 		{ "execute", 2881709059, 1488299882 }, | ||||
| 		{ "shell_show_in_file_manager", 885841341, 3565188097 }, | ||||
| 		{ "set_restart_on_exit", 611198603, 3331453935 }, | ||||
| 		{ "get_system_dir", 1965199849, 3073895123 }, | ||||
| 	}); | ||||
| 	mappings.insert("Object", { | ||||
| 		{ "add_user_signal", 3780025912, 85656714 }, | ||||
| 		{ "connect", 1469446357, 1518946055 }, | ||||
| 		{ "tr", 2475554935, 1195764410 }, | ||||
| 		{ "tr_n", 4021311862, 162698058 }, | ||||
| 	}); | ||||
| 	mappings.insert("OptionButton", { | ||||
| 		{ "add_item", 3043792800, 2697778442 }, | ||||
| 		{ "add_icon_item", 3944051090, 3781678508 }, | ||||
| 	}); | ||||
| 	mappings.insert("PCKPacker", { | ||||
| 		{ "pck_start", 3232891339, 508410629 }, | ||||
| 	}); | ||||
| 	mappings.insert("PacketPeerDTLS", { | ||||
| 		{ "connect_to_peer", 1801538152, 2880188099 }, | ||||
| 	}); | ||||
| 	mappings.insert("PacketPeerUDP", { | ||||
| 		{ "bind", 4290438434, 4051239242 }, | ||||
| 	}); | ||||
| 	mappings.insert("Performance", { | ||||
| 		{ "add_custom_monitor", 2865980031, 4099036814 }, | ||||
| 	}); | ||||
| 	mappings.insert("PhysicalBone3D", { | ||||
| #ifdef REAL_T_IS_DOUBLE | ||||
| 		{ "apply_impulse", 1002852006, 2485728502 }, | ||||
| #else | ||||
| 		{ "apply_impulse", 1002852006, 2754756483 }, | ||||
| #endif | ||||
| 	}); | ||||
| 	mappings.insert("PhysicsBody2D", { | ||||
| 		{ "move_and_collide", 1529961754, 3681923724 }, | ||||
| 		{ "test_move", 1369208982, 3324464701 }, | ||||
| 	}); | ||||
| 	mappings.insert("PhysicsBody3D", { | ||||
| 		{ "move_and_collide", 2825704414, 3208792678 }, | ||||
| 		{ "test_move", 680299713, 2481691619 }, | ||||
| 	}); | ||||
| 	mappings.insert("PhysicsDirectBodyState2D", { | ||||
| #ifdef REAL_T_IS_DOUBLE | ||||
| 		{ "apply_impulse", 496058220, 1271588277 }, | ||||
| 		{ "apply_force", 496058220, 1271588277 }, | ||||
| 		{ "add_constant_force", 496058220, 1271588277 }, | ||||
| #else | ||||
| 		{ "apply_impulse", 496058220, 4288681949 }, | ||||
| 		{ "apply_force", 496058220, 4288681949 }, | ||||
| 		{ "add_constant_force", 496058220, 4288681949 }, | ||||
| #endif | ||||
| 	}); | ||||
| 	mappings.insert("PhysicsDirectBodyState3D", { | ||||
| #ifdef REAL_T_IS_DOUBLE | ||||
| 		{ "apply_impulse", 1002852006, 2485728502 }, | ||||
| 		{ "apply_force", 1002852006, 2485728502 }, | ||||
| 		{ "add_constant_force", 1002852006, 2485728502 }, | ||||
| #else | ||||
| 		{ "apply_impulse", 1002852006, 2754756483 }, | ||||
| 		{ "apply_force", 1002852006, 2754756483 }, | ||||
| 		{ "add_constant_force", 1002852006, 2754756483 }, | ||||
| #endif | ||||
| 	}); | ||||
| 	mappings.insert("PhysicsDirectSpaceState2D", { | ||||
| 		{ "intersect_point", 3278207904, 2118456068 }, | ||||
| 		{ "intersect_shape", 3803848594, 2488867228 }, | ||||
| 		{ "collide_shape", 3803848594, 2488867228 }, | ||||
| 	}); | ||||
| 	mappings.insert("PhysicsDirectSpaceState3D", { | ||||
| 		{ "intersect_point", 45993382, 975173756 }, | ||||
| 		{ "intersect_shape", 550215980, 3762137681 }, | ||||
| 		{ "collide_shape", 550215980, 3762137681 }, | ||||
| 	}); | ||||
| 	mappings.insert("PhysicsRayQueryParameters2D", { | ||||
| 		{ "create", 1118143851, 3196569324 }, | ||||
| 	}); | ||||
| 	mappings.insert("PhysicsRayQueryParameters3D", { | ||||
| 		{ "create", 680321959, 3110599579 }, | ||||
| 	}); | ||||
| 	mappings.insert("PhysicsServer2D", { | ||||
| #ifdef REAL_T_IS_DOUBLE | ||||
| 		{ "area_add_shape", 754862190, 3597527023 }, | ||||
| 		{ "body_add_shape", 754862190, 3597527023 }, | ||||
| 		{ "body_apply_impulse", 34330743, 1124035137 }, | ||||
| 		{ "body_apply_force", 34330743, 1124035137 }, | ||||
| 		{ "body_add_constant_force", 34330743, 1124035137 }, | ||||
| #else | ||||
| 		{ "area_add_shape", 754862190, 339056240 }, | ||||
| 		{ "body_add_shape", 754862190, 339056240 }, | ||||
| 		{ "body_apply_impulse", 34330743, 205485391 }, | ||||
| 		{ "body_apply_force", 34330743, 205485391 }, | ||||
| 		{ "body_add_constant_force", 34330743, 205485391 }, | ||||
| #endif | ||||
| 		{ "joint_make_pin", 2288600450, 1612646186 }, | ||||
| 		{ "joint_make_groove", 3573265764, 481430435 }, | ||||
| 		{ "joint_make_damped_spring", 206603952, 1994657646 }, | ||||
| 	}); | ||||
| 	mappings.insert("PhysicsServer3D", { | ||||
| #ifdef REAL_T_IS_DOUBLE | ||||
| 		{ "area_add_shape", 4040559639, 183938777 }, | ||||
| 		{ "body_add_shape", 4040559639, 183938777 }, | ||||
| 		{ "body_apply_impulse", 110375048, 2238283471 }, | ||||
| 		{ "body_apply_force", 110375048, 2238283471 }, | ||||
| 		{ "body_add_constant_force", 110375048, 2238283471 }, | ||||
| #else | ||||
| 		{ "area_add_shape", 4040559639, 3711419014 }, | ||||
| 		{ "body_add_shape", 4040559639, 3711419014 }, | ||||
| 		{ "body_apply_impulse", 110375048, 390416203 }, | ||||
| 		{ "body_apply_force", 110375048, 390416203 }, | ||||
| 		{ "body_add_constant_force", 110375048, 390416203 }, | ||||
| #endif | ||||
| 	}); | ||||
| 	mappings.insert("PopupMenu", { | ||||
| 		{ "add_item", 3224536192, 3674230041 }, | ||||
| 		{ "add_icon_item", 1200674553, 1086190128 }, | ||||
| 		{ "add_check_item", 3224536192, 3674230041 }, | ||||
| 		{ "add_icon_check_item", 1200674553, 1086190128 }, | ||||
| 		{ "add_radio_check_item", 3224536192, 3674230041 }, | ||||
| 		{ "add_icon_radio_check_item", 1200674553, 1086190128 }, | ||||
| 		{ "add_multistate_item", 1585218420, 150780458 }, | ||||
| 		{ "add_shortcut", 2482211467, 3451850107 }, | ||||
| 		{ "add_icon_shortcut", 3060251822, 2997871092 }, | ||||
| 		{ "add_check_shortcut", 2168272394, 1642193386 }, | ||||
| 		{ "add_icon_check_shortcut", 68101841, 3856247530 }, | ||||
| 		{ "add_radio_check_shortcut", 2168272394, 1642193386 }, | ||||
| 		{ "add_icon_radio_check_shortcut", 68101841, 3856247530 }, | ||||
| 		{ "add_submenu_item", 3728518296, 2979222410 }, | ||||
| 		// Pre-existing compatibility hashes.
 | ||||
| 		{ "add_icon_shortcut", 68101841, 3856247530 }, | ||||
| 		{ "add_shortcut", 2168272394, 1642193386 }, | ||||
| 	}); | ||||
| 	mappings.insert("PortableCompressedTexture2D", { | ||||
| 		{ "create_from_image", 97251393, 3679243433 }, | ||||
| 	}); | ||||
| 	mappings.insert("ProjectSettings", { | ||||
| 		{ "load_resource_pack", 3001721055, 708980503 }, | ||||
| 	}); | ||||
| 	mappings.insert("RegEx", { | ||||
| 		{ "search", 4087180739, 3365977994 }, | ||||
| 		{ "search_all", 3354100289, 849021363 }, | ||||
| 		{ "sub", 758293621, 54019702 }, | ||||
| 	}); | ||||
| 	mappings.insert("RenderingDevice", { | ||||
| 		{ "texture_create", 3011278298, 3709173589 }, | ||||
| 		{ "texture_create_shared_from_slice", 864132525, 1808971279 }, | ||||
| 		{ "texture_update", 2736912341, 2096463824 }, | ||||
| 		{ "texture_copy", 3741367532, 2339493201 }, | ||||
| 		{ "texture_clear", 3423681478, 3396867530 }, | ||||
| 		{ "texture_resolve_multisample", 2126834943, 594679454 }, | ||||
| 		{ "framebuffer_format_create", 2635475316, 697032759 }, | ||||
| 		{ "framebuffer_format_create_multipass", 1992489524, 2647479094 }, | ||||
| 		{ "framebuffer_format_get_texture_samples", 1036806638, 4223391010 }, | ||||
| 		{ "framebuffer_create", 1884747791, 3284231055 }, | ||||
| 		{ "framebuffer_create_multipass", 452534725, 1750306695 }, | ||||
| 		{ "framebuffer_create_empty", 382373098, 3058360618 }, | ||||
| 		{ "vertex_buffer_create", 3491282828, 3410049843 }, | ||||
| 		{ "vertex_array_create", 3137892244, 3799816279 }, | ||||
| 		{ "index_buffer_create", 975915977, 3935920523 }, | ||||
| 		{ "shader_compile_spirv_from_source", 3459523685, 1178973306 }, | ||||
| 		{ "shader_compile_binary_from_spirv", 1395027180, 134910450 }, | ||||
| 		{ "shader_create_from_spirv", 3297482566, 342949005 }, | ||||
| 		{ "shader_create_from_bytecode", 2078349841, 1687031350 }, | ||||
| 		{ "uniform_buffer_create", 1453158401, 34556762 }, | ||||
| 		{ "storage_buffer_create", 1173156076, 2316365934 }, | ||||
| 		{ "texture_buffer_create", 2344087557, 1470338698 }, | ||||
| 		{ "buffer_update", 652628289, 3793150683 }, | ||||
| 		{ "buffer_clear", 1645170096, 2797041220 }, | ||||
| 		{ "buffer_get_data", 125363422, 3101830688 }, | ||||
| 		{ "render_pipeline_create", 2911419500, 2385451958 }, | ||||
| 		{ "compute_pipeline_create", 403593840, 1448838280 }, | ||||
| 		{ "draw_list_draw", 3710874499, 4230067973 }, | ||||
| #ifdef REAL_T_IS_DOUBLE | ||||
| 		{ "draw_list_begin", 4252992020, 848735039 }, | ||||
| 		{ "draw_list_begin_split", 832527510, 2228306807 }, | ||||
| 		{ "draw_list_enable_scissor", 338791288, 730833978 }, | ||||
| #else | ||||
| 		{ "draw_list_begin", 4252992020, 2468082605 }, | ||||
| 		{ "draw_list_begin_split", 832527510, 2406300660 }, | ||||
| 		{ "draw_list_enable_scissor", 338791288, 244650101 }, | ||||
| #endif | ||||
| 	}); | ||||
| 	mappings.insert("RenderingServer", { | ||||
| 		{ "texture_rd_create", 3291180269, 1434128712 }, | ||||
| 		{ "shader_set_default_texture_parameter", 3864903085, 4094001817 }, | ||||
| 		{ "shader_get_default_texture_parameter", 2523186822, 1464608890 }, | ||||
| 		{ "mesh_create_from_surfaces", 4007581507, 4291747531 }, | ||||
| 		{ "mesh_add_surface_from_arrays", 1247008646, 2342446560 }, | ||||
| 		{ "environment_set_ambient_light", 491659071, 1214961493 }, | ||||
| 		{ "instances_cull_aabb", 2031554939, 2570105777 }, | ||||
| 		{ "instances_cull_ray", 3388524336, 2208759584 }, | ||||
| 		{ "instances_cull_convex", 3690700105, 2488539944 }, | ||||
| 		{ "canvas_item_add_line", 2843922985, 1819681853 }, | ||||
| 		{ "canvas_item_add_polyline", 3438017257, 3098767073 }, | ||||
| 		{ "canvas_item_add_multiline", 3176074788, 2088642721 }, | ||||
| 		{ "canvas_item_add_texture_rect", 3205360868, 324864032 }, | ||||
| 		{ "canvas_item_add_msdf_texture_rect_region", 349157222, 97408773 }, | ||||
| 		{ "canvas_item_add_texture_rect_region", 2782979504, 485157892 }, | ||||
| 		{ "canvas_item_add_nine_patch", 904428547, 389957886 }, | ||||
| 		{ "canvas_item_add_polygon", 2907936855, 3580000528 }, | ||||
| 		{ "canvas_item_add_triangle_array", 749685193, 660261329 }, | ||||
| 		{ "canvas_item_add_multimesh", 1541595251, 2131855138 }, | ||||
| 		{ "canvas_item_add_animation_slice", 4107531031, 2646834499 }, | ||||
| 		{ "canvas_item_set_canvas_group_mode", 41973386, 3973586316 }, | ||||
| 		{ "set_boot_image", 2244367877, 3759744527 }, | ||||
| #ifdef REAL_T_IS_DOUBLE | ||||
| 		{ "viewport_attach_to_screen", 1410474027, 2248302004 }, | ||||
| 		{ "canvas_item_set_custom_rect", 2180266943, 1134449082 }, | ||||
| 		{ "canvas_item_add_mesh", 3877492181, 3024949314 }, | ||||
| #else | ||||
| 		{ "viewport_attach_to_screen", 1278520651, 1062245816 }, | ||||
| 		{ "canvas_item_set_custom_rect", 2180266943, 1333997032 }, | ||||
| 		{ "canvas_item_add_mesh", 3548053052, 316450961 }, | ||||
| #endif | ||||
| 	}); | ||||
| 	mappings.insert("ResourceLoader", { | ||||
| 		{ "load_threaded_request", 1939848623, 3614384323 }, | ||||
| 		{ "load_threaded_get_status", 3931021148, 4137685479 }, | ||||
| 		{ "load", 2622212233, 3358495409 }, | ||||
| 		{ "exists", 2220807150, 4185558881 }, | ||||
| 	}); | ||||
| 	mappings.insert("ResourceSaver", { | ||||
| 		{ "save", 2303056517, 2983274697 }, | ||||
| 	}); | ||||
| 	mappings.insert("RichTextLabel", { | ||||
| 		{ "push_font", 814287596, 2347424842 }, | ||||
| 		{ "push_paragraph", 3218895358, 3089306873 }, | ||||
| 		{ "push_list", 4036303897, 3017143144 }, | ||||
| 		{ "push_table", 1125058220, 2623499273 }, | ||||
| 		{ "set_table_column_expand", 4132157579, 2185176273 }, | ||||
| #ifdef REAL_T_IS_DOUBLE | ||||
| 		{ "add_image", 3346058748, 1507062345 }, | ||||
| 		{ "push_dropcap", 981432822, 763534173 }, | ||||
| #else | ||||
| 		{ "add_image", 3346058748, 3580801207 }, | ||||
| 		{ "push_dropcap", 311501835, 4061635501 }, | ||||
| #endif | ||||
| 	}); | ||||
| 	mappings.insert("RigidBody2D", { | ||||
| #ifdef REAL_T_IS_DOUBLE | ||||
| 		{ "apply_impulse", 496058220, 1271588277 }, | ||||
| 		{ "apply_force", 496058220, 1271588277 }, | ||||
| 		{ "add_constant_force", 496058220, 1271588277 }, | ||||
| #else | ||||
| 		{ "apply_impulse", 496058220, 4288681949 }, | ||||
| 		{ "apply_force", 496058220, 4288681949 }, | ||||
| 		{ "add_constant_force", 496058220, 4288681949 }, | ||||
| #endif | ||||
| 	}); | ||||
| 	mappings.insert("RigidBody3D", { | ||||
| #ifdef REAL_T_IS_DOUBLE | ||||
| 		{ "apply_impulse", 1002852006, 2485728502 }, | ||||
| 		{ "apply_force", 1002852006, 2485728502 }, | ||||
| 		{ "add_constant_force", 1002852006, 2485728502 }, | ||||
| #else | ||||
| 		{ "apply_impulse", 1002852006, 2754756483 }, | ||||
| 		{ "apply_force", 1002852006, 2754756483 }, | ||||
| 		{ "add_constant_force", 1002852006, 2754756483 }, | ||||
| #endif | ||||
| 	}); | ||||
| 	mappings.insert("SceneMultiplayer", { | ||||
| 		{ "send_bytes", 2742700601, 1307428718 }, | ||||
| 	}); | ||||
| 	mappings.insert("SceneReplicationConfig", { | ||||
| 		{ "add_property", 3818401521, 4094619021 }, | ||||
| 	}); | ||||
| 	mappings.insert("SceneTree", { | ||||
| 		{ "create_timer", 1780978058, 2709170273 }, | ||||
| 	}); | ||||
| 	mappings.insert("ScriptCreateDialog", { | ||||
| 		{ "config", 4210001628, 869314288 }, | ||||
| 	}); | ||||
| 	mappings.insert("Shader", { | ||||
| 		{ "set_default_texture_parameter", 1628453603, 2750740428 }, | ||||
| 		{ "get_default_texture_parameter", 3823812009, 3090538643 }, | ||||
| 	}); | ||||
| 	mappings.insert("Skeleton3D", { | ||||
| 		{ "set_bone_enabled", 4023243586, 972357352 }, | ||||
| 	}); | ||||
| 	mappings.insert("SpriteFrames", { | ||||
| 		{ "add_frame", 407562921, 1351332740 }, | ||||
| 		{ "set_frame", 3155743884, 56804795 }, | ||||
| 	}); | ||||
| 	mappings.insert("StreamPeerTCP", { | ||||
| 		{ "bind", 4025329869, 3167955072 }, | ||||
| 	}); | ||||
| 	mappings.insert("StreamPeerTLS", { | ||||
| 		{ "connect_to_stream", 1325480781, 57169517 }, | ||||
| 	}); | ||||
| 	mappings.insert("SurfaceTool", { | ||||
| 		{ "add_triangle_fan", 297960074, 2235017613 }, | ||||
| 		{ "generate_lod", 1894448909, 1938056459 }, | ||||
| 	}); | ||||
| 	mappings.insert("TCPServer", { | ||||
| 		{ "listen", 4025329869, 3167955072 }, | ||||
| 	}); | ||||
| 	mappings.insert("TextEdit", { | ||||
| 		{ "get_line_width", 3294126239, 688195400 }, | ||||
| 		{ "insert_text_at_caret", 3043792800, 2697778442 }, | ||||
| 		{ "get_line_column_at_pos", 850652858, 239517838 }, | ||||
| 		{ "is_mouse_over_selection", 1099474134, 1840282309 }, | ||||
| 		{ "set_caret_line", 1413195636, 1302582944 }, | ||||
| 		{ "set_caret_column", 1071284433, 3796796178 }, | ||||
| 		{ "set_selection_mode", 2920622473, 1443345937 }, | ||||
| 		{ "select", 4269665324, 2560984452 }, | ||||
| 		{ "get_scroll_pos_for_line", 3274652423, 3929084198 }, | ||||
| 		{ "set_line_as_first_visible", 3023605688, 2230941749 }, | ||||
| 		{ "set_line_as_center_visible", 3023605688, 2230941749 }, | ||||
| 		{ "set_line_as_last_visible", 3023605688, 2230941749 }, | ||||
| 	}); | ||||
| 	mappings.insert("TextLine", { | ||||
| 		{ "add_string", 867188035, 621426851 }, | ||||
| 		{ "add_object", 735420116, 1316529304 }, | ||||
| 		{ "resize_object", 960819067, 2095776372 }, | ||||
| 		{ "draw", 1164457837, 856975658 }, | ||||
| 		{ "draw_outline", 1364491366, 1343401456 }, | ||||
| 	}); | ||||
| 	mappings.insert("TextParagraph", { | ||||
| #ifdef REAL_T_IS_DOUBLE | ||||
| 		{ "set_dropcap", 2613124475, 2897844600 }, | ||||
| #else | ||||
| 		{ "set_dropcap", 2613124475, 2498990330 }, | ||||
| #endif | ||||
| 		{ "add_string", 867188035, 621426851 }, | ||||
| 		{ "add_object", 735420116, 1316529304 }, | ||||
| 		{ "resize_object", 960819067, 2095776372 }, | ||||
| 		{ "draw", 367324453, 1567802413 }, | ||||
| 		{ "draw_outline", 2159523405, 1893131224 }, | ||||
| 		{ "draw_line", 3963848920, 1242169894 }, | ||||
| 		{ "draw_line_outline", 1814903311, 2664926980 }, | ||||
| 		{ "draw_dropcap", 1164457837, 856975658 }, | ||||
| 		{ "draw_dropcap_outline", 1364491366, 1343401456 }, | ||||
| 	}); | ||||
| 	mappings.insert("TextServer", { | ||||
| 		{ "font_draw_glyph", 1821196351, 1339057948 }, | ||||
| 		{ "font_draw_glyph_outline", 1124898203, 2626165733 }, | ||||
| 		{ "shaped_text_set_direction", 2616949700, 1551430183 }, | ||||
| 		{ "shaped_text_set_orientation", 104095128, 3019609126 }, | ||||
| 		{ "shaped_text_add_string", 2621279422, 623473029 }, | ||||
| 		{ "shaped_text_add_object", 2838446185, 3664424789 }, | ||||
| 		{ "shaped_text_resize_object", 2353789835, 790361552 }, | ||||
| 		{ "shaped_set_span_update_font", 1578983057, 2022725822 }, | ||||
| 		{ "shaped_text_fit_to_width", 603718830, 530670926 }, | ||||
| 		{ "shaped_text_get_line_breaks_adv", 4206849830, 2376991424 }, | ||||
| 		{ "shaped_text_get_line_breaks", 303410369, 2651359741 }, | ||||
| 		{ "shaped_text_get_word_breaks", 3299477123, 185957063 }, | ||||
| 		{ "shaped_text_overrun_trim_to_width", 1572579718, 2723146520 }, | ||||
| 		{ "shaped_text_draw", 70679950, 880389142 }, | ||||
| 		{ "shaped_text_draw_outline", 2673671346, 2559184194 }, | ||||
| 		{ "format_number", 2305636099, 2664628024 }, | ||||
| 		{ "parse_number", 2305636099, 2664628024 }, | ||||
| 		{ "string_get_word_breaks", 1398910359, 581857818 }, | ||||
| 		{ "string_get_character_breaks", 1586579831, 2333794773 }, | ||||
| 		{ "string_to_upper", 2305636099, 2664628024 }, | ||||
| 		{ "string_to_lower", 2305636099, 2664628024 }, | ||||
| 	}); | ||||
| 	mappings.insert("Texture2D", { | ||||
| 		{ "draw", 1115460088, 2729649137 }, | ||||
| 		{ "draw_rect", 575156982, 3499451691 }, | ||||
| 		{ "draw_rect_region", 1066564656, 2963678660 }, | ||||
| 	}); | ||||
| 	mappings.insert("Thread", { | ||||
| 		{ "start", 2779832528, 1327203254 }, | ||||
| 	}); | ||||
| 	mappings.insert("TileMap", { | ||||
| 		{ "set_cell", 1732664643, 966713560 }, | ||||
| 		{ "set_cells_terrain_connect", 3072115677, 3578627656 }, | ||||
| 		{ "set_cells_terrain_path", 3072115677, 3578627656 }, | ||||
| 		{ "get_used_cells_by_id", 4152068407, 2931012785 }, | ||||
| 	}); | ||||
| 	mappings.insert("TileMapPattern", { | ||||
| 		{ "set_cell", 634000503, 2224802556 }, | ||||
| 	}); | ||||
| 	mappings.insert("TileSet", { | ||||
| 		{ "add_source", 276991387, 1059186179 }, | ||||
| 		{ "add_terrain", 3023605688, 1230568737 }, | ||||
| 		{ "add_pattern", 3009264082, 763712015 }, | ||||
| 	}); | ||||
| 	mappings.insert("TileSetAtlasSource", { | ||||
| 		{ "create_tile", 1583819816, 190528769 }, | ||||
| 		{ "move_tile_in_atlas", 1375626516, 3870111920 }, | ||||
| 		{ "has_room_for_tile", 4182444377, 3018597268 }, | ||||
| 		{ "create_alternative_tile", 3531100812, 2226298068 }, | ||||
| 		{ "get_tile_texture_region", 1321423751, 241857547 }, | ||||
| 	}); | ||||
| 	mappings.insert("TileSetScenesCollectionSource", { | ||||
| 		{ "create_scene_tile", 2633389122, 1117465415 }, | ||||
| 	}); | ||||
| 	mappings.insert("Translation", { | ||||
| 		{ "add_message", 971803314, 3898530326 }, | ||||
| 		{ "add_plural_message", 360316719, 2356982266 }, | ||||
| 		{ "get_message", 58037827, 1829228469 }, | ||||
| 		{ "get_plural_message", 1333931916, 229954002 }, | ||||
| 		{ "erase_message", 3919944288, 3959009644 }, | ||||
| 	}); | ||||
| 	mappings.insert("TranslationServer", { | ||||
| 		{ "translate", 58037827, 1829228469 }, | ||||
| 		{ "translate_plural", 1333931916, 229954002 }, | ||||
| 	}); | ||||
| 	mappings.insert("Tree", { | ||||
| 		{ "get_item_area_rect", 1235226180, 47968679 }, | ||||
| 	}); | ||||
| 	mappings.insert("TreeItem", { | ||||
| 		{ "propagate_check", 4023243586, 972357352 }, | ||||
| 		{ "add_button", 1507727907, 1688223362 }, | ||||
| 	}); | ||||
| 	mappings.insert("UDPServer", { | ||||
| 		{ "listen", 4025329869, 3167955072 }, | ||||
| 	}); | ||||
| 	mappings.insert("UPNP", { | ||||
| 		{ "add_port_mapping", 3358934458, 818314583 }, | ||||
| 		{ "delete_port_mapping", 760296170, 3444187325 }, | ||||
| 	}); | ||||
| 	mappings.insert("UPNPDevice", { | ||||
| 		{ "add_port_mapping", 3358934458, 818314583 }, | ||||
| 		{ "delete_port_mapping", 760296170, 3444187325 }, | ||||
| 	}); | ||||
| 	mappings.insert("UndoRedo", { | ||||
| 		{ "create_action", 3900135403, 3171901514 }, | ||||
| 	}); | ||||
| 	mappings.insert("VideoStreamPlayback", { | ||||
| 		{ "mix_audio", 1369271885, 93876830 }, | ||||
| 	}); | ||||
| 	mappings.insert("WebRTCMultiplayerPeer", { | ||||
| 		{ "create_client", 1777354631, 2641732907 }, | ||||
| 		{ "create_mesh", 1777354631, 2641732907 }, | ||||
| 		{ "add_peer", 2555866323, 4078953270 }, | ||||
| 	}); | ||||
| 	mappings.insert("WebRTCPeerConnection", { | ||||
| 		{ "create_data_channel", 3997447457, 1288557393 }, | ||||
| 	}); | ||||
| 	mappings.insert("WebSocketMultiplayerPeer", { | ||||
| 		{ "create_client", 3097527179, 1966198364 }, | ||||
| 		{ "create_server", 337374795, 2400822951 }, | ||||
| 	}); | ||||
| 	mappings.insert("WebSocketPeer", { | ||||
| 		{ "connect_to_url", 3097527179, 1966198364 }, | ||||
| 		{ "send", 3440492527, 2780360567 }, | ||||
| 	}); | ||||
| 	mappings.insert("Window", { | ||||
| 		{ "get_theme_icon", 2336455395, 3163973443 }, | ||||
| 		{ "get_theme_stylebox", 2759935355, 604739069 }, | ||||
| 		{ "get_theme_font", 387378635, 2826986490 }, | ||||
| 		{ "get_theme_font_size", 229578101, 1327056374 }, | ||||
| 		{ "get_theme_color", 2377051548, 2798751242 }, | ||||
| 		{ "get_theme_constant", 229578101, 1327056374 }, | ||||
| 		{ "has_theme_icon", 1187511791, 866386512 }, | ||||
| 		{ "has_theme_stylebox", 1187511791, 866386512 }, | ||||
| 		{ "has_theme_font", 1187511791, 866386512 }, | ||||
| 		{ "has_theme_font_size", 1187511791, 866386512 }, | ||||
| 		{ "has_theme_color", 1187511791, 866386512 }, | ||||
| 		{ "has_theme_constant", 1187511791, 866386512 }, | ||||
| 		{ "popup_exclusive", 1728044812, 2134721627 }, | ||||
| 		{ "popup_exclusive_centered", 2561668109, 3357594017 }, | ||||
| 		{ "popup_exclusive_centered_ratio", 4257659513, 2284776287 }, | ||||
| 		{ "popup_exclusive_centered_clamped", 224798062, 2612708785 }, | ||||
| 	}); | ||||
| 	mappings.insert("WorkerThreadPool", { | ||||
| 		{ "add_task", 3976347598, 3745067146 }, | ||||
| 		{ "add_group_task", 2377228549, 1801953219 }, | ||||
| 	}); | ||||
| 	mappings.insert("ZIPPacker", { | ||||
| 		{ "open", 3715508516, 1936816515 }, | ||||
| 	}); | ||||
| 	mappings.insert("ZIPReader", { | ||||
| 		{ "read_file", 156385007, 740857591 }, | ||||
| 		{ "file_exists", 1676256, 35364943 }, | ||||
| 	}); | ||||
| 	// clang-format on
 | ||||
| } | ||||
| 
 | ||||
| void GDExtensionCompatHashes::finalize() { | ||||
| 	mappings.clear(); | ||||
| } | ||||
| 
 | ||||
| #endif // DISABLE_DEPRECATED
 | ||||
							
								
								
									
										58
									
								
								engine/core/extension/gdextension_compat_hashes.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								engine/core/extension/gdextension_compat_hashes.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,58 @@ | |||
| /**************************************************************************/ | ||||
| /*  gdextension_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.                 */ | ||||
| /**************************************************************************/ | ||||
| 
 | ||||
| #ifndef GDEXTENSION_COMPAT_HASHES_H | ||||
| #define GDEXTENSION_COMPAT_HASHES_H | ||||
| 
 | ||||
| #ifndef DISABLE_DEPRECATED | ||||
| 
 | ||||
| #include "core/string/string_name.h" | ||||
| #include "core/templates/hash_map.h" | ||||
| #include "core/templates/local_vector.h" | ||||
| 
 | ||||
| class GDExtensionCompatHashes { | ||||
| 	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
 | ||||
| 
 | ||||
| #endif // GDEXTENSION_COMPAT_HASHES_H
 | ||||
							
								
								
									
										1715
									
								
								engine/core/extension/gdextension_interface.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1715
									
								
								engine/core/extension/gdextension_interface.cpp
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										2964
									
								
								engine/core/extension/gdextension_interface.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2964
									
								
								engine/core/extension/gdextension_interface.h
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										304
									
								
								engine/core/extension/gdextension_manager.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										304
									
								
								engine/core/extension/gdextension_manager.cpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,304 @@ | |||
| /**************************************************************************/ | ||||
| /*  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_compat_hashes.h" | ||||
| #include "core/io/file_access.h" | ||||
| #include "core/object/script_language.h" | ||||
| 
 | ||||
| GDExtensionManager::LoadStatus GDExtensionManager::_load_extension_internal(const Ref<GDExtension> &p_extension) { | ||||
| 	if (level >= 0) { // Already initialized up to some level.
 | ||||
| 		int32_t 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; | ||||
| 	} | ||||
| 
 | ||||
| 	return LOAD_STATUS_OK; | ||||
| } | ||||
| 
 | ||||
| GDExtensionManager::LoadStatus GDExtensionManager::_unload_extension_internal(const Ref<GDExtension> &p_extension) { | ||||
| 	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 (gdextension_map.has(p_path)) { | ||||
| 		return LOAD_STATUS_ALREADY_LOADED; | ||||
| 	} | ||||
| 	Ref<GDExtension> extension = ResourceLoader::load(p_path); | ||||
| 	if (extension.is_null()) { | ||||
| 		return LOAD_STATUS_FAILED; | ||||
| 	} | ||||
| 
 | ||||
| 	LoadStatus status = _load_extension_internal(extension); | ||||
| 	if (status != LOAD_STATUS_OK) { | ||||
| 		return status; | ||||
| 	} | ||||
| 
 | ||||
| 	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 (!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 = GDExtensionResourceLoader::load_gdextension_resource(p_path, extension); | ||||
| 	if (err != OK) { | ||||
| 		return LOAD_STATUS_FAILED; | ||||
| 	} | ||||
| 
 | ||||
| 	status = _load_extension_internal(extension); | ||||
| 	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 (!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) { | ||||
| 	ERR_FAIL_COND(int32_t(p_level) - 1 != level); | ||||
| 	for (KeyValue<String, Ref<GDExtension>> &E : gdextension_map) { | ||||
| 		E.value->initialize_library(p_level); | ||||
| 	} | ||||
| 	level = p_level; | ||||
| } | ||||
| 
 | ||||
| void GDExtensionManager::deinitialize_extensions(GDExtension::InitializationLevel p_level) { | ||||
| 	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() { | ||||
| 	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, "Error loading extension: " + s); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	OS::get_singleton()->load_platform_gdextensions(); | ||||
| } | ||||
| 
 | ||||
| void GDExtensionManager::reload_extensions() { | ||||
| #ifdef TOOLS_ENABLED | ||||
| 	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 | ||||
| } | ||||
| 
 | ||||
| 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")); | ||||
| } | ||||
| 
 | ||||
| GDExtensionManager *GDExtensionManager::singleton = nullptr; | ||||
| 
 | ||||
| GDExtensionManager::GDExtensionManager() { | ||||
| 	ERR_FAIL_COND(singleton != nullptr); | ||||
| 	singleton = this; | ||||
| 
 | ||||
| #ifndef DISABLE_DEPRECATED | ||||
| 	GDExtensionCompatHashes::initialize(); | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| GDExtensionManager::~GDExtensionManager() { | ||||
| 	if (singleton == this) { | ||||
| 		singleton = nullptr; | ||||
| 	} | ||||
| #ifndef DISABLE_DEPRECATED | ||||
| 	GDExtensionCompatHashes::finalize(); | ||||
| #endif | ||||
| } | ||||
							
								
								
									
										94
									
								
								engine/core/extension/gdextension_manager.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								engine/core/extension/gdextension_manager.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,94 @@ | |||
| /**************************************************************************/ | ||||
| /*  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.                 */ | ||||
| /**************************************************************************/ | ||||
| 
 | ||||
| #ifndef GDEXTENSION_MANAGER_H | ||||
| #define GDEXTENSION_MANAGER_H | ||||
| 
 | ||||
| #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); | ||||
| 	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 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(); | ||||
| 
 | ||||
| 	GDExtensionManager(); | ||||
| 	~GDExtensionManager(); | ||||
| }; | ||||
| 
 | ||||
| VARIANT_ENUM_CAST(GDExtensionManager::LoadStatus) | ||||
| 
 | ||||
| #endif // GDEXTENSION_MANAGER_H
 | ||||
							
								
								
									
										55
									
								
								engine/core/extension/make_interface_dumper.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								engine/core/extension/make_interface_dumper.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,55 @@ | |||
| import zlib | ||||
| 
 | ||||
| 
 | ||||
| def run(target, source, env): | ||||
|     src = str(source[0]) | ||||
|     dst = str(target[0]) | ||||
|     with open(src, "rb") as f, open(dst, "w", encoding="utf-8", newline="\n") as g: | ||||
|         buf = f.read() | ||||
|         decomp_size = len(buf) | ||||
| 
 | ||||
|         # Use maximum zlib compression level to further reduce file size | ||||
|         # (at the cost of initial build times). | ||||
|         buf = zlib.compress(buf, zlib.Z_BEST_COMPRESSION) | ||||
| 
 | ||||
|         g.write( | ||||
|             """/* THIS FILE IS GENERATED DO NOT EDIT */ | ||||
| #ifndef GDEXTENSION_INTERFACE_DUMP_H | ||||
| #define GDEXTENSION_INTERFACE_DUMP_H | ||||
| 
 | ||||
| #ifdef TOOLS_ENABLED | ||||
| 
 | ||||
| #include "core/io/compression.h" | ||||
| #include "core/io/file_access.h" | ||||
| #include "core/string/ustring.h" | ||||
| 
 | ||||
| """ | ||||
|         ) | ||||
| 
 | ||||
|         g.write("static const int _gdextension_interface_data_compressed_size = " + str(len(buf)) + ";\n") | ||||
|         g.write("static const int _gdextension_interface_data_uncompressed_size = " + str(decomp_size) + ";\n") | ||||
|         g.write("static const unsigned char _gdextension_interface_data_compressed[] = {\n") | ||||
|         for i in range(len(buf)): | ||||
|             g.write("\t" + str(buf[i]) + ",\n") | ||||
|         g.write("};\n") | ||||
| 
 | ||||
|         g.write( | ||||
|             """ | ||||
| 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 | ||||
| 
 | ||||
| #endif // GDEXTENSION_INTERFACE_DUMP_H | ||||
| """ | ||||
|         ) | ||||
							
								
								
									
										144
									
								
								engine/core/extension/make_wrappers.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										144
									
								
								engine/core/extension/make_wrappers.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,144 @@ | |||
| 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($RETTYPE_##m_name$ARG)\\ | ||||
| virtual $RETVAL m_name($FUNCARGS) $CONST override { \\ | ||||
|     $RETPRE\\ | ||||
|     GDVIRTUAL_REQUIRED_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 = """ | ||||
| #ifndef GDEXTENSION_WRAPPERS_GEN_H | ||||
| #define GDEXTENSION_WRAPPERS_GEN_H | ||||
| """ | ||||
| 
 | ||||
|     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) | ||||
| 
 | ||||
|     txt += "\n#endif\n" | ||||
| 
 | ||||
|     with open(str(target[0]), "w", encoding="utf-8", newline="\n") as f: | ||||
|         f.write(txt) | ||||
							
								
								
									
										20
									
								
								engine/core/input/SCsub
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								engine/core/input/SCsub
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,20 @@ | |||
| #!/usr/bin/env python | ||||
| 
 | ||||
| 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) | ||||
							
								
								
									
										39
									
								
								engine/core/input/default_controller_mappings.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								engine/core/input/default_controller_mappings.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,39 @@ | |||
| /**************************************************************************/ | ||||
| /*  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.                 */ | ||||
| /**************************************************************************/ | ||||
| 
 | ||||
| #ifndef DEFAULT_CONTROLLER_MAPPINGS_H | ||||
| #define DEFAULT_CONTROLLER_MAPPINGS_H | ||||
| 
 | ||||
| class DefaultControllerMappings { | ||||
| public: | ||||
| 	static const char *mappings[]; | ||||
| }; | ||||
| 
 | ||||
| #endif // DEFAULT_CONTROLLER_MAPPINGS_H
 | ||||
							
								
								
									
										2040
									
								
								engine/core/input/gamecontrollerdb.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2040
									
								
								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:b6,righttrigger:b7,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
 | ||||
							
								
								
									
										1692
									
								
								engine/core/input/input.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1692
									
								
								engine/core/input/input.cpp
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										386
									
								
								engine/core/input/input.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										386
									
								
								engine/core/input/input.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,386 @@ | |||
| /**************************************************************************/ | ||||
| /*  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.                 */ | ||||
| /**************************************************************************/ | ||||
| 
 | ||||
| #ifndef INPUT_H | ||||
| #define INPUT_H | ||||
| 
 | ||||
| #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: | ||||
| 	enum MouseMode { | ||||
| 		MOUSE_MODE_VISIBLE, | ||||
| 		MOUSE_MODE_HIDDEN, | ||||
| 		MOUSE_MODE_CAPTURED, | ||||
| 		MOUSE_MODE_CONFINED, | ||||
| 		MOUSE_MODE_CONFINED_HIDDEN, | ||||
| 	}; | ||||
| 
 | ||||
| #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; | ||||
| 
 | ||||
| 	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;
 | ||||
| 	Vector3 gravity; | ||||
| 	Vector3 accelerometer; | ||||
| 	Vector3 magnetometer; | ||||
| 	Vector3 gyroscope; | ||||
| 	Vector2 mouse_pos; | ||||
| 	int64_t mouse_window = 0; | ||||
| 	bool legacy_just_pressed_behavior = 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; | ||||
| 
 | ||||
| 	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; | ||||
| 
 | ||||
| 	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 (*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; | ||||
| 
 | ||||
| #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_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); | ||||
| 
 | ||||
| 	Input(); | ||||
| 	~Input(); | ||||
| }; | ||||
| 
 | ||||
| VARIANT_ENUM_CAST(Input::MouseMode); | ||||
| VARIANT_ENUM_CAST(Input::CursorShape); | ||||
| 
 | ||||
| #endif // INPUT_H
 | ||||
							
								
								
									
										59
									
								
								engine/core/input/input_builders.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								engine/core/input/input_builders.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,59 @@ | |||
| """Functions used to generate source files during build time""" | ||||
| 
 | ||||
| from collections import OrderedDict | ||||
| 
 | ||||
| 
 | ||||
| def make_default_controller_mappings(target, source, env): | ||||
|     dst = str(target[0]) | ||||
|     with open(dst, "w", encoding="utf-8", newline="\n") as g: | ||||
|         g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n") | ||||
|         g.write('#include "core/typedefs.h"\n') | ||||
|         g.write('#include "core/input/default_controller_mappings.h"\n') | ||||
| 
 | ||||
|         # ensure mappings have a consistent order | ||||
|         platform_mappings: dict = OrderedDict() | ||||
|         for src_path in source: | ||||
|             with open(str(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]: | ||||
|                         g.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": "#ifdef LINUXBSD_ENABLED", | ||||
|             "Windows": "#ifdef WINDOWS_ENABLED", | ||||
|             "Mac OS X": "#ifdef MACOS_ENABLED", | ||||
|             "Android": "#ifdef ANDROID_ENABLED", | ||||
|             "iOS": "#ifdef IOS_ENABLED", | ||||
|             "Web": "#ifdef WEB_ENABLED", | ||||
|         } | ||||
| 
 | ||||
|         g.write("const char* DefaultControllerMappings::mappings[] = {\n") | ||||
|         for platform, mappings in platform_mappings.items(): | ||||
|             variable = platform_variables[platform] | ||||
|             g.write("{}\n".format(variable)) | ||||
|             for mapping in mappings.values(): | ||||
|                 g.write('\t"{}",\n'.format(mapping)) | ||||
|             g.write("#endif\n") | ||||
| 
 | ||||
|         g.write("\tnullptr\n};\n") | ||||
							
								
								
									
										137
									
								
								engine/core/input/input_enums.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										137
									
								
								engine/core/input/input_enums.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,137 @@ | |||
| /**************************************************************************/ | ||||
| /*  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.                 */ | ||||
| /**************************************************************************/ | ||||
| 
 | ||||
| #ifndef INPUT_ENUMS_H | ||||
| #define INPUT_ENUMS_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) { | ||||
| 	return MouseButtonMask(1 << ((int)button - 1)); | ||||
| } | ||||
| 
 | ||||
| #endif // INPUT_ENUMS_H
 | ||||
							
								
								
									
										1920
									
								
								engine/core/input/input_event.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1920
									
								
								engine/core/input/input_event.cpp
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										599
									
								
								engine/core/input/input_event.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										599
									
								
								engine/core/input/input_event.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,599 @@ | |||
| /**************************************************************************/ | ||||
| /*  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.                 */ | ||||
| /**************************************************************************/ | ||||
| 
 | ||||
| #ifndef INPUT_EVENT_H | ||||
| #define INPUT_EVENT_H | ||||
| 
 | ||||
| #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; | ||||
| 
 | ||||
| 	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(); | ||||
| }; | ||||
| 
 | ||||
| #endif // INPUT_EVENT_H
 | ||||
							
								
								
									
										860
									
								
								engine/core/input/input_map.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										860
									
								
								engine/core/input/input_map.cpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,860 @@ | |||
| /**************************************************************************/ | ||||
| /*  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 "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(0.5f)); | ||||
| 	ClassDB::bind_method(D_METHOD("erase_action", "action"), &InputMap::erase_action); | ||||
| 
 | ||||
| 	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("/") + 1, pi.name.length()); | ||||
| 			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), "InputMap already has action \"" + 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_valid(), 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); | ||||
| } | ||||
| 
 | ||||
| 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; | ||||
| 	event_get_action_status(p_event, p_action, p_exact_match, nullptr, nullptr, nullptr, &index); | ||||
| 	return index; | ||||
| } | ||||
| 
 | ||||
| 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("/") + 1, pi.name.length()); | ||||
| 
 | ||||
| 		Dictionary action = GLOBAL_GET(pi.name); | ||||
| 		float deadzone = action.has("deadzone") ? (float)action["deadzone"] : 0.5f; | ||||
| 		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_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_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") }, | ||||
|     { "",                                              ""} | ||||
| 	/* clang-format on */ | ||||
| }; | ||||
| 
 | ||||
| String InputMap::get_builtin_display_name(const String &p_name) const { | ||||
| 	int len = sizeof(_builtin_action_display_names) / sizeof(_BuiltinActionDisplayName); | ||||
| 
 | ||||
| 	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); | ||||
| 
 | ||||
| 	// ///// 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::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(Key::ENTER)); | ||||
| 	inputs.push_back(InputEventKey::create_reference(Key::KP_ENTER)); | ||||
| 	default_builtin_cache.insert("ui_text_completion_accept", inputs); | ||||
| 
 | ||||
| 	inputs = List<Ref<InputEvent>>(); | ||||
| 	inputs.push_back(InputEventKey::create_reference(Key::TAB)); | ||||
| 	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); | ||||
| 
 | ||||
| 	// ///// 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); | ||||
| 
 | ||||
| 	// ///// 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); | ||||
| 
 | ||||
| 	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; | ||||
| } | ||||
							
								
								
									
										111
									
								
								engine/core/input/input_map.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								engine/core/input/input_map.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,111 @@ | |||
| /**************************************************************************/ | ||||
| /*  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.                 */ | ||||
| /**************************************************************************/ | ||||
| 
 | ||||
| #ifndef INPUT_MAP_H | ||||
| #define INPUT_MAP_H | ||||
| 
 | ||||
| #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; | ||||
| 	}; | ||||
| 
 | ||||
| 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(); | ||||
| 
 | ||||
| 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 = 0.5); | ||||
| 	void erase_action(const StringName &p_action); | ||||
| 
 | ||||
| 	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(); | ||||
| }; | ||||
| 
 | ||||
| #endif // INPUT_MAP_H
 | ||||
							
								
								
									
										132
									
								
								engine/core/input/shortcut.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										132
									
								
								engine/core/input/shortcut.cpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,132 @@ | |||
| /**************************************************************************/ | ||||
| /*  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" | ||||
| #include "core/os/keyboard.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; | ||||
| } | ||||
							
								
								
									
										59
									
								
								engine/core/input/shortcut.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								engine/core/input/shortcut.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,59 @@ | |||
| /**************************************************************************/ | ||||
| /*  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.                 */ | ||||
| /**************************************************************************/ | ||||
| 
 | ||||
| #ifndef SHORTCUT_H | ||||
| #define SHORTCUT_H | ||||
| 
 | ||||
| #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); | ||||
| }; | ||||
| 
 | ||||
| #endif // SHORTCUT_H
 | ||||
							
								
								
									
										5
									
								
								engine/core/io/SCsub
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								engine/core/io/SCsub
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,5 @@ | |||
| #!/usr/bin/env python | ||||
| 
 | ||||
| Import("env") | ||||
| 
 | ||||
| env.add_source_files(env.core_sources, "*.cpp") | ||||
							
								
								
									
										357
									
								
								engine/core/io/compression.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										357
									
								
								engine/core/io/compression.cpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,357 @@ | |||
| /**************************************************************************/ | ||||
| /*  compression.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 "compression.h" | ||||
| 
 | ||||
| #include "core/config/project_settings.h" | ||||
| #include "core/io/zip_io.h" | ||||
| 
 | ||||
| #include "thirdparty/misc/fastlz.h" | ||||
| 
 | ||||
| #include <zlib.h> | ||||
| #include <zstd.h> | ||||
| 
 | ||||
| #ifdef BROTLI_ENABLED | ||||
| #include <brotli/decode.h> | ||||
| #endif | ||||
| 
 | ||||
| int Compression::compress(uint8_t *p_dst, const uint8_t *p_src, int p_src_size, Mode p_mode) { | ||||
| 	switch (p_mode) { | ||||
| 		case MODE_BROTLI: { | ||||
| 			ERR_FAIL_V_MSG(-1, "Only brotli decompression is supported."); | ||||
| 		} break; | ||||
| 		case MODE_FASTLZ: { | ||||
| 			if (p_src_size < 16) { | ||||
| 				uint8_t src[16]; | ||||
| 				memset(&src[p_src_size], 0, 16 - p_src_size); | ||||
| 				memcpy(src, p_src, p_src_size); | ||||
| 				return fastlz_compress(src, 16, p_dst); | ||||
| 			} else { | ||||
| 				return fastlz_compress(p_src, p_src_size, p_dst); | ||||
| 			} | ||||
| 
 | ||||
| 		} break; | ||||
| 		case MODE_DEFLATE: | ||||
| 		case MODE_GZIP: { | ||||
| 			int window_bits = p_mode == MODE_DEFLATE ? 15 : 15 + 16; | ||||
| 
 | ||||
| 			z_stream strm; | ||||
| 			strm.zalloc = zipio_alloc; | ||||
| 			strm.zfree = zipio_free; | ||||
| 			strm.opaque = Z_NULL; | ||||
| 			int level = p_mode == MODE_DEFLATE ? zlib_level : gzip_level; | ||||
| 			int err = deflateInit2(&strm, level, Z_DEFLATED, window_bits, 8, Z_DEFAULT_STRATEGY); | ||||
| 			if (err != Z_OK) { | ||||
| 				return -1; | ||||
| 			} | ||||
| 
 | ||||
| 			strm.avail_in = p_src_size; | ||||
| 			int aout = deflateBound(&strm, p_src_size); | ||||
| 			strm.avail_out = aout; | ||||
| 			strm.next_in = (Bytef *)p_src; | ||||
| 			strm.next_out = p_dst; | ||||
| 			deflate(&strm, Z_FINISH); | ||||
| 			aout = aout - strm.avail_out; | ||||
| 			deflateEnd(&strm); | ||||
| 			return aout; | ||||
| 
 | ||||
| 		} break; | ||||
| 		case MODE_ZSTD: { | ||||
| 			ZSTD_CCtx *cctx = ZSTD_createCCtx(); | ||||
| 			ZSTD_CCtx_setParameter(cctx, ZSTD_c_compressionLevel, zstd_level); | ||||
| 			if (zstd_long_distance_matching) { | ||||
| 				ZSTD_CCtx_setParameter(cctx, ZSTD_c_enableLongDistanceMatching, 1); | ||||
| 				ZSTD_CCtx_setParameter(cctx, ZSTD_c_windowLog, zstd_window_log_size); | ||||
| 			} | ||||
| 			int max_dst_size = get_max_compressed_buffer_size(p_src_size, MODE_ZSTD); | ||||
| 			int ret = ZSTD_compressCCtx(cctx, p_dst, max_dst_size, p_src, p_src_size, zstd_level); | ||||
| 			ZSTD_freeCCtx(cctx); | ||||
| 			return ret; | ||||
| 		} break; | ||||
| 	} | ||||
| 
 | ||||
| 	ERR_FAIL_V(-1); | ||||
| } | ||||
| 
 | ||||
| int Compression::get_max_compressed_buffer_size(int p_src_size, Mode p_mode) { | ||||
| 	switch (p_mode) { | ||||
| 		case MODE_BROTLI: { | ||||
| 			ERR_FAIL_V_MSG(-1, "Only brotli decompression is supported."); | ||||
| 		} break; | ||||
| 		case MODE_FASTLZ: { | ||||
| 			int ss = p_src_size + p_src_size * 6 / 100; | ||||
| 			if (ss < 66) { | ||||
| 				ss = 66; | ||||
| 			} | ||||
| 			return ss; | ||||
| 
 | ||||
| 		} break; | ||||
| 		case MODE_DEFLATE: | ||||
| 		case MODE_GZIP: { | ||||
| 			int window_bits = p_mode == MODE_DEFLATE ? 15 : 15 + 16; | ||||
| 
 | ||||
| 			z_stream strm; | ||||
| 			strm.zalloc = zipio_alloc; | ||||
| 			strm.zfree = zipio_free; | ||||
| 			strm.opaque = Z_NULL; | ||||
| 			int err = deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, window_bits, 8, Z_DEFAULT_STRATEGY); | ||||
| 			if (err != Z_OK) { | ||||
| 				return -1; | ||||
| 			} | ||||
| 			int aout = deflateBound(&strm, p_src_size); | ||||
| 			deflateEnd(&strm); | ||||
| 			return aout; | ||||
| 		} break; | ||||
| 		case MODE_ZSTD: { | ||||
| 			return ZSTD_compressBound(p_src_size); | ||||
| 		} break; | ||||
| 	} | ||||
| 
 | ||||
| 	ERR_FAIL_V(-1); | ||||
| } | ||||
| 
 | ||||
| int Compression::decompress(uint8_t *p_dst, int p_dst_max_size, const uint8_t *p_src, int p_src_size, Mode p_mode) { | ||||
| 	switch (p_mode) { | ||||
| 		case MODE_BROTLI: { | ||||
| #ifdef BROTLI_ENABLED | ||||
| 			size_t ret_size = p_dst_max_size; | ||||
| 			BrotliDecoderResult res = BrotliDecoderDecompress(p_src_size, p_src, &ret_size, p_dst); | ||||
| 			ERR_FAIL_COND_V(res != BROTLI_DECODER_RESULT_SUCCESS, -1); | ||||
| 			return ret_size; | ||||
| #else | ||||
| 			ERR_FAIL_V_MSG(-1, "Godot was compiled without brotli support."); | ||||
| #endif | ||||
| 		} break; | ||||
| 		case MODE_FASTLZ: { | ||||
| 			int ret_size = 0; | ||||
| 
 | ||||
| 			if (p_dst_max_size < 16) { | ||||
| 				uint8_t dst[16]; | ||||
| 				fastlz_decompress(p_src, p_src_size, dst, 16); | ||||
| 				memcpy(p_dst, dst, p_dst_max_size); | ||||
| 				ret_size = p_dst_max_size; | ||||
| 			} else { | ||||
| 				ret_size = fastlz_decompress(p_src, p_src_size, p_dst, p_dst_max_size); | ||||
| 			} | ||||
| 			return ret_size; | ||||
| 		} break; | ||||
| 		case MODE_DEFLATE: | ||||
| 		case MODE_GZIP: { | ||||
| 			int window_bits = p_mode == MODE_DEFLATE ? 15 : 15 + 16; | ||||
| 
 | ||||
| 			z_stream strm; | ||||
| 			strm.zalloc = zipio_alloc; | ||||
| 			strm.zfree = zipio_free; | ||||
| 			strm.opaque = Z_NULL; | ||||
| 			strm.avail_in = 0; | ||||
| 			strm.next_in = Z_NULL; | ||||
| 			int err = inflateInit2(&strm, window_bits); | ||||
| 			ERR_FAIL_COND_V(err != Z_OK, -1); | ||||
| 
 | ||||
| 			strm.avail_in = p_src_size; | ||||
| 			strm.avail_out = p_dst_max_size; | ||||
| 			strm.next_in = (Bytef *)p_src; | ||||
| 			strm.next_out = p_dst; | ||||
| 
 | ||||
| 			err = inflate(&strm, Z_FINISH); | ||||
| 			int total = strm.total_out; | ||||
| 			inflateEnd(&strm); | ||||
| 			ERR_FAIL_COND_V(err != Z_STREAM_END, -1); | ||||
| 			return total; | ||||
| 		} break; | ||||
| 		case MODE_ZSTD: { | ||||
| 			ZSTD_DCtx *dctx = ZSTD_createDCtx(); | ||||
| 			if (zstd_long_distance_matching) { | ||||
| 				ZSTD_DCtx_setParameter(dctx, ZSTD_d_windowLogMax, zstd_window_log_size); | ||||
| 			} | ||||
| 			int ret = ZSTD_decompressDCtx(dctx, p_dst, p_dst_max_size, p_src, p_src_size); | ||||
| 			ZSTD_freeDCtx(dctx); | ||||
| 			return ret; | ||||
| 		} break; | ||||
| 	} | ||||
| 
 | ||||
| 	ERR_FAIL_V(-1); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
| 	This will handle both Gzip and Deflate streams. It will automatically allocate the output buffer into the provided p_dst_vect Vector. | ||||
| 	This is required for compressed data whose final uncompressed size is unknown, as is the case for HTTP response bodies. | ||||
| 	This is much slower however than using Compression::decompress because it may result in multiple full copies of the output buffer. | ||||
| */ | ||||
| int Compression::decompress_dynamic(Vector<uint8_t> *p_dst_vect, int p_max_dst_size, const uint8_t *p_src, int p_src_size, Mode p_mode) { | ||||
| 	uint8_t *dst = nullptr; | ||||
| 	int out_mark = 0; | ||||
| 
 | ||||
| 	ERR_FAIL_COND_V(p_src_size <= 0, Z_DATA_ERROR); | ||||
| 
 | ||||
| 	if (p_mode == MODE_BROTLI) { | ||||
| #ifdef BROTLI_ENABLED | ||||
| 		BrotliDecoderResult ret; | ||||
| 		BrotliDecoderState *state = BrotliDecoderCreateInstance(nullptr, nullptr, nullptr); | ||||
| 		ERR_FAIL_NULL_V(state, Z_DATA_ERROR); | ||||
| 
 | ||||
| 		// Setup the stream inputs.
 | ||||
| 		const uint8_t *next_in = p_src; | ||||
| 		size_t avail_in = p_src_size; | ||||
| 		uint8_t *next_out = nullptr; | ||||
| 		size_t avail_out = 0; | ||||
| 		size_t total_out = 0; | ||||
| 
 | ||||
| 		// Ensure the destination buffer is empty.
 | ||||
| 		p_dst_vect->clear(); | ||||
| 
 | ||||
| 		// Decompress until stream ends or end of file.
 | ||||
| 		do { | ||||
| 			// Add another chunk size to the output buffer.
 | ||||
| 			// This forces a copy of the whole buffer.
 | ||||
| 			p_dst_vect->resize(p_dst_vect->size() + gzip_chunk); | ||||
| 			// Get pointer to the actual output buffer.
 | ||||
| 			dst = p_dst_vect->ptrw(); | ||||
| 
 | ||||
| 			// Set the stream to the new output stream.
 | ||||
| 			// Since it was copied, we need to reset the stream to the new buffer.
 | ||||
| 			next_out = &(dst[out_mark]); | ||||
| 			avail_out += gzip_chunk; | ||||
| 
 | ||||
| 			ret = BrotliDecoderDecompressStream(state, &avail_in, &next_in, &avail_out, &next_out, &total_out); | ||||
| 			if (ret == BROTLI_DECODER_RESULT_ERROR) { | ||||
| 				WARN_PRINT(BrotliDecoderErrorString(BrotliDecoderGetErrorCode(state))); | ||||
| 				BrotliDecoderDestroyInstance(state); | ||||
| 				p_dst_vect->clear(); | ||||
| 				return Z_DATA_ERROR; | ||||
| 			} | ||||
| 
 | ||||
| 			out_mark += gzip_chunk - avail_out; | ||||
| 
 | ||||
| 			// Enforce max output size.
 | ||||
| 			if (p_max_dst_size > -1 && total_out > (uint64_t)p_max_dst_size) { | ||||
| 				BrotliDecoderDestroyInstance(state); | ||||
| 				p_dst_vect->clear(); | ||||
| 				return Z_BUF_ERROR; | ||||
| 			} | ||||
| 		} while (ret != BROTLI_DECODER_RESULT_SUCCESS); | ||||
| 
 | ||||
| 		// If all done successfully, resize the output if it's larger than the actual output.
 | ||||
| 		if ((unsigned long)p_dst_vect->size() > total_out) { | ||||
| 			p_dst_vect->resize(total_out); | ||||
| 		} | ||||
| 
 | ||||
| 		// Clean up and return.
 | ||||
| 		BrotliDecoderDestroyInstance(state); | ||||
| 		return Z_OK; | ||||
| #else | ||||
| 		ERR_FAIL_V_MSG(Z_ERRNO, "Godot was compiled without brotli support."); | ||||
| #endif | ||||
| 	} else { | ||||
| 		// This function only supports GZip and Deflate.
 | ||||
| 		ERR_FAIL_COND_V(p_mode != MODE_DEFLATE && p_mode != MODE_GZIP, Z_ERRNO); | ||||
| 
 | ||||
| 		int ret; | ||||
| 		z_stream strm; | ||||
| 		int window_bits = p_mode == MODE_DEFLATE ? 15 : 15 + 16; | ||||
| 
 | ||||
| 		// Initialize the stream.
 | ||||
| 		strm.zalloc = Z_NULL; | ||||
| 		strm.zfree = Z_NULL; | ||||
| 		strm.opaque = Z_NULL; | ||||
| 		strm.avail_in = 0; | ||||
| 		strm.next_in = Z_NULL; | ||||
| 
 | ||||
| 		int err = inflateInit2(&strm, window_bits); | ||||
| 		ERR_FAIL_COND_V(err != Z_OK, -1); | ||||
| 
 | ||||
| 		// Setup the stream inputs.
 | ||||
| 		strm.next_in = (Bytef *)p_src; | ||||
| 		strm.avail_in = p_src_size; | ||||
| 
 | ||||
| 		// Ensure the destination buffer is empty.
 | ||||
| 		p_dst_vect->clear(); | ||||
| 
 | ||||
| 		// Decompress until deflate stream ends or end of file.
 | ||||
| 		do { | ||||
| 			// Add another chunk size to the output buffer.
 | ||||
| 			// This forces a copy of the whole buffer.
 | ||||
| 			p_dst_vect->resize(p_dst_vect->size() + gzip_chunk); | ||||
| 			// Get pointer to the actual output buffer.
 | ||||
| 			dst = p_dst_vect->ptrw(); | ||||
| 
 | ||||
| 			// Set the stream to the new output stream.
 | ||||
| 			// Since it was copied, we need to reset the stream to the new buffer.
 | ||||
| 			strm.next_out = &(dst[out_mark]); | ||||
| 			strm.avail_out = gzip_chunk; | ||||
| 
 | ||||
| 			// Run inflate() on input until output buffer is full and needs to be resized or input runs out.
 | ||||
| 			do { | ||||
| 				ret = inflate(&strm, Z_SYNC_FLUSH); | ||||
| 
 | ||||
| 				switch (ret) { | ||||
| 					case Z_NEED_DICT: | ||||
| 						ret = Z_DATA_ERROR; | ||||
| 						[[fallthrough]]; | ||||
| 					case Z_DATA_ERROR: | ||||
| 					case Z_MEM_ERROR: | ||||
| 					case Z_STREAM_ERROR: | ||||
| 					case Z_BUF_ERROR: | ||||
| 						if (strm.msg) { | ||||
| 							WARN_PRINT(strm.msg); | ||||
| 						} | ||||
| 						(void)inflateEnd(&strm); | ||||
| 						p_dst_vect->clear(); | ||||
| 						return ret; | ||||
| 				} | ||||
| 			} while (strm.avail_out > 0 && strm.avail_in > 0); | ||||
| 
 | ||||
| 			out_mark += gzip_chunk; | ||||
| 
 | ||||
| 			// Enforce max output size.
 | ||||
| 			if (p_max_dst_size > -1 && strm.total_out > (uint64_t)p_max_dst_size) { | ||||
| 				(void)inflateEnd(&strm); | ||||
| 				p_dst_vect->clear(); | ||||
| 				return Z_BUF_ERROR; | ||||
| 			} | ||||
| 		} while (ret != Z_STREAM_END); | ||||
| 
 | ||||
| 		// If all done successfully, resize the output if it's larger than the actual output.
 | ||||
| 		if ((unsigned long)p_dst_vect->size() > strm.total_out) { | ||||
| 			p_dst_vect->resize(strm.total_out); | ||||
| 		} | ||||
| 
 | ||||
| 		// Clean up and return.
 | ||||
| 		(void)inflateEnd(&strm); | ||||
| 		return Z_OK; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| int Compression::zlib_level = Z_DEFAULT_COMPRESSION; | ||||
| int Compression::gzip_level = Z_DEFAULT_COMPRESSION; | ||||
| int Compression::zstd_level = 3; | ||||
| bool Compression::zstd_long_distance_matching = false; | ||||
| int Compression::zstd_window_log_size = 27; // ZSTD_WINDOWLOG_LIMIT_DEFAULT
 | ||||
| int Compression::gzip_chunk = 16384; | ||||
							
								
								
									
										60
									
								
								engine/core/io/compression.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								engine/core/io/compression.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,60 @@ | |||
| /**************************************************************************/ | ||||
| /*  compression.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.                 */ | ||||
| /**************************************************************************/ | ||||
| 
 | ||||
| #ifndef COMPRESSION_H | ||||
| #define COMPRESSION_H | ||||
| 
 | ||||
| #include "core/templates/vector.h" | ||||
| #include "core/typedefs.h" | ||||
| 
 | ||||
| class Compression { | ||||
| public: | ||||
| 	static int zlib_level; | ||||
| 	static int gzip_level; | ||||
| 	static int zstd_level; | ||||
| 	static bool zstd_long_distance_matching; | ||||
| 	static int zstd_window_log_size; | ||||
| 	static int gzip_chunk; | ||||
| 
 | ||||
| 	enum Mode { | ||||
| 		MODE_FASTLZ, | ||||
| 		MODE_DEFLATE, | ||||
| 		MODE_ZSTD, | ||||
| 		MODE_GZIP, | ||||
| 		MODE_BROTLI | ||||
| 	}; | ||||
| 
 | ||||
| 	static int compress(uint8_t *p_dst, const uint8_t *p_src, int p_src_size, Mode p_mode = MODE_ZSTD); | ||||
| 	static int get_max_compressed_buffer_size(int p_src_size, Mode p_mode = MODE_ZSTD); | ||||
| 	static int decompress(uint8_t *p_dst, int p_dst_max_size, const uint8_t *p_src, int p_src_size, Mode p_mode = MODE_ZSTD); | ||||
| 	static int decompress_dynamic(Vector<uint8_t> *p_dst_vect, int p_max_dst_size, const uint8_t *p_src, int p_src_size, Mode p_mode); | ||||
| }; | ||||
| 
 | ||||
| #endif // COMPRESSION_H
 | ||||
							
								
								
									
										350
									
								
								engine/core/io/config_file.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										350
									
								
								engine/core/io/config_file.cpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,350 @@ | |||
| /**************************************************************************/ | ||||
| /*  config_file.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 "config_file.h" | ||||
| 
 | ||||
| #include "core/io/file_access_encrypted.h" | ||||
| #include "core/os/keyboard.h" | ||||
| #include "core/string/string_builder.h" | ||||
| #include "core/variant/variant_parser.h" | ||||
| 
 | ||||
| PackedStringArray ConfigFile::_get_sections() const { | ||||
| 	List<String> s; | ||||
| 	get_sections(&s); | ||||
| 	PackedStringArray arr; | ||||
| 	arr.resize(s.size()); | ||||
| 	int idx = 0; | ||||
| 	for (const String &E : s) { | ||||
| 		arr.set(idx++, E); | ||||
| 	} | ||||
| 
 | ||||
| 	return arr; | ||||
| } | ||||
| 
 | ||||
| PackedStringArray ConfigFile::_get_section_keys(const String &p_section) const { | ||||
| 	List<String> s; | ||||
| 	get_section_keys(p_section, &s); | ||||
| 	PackedStringArray arr; | ||||
| 	arr.resize(s.size()); | ||||
| 	int idx = 0; | ||||
| 	for (const String &E : s) { | ||||
| 		arr.set(idx++, E); | ||||
| 	} | ||||
| 
 | ||||
| 	return arr; | ||||
| } | ||||
| 
 | ||||
| void ConfigFile::set_value(const String &p_section, const String &p_key, const Variant &p_value) { | ||||
| 	if (p_value.get_type() == Variant::NIL) { // Erase key.
 | ||||
| 		if (!values.has(p_section)) { | ||||
| 			return; | ||||
| 		} | ||||
| 
 | ||||
| 		values[p_section].erase(p_key); | ||||
| 		if (values[p_section].is_empty()) { | ||||
| 			values.erase(p_section); | ||||
| 		} | ||||
| 	} else { | ||||
| 		if (!values.has(p_section)) { | ||||
| 			// Insert section-less keys at the beginning.
 | ||||
| 			values.insert(p_section, HashMap<String, Variant>(), p_section.is_empty()); | ||||
| 		} | ||||
| 
 | ||||
| 		values[p_section][p_key] = p_value; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| Variant ConfigFile::get_value(const String &p_section, const String &p_key, const Variant &p_default) const { | ||||
| 	if (!values.has(p_section) || !values[p_section].has(p_key)) { | ||||
| 		ERR_FAIL_COND_V_MSG(p_default.get_type() == Variant::NIL, Variant(), | ||||
| 				vformat("Couldn't find the given section \"%s\" and key \"%s\", and no default was given.", p_section, p_key)); | ||||
| 		return p_default; | ||||
| 	} | ||||
| 
 | ||||
| 	return values[p_section][p_key]; | ||||
| } | ||||
| 
 | ||||
| bool ConfigFile::has_section(const String &p_section) const { | ||||
| 	return values.has(p_section); | ||||
| } | ||||
| 
 | ||||
| bool ConfigFile::has_section_key(const String &p_section, const String &p_key) const { | ||||
| 	if (!values.has(p_section)) { | ||||
| 		return false; | ||||
| 	} | ||||
| 	return values[p_section].has(p_key); | ||||
| } | ||||
| 
 | ||||
| void ConfigFile::get_sections(List<String> *r_sections) const { | ||||
| 	for (const KeyValue<String, HashMap<String, Variant>> &E : values) { | ||||
| 		r_sections->push_back(E.key); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void ConfigFile::get_section_keys(const String &p_section, List<String> *r_keys) const { | ||||
| 	ERR_FAIL_COND_MSG(!values.has(p_section), vformat("Cannot get keys from nonexistent section \"%s\".", p_section)); | ||||
| 
 | ||||
| 	for (const KeyValue<String, Variant> &E : values[p_section]) { | ||||
| 		r_keys->push_back(E.key); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void ConfigFile::erase_section(const String &p_section) { | ||||
| 	ERR_FAIL_COND_MSG(!values.has(p_section), vformat("Cannot erase nonexistent section \"%s\".", p_section)); | ||||
| 	values.erase(p_section); | ||||
| } | ||||
| 
 | ||||
| void ConfigFile::erase_section_key(const String &p_section, const String &p_key) { | ||||
| 	ERR_FAIL_COND_MSG(!values.has(p_section), vformat("Cannot erase key \"%s\" from nonexistent section \"%s\".", p_key, p_section)); | ||||
| 	ERR_FAIL_COND_MSG(!values[p_section].has(p_key), vformat("Cannot erase nonexistent key \"%s\" from section \"%s\".", p_key, p_section)); | ||||
| 
 | ||||
| 	values[p_section].erase(p_key); | ||||
| 	if (values[p_section].is_empty()) { | ||||
| 		values.erase(p_section); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| String ConfigFile::encode_to_text() const { | ||||
| 	StringBuilder sb; | ||||
| 	bool first = true; | ||||
| 	for (const KeyValue<String, HashMap<String, Variant>> &E : values) { | ||||
| 		if (first) { | ||||
| 			first = false; | ||||
| 		} else { | ||||
| 			sb.append("\n"); | ||||
| 		} | ||||
| 		if (!E.key.is_empty()) { | ||||
| 			sb.append("[" + E.key + "]\n\n"); | ||||
| 		} | ||||
| 
 | ||||
| 		for (const KeyValue<String, Variant> &F : E.value) { | ||||
| 			String vstr; | ||||
| 			VariantWriter::write_to_string(F.value, vstr); | ||||
| 			sb.append(F.key.property_name_encode() + "=" + vstr + "\n"); | ||||
| 		} | ||||
| 	} | ||||
| 	return sb.as_string(); | ||||
| } | ||||
| 
 | ||||
| Error ConfigFile::save(const String &p_path) { | ||||
| 	Error err; | ||||
| 	Ref<FileAccess> file = FileAccess::open(p_path, FileAccess::WRITE, &err); | ||||
| 
 | ||||
| 	if (err) { | ||||
| 		return err; | ||||
| 	} | ||||
| 
 | ||||
| 	return _internal_save(file); | ||||
| } | ||||
| 
 | ||||
| Error ConfigFile::save_encrypted(const String &p_path, const Vector<uint8_t> &p_key) { | ||||
| 	Error err; | ||||
| 	Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::WRITE, &err); | ||||
| 
 | ||||
| 	if (err) { | ||||
| 		return err; | ||||
| 	} | ||||
| 
 | ||||
| 	Ref<FileAccessEncrypted> fae; | ||||
| 	fae.instantiate(); | ||||
| 	err = fae->open_and_parse(f, p_key, FileAccessEncrypted::MODE_WRITE_AES256); | ||||
| 	if (err) { | ||||
| 		return err; | ||||
| 	} | ||||
| 	return _internal_save(fae); | ||||
| } | ||||
| 
 | ||||
| Error ConfigFile::save_encrypted_pass(const String &p_path, const String &p_pass) { | ||||
| 	Error err; | ||||
| 	Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::WRITE, &err); | ||||
| 
 | ||||
| 	if (err) { | ||||
| 		return err; | ||||
| 	} | ||||
| 
 | ||||
| 	Ref<FileAccessEncrypted> fae; | ||||
| 	fae.instantiate(); | ||||
| 	err = fae->open_and_parse_password(f, p_pass, FileAccessEncrypted::MODE_WRITE_AES256); | ||||
| 	if (err) { | ||||
| 		return err; | ||||
| 	} | ||||
| 
 | ||||
| 	return _internal_save(fae); | ||||
| } | ||||
| 
 | ||||
| Error ConfigFile::_internal_save(Ref<FileAccess> file) { | ||||
| 	bool first = true; | ||||
| 	for (const KeyValue<String, HashMap<String, Variant>> &E : values) { | ||||
| 		if (first) { | ||||
| 			first = false; | ||||
| 		} else { | ||||
| 			file->store_string("\n"); | ||||
| 		} | ||||
| 		if (!E.key.is_empty()) { | ||||
| 			file->store_string("[" + E.key.replace("]", "\\]") + "]\n\n"); | ||||
| 		} | ||||
| 
 | ||||
| 		for (const KeyValue<String, Variant> &F : E.value) { | ||||
| 			String vstr; | ||||
| 			VariantWriter::write_to_string(F.value, vstr); | ||||
| 			file->store_string(F.key.property_name_encode() + "=" + vstr + "\n"); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return OK; | ||||
| } | ||||
| 
 | ||||
| Error ConfigFile::load(const String &p_path) { | ||||
| 	Error err; | ||||
| 	Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ, &err); | ||||
| 
 | ||||
| 	if (f.is_null()) { | ||||
| 		return err; | ||||
| 	} | ||||
| 
 | ||||
| 	return _internal_load(p_path, f); | ||||
| } | ||||
| 
 | ||||
| Error ConfigFile::load_encrypted(const String &p_path, const Vector<uint8_t> &p_key) { | ||||
| 	Error err; | ||||
| 	Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ, &err); | ||||
| 
 | ||||
| 	if (err) { | ||||
| 		return err; | ||||
| 	} | ||||
| 
 | ||||
| 	Ref<FileAccessEncrypted> fae; | ||||
| 	fae.instantiate(); | ||||
| 	err = fae->open_and_parse(f, p_key, FileAccessEncrypted::MODE_READ); | ||||
| 	if (err) { | ||||
| 		return err; | ||||
| 	} | ||||
| 	return _internal_load(p_path, fae); | ||||
| } | ||||
| 
 | ||||
| Error ConfigFile::load_encrypted_pass(const String &p_path, const String &p_pass) { | ||||
| 	Error err; | ||||
| 	Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ, &err); | ||||
| 
 | ||||
| 	if (err) { | ||||
| 		return err; | ||||
| 	} | ||||
| 
 | ||||
| 	Ref<FileAccessEncrypted> fae; | ||||
| 	fae.instantiate(); | ||||
| 	err = fae->open_and_parse_password(f, p_pass, FileAccessEncrypted::MODE_READ); | ||||
| 	if (err) { | ||||
| 		return err; | ||||
| 	} | ||||
| 
 | ||||
| 	return _internal_load(p_path, fae); | ||||
| } | ||||
| 
 | ||||
| Error ConfigFile::_internal_load(const String &p_path, Ref<FileAccess> f) { | ||||
| 	VariantParser::StreamFile stream; | ||||
| 	stream.f = f; | ||||
| 
 | ||||
| 	Error err = _parse(p_path, &stream); | ||||
| 
 | ||||
| 	return err; | ||||
| } | ||||
| 
 | ||||
| Error ConfigFile::parse(const String &p_data) { | ||||
| 	VariantParser::StreamString stream; | ||||
| 	stream.s = p_data; | ||||
| 	return _parse("<string>", &stream); | ||||
| } | ||||
| 
 | ||||
| Error ConfigFile::_parse(const String &p_path, VariantParser::Stream *p_stream) { | ||||
| 	String assign; | ||||
| 	Variant value; | ||||
| 	VariantParser::Tag next_tag; | ||||
| 
 | ||||
| 	int lines = 0; | ||||
| 	String error_text; | ||||
| 
 | ||||
| 	String section; | ||||
| 
 | ||||
| 	while (true) { | ||||
| 		assign = Variant(); | ||||
| 		next_tag.fields.clear(); | ||||
| 		next_tag.name = String(); | ||||
| 
 | ||||
| 		Error err = VariantParser::parse_tag_assign_eof(p_stream, lines, error_text, next_tag, assign, value, nullptr, true); | ||||
| 		if (err == ERR_FILE_EOF) { | ||||
| 			return OK; | ||||
| 		} else if (err != OK) { | ||||
| 			ERR_PRINT(vformat("ConfigFile parse error at %s:%d: %s.", p_path, lines, error_text)); | ||||
| 			return err; | ||||
| 		} | ||||
| 
 | ||||
| 		if (!assign.is_empty()) { | ||||
| 			set_value(section, assign, value); | ||||
| 		} else if (!next_tag.name.is_empty()) { | ||||
| 			section = next_tag.name.replace("\\]", "]"); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return OK; | ||||
| } | ||||
| 
 | ||||
| void ConfigFile::clear() { | ||||
| 	values.clear(); | ||||
| } | ||||
| 
 | ||||
| void ConfigFile::_bind_methods() { | ||||
| 	ClassDB::bind_method(D_METHOD("set_value", "section", "key", "value"), &ConfigFile::set_value); | ||||
| 	ClassDB::bind_method(D_METHOD("get_value", "section", "key", "default"), &ConfigFile::get_value, DEFVAL(Variant())); | ||||
| 
 | ||||
| 	ClassDB::bind_method(D_METHOD("has_section", "section"), &ConfigFile::has_section); | ||||
| 	ClassDB::bind_method(D_METHOD("has_section_key", "section", "key"), &ConfigFile::has_section_key); | ||||
| 
 | ||||
| 	ClassDB::bind_method(D_METHOD("get_sections"), &ConfigFile::_get_sections); | ||||
| 	ClassDB::bind_method(D_METHOD("get_section_keys", "section"), &ConfigFile::_get_section_keys); | ||||
| 
 | ||||
| 	ClassDB::bind_method(D_METHOD("erase_section", "section"), &ConfigFile::erase_section); | ||||
| 	ClassDB::bind_method(D_METHOD("erase_section_key", "section", "key"), &ConfigFile::erase_section_key); | ||||
| 
 | ||||
| 	ClassDB::bind_method(D_METHOD("load", "path"), &ConfigFile::load); | ||||
| 	ClassDB::bind_method(D_METHOD("parse", "data"), &ConfigFile::parse); | ||||
| 	ClassDB::bind_method(D_METHOD("save", "path"), &ConfigFile::save); | ||||
| 
 | ||||
| 	ClassDB::bind_method(D_METHOD("encode_to_text"), &ConfigFile::encode_to_text); | ||||
| 
 | ||||
| 	BIND_METHOD_ERR_RETURN_DOC("load", ERR_FILE_CANT_OPEN); | ||||
| 
 | ||||
| 	ClassDB::bind_method(D_METHOD("load_encrypted", "path", "key"), &ConfigFile::load_encrypted); | ||||
| 	ClassDB::bind_method(D_METHOD("load_encrypted_pass", "path", "password"), &ConfigFile::load_encrypted_pass); | ||||
| 
 | ||||
| 	ClassDB::bind_method(D_METHOD("save_encrypted", "path", "key"), &ConfigFile::save_encrypted); | ||||
| 	ClassDB::bind_method(D_METHOD("save_encrypted_pass", "path", "password"), &ConfigFile::save_encrypted_pass); | ||||
| 
 | ||||
| 	ClassDB::bind_method(D_METHOD("clear"), &ConfigFile::clear); | ||||
| } | ||||
							
								
								
									
										82
									
								
								engine/core/io/config_file.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								engine/core/io/config_file.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,82 @@ | |||
| /**************************************************************************/ | ||||
| /*  config_file.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.                 */ | ||||
| /**************************************************************************/ | ||||
| 
 | ||||
| #ifndef CONFIG_FILE_H | ||||
| #define CONFIG_FILE_H | ||||
| 
 | ||||
| #include "core/io/file_access.h" | ||||
| #include "core/object/ref_counted.h" | ||||
| #include "core/templates/hash_map.h" | ||||
| #include "core/variant/variant_parser.h" | ||||
| 
 | ||||
| class ConfigFile : public RefCounted { | ||||
| 	GDCLASS(ConfigFile, RefCounted); | ||||
| 
 | ||||
| 	HashMap<String, HashMap<String, Variant>> values; | ||||
| 
 | ||||
| 	PackedStringArray _get_sections() const; | ||||
| 	PackedStringArray _get_section_keys(const String &p_section) const; | ||||
| 	Error _internal_load(const String &p_path, Ref<FileAccess> f); | ||||
| 	Error _internal_save(Ref<FileAccess> file); | ||||
| 
 | ||||
| 	Error _parse(const String &p_path, VariantParser::Stream *p_stream); | ||||
| 
 | ||||
| protected: | ||||
| 	static void _bind_methods(); | ||||
| 
 | ||||
| public: | ||||
| 	void set_value(const String &p_section, const String &p_key, const Variant &p_value); | ||||
| 	Variant get_value(const String &p_section, const String &p_key, const Variant &p_default = Variant()) const; | ||||
| 
 | ||||
| 	bool has_section(const String &p_section) const; | ||||
| 	bool has_section_key(const String &p_section, const String &p_key) const; | ||||
| 
 | ||||
| 	void get_sections(List<String> *r_sections) const; | ||||
| 	void get_section_keys(const String &p_section, List<String> *r_keys) const; | ||||
| 
 | ||||
| 	void erase_section(const String &p_section); | ||||
| 	void erase_section_key(const String &p_section, const String &p_key); | ||||
| 
 | ||||
| 	Error save(const String &p_path); | ||||
| 	Error load(const String &p_path); | ||||
| 	Error parse(const String &p_data); | ||||
| 
 | ||||
| 	String encode_to_text() const; // used by exporter
 | ||||
| 
 | ||||
| 	void clear(); | ||||
| 
 | ||||
| 	Error load_encrypted(const String &p_path, const Vector<uint8_t> &p_key); | ||||
| 	Error load_encrypted_pass(const String &p_path, const String &p_pass); | ||||
| 
 | ||||
| 	Error save_encrypted(const String &p_path, const Vector<uint8_t> &p_key); | ||||
| 	Error save_encrypted_pass(const String &p_path, const String &p_pass); | ||||
| }; | ||||
| 
 | ||||
| #endif // CONFIG_FILE_H
 | ||||
							
								
								
									
										600
									
								
								engine/core/io/dir_access.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										600
									
								
								engine/core/io/dir_access.cpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,600 @@ | |||
| /**************************************************************************/ | ||||
| /*  dir_access.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 "dir_access.h" | ||||
| 
 | ||||
| #include "core/config/project_settings.h" | ||||
| #include "core/io/file_access.h" | ||||
| #include "core/os/memory.h" | ||||
| #include "core/os/os.h" | ||||
| #include "core/templates/local_vector.h" | ||||
| 
 | ||||
| thread_local Error DirAccess::last_dir_open_error = OK; | ||||
| 
 | ||||
| String DirAccess::_get_root_path() const { | ||||
| 	switch (_access_type) { | ||||
| 		case ACCESS_RESOURCES: | ||||
| 			return ProjectSettings::get_singleton()->get_resource_path(); | ||||
| 		case ACCESS_USERDATA: | ||||
| 			return OS::get_singleton()->get_user_data_dir(); | ||||
| 		default: | ||||
| 			return ""; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| String DirAccess::_get_root_string() const { | ||||
| 	switch (_access_type) { | ||||
| 		case ACCESS_RESOURCES: | ||||
| 			return "res://"; | ||||
| 		case ACCESS_USERDATA: | ||||
| 			return "user://"; | ||||
| 		default: | ||||
| 			return ""; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| int DirAccess::get_current_drive() { | ||||
| 	String path = get_current_dir().to_lower(); | ||||
| 	for (int i = 0; i < get_drive_count(); i++) { | ||||
| 		String d = get_drive(i).to_lower(); | ||||
| 		if (path.begins_with(d)) { | ||||
| 			return i; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| bool DirAccess::drives_are_shortcuts() { | ||||
| 	return false; | ||||
| } | ||||
| 
 | ||||
| static Error _erase_recursive(DirAccess *da) { | ||||
| 	List<String> dirs; | ||||
| 	List<String> files; | ||||
| 
 | ||||
| 	da->list_dir_begin(); | ||||
| 	String n = da->get_next(); | ||||
| 	while (!n.is_empty()) { | ||||
| 		if (n != "." && n != "..") { | ||||
| 			if (da->current_is_dir() && !da->is_link(n)) { | ||||
| 				dirs.push_back(n); | ||||
| 			} else { | ||||
| 				files.push_back(n); | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		n = da->get_next(); | ||||
| 	} | ||||
| 
 | ||||
| 	da->list_dir_end(); | ||||
| 
 | ||||
| 	for (const String &E : dirs) { | ||||
| 		Error err = da->change_dir(E); | ||||
| 		if (err == OK) { | ||||
| 			err = _erase_recursive(da); | ||||
| 			if (err) { | ||||
| 				da->change_dir(".."); | ||||
| 				return err; | ||||
| 			} | ||||
| 			err = da->change_dir(".."); | ||||
| 			if (err) { | ||||
| 				return err; | ||||
| 			} | ||||
| 			err = da->remove(da->get_current_dir().path_join(E)); | ||||
| 			if (err) { | ||||
| 				return err; | ||||
| 			} | ||||
| 		} else { | ||||
| 			return err; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	for (const String &E : files) { | ||||
| 		Error err = da->remove(da->get_current_dir().path_join(E)); | ||||
| 		if (err) { | ||||
| 			return err; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return OK; | ||||
| } | ||||
| 
 | ||||
| Error DirAccess::erase_contents_recursive() { | ||||
| 	return _erase_recursive(this); | ||||
| } | ||||
| 
 | ||||
| Error DirAccess::make_dir_recursive(const String &p_dir) { | ||||
| 	if (p_dir.length() < 1) { | ||||
| 		return OK; | ||||
| 	} | ||||
| 
 | ||||
| 	String full_dir; | ||||
| 
 | ||||
| 	if (p_dir.is_relative_path()) { | ||||
| 		//append current
 | ||||
| 		full_dir = get_current_dir().path_join(p_dir); | ||||
| 
 | ||||
| 	} else { | ||||
| 		full_dir = p_dir; | ||||
| 	} | ||||
| 
 | ||||
| 	full_dir = full_dir.replace("\\", "/"); | ||||
| 
 | ||||
| 	String base; | ||||
| 
 | ||||
| 	if (full_dir.begins_with("res://")) { | ||||
| 		base = "res://"; | ||||
| 	} else if (full_dir.begins_with("user://")) { | ||||
| 		base = "user://"; | ||||
| 	} else if (full_dir.is_network_share_path()) { | ||||
| 		int pos = full_dir.find("/", 2); | ||||
| 		ERR_FAIL_COND_V(pos < 0, ERR_INVALID_PARAMETER); | ||||
| 		pos = full_dir.find("/", pos + 1); | ||||
| 		ERR_FAIL_COND_V(pos < 0, ERR_INVALID_PARAMETER); | ||||
| 		base = full_dir.substr(0, pos + 1); | ||||
| 	} else if (full_dir.begins_with("/")) { | ||||
| 		base = "/"; | ||||
| 	} else if (full_dir.contains(":/")) { | ||||
| 		base = full_dir.substr(0, full_dir.find(":/") + 2); | ||||
| 	} else { | ||||
| 		ERR_FAIL_V(ERR_INVALID_PARAMETER); | ||||
| 	} | ||||
| 
 | ||||
| 	full_dir = full_dir.replace_first(base, "").simplify_path(); | ||||
| 
 | ||||
| 	Vector<String> subdirs = full_dir.split("/"); | ||||
| 
 | ||||
| 	String curpath = base; | ||||
| 	for (int i = 0; i < subdirs.size(); i++) { | ||||
| 		curpath = curpath.path_join(subdirs[i]); | ||||
| 		Error err = make_dir(curpath); | ||||
| 		if (err != OK && err != ERR_ALREADY_EXISTS) { | ||||
| 			ERR_FAIL_V_MSG(err, "Could not create directory: " + curpath); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return OK; | ||||
| } | ||||
| 
 | ||||
| DirAccess::AccessType DirAccess::get_access_type() const { | ||||
| 	return _access_type; | ||||
| } | ||||
| 
 | ||||
| String DirAccess::fix_path(const String &p_path) const { | ||||
| 	switch (_access_type) { | ||||
| 		case ACCESS_RESOURCES: { | ||||
| 			if (ProjectSettings::get_singleton()) { | ||||
| 				if (p_path.begins_with("res://")) { | ||||
| 					String resource_path = ProjectSettings::get_singleton()->get_resource_path(); | ||||
| 					if (!resource_path.is_empty()) { | ||||
| 						return p_path.replace_first("res:/", resource_path); | ||||
| 					} | ||||
| 					return p_path.replace_first("res://", ""); | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 		} break; | ||||
| 		case ACCESS_USERDATA: { | ||||
| 			if (p_path.begins_with("user://")) { | ||||
| 				String data_dir = OS::get_singleton()->get_user_data_dir(); | ||||
| 				if (!data_dir.is_empty()) { | ||||
| 					return p_path.replace_first("user:/", data_dir); | ||||
| 				} | ||||
| 				return p_path.replace_first("user://", ""); | ||||
| 			} | ||||
| 
 | ||||
| 		} break; | ||||
| 		case ACCESS_FILESYSTEM: { | ||||
| 			return p_path; | ||||
| 		} break; | ||||
| 		case ACCESS_MAX: | ||||
| 			break; // Can't happen, but silences warning
 | ||||
| 	} | ||||
| 
 | ||||
| 	return p_path; | ||||
| } | ||||
| 
 | ||||
| DirAccess::CreateFunc DirAccess::create_func[ACCESS_MAX] = { nullptr, nullptr, nullptr }; | ||||
| 
 | ||||
| Ref<DirAccess> DirAccess::create_for_path(const String &p_path) { | ||||
| 	Ref<DirAccess> da; | ||||
| 	if (p_path.begins_with("res://")) { | ||||
| 		da = create(ACCESS_RESOURCES); | ||||
| 	} else if (p_path.begins_with("user://")) { | ||||
| 		da = create(ACCESS_USERDATA); | ||||
| 	} else { | ||||
| 		da = create(ACCESS_FILESYSTEM); | ||||
| 	} | ||||
| 
 | ||||
| 	return da; | ||||
| } | ||||
| 
 | ||||
| Ref<DirAccess> DirAccess::open(const String &p_path, Error *r_error) { | ||||
| 	Ref<DirAccess> da = create_for_path(p_path); | ||||
| 	ERR_FAIL_COND_V_MSG(da.is_null(), nullptr, "Cannot create DirAccess for path '" + p_path + "'."); | ||||
| 	Error err = da->change_dir(p_path); | ||||
| 	if (r_error) { | ||||
| 		*r_error = err; | ||||
| 	} | ||||
| 	if (err != OK) { | ||||
| 		return nullptr; | ||||
| 	} | ||||
| 
 | ||||
| 	return da; | ||||
| } | ||||
| 
 | ||||
| Ref<DirAccess> DirAccess::_open(const String &p_path) { | ||||
| 	Error err = OK; | ||||
| 	Ref<DirAccess> da = open(p_path, &err); | ||||
| 	last_dir_open_error = err; | ||||
| 	if (err) { | ||||
| 		return Ref<DirAccess>(); | ||||
| 	} | ||||
| 	return da; | ||||
| } | ||||
| 
 | ||||
| int DirAccess::_get_drive_count() { | ||||
| 	Ref<DirAccess> d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); | ||||
| 	return d->get_drive_count(); | ||||
| } | ||||
| 
 | ||||
| String DirAccess::get_drive_name(int p_idx) { | ||||
| 	Ref<DirAccess> d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); | ||||
| 	return d->get_drive(p_idx); | ||||
| } | ||||
| 
 | ||||
| Error DirAccess::make_dir_absolute(const String &p_dir) { | ||||
| 	Ref<DirAccess> d = DirAccess::create_for_path(p_dir); | ||||
| 	return d->make_dir(p_dir); | ||||
| } | ||||
| 
 | ||||
| Error DirAccess::make_dir_recursive_absolute(const String &p_dir) { | ||||
| 	Ref<DirAccess> d = DirAccess::create_for_path(p_dir); | ||||
| 	return d->make_dir_recursive(p_dir); | ||||
| } | ||||
| 
 | ||||
| bool DirAccess::dir_exists_absolute(const String &p_dir) { | ||||
| 	Ref<DirAccess> d = DirAccess::create_for_path(p_dir); | ||||
| 	return d->dir_exists(p_dir); | ||||
| } | ||||
| 
 | ||||
| Error DirAccess::copy_absolute(const String &p_from, const String &p_to, int p_chmod_flags) { | ||||
| 	Ref<DirAccess> d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); | ||||
| 	// Support copying from res:// to user:// etc.
 | ||||
| 	String from = ProjectSettings::get_singleton()->globalize_path(p_from); | ||||
| 	String to = ProjectSettings::get_singleton()->globalize_path(p_to); | ||||
| 	return d->copy(from, to, p_chmod_flags); | ||||
| } | ||||
| 
 | ||||
| Error DirAccess::rename_absolute(const String &p_from, const String &p_to) { | ||||
| 	Ref<DirAccess> d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); | ||||
| 	String from = ProjectSettings::get_singleton()->globalize_path(p_from); | ||||
| 	String to = ProjectSettings::get_singleton()->globalize_path(p_to); | ||||
| 	return d->rename(from, to); | ||||
| } | ||||
| 
 | ||||
| Error DirAccess::remove_absolute(const String &p_path) { | ||||
| 	Ref<DirAccess> d = DirAccess::create_for_path(p_path); | ||||
| 	return d->remove(p_path); | ||||
| } | ||||
| 
 | ||||
| Ref<DirAccess> DirAccess::create(AccessType p_access) { | ||||
| 	Ref<DirAccess> da = create_func[p_access] ? create_func[p_access]() : nullptr; | ||||
| 	if (da.is_valid()) { | ||||
| 		da->_access_type = p_access; | ||||
| 
 | ||||
| 		// for ACCESS_RESOURCES and ACCESS_FILESYSTEM, current_dir already defaults to where game was started
 | ||||
| 		// in case current directory is force changed elsewhere for ACCESS_RESOURCES
 | ||||
| 		if (p_access == ACCESS_RESOURCES) { | ||||
| 			da->change_dir("res://"); | ||||
| 		} else if (p_access == ACCESS_USERDATA) { | ||||
| 			da->change_dir("user://"); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return da; | ||||
| } | ||||
| 
 | ||||
| Error DirAccess::get_open_error() { | ||||
| 	return last_dir_open_error; | ||||
| } | ||||
| 
 | ||||
| String DirAccess::get_full_path(const String &p_path, AccessType p_access) { | ||||
| 	Ref<DirAccess> d = DirAccess::create(p_access); | ||||
| 	if (d.is_null()) { | ||||
| 		return p_path; | ||||
| 	} | ||||
| 
 | ||||
| 	d->change_dir(p_path); | ||||
| 	String full = d->get_current_dir(); | ||||
| 	return full; | ||||
| } | ||||
| 
 | ||||
| Error DirAccess::copy(const String &p_from, const String &p_to, int p_chmod_flags) { | ||||
| 	ERR_FAIL_COND_V_MSG(p_from == p_to, ERR_INVALID_PARAMETER, "Source and destination path are equal."); | ||||
| 
 | ||||
| 	//printf("copy %s -> %s\n",p_from.ascii().get_data(),p_to.ascii().get_data());
 | ||||
| 	Error err; | ||||
| 	{ | ||||
| 		Ref<FileAccess> fsrc = FileAccess::open(p_from, FileAccess::READ, &err); | ||||
| 		ERR_FAIL_COND_V_MSG(err != OK, err, "Failed to open " + p_from); | ||||
| 
 | ||||
| 		Ref<FileAccess> fdst = FileAccess::open(p_to, FileAccess::WRITE, &err); | ||||
| 		ERR_FAIL_COND_V_MSG(err != OK, err, "Failed to open " + p_to); | ||||
| 
 | ||||
| 		const size_t copy_buffer_limit = 65536; // 64 KB
 | ||||
| 
 | ||||
| 		fsrc->seek_end(0); | ||||
| 		uint64_t size = fsrc->get_position(); | ||||
| 		fsrc->seek(0); | ||||
| 		err = OK; | ||||
| 		size_t buffer_size = MIN(size * sizeof(uint8_t), copy_buffer_limit); | ||||
| 		LocalVector<uint8_t> buffer; | ||||
| 		buffer.resize(buffer_size); | ||||
| 		while (size > 0) { | ||||
| 			if (fsrc->get_error() != OK) { | ||||
| 				err = fsrc->get_error(); | ||||
| 				break; | ||||
| 			} | ||||
| 			if (fdst->get_error() != OK) { | ||||
| 				err = fdst->get_error(); | ||||
| 				break; | ||||
| 			} | ||||
| 
 | ||||
| 			int bytes_read = fsrc->get_buffer(buffer.ptr(), buffer_size); | ||||
| 			if (bytes_read <= 0) { | ||||
| 				err = FAILED; | ||||
| 				break; | ||||
| 			} | ||||
| 			fdst->store_buffer(buffer.ptr(), bytes_read); | ||||
| 
 | ||||
| 			size -= bytes_read; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if (err == OK && p_chmod_flags != -1) { | ||||
| 		err = FileAccess::set_unix_permissions(p_to, p_chmod_flags); | ||||
| 		// If running on a platform with no chmod support (i.e., Windows), don't fail
 | ||||
| 		if (err == ERR_UNAVAILABLE) { | ||||
| 			err = OK; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return err; | ||||
| } | ||||
| 
 | ||||
| // Changes dir for the current scope, returning back to the original dir
 | ||||
| // when scope exits
 | ||||
| class DirChanger { | ||||
| 	DirAccess *da; | ||||
| 	String original_dir; | ||||
| 
 | ||||
| public: | ||||
| 	DirChanger(DirAccess *p_da, const String &p_dir) : | ||||
| 			da(p_da), | ||||
| 			original_dir(p_da->get_current_dir()) { | ||||
| 		p_da->change_dir(p_dir); | ||||
| 	} | ||||
| 
 | ||||
| 	~DirChanger() { | ||||
| 		da->change_dir(original_dir); | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| Error DirAccess::_copy_dir(Ref<DirAccess> &p_target_da, const String &p_to, int p_chmod_flags, bool p_copy_links) { | ||||
| 	List<String> dirs; | ||||
| 
 | ||||
| 	String curdir = get_current_dir(); | ||||
| 	list_dir_begin(); | ||||
| 	String n = get_next(); | ||||
| 	while (!n.is_empty()) { | ||||
| 		if (n != "." && n != "..") { | ||||
| 			if (p_copy_links && is_link(get_current_dir().path_join(n))) { | ||||
| 				create_link(read_link(get_current_dir().path_join(n)), p_to + n); | ||||
| 			} else if (current_is_dir()) { | ||||
| 				dirs.push_back(n); | ||||
| 			} else { | ||||
| 				const String &rel_path = n; | ||||
| 				if (!n.is_relative_path()) { | ||||
| 					list_dir_end(); | ||||
| 					return ERR_BUG; | ||||
| 				} | ||||
| 				Error err = copy(get_current_dir().path_join(n), p_to + rel_path, p_chmod_flags); | ||||
| 				if (err) { | ||||
| 					list_dir_end(); | ||||
| 					return err; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		n = get_next(); | ||||
| 	} | ||||
| 
 | ||||
| 	list_dir_end(); | ||||
| 
 | ||||
| 	for (const String &rel_path : dirs) { | ||||
| 		String target_dir = p_to + rel_path; | ||||
| 		if (!p_target_da->dir_exists(target_dir)) { | ||||
| 			Error err = p_target_da->make_dir(target_dir); | ||||
| 			ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot create directory '" + target_dir + "'."); | ||||
| 		} | ||||
| 
 | ||||
| 		Error err = change_dir(rel_path); | ||||
| 		ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot change current directory to '" + rel_path + "'."); | ||||
| 
 | ||||
| 		err = _copy_dir(p_target_da, p_to + rel_path + "/", p_chmod_flags, p_copy_links); | ||||
| 		if (err) { | ||||
| 			change_dir(".."); | ||||
| 			ERR_FAIL_V_MSG(err, "Failed to copy recursively."); | ||||
| 		} | ||||
| 		err = change_dir(".."); | ||||
| 		ERR_FAIL_COND_V_MSG(err != OK, err, "Failed to go back."); | ||||
| 	} | ||||
| 
 | ||||
| 	return OK; | ||||
| } | ||||
| 
 | ||||
| Error DirAccess::copy_dir(const String &p_from, String p_to, int p_chmod_flags, bool p_copy_links) { | ||||
| 	ERR_FAIL_COND_V_MSG(!dir_exists(p_from), ERR_FILE_NOT_FOUND, "Source directory doesn't exist."); | ||||
| 
 | ||||
| 	Ref<DirAccess> target_da = DirAccess::create_for_path(p_to); | ||||
| 	ERR_FAIL_COND_V_MSG(target_da.is_null(), ERR_CANT_CREATE, "Cannot create DirAccess for path '" + p_to + "'."); | ||||
| 
 | ||||
| 	if (!target_da->dir_exists(p_to)) { | ||||
| 		Error err = target_da->make_dir_recursive(p_to); | ||||
| 		ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot create directory '" + p_to + "'."); | ||||
| 	} | ||||
| 
 | ||||
| 	if (!p_to.ends_with("/")) { | ||||
| 		p_to = p_to + "/"; | ||||
| 	} | ||||
| 
 | ||||
| 	DirChanger dir_changer(this, p_from); | ||||
| 	Error err = _copy_dir(target_da, p_to, p_chmod_flags, p_copy_links); | ||||
| 
 | ||||
| 	return err; | ||||
| } | ||||
| 
 | ||||
| bool DirAccess::exists(const String &p_dir) { | ||||
| 	Ref<DirAccess> da = DirAccess::create_for_path(p_dir); | ||||
| 	return da->change_dir(p_dir) == OK; | ||||
| } | ||||
| 
 | ||||
| PackedStringArray DirAccess::get_files() { | ||||
| 	return _get_contents(false); | ||||
| } | ||||
| 
 | ||||
| PackedStringArray DirAccess::get_files_at(const String &p_path) { | ||||
| 	Ref<DirAccess> da = DirAccess::open(p_path); | ||||
| 	ERR_FAIL_COND_V_MSG(da.is_null(), PackedStringArray(), vformat("Couldn't open directory at path \"%s\".", p_path)); | ||||
| 	return da->get_files(); | ||||
| } | ||||
| 
 | ||||
| PackedStringArray DirAccess::get_directories() { | ||||
| 	return _get_contents(true); | ||||
| } | ||||
| 
 | ||||
| PackedStringArray DirAccess::get_directories_at(const String &p_path) { | ||||
| 	Ref<DirAccess> da = DirAccess::open(p_path); | ||||
| 	ERR_FAIL_COND_V_MSG(da.is_null(), PackedStringArray(), vformat("Couldn't open directory at path \"%s\".", p_path)); | ||||
| 	return da->get_directories(); | ||||
| } | ||||
| 
 | ||||
| PackedStringArray DirAccess::_get_contents(bool p_directories) { | ||||
| 	PackedStringArray ret; | ||||
| 
 | ||||
| 	list_dir_begin(); | ||||
| 	String s = _get_next(); | ||||
| 	while (!s.is_empty()) { | ||||
| 		if (current_is_dir() == p_directories) { | ||||
| 			ret.append(s); | ||||
| 		} | ||||
| 		s = _get_next(); | ||||
| 	} | ||||
| 
 | ||||
| 	ret.sort(); | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| String DirAccess::_get_next() { | ||||
| 	String next = get_next(); | ||||
| 	while (!next.is_empty() && ((!include_navigational && (next == "." || next == "..")) || (!include_hidden && current_is_hidden()))) { | ||||
| 		next = get_next(); | ||||
| 	} | ||||
| 	return next; | ||||
| } | ||||
| 
 | ||||
| void DirAccess::set_include_navigational(bool p_enable) { | ||||
| 	include_navigational = p_enable; | ||||
| } | ||||
| 
 | ||||
| bool DirAccess::get_include_navigational() const { | ||||
| 	return include_navigational; | ||||
| } | ||||
| 
 | ||||
| void DirAccess::set_include_hidden(bool p_enable) { | ||||
| 	include_hidden = p_enable; | ||||
| } | ||||
| 
 | ||||
| bool DirAccess::get_include_hidden() const { | ||||
| 	return include_hidden; | ||||
| } | ||||
| 
 | ||||
| bool DirAccess::is_case_sensitive(const String &p_path) const { | ||||
| 	return true; | ||||
| } | ||||
| 
 | ||||
| void DirAccess::_bind_methods() { | ||||
| 	ClassDB::bind_static_method("DirAccess", D_METHOD("open", "path"), &DirAccess::_open); | ||||
| 	ClassDB::bind_static_method("DirAccess", D_METHOD("get_open_error"), &DirAccess::get_open_error); | ||||
| 
 | ||||
| 	ClassDB::bind_method(D_METHOD("list_dir_begin"), &DirAccess::list_dir_begin, DEFVAL(false), DEFVAL(false)); | ||||
| 	ClassDB::bind_method(D_METHOD("get_next"), &DirAccess::_get_next); | ||||
| 	ClassDB::bind_method(D_METHOD("current_is_dir"), &DirAccess::current_is_dir); | ||||
| 	ClassDB::bind_method(D_METHOD("list_dir_end"), &DirAccess::list_dir_end); | ||||
| 	ClassDB::bind_method(D_METHOD("get_files"), &DirAccess::get_files); | ||||
| 	ClassDB::bind_static_method("DirAccess", D_METHOD("get_files_at", "path"), &DirAccess::get_files_at); | ||||
| 	ClassDB::bind_method(D_METHOD("get_directories"), &DirAccess::get_directories); | ||||
| 	ClassDB::bind_static_method("DirAccess", D_METHOD("get_directories_at", "path"), &DirAccess::get_directories_at); | ||||
| 	ClassDB::bind_static_method("DirAccess", D_METHOD("get_drive_count"), &DirAccess::_get_drive_count); | ||||
| 	ClassDB::bind_static_method("DirAccess", D_METHOD("get_drive_name", "idx"), &DirAccess::get_drive_name); | ||||
| 	ClassDB::bind_method(D_METHOD("get_current_drive"), &DirAccess::get_current_drive); | ||||
| 	ClassDB::bind_method(D_METHOD("change_dir", "to_dir"), &DirAccess::change_dir); | ||||
| 	ClassDB::bind_method(D_METHOD("get_current_dir", "include_drive"), &DirAccess::get_current_dir, DEFVAL(true)); | ||||
| 	ClassDB::bind_method(D_METHOD("make_dir", "path"), &DirAccess::make_dir); | ||||
| 	ClassDB::bind_static_method("DirAccess", D_METHOD("make_dir_absolute", "path"), &DirAccess::make_dir_absolute); | ||||
| 	ClassDB::bind_method(D_METHOD("make_dir_recursive", "path"), &DirAccess::make_dir_recursive); | ||||
| 	ClassDB::bind_static_method("DirAccess", D_METHOD("make_dir_recursive_absolute", "path"), &DirAccess::make_dir_recursive_absolute); | ||||
| 	ClassDB::bind_method(D_METHOD("file_exists", "path"), &DirAccess::file_exists); | ||||
| 	ClassDB::bind_method(D_METHOD("dir_exists", "path"), &DirAccess::dir_exists); | ||||
| 	ClassDB::bind_static_method("DirAccess", D_METHOD("dir_exists_absolute", "path"), &DirAccess::dir_exists_absolute); | ||||
| 	ClassDB::bind_method(D_METHOD("get_space_left"), &DirAccess::get_space_left); | ||||
| 	ClassDB::bind_method(D_METHOD("copy", "from", "to", "chmod_flags"), &DirAccess::copy, DEFVAL(-1)); | ||||
| 	ClassDB::bind_static_method("DirAccess", D_METHOD("copy_absolute", "from", "to", "chmod_flags"), &DirAccess::copy_absolute, DEFVAL(-1)); | ||||
| 	ClassDB::bind_method(D_METHOD("rename", "from", "to"), &DirAccess::rename); | ||||
| 	ClassDB::bind_static_method("DirAccess", D_METHOD("rename_absolute", "from", "to"), &DirAccess::rename_absolute); | ||||
| 	ClassDB::bind_method(D_METHOD("remove", "path"), &DirAccess::remove); | ||||
| 	ClassDB::bind_static_method("DirAccess", D_METHOD("remove_absolute", "path"), &DirAccess::remove_absolute); | ||||
| 
 | ||||
| 	ClassDB::bind_method(D_METHOD("is_link", "path"), &DirAccess::is_link); | ||||
| 	ClassDB::bind_method(D_METHOD("read_link", "path"), &DirAccess::read_link); | ||||
| 	ClassDB::bind_method(D_METHOD("create_link", "source", "target"), &DirAccess::create_link); | ||||
| 
 | ||||
| 	ClassDB::bind_method(D_METHOD("set_include_navigational", "enable"), &DirAccess::set_include_navigational); | ||||
| 	ClassDB::bind_method(D_METHOD("get_include_navigational"), &DirAccess::get_include_navigational); | ||||
| 	ClassDB::bind_method(D_METHOD("set_include_hidden", "enable"), &DirAccess::set_include_hidden); | ||||
| 	ClassDB::bind_method(D_METHOD("get_include_hidden"), &DirAccess::get_include_hidden); | ||||
| 
 | ||||
| 	ClassDB::bind_method(D_METHOD("is_case_sensitive", "path"), &DirAccess::is_case_sensitive); | ||||
| 
 | ||||
| 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "include_navigational"), "set_include_navigational", "get_include_navigational"); | ||||
| 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "include_hidden"), "set_include_hidden", "get_include_hidden"); | ||||
| } | ||||
							
								
								
									
										168
									
								
								engine/core/io/dir_access.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										168
									
								
								engine/core/io/dir_access.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,168 @@ | |||
| /**************************************************************************/ | ||||
| /*  dir_access.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.                 */ | ||||
| /**************************************************************************/ | ||||
| 
 | ||||
| #ifndef DIR_ACCESS_H | ||||
| #define DIR_ACCESS_H | ||||
| 
 | ||||
| #include "core/object/ref_counted.h" | ||||
| #include "core/string/ustring.h" | ||||
| #include "core/typedefs.h" | ||||
| 
 | ||||
| //@ TODO, excellent candidate for THREAD_SAFE MACRO, should go through all these and add THREAD_SAFE where it applies
 | ||||
| class DirAccess : public RefCounted { | ||||
| 	GDCLASS(DirAccess, RefCounted); | ||||
| 
 | ||||
| public: | ||||
| 	enum AccessType { | ||||
| 		ACCESS_RESOURCES, | ||||
| 		ACCESS_USERDATA, | ||||
| 		ACCESS_FILESYSTEM, | ||||
| 		ACCESS_MAX | ||||
| 	}; | ||||
| 
 | ||||
| 	typedef Ref<DirAccess> (*CreateFunc)(); | ||||
| 
 | ||||
| private: | ||||
| 	AccessType _access_type = ACCESS_FILESYSTEM; | ||||
| 	static CreateFunc create_func[ACCESS_MAX]; ///< set this to instance a filesystem object
 | ||||
| 	static Ref<DirAccess> _open(const String &p_path); | ||||
| 
 | ||||
| 	Error _copy_dir(Ref<DirAccess> &p_target_da, const String &p_to, int p_chmod_flags, bool p_copy_links); | ||||
| 	PackedStringArray _get_contents(bool p_directories); | ||||
| 
 | ||||
| 	thread_local static Error last_dir_open_error; | ||||
| 	bool include_navigational = false; | ||||
| 	bool include_hidden = false; | ||||
| 
 | ||||
| protected: | ||||
| 	static void _bind_methods(); | ||||
| 
 | ||||
| 	String _get_root_path() const; | ||||
| 	virtual String _get_root_string() const; | ||||
| 
 | ||||
| 	AccessType get_access_type() const; | ||||
| 	virtual String fix_path(const String &p_path) const; | ||||
| 
 | ||||
| 	template <typename T> | ||||
| 	static Ref<DirAccess> _create_builtin() { | ||||
| 		return memnew(T); | ||||
| 	} | ||||
| 
 | ||||
| public: | ||||
| 	virtual Error list_dir_begin() = 0; ///< This starts dir listing
 | ||||
| 	virtual String get_next() = 0; | ||||
| 	virtual bool current_is_dir() const = 0; | ||||
| 	virtual bool current_is_hidden() const = 0; | ||||
| 
 | ||||
| 	virtual void list_dir_end() = 0; ///<
 | ||||
| 
 | ||||
| 	virtual int get_drive_count() = 0; | ||||
| 	virtual String get_drive(int p_drive) = 0; | ||||
| 	virtual int get_current_drive(); | ||||
| 	virtual bool drives_are_shortcuts(); | ||||
| 
 | ||||
| 	virtual Error change_dir(String p_dir) = 0; ///< can be relative or absolute, return false on success
 | ||||
| 	virtual String get_current_dir(bool p_include_drive = true) const = 0; ///< return current dir location
 | ||||
| 	virtual Error make_dir(String p_dir) = 0; | ||||
| 	virtual Error make_dir_recursive(const String &p_dir); | ||||
| 	virtual Error erase_contents_recursive(); //super dangerous, use with care!
 | ||||
| 
 | ||||
| 	virtual bool file_exists(String p_file) = 0; | ||||
| 	virtual bool dir_exists(String p_dir) = 0; | ||||
| 	virtual bool is_readable(String p_dir) { return true; }; | ||||
| 	virtual bool is_writable(String p_dir) { return true; }; | ||||
| 	static bool exists(const String &p_dir); | ||||
| 	virtual uint64_t get_space_left() = 0; | ||||
| 
 | ||||
| 	Error copy_dir(const String &p_from, String p_to, int p_chmod_flags = -1, bool p_copy_links = false); | ||||
| 	virtual Error copy(const String &p_from, const String &p_to, int p_chmod_flags = -1); | ||||
| 	virtual Error rename(String p_from, String p_to) = 0; | ||||
| 	virtual Error remove(String p_name) = 0; | ||||
| 
 | ||||
| 	virtual bool is_link(String p_file) = 0; | ||||
| 	virtual String read_link(String p_file) = 0; | ||||
| 	virtual Error create_link(String p_source, String p_target) = 0; | ||||
| 
 | ||||
| 	// Meant for editor code when we want to quickly remove a file without custom
 | ||||
| 	// handling (e.g. removing a cache file).
 | ||||
| 	static void remove_file_or_error(const String &p_path) { | ||||
| 		Ref<DirAccess> da = create(ACCESS_FILESYSTEM); | ||||
| 		if (da->file_exists(p_path)) { | ||||
| 			if (da->remove(p_path) != OK) { | ||||
| 				ERR_FAIL_MSG("Cannot remove file or directory: " + p_path); | ||||
| 			} | ||||
| 		} else { | ||||
| 			ERR_FAIL_MSG("Cannot remove non-existent file or directory: " + p_path); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	virtual String get_filesystem_type() const = 0; | ||||
| 	static String get_full_path(const String &p_path, AccessType p_access); | ||||
| 	static Ref<DirAccess> create_for_path(const String &p_path); | ||||
| 
 | ||||
| 	static Ref<DirAccess> create(AccessType p_access); | ||||
| 	static Error get_open_error(); | ||||
| 
 | ||||
| 	template <typename T> | ||||
| 	static void make_default(AccessType p_access) { | ||||
| 		create_func[p_access] = _create_builtin<T>; | ||||
| 	} | ||||
| 
 | ||||
| 	static Ref<DirAccess> open(const String &p_path, Error *r_error = nullptr); | ||||
| 
 | ||||
| 	static int _get_drive_count(); | ||||
| 	static String get_drive_name(int p_idx); | ||||
| 
 | ||||
| 	static Error make_dir_absolute(const String &p_dir); | ||||
| 	static Error make_dir_recursive_absolute(const String &p_dir); | ||||
| 	static bool dir_exists_absolute(const String &p_dir); | ||||
| 
 | ||||
| 	static Error copy_absolute(const String &p_from, const String &p_to, int p_chmod_flags = -1); | ||||
| 	static Error rename_absolute(const String &p_from, const String &p_to); | ||||
| 	static Error remove_absolute(const String &p_path); | ||||
| 
 | ||||
| 	PackedStringArray get_files(); | ||||
| 	static PackedStringArray get_files_at(const String &p_path); | ||||
| 	PackedStringArray get_directories(); | ||||
| 	static PackedStringArray get_directories_at(const String &p_path); | ||||
| 	String _get_next(); | ||||
| 
 | ||||
| 	void set_include_navigational(bool p_enable); | ||||
| 	bool get_include_navigational() const; | ||||
| 	void set_include_hidden(bool p_enable); | ||||
| 	bool get_include_hidden() const; | ||||
| 
 | ||||
| 	virtual bool is_case_sensitive(const String &p_path) const; | ||||
| 
 | ||||
| 	DirAccess() {} | ||||
| 	virtual ~DirAccess() {} | ||||
| }; | ||||
| 
 | ||||
| #endif // DIR_ACCESS_H
 | ||||
Some files were not shown because too many files have changed in this diff Show more
		Loading…
	
		Reference in a new issue
	
	 Jan van der Weide
						Jan van der Weide