- Renamed `ConvertToX` to `ConvertToNativeX`.
- Renamed `ConvertToXObject` to `ConvertToX`.
- Renamed `ConvertToXManaged` to `ConvertToX`.
- Fix `Signal` name in bindings generator and csharp script.
ReadOnly properties are currently not allowed because the generated code
needs to set them, this also apply to `init` properties because they
need to be set after initialization.
Non-exhaustive list of case-sensitive renames:
GDExtension -> GDNative
GDNATIVE -> GDEXTENSION
gdextension -> gdnative
ExtensionExtension ->Extension (for where there was GDNativeExtension)
EXTENSION_EXTENSION ->EXTENSION (for where there was GDNATIVE_EXTENSION)
gdnlib -> gdextension
gdn_interface -> gde_interface
gdni -> gde_interface
This was a regression from 17b2838f39.
`MarshalUtils` was changed in the source generators to use
`ConvertTo<T>` and `CreateFrom<T>`, which don't support `GodotObject[]`
because it would need reflection.
As such, we need to keep the custom cases for `GodotObject[]` in
`MarshalUtils`.
- Exposes the properties of C# attribute so they can be accessed from
reflection, renaming them to PascalCase to follow .NET conventions.
- Added some documentation to the newly exposed members.
- Made attribute properties readonly to avoid giving the impression that
they could be modified.
This change aims to reduce the number of places that need to be changed
when adding or editing a Godot type to the bindings.
Since the addition of `Variant.From<T>/As<T>` and
`VariantUtils.CreateFrom<T>/ConvertTo<T>`, we can now replace a lot of
the previous code in the bindings generator and the source generators
that specify these conversions for each type manually.
The only exceptions are the generic Godot collections (`Array<T>` and
`Dictionary<TKey, TValue>`) which still use the old version, as that
one cannot be matched by our new conversion methods (limitation in the
language with generics, forcing us to use delegate pointers).
The cleanup applies to:
- Bindings generator:
- `TypeInterface.cs_variant_to_managed`
- `TypeInterface.cs_managed_to_variant`
- Source generators:
- `MarshalUtils.AppendNativeVariantToManagedExpr`
- `MarshalUtils.AppendManagedToNativeVariantExpr`
- `MarshalUtils.AppendVariantToManagedExpr`
- `MarshalUtils.AppendManagedToVariantExpr`
This commit replaces most usages of `ConvertManagedObjectToVariant` and
`ConvertVariantToManagedObjectOfType`, by using the `Godot.Variant`
struct instead of `System.Object`.
The most notable change is to the `GetGodotPropertyDefaultValues` method
that's generated for scripts. The dictionary it returns now stores
`Godot.Variant` values.
Remaining usages are:
- The `DelegateUtils` class, for the serialization of closure display
classes during assembly reloading by the editor. These display classes
are compiler generated classes to store values captured by a closure.
Since it's generated by the compiler, the only way we have to access
the fields is through reflection. This leads to using `System.Object`.
- Converting parameters when invoking constructors from the engine.
This will be replaced with source generators in the future.
- Legacy support for old `GetGodotPropertyDefaultValues` return values.
We need to keep supporting the old version of this generated method
for some time. Otherwise, if loading a project built with the previous
version, it could lead to the loss of exported property values.
Ideally, we should remove this legacy support before a stable release.
Its two usages were:
- The Array `ICollection.CopyTo` implementation.
It's possible that this class shouldn't be implementing the
non-generic `ICollection`, but this commit doesn't change that.
The new implementation stores the elements as boxed `Variant` values.
- The `Variant.Obj` property.
I'm not sure if this property's existence is justified, but for now
I rewrote it as a simpler version of `ConvertVariantToManagedObject`.
- Removed `UnicodeAt`
- Removed `EndsWith`
- Removed `LPad` and `RPad`
- Deprecated `BeginsWith` in favor of `string.StartsWith`
- Deprecated `LStrip` and `RStrip` in favor of `string.TrimStart` and `string.TrimEnd`
- Remove `VariantSpanDisposer`, no need to dispose of the Variant Spans
since we are now borrowing the Variants instead of copying them.
- Remove `VariantSpanExtensions.Cleared` that was only used so the
Span was initialized for `VariantSpanDisposer` to know what to dispose.
- Fix stackalloc Spans to use constant VarArgsSpanThreshold
and avoid bound checks.
- Replaced `MD5Buffer`, `MD5Text`, `SHA256Buffer` and `SHA256Text` implementation to use the `System.Security.Cryptography` classes and avoid marshaling.
- Added `SHA1Buffer` and `SHA1Text`.
- Renamed `ToUTF8` to `ToUTF8Buffer`.
- Renamed `ToAscii` to `ToASCIIBuffer`.
- Added `ToUTF16Buffer` and `ToUTF32Buffer`.
- Added `GetStringFromUTF16` and `GetStringFromUTF32`.
- Renamed `IsValidInteger` to `IsValidInt`.
- Added `IsValidFileName`.
- Added `IsValidHexNumber`.
- Added support for IPv6 to `IsValidIPAddress`.
- Added `ValidateNodeName`.
- Updated the documentation of the `IsValid*` methods.
- Moved `GetBaseName` to keep methods alphabetically sorted.
- Removed `Length`, users should just use the Length property.
- Removed `Insert`, string already has a method with the same signature that takes precedence.
- Removed `Erase`.
- Removed `ToLower` and `ToUpper`, string already has methods with the same signature that take precedence.
- Removed `FindLast` in favor of `RFind`.
- Replaced `RFind` and `RFindN` implemenation with a ca ll to `string.LastIndexOf` to avoid marshaling.
- Added `LPad` and `RPad`.
- Added `StripEscapes`.
- Replaced `LStrip` and `RStrip` implementation with a call to `string.TrimStart` and `string.TrimEnd`.
- Added `TrimPrefix` and `TrimSuffix`.
- Renamed `OrdAt` to `UnicodeAt`.
- Added `CountN` and move the `caseSensitive` parameter of `Count` to the end.
- Added `Indent` and `Dedent`.
These callbacks are used for marshaling by callables and generic Godot
collections.
C# generics don't support specialization the way C++ templates do.
I knew NativeAOT could optimize away many type checks when the types
are known at compile time, but I didn't trust the JIT would do as good
a job, so I initially went with cached function pointers.
Well, it turns out the JIT is also very good at optimizing in this
scenario, so I'm changing the methods to do the conversion directly,
rather than returning a function pointer for the conversion.
The methods were moved to `VariantUtils`, and were renamed from
`GetFromVariantCallback/GetToVariantCallback` to `ConvertTo/CreateFrom`.
The new implementation looks like it goes through many `if` checks
at runtime to find the right branch for the type, but in practice it
works pretty much like template specialization. The JIT only generates
code for the relevant branch. Together with inlining, the result is
very close or the same as doing the conversion manually:
```cs
godot_variant variant;
int foo = variant.Int;
int bar = VariantUtils.ConvertTo<int>(variant);
```
If the type is a generic Godot collection, the conversion still goes
through a function pointer call.
The new code happens to be much shorter as well, with the file going
from 1057 lines to 407.
Side note: `Variant.cs` was mistakenly created in the wrong folder,
so I moved it to the `Core` folder.
This allows using generic Godot collections as type arguments for other
generic Godot collections. This also allows generic Godot collections
as parameter or return type in dynamic Callable invocations.
We aim to make the C# API reflection-free, mainly for concerns about
performance, and to be able to target NativeAOT in refletion-free mode,
which reduces the binary size.
One of the main usages of reflection still left was the dynamic
invokation of callable delegates, and for some time I wasn't sure
I would find an alternative solution that I'd be happy with.
The new solution uses trampoline functions to invoke the delegates:
```
static void Trampoline(object delegateObj, NativeVariantPtrArgs args, out godot_variant ret)
{
if (args.Count != 1)
throw new ArgumentException($"Callable expected 1 arguments but received {args.Count}.");
string res = ((Func<int, string>)delegateObj)(
VariantConversionCallbacks.GetToManagedCallback<int>()(args[0])
);
ret = VariantConversionCallbacks.GetToVariantCallback<string>()(res);
}
Callable.CreateWithUnsafeTrampoline((int num) => "Foo" + num, &Trampoline);
```
Of course, this is too much boilerplate for user code. To improve this,
the `Callable.From` methods were added. These are overloads that take
`Action` and `Func` delegates, which covers the most common use cases:
lambdas and method groups:
```
// Lambda
Callable.From((int num) => "Foo" + num);
// Method group
string AppendNum(int num) => "Foo" + num;
Callable.From(AppendNum);
```
Unfortunately, due to limitations in the C# language, implicit
conversions from delegates to `Callable` are not supported.
`Callable.From` does not support custom delegates. These should be
uncommon, but the Godot C# API actually uses them for event signals.
As such, the bindings generator was updated to generate trampoline
functions for event signals. It was also optimized to use `Action`
instead of a custom delegate for parameterless signals, which removes
the need for the trampoline functions for those signals.
The change to reflection-free invokation removes one of the last needs
for `ConvertVariantToManagedObjectOfType`. The only remaining usage is
from calling script constructors with parameters from the engine
(`CreateManagedForGodotObjectScriptInstance`). Once that one is made
reflection-free, `ConvertVariantToManagedObjectOfType` can be removed.
We use collectible AssemblyLoadContexts as that's the only way to allow
reloading assemblies after building. However, collectible assemblies
have some restrictions:
- https://learn.microsoft.com/en-us/dotnet/framework/reflection-and-codedom/collectible-assemblies#restrictions-on-collectible-assemblies
Those restrictions can cause issues with third-party code, such as some
mocking libraries.
In order to work around this problem, we're going to load assemblies
as collectible only in Godot editor, and not when running games.
These issues will still exist in the editor, but this will be enough
for some users.
- Use `long` and `double` types since signals currently only support 64-bit types.
- Fix bug for checking if the type name is a class registered in ClassDB.
Ensures that the versions always match the Godot version, albeit following
SemVer 2.0 so inserting a dot between "beta" and the build number.
For "stable" status, we omit the suffix as this would be interpreted as a
pre-release build too.
So we have:
| Godot version | Nupkg version |
| -------------- | -------------- |
| 4.0.0-beta | 4.0.0-beta |
| 4.0.0-beta2 | 4.0.0-beta.2 |
| 4.0.0-rc1 | 4.0.0-rc.1 |
| 4.0.0-stable | 4.0.0 |
Android was the last platform to still attempt to disable RTTI (for binary
size), but both the Android editor and now the ICU library used by templates
need RTTI.
There could still be the possibility to support this for non-ICU template
builds (i.e. without the TextServerAdvanced module), but since this isn't one
of the build configurations we test regularly it's pretty risky to keep this
option only for that specific use case. And our code is already littered with
`dynamic_cast`s which weren't guarded with `!defined(NO_SAFE_CAST)`.
If the delegate target is an Object, the connected signal will be registered in that object instead of the middleman. So when that object is destroyed, the signal will be properly disconnected.
Implements https://github.com/godotengine/godot-proposals/issues/3371.
New `target` presets
====================
The `tools` option is removed and `target` changes to use three new presets,
which match the builds users are familiar with. These targets control the
default optimization level and enable editor-specific and debugging code:
- `editor`: Replaces `tools=yes target=release_debug`.
* Defines: `TOOLS_ENABLED`, `DEBUG_ENABLED`, `-O2`/`/O2`
- `template_debug`: Replaces `tools=no target=release_debug`.
* Defines: `DEBUG_ENABLED`, `-O2`/`/O2`
- `template_release`: Replaces `tools=no target=release`.
* Defines: `-O3`/`/O2`
New `dev_build` option
======================
The previous `target=debug` is now replaced by a separate `dev_build=yes`
option, which can be used in combination with either of the three targets,
and changes the following:
- `dev_build`: Defines `DEV_ENABLED`, disables optimization (`-O0`/`/0d`),
enables generating debug symbols, does not define `NDEBUG` so `assert()`
works in thirdparty libraries, adds a `.dev` suffix to the binary name.
Note: Unlike previously, `dev_build` defaults to off so that users who
compile Godot from source get an optimized and small build by default.
Engine contributors should now set `dev_build=yes` in their build scripts or
IDE configuration manually.
Changed binary names
====================
The name of generated binaries and object files are changed too, to follow
this format:
`godot.<platform>.<target>[.dev][.double].<arch>[.<extra_suffix>][.<ext>]`
For example:
- `godot.linuxbsd.editor.dev.arm64`
- `godot.windows.template_release.double.x86_64.mono.exe`
Be sure to update your links/scripts/IDE config accordingly.
More flexible `optimize` and `debug_symbols` options
====================================================
The optimization level and whether to generate debug symbols can be further
specified with the `optimize` and `debug_symbols` options. So the default
values listed above for the various `target` and `dev_build` combinations
are indicative and can be replaced when compiling, e.g.:
`scons p=linuxbsd target=template_debug dev_build=yes optimize=debug`
will make a "debug" export template with dev-only code enabled, `-Og`
optimization level for GCC/Clang, and debug symbols. Perfect for debugging
complex crashes at runtime in an exported project.
Ensures the `push_nupkgs_local` argument in build_assemblies.py is an
absolute path so the argument can be
given as a relative path and it will be converted.
- Creates a `Godot.Offline.Config` file to configurate NuGet with
Godot's fallback folder. This is easier because now we can assume we can
override the entire file since user config will likely be in the default
`NuGet.Config` file or an additional `*.config` file.
- Ensure the NuGet fallback folder is created at the same time it is
added to the NuGet configuration so future builds don't fail.
- Add `GodotSharp` and `GodotSharpEditor` packages to the fallback folder.
- Add `.nupkg.metadata` file to packages in fallback folder.
- Refer to `Godot.SourceGenerators` using the specific non-floating version
since floating versions don't seem to work with fallbackPackageFolders.
We want to replace libnethost as it gives us issues with some compilers.
Our implementation tries to mimic libnethost's hostfxr_resolver search
logic. We try to use the same function names for easier comparing in
case we need to update this in the future.
Scripts that are instantiated at some point will always be recreated
if they ever become placeholders to prevent non-tool scripts
instantiated manually by users to become placeholders, if they
do become placeholders due to errors that prevent instantiation
(such as a missing parameterless constructor) these scripts
will also be recreated replacing the temporary placeholder.
If a script is marked as a tool but becomes a non-tool script
in a rebuild, the script will become a placeholder and will
no longer be considered applicable to be replaced by an instance
since the user explicitly removed the Tool attribute.
Since the list of signals in `CSharpScript::event_signals` retrieved
from calling `ScriptManagerBridge.UpdateScriptClassInfo` already
includes the signals from base scripts there is no need to iterate the
hierarchy again on `CSharpInstance::connect_event_signals`.
When the C# bindings generator finds a type without meta assume the type
refers to the 64-bit version of the type:
- `float` is converted to `double`
- `int` is converted to `long`
Vector4 and Vector4i were implemented incorrectly in godot_variant.
They were also missing their respective Variant conversion callbacks
(used for generic collections).
Took the chance to remove unnecessary native calls for creating
Variant from Vector4, as now it can be done from C# (which is faster).
- Use different syntax for object printing to avoid confusion with arrays.
- Print null as `<null>` to avoid confusion with a string `"null"`.
- Display `<empty>` in editor resource pickers to avoid confusion
with array-based properties.
- Fix platform detection after Linux OS name was renamed from `LinuxBSD`
to `Linux`
- Fix arch detection after renaming `64` to `x86_64`
- Fix typo in `find_hostfxr`
- Replace `IndexOutOfRangeException` with `ArgumentOutOfRangeException`
- Replace `Exception` with a more specific exception
- Add the parameter name to argument exception
- Update documentation for methods that throw exceptions
- Use `StringBuilder` to build exception messages
- Ensure exception messages end with a period
`libnethost.a` detection failed on my Linux system (Mageia 9, using Fedora 36
dotnet repos), because it used the first match which isn't the one matching
the rest of the SDK:
```
$ dotnet --list-runtimes
Microsoft.AspNetCore.App 3.1.28 [/usr/share/dotnet/shared/Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 6.0.8 [/usr/share/dotnet/shared/Microsoft.AspNetCore.App]
Microsoft.NETCore.App 3.1.28 [/usr/share/dotnet/shared/Microsoft.NETCore.App]
Microsoft.NETCore.App 6.0.5 [/usr/share/dotnet/shared/Microsoft.NETCore.App]
Microsoft.NETCore.App 6.0.8 [/usr/share/dotnet/shared/Microsoft.NETCore.App]
```
No idea why I still have 6.0.5 installed but it should pick the highest I guess.
- Remove event as a valid target of `SignalAttribute`
- Stop adding the `[Signal]` attribute to events in bindings_generator
- Make bindings_generator use the `EventHandler` suffix to be consistent with the C# source generator
- Remove obsolete comment about the signal's delegate name
- MustBeVariant attribute can be used to enforce that generic types must
be a marshable from/to Variant.
- Also renames all diagnostic ids to be valid unicode identifiers.
Node methods in C# extended to use generics
now have the optional parameter `includeInternal`
like their non-generic equivalents.
Also, fixed a typo in the `Node.get_child` documentation.
- In cases where both `Xform`/`XformInv` and the `*` operator were
implemented the `Xform`/`XformInv` methods were removed in favor of the
`*` operator.
- In cases where the `Xform`/`XformInv` existed but not the `*` operator,
the `Xform`/`XformInv` methods were replaced with the `*` operator.
- In cases where no method existed, a new `*` operator has been
implemented to support the same operations that are supported in GDScript.
- Fixes the `Transform.Xform` and `Transform.XformInv` with `Rect2`
implementation to use a zero `Rect2` size to start expanding from
(which is how it's implemented in C++).
If the project assembly does not exist, return `false` directly instead
of trying to load it.
This prevents the `System.InvalidOperationException` thrown for failing
to locate managed application.
- Moves interop functions to UnmanagedCallbacks struct that
contains the function pointers and is passed to C#.
- Implements UnmanagedCallbacksGenerator, a C# source generator that
generates the UnmanagedCallbacks struct in C# and the body for the
NativeFuncs methods (their implementation just calls the function
pointer in the UnmanagedCallbacks). The generated methods are needed
because .NET pins byref parameters of native calls, even if they are
'ref struct's, which don't need pinning. The generated methods use
`Unsafe.AsPointer` so that we can benefit from byref parameters
without suffering overhead of pinning.
Co-authored-by: Raul Santos <raulsntos@gmail.com>
We were using it to workaround a limitation of `Unsafe.AsPointer` and
`ref struct`s. However, we can get the same result with some tricks,
since we have control over the declaration of these structs.
The setting is initially assigned the name of the Godot project,
but it's kept freezed to prevent issues when renaming the Godot
project.
The user can always rename the C# project and solution manually and
change the setting to the new name.
This new version does not support the following type arguments:
- Generic types
- Array of Godot Object (Godot.Object[]) or derived types
The new implementation uses delegate pointers to call the Variant
conversion methods. We do type checking only once in the static
constructor to get the conversion delegates.
Now, we no longer need to do type checking every time, and we no
longer have to box value types.
This is the best implementation I could come up with, as C# generics
don't support anything similar to C++ template specializations.
- Array and Dictionary now store `Variant` instead of `System.Object`.
- Removed generic Array and Dictionary.
They cause too much issues, heavily relying on reflection and
very limited by the lack of a generic specialization.
- Removed support for non-Godot collections.
Support for them also relied heavily on reflection for marshaling.
Support for them will likely be re-introduced in the future, but
it will have to rely on source generators instead of reflection.
- Reduced our use of reflection.
The remaining usages will be moved to source generators soon.
The only usage that I'm not sure yet how to replace is dynamic
invocation of delegates.
Changed the signal declaration signal to:
```
// The following generates a MySignal event
[Signal] public delegate void MySignalEventHandler(int param);
```
In the past, the Godot editor distributed the API assemblies and
copied them to project directories for projects to reference them.
This changed with the move to .NET 5/6. Godot no longer copies the
assemblies to project directories. However, the project Sdk still
tried to reference them from the same location.
From now on, the GodotSharp API is distributed as a NuGet package,
which the Sdk can reference.
Added an option to `build_assemblies.py` to copy all Godot NuGet
packages to an existing local NuGet source. This will be needed
during development, while packages are not published to a remote
NuGet repository.
This option also makes sure to remove packages of the same version
installed (~/.nuget/packages). Very useful during development, when
packages change, to make sure the package being used by a project is
the same we just built and not one from a previous build.
A local NuGet source can be created like this:
```
mkdir ~/MyLocalNuGetSource && \
dotnet nuget add source ~/MyLocalNuGetSource/ -n MyLocalNuGetSource
```
Previously, we added source generators for invoking/accessing methods,
properties and fields in scripts. This freed us from the overhead of
reflection. However, the generated code still used our dynamic
marshaling functions, which do runtime type checking and box value
types.
This commit changes the bindings and source generators to include
'static' marshaling. Based on the types known at compile time, now
we generate the appropriate marshaling call for each type.
Previously, for each scripts class instance that was created from code
rather than by the engine, we were constructing, configuring and
assigning a new CSharpScript.
This has changed now and we make sure there's only one CSharpScript
associated to each type.
The editor no longer needs to create temporary instances to get the
default values. The initializer values of the exported properties are
still evaluated at runtime. For example, in the following example,
`GetInitialValue()` will be called when first looks for default values:
```
[Export] int MyValue = GetInitialValue();
```
Exporting fields with a non-supported type now results in a compiler
error rather than a runtime error when the script is used.
This base implementation is still very barebones but it defines the path
for how exporting will work (at least when embedding the .NET runtime).
Many manual steps are still needed, which should be automatized in the
future. For example, in addition to the API assemblies, now you also
need to copy the GodotPlugins assembly to each game project.
Finalizers are longer guaranteed to be called on exit now that
we switched to .NET Core. This results in native instances leaking.
The only solution I can think of so far is to keep a list of all
instances alive to dispose when the AssemblyLoadContext.Unloading
event is raised.
This replaces the way we invoke methods and set/get properties.
This first iteration rids us of runtime type checking in those
cases, as it's now done at compile time.
Later it will also stop needing the use of reflection. After that,
we will only depend on reflection for generic Godot Array and
Dictionary. We're stuck with reflection in generic collections
for now as C# doesn't support generic/template specialization.
This is only the initial implementation. Further iterations are
coming, specially once we switch to the native extension system
which completely changes the way members are accessed/invoked.
For example, with the native extension system we will likely need
to create `UnmanagedCallersOnly` invoke wrapper methods and return
function pointers to the engine.
Other kind of members, like event signals will be receiving the
same treatment in the future.
Some Linux distros use their distro name as the RID for directory names.
If the .NET Host directory cannot be found with the generic RID,
try to get the rid from `dotnet --info`.
The generic RID should still be the first choice. Some platforms like
Windows 10 define the RID as `win10-x64` but still use the generic
`win-x64` for directory names.
Co-authored-by: Lewis James <lewiji+github@gmail.com>
We're targeting .NET 5 for now to make development easier while
.NET 6 is not yet released.
TEMPORARY REGRESSIONS
---------------------
Assembly unloading is not implemented yet. As such, many Godot
resources are leaked at exit. This will be re-implemented later
together with assembly hot-reloading.
The main focus here was to remove the majority of code that relied on
Mono's embedding APIs, specially the reflection APIs. The embedding
APIs we still use are the bare minimum we need for things to work.
A lot of code was moved to C#. We no longer deal with any managed
objects (`MonoObject*`, and such) in native code, and all marshaling
is done in C#.
The reason for restructuring the code and move away from embedding APIs
is that once we move to .NET Core, we will be limited by the much more
minimal .NET hosting.
PERFORMANCE REGRESSIONS
-----------------------
Some parts of the code were written with little to no concern about
performance. This includes code that calls into script methods and
accesses script fields, properties and events.
The reason for this is that all of that will be moved to source
generators, so any work prior to that would be a waste of time.
DISABLED FEATURES
-----------------
Some code was removed as it no longer makes sense (or won't make sense
in the future).
Other parts were commented out with `#if 0`s and TODO warnings because
it doesn't make much sense to work on them yet as those parts will
change heavily when we switch to .NET Core but also when we start
introducing source generators.
As such, the following features were disabled temporarily:
- Assembly-reloading (will be done with ALCs in .NET Core).
- Properties/fields exports and script method listing (will be
handled by source generators in the future).
- Exception logging in the editor and stack info for errors.
- Exporting games.
- Building of C# projects. We no longer copy the Godot API assemblies
to the project directory, so MSBuild won't be able to find them. The
idea is to turn them into NuGet packages in the future, which could
also be obtained from local NuGet sources during development.
We are moving in the direction of no dynamic code generation,
so this is no longer desired.
The feature can still be easily implemented by any project that
still want it.