Merge branch 'GoaLitiuM-dotnet7' into dotnet7

This commit is contained in:
Wojciech Figat
2022-12-16 13:12:27 +01:00
134 changed files with 7253 additions and 596 deletions

View File

@@ -5,6 +5,7 @@ jobs:
# Game
game-windows:
if: ${{ false }}
name: Game (Android, Release ARM64)
runs-on: "windows-2019"
steps:

View File

@@ -5,6 +5,7 @@ jobs:
# Editor
editor-linux:
if: ${{ false }}
name: Editor (Linux, Development x64)
runs-on: "ubuntu-20.04"
steps:
@@ -28,6 +29,7 @@ jobs:
# Game
game-linux:
if: ${{ false }}
name: Game (Linux, Release x64)
runs-on: "ubuntu-20.04"
steps:

View File

@@ -5,6 +5,7 @@ jobs:
# Editor
editor-mac:
if: ${{ false }}
name: Editor (Mac, Development x64)
runs-on: "macos-latest"
steps:
@@ -22,6 +23,7 @@ jobs:
# Game
game-mac:
if: ${{ false }}
name: Game (Mac, Release x64)
runs-on: "macos-latest"
steps:

View File

@@ -6,12 +6,19 @@ jobs:
# Editor
editor-windows:
name: Editor (Windows, Development x64)
runs-on: "windows-2019"
runs-on: "windows-2022"
steps:
- name: Checkout repo
uses: actions/checkout@v3
- name: Setup Vulkan
uses: ./.github/actions/vulkan
- name: Setup .NET
uses: actions/setup-dotnet@v3
with:
dotnet-version: 7.0.x
- name: Check .NET versions
run: |
dotnet --list-sdks
- name: Checkout LFS
run: |
git lfs version
@@ -23,12 +30,16 @@ jobs:
# Game
game-windows:
name: Game (Windows, Release x64)
runs-on: "windows-2019"
runs-on: "windows-2022"
steps:
- name: Checkout repo
uses: actions/checkout@v3
- name: Setup Vulkan
uses: ./.github/actions/vulkan
- name: Setup .NET
uses: actions/setup-dotnet@v3
with:
dotnet-version: 7.0.x
- name: Checkout LFS
run: |
git lfs version

View File

@@ -5,6 +5,7 @@ jobs:
# Tests
tests-linux:
if: ${{ false }}
name: Tests (Linux)
runs-on: "ubuntu-20.04"
steps:

View File

@@ -10,8 +10,9 @@ if [ $testfilesize -le 1000 ]; then
fi
# Compile the build tool.
xbuild /nologo /verbosity:quiet "Source/Tools/Flax.Build/Flax.Build.csproj" /property:Configuration=Release /property:Platform=AnyCPU /target:Build
dotnet msbuild /nologo /verbosity:quiet "Source/Tools/Flax.Build/Flax.Build.csproj" /property:Configuration=Release /target:Restore,Clean /property:RestorePackagesConfig=True /p:RuntimeIdentifiers=linux-x64
dotnet msbuild /nologo /verbosity:quiet "Source/Tools/Flax.Build/Flax.Build.csproj" /property:Configuration=Release /target:Build /property:SelfContained=False /property:RuntimeIdentifiers=linux-x64
# Run the build tool using the provided arguments.
#mono --debug --debugger-agent=transport=dt_socket,server=y,address=127.0.0.1:55555 Binaries/Tools/Flax.Build.exe "$@"
mono Binaries/Tools/Flax.Build.exe "$@"
Binaries/Tools/Flax.Build "$@"

View File

@@ -28,9 +28,9 @@ fc /b Cache\Intermediate\Build\Flax.Build.Files.txt Cache\Intermediate\Build\Fla
if not errorlevel 1 goto SkipClean
copy /y Cache\Intermediate\Build\Flax.Build.Files.txt Cache\Intermediate\Build\Flax.Build.PrevFiles.txt >nul
%MSBUILD_PATH% /nologo /verbosity:quiet Source\Tools\Flax.Build\Flax.Build.csproj /property:Configuration=Release /property:Platform=AnyCPU /target:Clean
%MSBUILD_PATH% /nologo /verbosity:quiet Source\Tools\Flax.Build\Flax.Build.csproj /property:Configuration=Release /target:Restore,Clean /property:RestorePackagesConfig=True /p:RuntimeIdentifiers=win-x64
:SkipClean
%MSBUILD_PATH% /nologo /verbosity:quiet Source\Tools\Flax.Build\Flax.Build.csproj /property:Configuration=Release /property:Platform=AnyCPU /target:Build
%MSBUILD_PATH% /nologo /verbosity:quiet Source\Tools\Flax.Build\Flax.Build.csproj /property:Configuration=Release /target:Build /property:SelfContained=False /property:RuntimeIdentifiers=win-x64
if errorlevel 1 goto Error_CompilationFailed
Binaries\Tools\Flax.Build.exe %*

View File

@@ -11,6 +11,7 @@
"EditorTarget": "FlaxEditor",
"Configuration": {
"UseCSharp": true,
"UseLargeWorlds": false
"UseLargeWorlds": false,
"UseDotNet": true
}
}

View File

@@ -4,6 +4,7 @@ using System.ComponentModel;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;
using FlaxEngine;
namespace FlaxEditor.Content.Import
@@ -93,6 +94,7 @@ namespace FlaxEditor.Content.Import
[StructLayout(LayoutKind.Sequential)]
internal struct InternalOptions
{
[MarshalAs(UnmanagedType.I1)]
public AudioFormat Format;
public byte DisableStreaming;
public byte Is3D;
@@ -144,7 +146,7 @@ namespace FlaxEditor.Content.Import
/// Audio asset import entry.
/// </summary>
/// <seealso cref="AssetImportEntry" />
public class AudioImportEntry : AssetImportEntry
public partial class AudioImportEntry : AssetImportEntry
{
private AudioImportSettings _settings = new AudioImportSettings();
@@ -182,8 +184,9 @@ namespace FlaxEditor.Content.Import
#region Internal Calls
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern bool Internal_GetAudioImportOptions(string path, out AudioImportSettings.InternalOptions result);
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Content.Import.AudioImportEntry::Internal_GetAudioImportOptions", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(StringMarshaller))]
[return: MarshalAs(UnmanagedType.U1)]
internal static partial bool Internal_GetAudioImportOptions(string path, out AudioImportSettings.InternalOptions result);
#endregion
}

View File

@@ -1,8 +1,10 @@
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;
using FlaxEngine;
namespace FlaxEditor.Content.Import
@@ -355,6 +357,7 @@ namespace FlaxEditor.Content.Import
private bool ShowAnimation => Type == ModelType.Animation;
[StructLayout(LayoutKind.Sequential)]
[NativeMarshalling(typeof(InternalOptionsMarshaler))]
internal struct InternalOptions
{
public ModelType Type;
@@ -410,6 +413,162 @@ namespace FlaxEditor.Content.Import
public int ObjectIndex;
}
[CustomMarshaller(typeof(InternalOptions), MarshalMode.Default, typeof(InternalOptionsMarshaler))]
internal static class InternalOptionsMarshaler
{
[Unmanaged]
[StructLayout(LayoutKind.Sequential)]
internal struct InternalOptionsNative
{
public int Type;
// Geometry
public byte CalculateNormals;
public float SmoothingNormalsAngle;
public byte FlipNormals;
public float SmoothingTangentsAngle;
public byte CalculateTangents;
public byte OptimizeMeshes;
public byte MergeMeshes;
public byte ImportLODs;
public byte ImportVertexColors;
public byte ImportBlendShapes;
public int LightmapUVsSource;
//[MarshalAs(UnmanagedType.LPWStr)]
public IntPtr CollisionMeshesPrefix;
// Transform
public float Scale;
public Quaternion Rotation;
public Float3 Translation;
public byte CenterGeometry;
// Animation
public int Duration;
public float FramesRangeStart;
public float FramesRangeEnd;
public float DefaultFrameRate;
public float SamplingRate;
public byte SkipEmptyCurves;
public byte OptimizeKeyframes;
public byte EnableRootMotion;
//[MarshalAs(UnmanagedType.LPWStr)]
public IntPtr RootNodeName;
// Level Of Detail
public byte GenerateLODs;
public int BaseLOD;
public int LODCount;
public float TriangleReduction;
// Misc
public byte ImportMaterials;
public byte ImportTextures;
public byte RestoreMaterialsOnReimport;
// SDF
public byte GenerateSDF;
public float SDFResolution;
// Splitting
public byte SplitObjects;
public int ObjectIndex;
}
internal static InternalOptions ConvertToManaged(InternalOptionsNative unmanaged) => ToManaged(unmanaged);
internal static InternalOptionsNative ConvertToUnmanaged(InternalOptions managed) => ToNative(managed);
internal static InternalOptions ToManaged(InternalOptionsNative managed)
{
return new InternalOptions()
{
Type = (ModelType)managed.Type,
CalculateNormals = managed.CalculateNormals,
SmoothingNormalsAngle = managed.SmoothingNormalsAngle,
FlipNormals = managed.FlipNormals,
SmoothingTangentsAngle = managed.SmoothingTangentsAngle,
CalculateTangents = managed.CalculateTangents,
OptimizeMeshes = managed.OptimizeMeshes,
MergeMeshes = managed.MergeMeshes,
ImportLODs = managed.ImportLODs,
ImportVertexColors = managed.ImportVertexColors,
ImportBlendShapes = managed.ImportBlendShapes,
LightmapUVsSource = (ModelLightmapUVsSource)managed.LightmapUVsSource,
CollisionMeshesPrefix = ManagedString.ToManaged(managed.CollisionMeshesPrefix),
Scale = managed.Scale,
Rotation = managed.Rotation,
Translation = managed.Translation,
CenterGeometry = managed.CenterGeometry,
Duration = (AnimationDuration)managed.Duration,
FramesRangeStart = managed.FramesRangeStart,
FramesRangeEnd = managed.FramesRangeEnd,
DefaultFrameRate = managed.DefaultFrameRate,
SamplingRate = managed.SamplingRate,
SkipEmptyCurves = managed.SkipEmptyCurves,
OptimizeKeyframes = managed.OptimizeKeyframes,
EnableRootMotion = managed.EnableRootMotion,
RootNodeName = ManagedString.ToManaged(managed.RootNodeName),
GenerateLODs = managed.GenerateLODs,
BaseLOD = managed.BaseLOD,
LODCount = managed.LODCount,
TriangleReduction = managed.TriangleReduction,
ImportMaterials = managed.ImportMaterials,
ImportTextures = managed.ImportTextures,
RestoreMaterialsOnReimport = managed.RestoreMaterialsOnReimport,
GenerateSDF = managed.GenerateSDF,
SDFResolution = managed.SDFResolution,
SplitObjects = managed.SplitObjects,
ObjectIndex = managed.ObjectIndex,
};
}
internal static InternalOptionsNative ToNative(InternalOptions managed)
{
return new InternalOptionsNative()
{
Type = (int)managed.Type,
CalculateNormals = managed.CalculateNormals,
SmoothingNormalsAngle = managed.SmoothingNormalsAngle,
FlipNormals = managed.FlipNormals,
SmoothingTangentsAngle = managed.SmoothingTangentsAngle,
CalculateTangents = managed.CalculateTangents,
OptimizeMeshes = managed.OptimizeMeshes,
MergeMeshes = managed.MergeMeshes,
ImportLODs = managed.ImportLODs,
ImportVertexColors = managed.ImportVertexColors,
ImportBlendShapes = managed.ImportBlendShapes,
LightmapUVsSource = (int)managed.LightmapUVsSource,
CollisionMeshesPrefix = ManagedString.ToNative(managed.CollisionMeshesPrefix),
Scale = managed.Scale,
Rotation = managed.Rotation,
Translation = managed.Translation,
CenterGeometry = managed.CenterGeometry,
Duration = (int)managed.Duration,
FramesRangeStart = managed.FramesRangeStart,
FramesRangeEnd = managed.FramesRangeEnd,
DefaultFrameRate = managed.DefaultFrameRate,
SamplingRate = managed.SamplingRate,
SkipEmptyCurves = managed.SkipEmptyCurves,
OptimizeKeyframes = managed.OptimizeKeyframes,
EnableRootMotion = managed.EnableRootMotion,
RootNodeName = ManagedString.ToNative(managed.RootNodeName),
GenerateLODs = managed.GenerateLODs,
BaseLOD = managed.BaseLOD,
LODCount = managed.LODCount,
TriangleReduction = managed.TriangleReduction,
ImportMaterials = managed.ImportMaterials,
ImportTextures = managed.ImportTextures,
RestoreMaterialsOnReimport = managed.RestoreMaterialsOnReimport,
GenerateSDF = managed.GenerateSDF,
SDFResolution = managed.SDFResolution,
SplitObjects = managed.SplitObjects,
ObjectIndex = managed.ObjectIndex,
};
}
internal static void Free(InternalOptionsNative unmanaged)
{
}
}
internal void ToInternal(out InternalOptions options)
{
options = new InternalOptions
@@ -511,7 +670,7 @@ namespace FlaxEditor.Content.Import
/// Model asset import entry.
/// </summary>
/// <seealso cref="AssetImportEntry" />
public class ModelImportEntry : AssetImportEntry
public partial class ModelImportEntry : AssetImportEntry
{
private ModelImportSettings _settings = new ModelImportSettings();
@@ -548,8 +707,8 @@ namespace FlaxEditor.Content.Import
#region Internal Calls
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void Internal_GetModelImportOptions(string path, out ModelImportSettings.InternalOptions result);
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Content.Import.ModelImportEntry::Internal_GetModelImportOptions", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(StringMarshaller))]
internal static partial void Internal_GetModelImportOptions(string path, out ModelImportSettings.InternalOptions result);
#endregion
}

View File

@@ -1,10 +1,12 @@
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;
using FlaxEngine;
// ReSharper disable InconsistentNaming
@@ -297,6 +299,7 @@ namespace FlaxEditor.Content.Import
public List<SpriteInfo> Sprites = new List<SpriteInfo>();
[StructLayout(LayoutKind.Sequential)]
[NativeMarshalling(typeof(InternalOptionsMarshaler))]
internal struct InternalOptions
{
public TextureFormatType Type;
@@ -318,6 +321,85 @@ namespace FlaxEditor.Content.Import
public string[] SpriteNames;
}
[CustomMarshaller(typeof(InternalOptions), MarshalMode.Default, typeof(InternalOptionsMarshaler))]
internal static class InternalOptionsMarshaler
{
[StructLayout(LayoutKind.Sequential)]
internal struct InternalOptionsNative
{
public byte Type;
public byte IsAtlas;
public byte NeverStream;
public byte Compress;
public byte IndependentChannels;
public byte sRGB;
public byte GenerateMipMaps;
public byte FlipY;
public byte Resize;
public byte PreserveAlphaCoverage;
public float PreserveAlphaCoverageReference;
public float Scale;
public int MaxSize;
public int TextureGroup;
public Int2 Size;
public IntPtr SpriteAreas;
public IntPtr SpriteNames;
}
internal static InternalOptions ConvertToManaged(InternalOptionsNative unmanaged) => ToManaged(unmanaged);
internal static InternalOptionsNative ConvertToUnmanaged(InternalOptions managed) => ToNative(managed);
internal static InternalOptions ToManaged(InternalOptionsNative managed)
{
return new InternalOptions()
{
Type = (TextureFormatType)managed.Type,
IsAtlas = managed.IsAtlas,
NeverStream = managed.NeverStream,
Compress = managed.Compress,
IndependentChannels = managed.IndependentChannels,
sRGB = managed.sRGB,
GenerateMipMaps = managed.GenerateMipMaps,
FlipY = managed.FlipY,
Resize = managed.Resize,
PreserveAlphaCoverage = managed.PreserveAlphaCoverage,
PreserveAlphaCoverageReference = managed.PreserveAlphaCoverageReference,
Scale = managed.Scale,
MaxSize = managed.MaxSize,
TextureGroup = managed.TextureGroup,
Size = managed.Size,
SpriteAreas = managed.SpriteAreas != IntPtr.Zero ? NativeInterop.GCHandleArrayToManagedArray<Rectangle>((ManagedArray)GCHandle.FromIntPtr(managed.SpriteAreas).Target) : null,
SpriteNames = managed.SpriteNames != IntPtr.Zero ? NativeInterop.GCHandleArrayToManagedArray<string>((ManagedArray)GCHandle.FromIntPtr(managed.SpriteNames).Target) : null,
};
}
internal static InternalOptionsNative ToNative(InternalOptions managed)
{
return new InternalOptionsNative()
{
Type = (byte)managed.Type,
IsAtlas = managed.IsAtlas,
NeverStream = managed.NeverStream,
Compress = managed.Compress,
IndependentChannels = managed.IndependentChannels,
sRGB = managed.sRGB,
GenerateMipMaps = managed.GenerateMipMaps,
FlipY = managed.FlipY,
Resize = managed.Resize,
PreserveAlphaCoverage = managed.PreserveAlphaCoverage,
PreserveAlphaCoverageReference = managed.PreserveAlphaCoverageReference,
Scale = managed.Scale,
MaxSize = managed.MaxSize,
TextureGroup = managed.TextureGroup,
Size = managed.Size,
SpriteAreas = managed.SpriteAreas?.Length > 0 ? GCHandle.ToIntPtr(GCHandle.Alloc(ManagedArray.Get(NativeInterop.ManagedArrayToGCHandleArray(managed.SpriteAreas)))) : IntPtr.Zero,
SpriteNames = managed.SpriteNames?.Length > 0 ? GCHandle.ToIntPtr(GCHandle.Alloc(ManagedArray.Get(NativeInterop.ManagedArrayToGCHandleArray(managed.SpriteNames)))) : IntPtr.Zero,
};
}
internal static void Free(InternalOptionsNative unmanaged)
{
}
}
internal void ToInternal(out InternalOptions options)
{
options = new InternalOptions
@@ -406,7 +488,7 @@ namespace FlaxEditor.Content.Import
/// Texture asset import entry.
/// </summary>
/// <seealso cref="AssetImportEntry" />
public class TextureImportEntry : AssetImportEntry
public partial class TextureImportEntry : AssetImportEntry
{
private TextureImportSettings _settings = new TextureImportSettings();
@@ -509,8 +591,9 @@ namespace FlaxEditor.Content.Import
#region Internal Calls
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern bool Internal_GetTextureImportOptions(string path, out TextureImportSettings.InternalOptions result);
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Content.Import.TextureImportEntry::Internal_GetTextureImportOptions", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
[return: MarshalAs(UnmanagedType.U1)]
internal static partial bool Internal_GetTextureImportOptions(string path, out TextureImportSettings.InternalOptions result);
#endregion
}

View File

@@ -4,13 +4,15 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;
using FlaxEditor.CustomEditors.Editors;
using FlaxEditor.Scripting;
using FlaxEngine;
namespace FlaxEditor.CustomEditors
{
internal static class CustomEditorsUtil
internal static partial class CustomEditorsUtil
{
internal static readonly Dictionary<Type, string> InBuildTypeNames = new Dictionary<Type, string>()
{
@@ -129,7 +131,8 @@ namespace FlaxEditor.CustomEditors
return new GenericEditor();
}
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern Type Internal_GetCustomEditor(Type targetType);
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.CustomEditors.CustomEditorsUtil::Internal_GetCustomEditor", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
[return: MarshalUsing(typeof(SystemTypeMarshaller))]
internal static partial Type Internal_GetCustomEditor([MarshalUsing(typeof(SystemTypeMarshaller))] Type targetType);
}
}

View File

@@ -5,6 +5,7 @@ using System.Collections.Generic;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;
using FlaxEditor.Content;
using FlaxEditor.Content.Import;
using FlaxEditor.Content.Settings;
@@ -20,6 +21,8 @@ using FlaxEngine.Assertions;
using FlaxEngine.GUI;
using FlaxEngine.Json;
#pragma warning disable CS1591
namespace FlaxEditor
{
/// <summary>
@@ -59,17 +62,20 @@ namespace FlaxEditor
/// <summary>
/// Gets a value indicating whether this Editor is running a dev instance of the engine.
/// </summary>
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern bool IsDevInstance();
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::IsDevInstance", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
[return: MarshalAs(UnmanagedType.U1)]
internal static partial bool IsDevInstance();
/// <summary>
/// Gets a value indicating whether this Editor is running as official build (distributed via Flax services).
/// </summary>
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern bool IsOfficialBuild();
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::IsOfficialBuild", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
[return: MarshalAs(UnmanagedType.U1)]
internal static partial bool IsOfficialBuild();
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern bool Internal_IsPlayMode();
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::Internal_IsPlayMode", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
[return: MarshalAs(UnmanagedType.U1)]
internal static partial bool Internal_IsPlayMode();
/// <summary>
/// True if the editor is running now in a play mode. Assigned by the managed editor instance.
@@ -1206,6 +1212,7 @@ namespace FlaxEditor
}
[StructLayout(LayoutKind.Sequential)]
[NativeMarshalling(typeof(VisualScriptLocalMarshaller))]
internal struct VisualScriptLocal
{
public string Value;
@@ -1214,7 +1221,48 @@ namespace FlaxEditor
public int BoxId;
}
[CustomMarshaller(typeof(VisualScriptLocal), MarshalMode.Default, typeof(VisualScriptLocalMarshaller))]
internal static class VisualScriptLocalMarshaller
{
[StructLayout(LayoutKind.Sequential)]
internal struct VisualScriptLocalNative
{
public IntPtr Value;
public IntPtr ValueTypeName;
public uint NodeId;
public int BoxId;
}
internal static VisualScriptLocal ConvertToManaged(VisualScriptLocalNative unmanaged) => ToManaged(unmanaged);
internal static VisualScriptLocalNative ConvertToUnmanaged(VisualScriptLocal managed) => ToNative(managed);
internal static VisualScriptLocal ToManaged(VisualScriptLocalNative managed)
{
return new VisualScriptLocal()
{
Value = ManagedString.ToManaged(managed.Value),
ValueTypeName = ManagedString.ToManaged(managed.ValueTypeName),
NodeId = managed.NodeId,
BoxId = managed.BoxId,
};
}
internal static VisualScriptLocalNative ToNative(VisualScriptLocal managed)
{
return new VisualScriptLocalNative()
{
Value = ManagedString.ToNative(managed.Value),
ValueTypeName = ManagedString.ToNative(managed.ValueTypeName),
NodeId = managed.NodeId,
BoxId = managed.BoxId,
};
}
internal static void Free(VisualScriptLocalNative unmanaged)
{
}
}
[StructLayout(LayoutKind.Sequential)]
[NativeMarshalling(typeof(VisualScriptStackFrameMarshaller))]
internal struct VisualScriptStackFrame
{
public VisualScript Script;
@@ -1222,6 +1270,43 @@ namespace FlaxEditor
public int BoxId;
}
[CustomMarshaller(typeof(VisualScriptStackFrame), MarshalMode.Default, typeof(VisualScriptStackFrameMarshaller))]
internal static class VisualScriptStackFrameMarshaller
{
[StructLayout(LayoutKind.Sequential)]
internal struct VisualScriptStackFrameNative
{
public IntPtr Script;
public uint NodeId;
public int BoxId;
}
internal static VisualScriptStackFrame ConvertToManaged(VisualScriptStackFrameNative unmanaged) => ToManaged(unmanaged);
internal static VisualScriptStackFrameNative ConvertToUnmanaged(VisualScriptStackFrame managed) => ToNative(managed);
internal static VisualScriptStackFrame ToManaged(VisualScriptStackFrameNative managed)
{
return new VisualScriptStackFrame()
{
Script = VisualScriptMarshaller.ConvertToManaged(managed.Script),
NodeId = managed.NodeId,
BoxId = managed.BoxId,
};
}
internal static VisualScriptStackFrameNative ToNative(VisualScriptStackFrame managed)
{
return new VisualScriptStackFrameNative()
{
Script = VisualScriptMarshaller.ConvertToUnmanaged(managed.Script),
NodeId = managed.NodeId,
BoxId = managed.BoxId,
};
}
internal static void Free(VisualScriptStackFrameNative unmanaged)
{
}
}
internal void BuildCommand(string arg)
{
if (TryBuildCommand(arg))
@@ -1419,116 +1504,133 @@ namespace FlaxEditor
Instance.StateMachine.StateChanged += RequestStartPlayOnEditMode;
}
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern int Internal_ReadOutputLogs(string[] outMessages, byte[] outLogTypes, long[] outLogTimes);
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::Internal_ReadOutputLogs", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
internal static partial int Internal_ReadOutputLogs([MarshalUsing(typeof(FlaxEngine.ArrayMarshaller<,>), CountElementName = "outCapacity")] ref string[] outMessages, [MarshalUsing(typeof(FlaxEngine.ArrayMarshaller<,>), CountElementName = "outCapacity")] ref byte[] outLogTypes, [MarshalUsing(typeof(FlaxEngine.ArrayMarshaller<,>), CountElementName = "outCapacity")] ref long[] outLogTimes, int outCapacity);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void Internal_SetPlayMode(bool value);
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::Internal_SetPlayMode", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
internal static partial void Internal_SetPlayMode([MarshalAs(UnmanagedType.U1)] bool value);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern string Internal_GetProjectPath();
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::Internal_GetProjectPath", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
internal static partial string Internal_GetProjectPath();
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern bool Internal_CloneAssetFile(string dstPath, string srcPath, ref Guid dstId);
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::Internal_CloneAssetFile", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
[return: MarshalAs(UnmanagedType.U1)]
internal static partial bool Internal_CloneAssetFile(string dstPath, string srcPath, ref Guid dstId);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern bool Internal_Import(string inputPath, string outputPath, IntPtr arg);
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::Internal_Import", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
[return: MarshalAs(UnmanagedType.U1)]
internal static partial bool Internal_Import(string inputPath, string outputPath, IntPtr arg);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern bool Internal_ImportTexture(string inputPath, string outputPath, ref TextureImportSettings.InternalOptions options);
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::Internal_ImportTexture", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
[return: MarshalAs(UnmanagedType.U1)]
internal static partial bool Internal_ImportTexture(string inputPath, string outputPath, ref TextureImportSettings.InternalOptions options);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern bool Internal_ImportModel(string inputPath, string outputPath, ref ModelImportSettings.InternalOptions options);
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::Internal_ImportModel", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
[return: MarshalAs(UnmanagedType.U1)]
internal static partial bool Internal_ImportModel(string inputPath, string outputPath, ref ModelImportSettings.InternalOptions options);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern bool Internal_ImportAudio(string inputPath, string outputPath, ref AudioImportSettings.InternalOptions options);
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::Internal_ImportAudio", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
[return: MarshalAs(UnmanagedType.U1)]
internal static partial bool Internal_ImportAudio(string inputPath, string outputPath, ref AudioImportSettings.InternalOptions options);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void Internal_GetAudioClipMetadata(IntPtr obj, out int originalSize, out int importedSize);
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::Internal_GetAudioClipMetadata", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
internal static partial void Internal_GetAudioClipMetadata(IntPtr obj, out int originalSize, out int importedSize);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern bool Internal_SaveJsonAsset(string outputPath, string data, string typename);
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::Internal_SaveJsonAsset", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
[return: MarshalAs(UnmanagedType.U1)]
internal static partial bool Internal_SaveJsonAsset(string outputPath, string data, string typename);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void Internal_CopyCache(ref Guid dstId, ref Guid srcId);
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::Internal_CopyCache", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
internal static partial void Internal_CopyCache(ref Guid dstId, ref Guid srcId);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void Internal_BakeLightmaps(bool cancel);
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::Internal_BakeLightmaps", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
internal static partial void Internal_BakeLightmaps([MarshalAs(UnmanagedType.U1)] bool cancel);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern string Internal_GetShaderAssetSourceCode(IntPtr obj);
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::Internal_GetShaderAssetSourceCode", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
internal static partial string Internal_GetShaderAssetSourceCode(IntPtr obj);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern bool Internal_CookMeshCollision(string path, CollisionDataType type, IntPtr model, int modelLodIndex, uint materialSlotsMask, ConvexMeshGenerationFlags convexFlags, int convexVertexLimit);
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::Internal_CookMeshCollision", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
[return: MarshalAs(UnmanagedType.U1)]
internal static partial bool Internal_CookMeshCollision(string path, CollisionDataType type, IntPtr model, int modelLodIndex, uint materialSlotsMask, ConvexMeshGenerationFlags convexFlags, int convexVertexLimit);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void Internal_GetCollisionWires(IntPtr collisionData, out Float3[] triangles, out int[] indices);
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::Internal_GetCollisionWires", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
internal static partial void Internal_GetCollisionWires(IntPtr collisionData, [MarshalUsing(typeof(FlaxEngine.ArrayMarshaller<,>), ConstantElementCount = 1)] out Float3[] triangles, [MarshalUsing(typeof(FlaxEngine.ArrayMarshaller<,>), ConstantElementCount = 1)] out int[] indices);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void Internal_GetEditorBoxWithChildren(IntPtr obj, out BoundingBox resultAsRef);
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::Internal_GetEditorBoxWithChildren", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
internal static partial void Internal_GetEditorBoxWithChildren(IntPtr obj, out BoundingBox resultAsRef);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void Internal_SetOptions(ref InternalOptions options);
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::Internal_SetOptions", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
internal static partial void Internal_SetOptions(ref InternalOptions options);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void Internal_DrawNavMesh();
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::Internal_DrawNavMesh", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
internal static partial void Internal_DrawNavMesh();
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void Internal_CloseSplashScreen();
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::Internal_CloseSplashScreen", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
internal static partial void Internal_CloseSplashScreen();
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern bool Internal_CreateAsset(NewAssetType type, string outputPath);
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::Internal_CreateAsset", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
[return: MarshalAs(UnmanagedType.U1)]
internal static partial bool Internal_CreateAsset(NewAssetType type, string outputPath);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern bool Internal_CreateVisualScript(string outputPath, string baseTypename);
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::Internal_CreateVisualScript", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
[return: MarshalAs(UnmanagedType.U1)]
internal static partial bool Internal_CreateVisualScript(string outputPath, string baseTypename);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern string Internal_CanImport(string extension);
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::Internal_CanImport", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
internal static partial string Internal_CanImport(string extension);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern bool Internal_CanExport(string path);
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::Internal_CanExport", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
[return: MarshalAs(UnmanagedType.U1)]
internal static partial bool Internal_CanExport(string path);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern bool Internal_Export(string inputPath, string outputFolder);
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::Internal_Export", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
[return: MarshalAs(UnmanagedType.U1)]
internal static partial bool Internal_Export(string inputPath, string outputFolder);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern bool Internal_GetIsEveryAssemblyLoaded();
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::Internal_GetIsEveryAssemblyLoaded", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
[return: MarshalAs(UnmanagedType.U1)]
internal static partial bool Internal_GetIsEveryAssemblyLoaded();
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern int Internal_GetLastProjectOpenedEngineBuild();
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::Internal_GetLastProjectOpenedEngineBuild", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
internal static partial int Internal_GetLastProjectOpenedEngineBuild();
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern bool Internal_GetIsCSGActive();
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::Internal_GetIsCSGActive", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
[return: MarshalAs(UnmanagedType.U1)]
internal static partial bool Internal_GetIsCSGActive();
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void Internal_RunVisualScriptBreakpointLoopTick(float deltaTime);
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::Internal_RunVisualScriptBreakpointLoopTick", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
internal static partial void Internal_RunVisualScriptBreakpointLoopTick(float deltaTime);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern VisualScriptLocal[] Internal_GetVisualScriptLocals();
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::Internal_GetVisualScriptLocals", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
[return: MarshalUsing(typeof(FlaxEngine.ArrayMarshaller<,>), ConstantElementCount = 1)]
internal static partial VisualScriptLocal[] Internal_GetVisualScriptLocals();
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern VisualScriptStackFrame[] Internal_GetVisualScriptStackFrames();
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::Internal_GetVisualScriptStackFrames", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
[return: MarshalUsing(typeof(FlaxEngine.ArrayMarshaller<,>), ConstantElementCount = 1)]
internal static partial VisualScriptStackFrame[] Internal_GetVisualScriptStackFrames();
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern VisualScriptStackFrame Internal_GetVisualScriptPreviousScopeFrame();
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::Internal_GetVisualScriptPreviousScopeFrame", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
internal static partial VisualScriptStackFrame Internal_GetVisualScriptPreviousScopeFrame();
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern bool Internal_EvaluateVisualScriptLocal(IntPtr script, ref VisualScriptLocal local);
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::Internal_EvaluateVisualScriptLocal", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
[return: MarshalAs(UnmanagedType.U1)]
internal static partial bool Internal_EvaluateVisualScriptLocal(IntPtr script, ref VisualScriptLocal local);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void Internal_DeserializeSceneObject(IntPtr sceneObject, string json);
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::Internal_DeserializeSceneObject", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
internal static partial void Internal_DeserializeSceneObject(IntPtr sceneObject, string json);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void Internal_LoadAsset(ref Guid id);
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::Internal_LoadAsset", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
internal static partial void Internal_LoadAsset(ref Guid id);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern bool Internal_CanSetToRoot(IntPtr prefab, IntPtr newRoot);
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::Internal_CanSetToRoot", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
[return: MarshalAs(UnmanagedType.U1)]
internal static partial bool Internal_CanSetToRoot(IntPtr prefab, IntPtr newRoot);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern float Internal_GetAnimationTime(IntPtr animatedModel);
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::Internal_GetAnimationTime", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
internal static partial float Internal_GetAnimationTime(IntPtr animatedModel);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void Internal_SetAnimationTime(IntPtr animatedModel, float time);
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Editor::Internal_SetAnimationTime", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
internal static partial void Internal_SetAnimationTime(IntPtr animatedModel, float time);
#endregion
}

View File

@@ -10,6 +10,7 @@ using FlaxEditor.GUI.Timeline.Tracks;
using FlaxEditor.Utilities;
using FlaxEditor.Viewport.Previews;
using FlaxEngine;
using FlaxEngine.Utilities;
namespace FlaxEditor.GUI.Timeline
{

View File

@@ -323,20 +323,25 @@ namespace CustomEditorsUtilInternal
{
MonoReflectionType* GetCustomEditor(MonoReflectionType* targetType)
{
SCRIPTING_EXPORT("FlaxEditor.CustomEditors.CustomEditorsUtil::Internal_GetCustomEditor")
return CustomEditorsUtil::GetCustomEditor(targetType);
}
}
namespace LayersAndTagsSettingsInternal1
{
MonoArray* GetCurrentTags()
MonoArray* GetCurrentTags(int* tagsCount)
{
SCRIPTING_EXPORT("FlaxEditor.Content.Settings.LayersAndTagsSettings::GetCurrentTags")
*tagsCount = Level::Tags.Count();
return MUtils::ToArray(Level::Tags);
}
MonoArray* GetCurrentLayers()
MonoArray* GetCurrentLayers(int* layersCount)
{
return MUtils::ToArray(Span<String>(Level::Layers, Math::Max(1, Level::GetNonEmptyLayerNamesCount())));
SCRIPTING_EXPORT("FlaxEditor.Content.Settings.LayersAndTagsSettings::GetCurrentLayers")
*layersCount = Math::Max(1, Level::GetNonEmptyLayerNamesCount());
return MUtils::ToArray(Span<String>(Level::Layers, *layersCount));
}
}
@@ -344,6 +349,7 @@ namespace GameSettingsInternal1
{
void Apply()
{
SCRIPTING_EXPORT("FlaxEditor.Content.Settings.GameSettings::Apply")
LOG(Info, "Apply game settings");
GameSettings::Load();
}
@@ -380,6 +386,7 @@ class ManagedEditorInternal
public:
static bool IsDevInstance()
{
SCRIPTING_EXPORT("FlaxEditor.Editor::IsDevInstance")
#if COMPILE_WITH_DEV_ENV
return true;
#else
@@ -389,6 +396,7 @@ public:
static bool IsOfficialBuild()
{
SCRIPTING_EXPORT("FlaxEditor.Editor::IsOfficialBuild")
#if OFFICIAL_BUILD
return true;
#else
@@ -398,18 +406,20 @@ public:
static bool IsPlayMode()
{
SCRIPTING_EXPORT("FlaxEditor.Editor::Internal_IsPlayMode")
return Editor::IsPlayMode;
}
static int32 ReadOutputLogs(MonoArray* outMessages, MonoArray* outLogTypes, MonoArray* outLogTimes)
static int32 ReadOutputLogs(MonoArray** outMessages, MonoArray** outLogTypes, MonoArray** outLogTimes, int outArraySize)
{
SCRIPTING_EXPORT("FlaxEditor.Editor::Internal_ReadOutputLogs")
ScopeLock lock(CachedLogDataLocker);
if (CachedLogData.IsEmpty() || CachedLogData.Get() == nullptr)
return 0;
int32 count = 0;
const int32 maxCount = (int32)mono_array_length(outMessages);
const int32 maxCount = outArraySize;//(int32)mono_array_length(*outMessages);
byte* ptr = CachedLogData.Get();
byte* end = ptr + CachedLogData.Count();
@@ -429,9 +439,9 @@ public:
auto msgObj = MUtils::ToString(StringView(msg, length));
mono_array_setref(outMessages, count, msgObj);
mono_array_set(outLogTypes, byte, count, type);
mono_array_set(outLogTimes, int64, count, time);
mono_array_setref(*outMessages, count, msgObj);
mono_array_set(*outLogTypes, byte, count, type);
mono_array_set(*outLogTimes, int64, count, time);
count++;
}
@@ -445,21 +455,25 @@ public:
static void SetPlayMode(bool value)
{
SCRIPTING_EXPORT("FlaxEditor.Editor::Internal_SetPlayMode")
Editor::IsPlayMode = value;
}
static MonoString* GetProjectPath()
{
SCRIPTING_EXPORT("FlaxEditor.Editor::Internal_GetProjectPath")
return MUtils::ToString(Editor::Project->ProjectPath);
}
static void CloseSplashScreen()
{
SCRIPTING_EXPORT("FlaxEditor.Editor::Internal_CloseSplashScreen")
Editor::CloseSplashScreen();
}
static bool CloneAssetFile(MonoString* dstPathObj, MonoString* srcPathObj, Guid* dstId)
{
SCRIPTING_EXPORT("FlaxEditor.Editor::Internal_CloneAssetFile")
// Get normalized paths
String dstPath, srcPath;
MUtils::ToString(dstPathObj, dstPath);
@@ -489,6 +503,7 @@ public:
static bool CreateAsset(NewAssetType type, MonoString* outputPathObj)
{
SCRIPTING_EXPORT("FlaxEditor.Editor::Internal_CreateAsset")
String tag;
switch (type)
{
@@ -541,6 +556,7 @@ public:
static bool CreateVisualScript(MonoString* outputPathObj, MonoString* baseTypenameObj)
{
SCRIPTING_EXPORT("FlaxEditor.Editor::Internal_CreateVisualScript")
String outputPath;
MUtils::ToString(outputPathObj, outputPath);
FileSystem::NormalizePath(outputPath);
@@ -551,6 +567,7 @@ public:
static MonoString* CanImport(MonoString* extensionObj)
{
SCRIPTING_EXPORT("FlaxEditor.Editor::Internal_CanImport")
String extension;
MUtils::ToString(extensionObj, extension);
if (extension.Length() > 0 && extension[0] == '.')
@@ -561,6 +578,7 @@ public:
static bool Import(MonoString* inputPathObj, MonoString* outputPathObj, void* arg)
{
SCRIPTING_EXPORT("FlaxEditor.Editor::Internal_Import")
String inputPath, outputPath;
MUtils::ToString(inputPathObj, inputPath);
MUtils::ToString(outputPathObj, outputPath);
@@ -572,6 +590,7 @@ public:
static bool ImportTexture(MonoString* inputPathObj, MonoString* outputPathObj, InternalTextureOptions* optionsObj)
{
SCRIPTING_EXPORT("FlaxEditor.Editor::Internal_ImportTexture")
ImportTexture::Options options;
InternalTextureOptions::Convert(optionsObj, &options);
@@ -580,6 +599,7 @@ public:
static bool ImportModel(MonoString* inputPathObj, MonoString* outputPathObj, InternalModelOptions* optionsObj)
{
SCRIPTING_EXPORT("FlaxEditor.Editor::Internal_ImportModel")
ImportModelFile::Options options;
InternalModelOptions::Convert(optionsObj, &options);
@@ -588,6 +608,7 @@ public:
static bool ImportAudio(MonoString* inputPathObj, MonoString* outputPathObj, InternalAudioOptions* optionsObj)
{
SCRIPTING_EXPORT("FlaxEditor.Editor::Internal_ImportAudio")
ImportAudio::Options options;
InternalAudioOptions::Convert(optionsObj, &options);
@@ -596,6 +617,7 @@ public:
static void GetAudioClipMetadata(AudioClip* clip, int32* originalSize, int32* importedSize)
{
SCRIPTING_EXPORT("FlaxEditor.Editor::Internal_GetAudioClipMetadata")
INTERNAL_CALL_CHECK(clip);
*originalSize = clip->AudioHeader.OriginalSize;
*importedSize = clip->AudioHeader.ImportedSize;
@@ -603,6 +625,7 @@ public:
static bool SaveJsonAsset(MonoString* outputPathObj, MonoString* dataObj, MonoString* dataTypeNameObj)
{
SCRIPTING_EXPORT("FlaxEditor.Editor::Internal_SaveJsonAsset")
String outputPath;
MUtils::ToString(outputPathObj, outputPath);
FileSystem::NormalizePath(outputPath);
@@ -623,6 +646,8 @@ public:
static bool GetTextureImportOptions(MonoString* pathObj, InternalTextureOptions* result)
{
SCRIPTING_EXPORT("FlaxEditor.Content.Import.TextureImportEntry::Internal_GetTextureImportOptions")
String path;
MUtils::ToString(pathObj, path);
FileSystem::NormalizePath(path);
@@ -641,6 +666,7 @@ public:
static void GetModelImportOptions(MonoString* pathObj, InternalModelOptions* result)
{
SCRIPTING_EXPORT("FlaxEditor.Content.Import.ModelImportEntry::Internal_GetModelImportOptions")
// Initialize defaults
ImportModelFile::Options options;
if (const auto* graphicsSettings = GraphicsSettings::Get())
@@ -660,6 +686,7 @@ public:
static bool GetAudioImportOptions(MonoString* pathObj, InternalAudioOptions* result)
{
SCRIPTING_EXPORT("FlaxEditor.Content.Import.AudioImportEntry::Internal_GetAudioImportOptions")
String path;
MUtils::ToString(pathObj, path);
FileSystem::NormalizePath(path);
@@ -678,6 +705,7 @@ public:
static bool CanExport(MonoString* pathObj)
{
SCRIPTING_EXPORT("FlaxEditor.Editor::Internal_CanExport")
#if COMPILE_WITH_ASSETS_EXPORTER
String path;
MUtils::ToString(pathObj, path);
@@ -691,6 +719,7 @@ public:
static bool Export(MonoString* inputPathObj, MonoString* outputFolderObj)
{
SCRIPTING_EXPORT("FlaxEditor.Editor::Internal_Export")
#if COMPILE_WITH_ASSETS_EXPORTER
String inputPath;
MUtils::ToString(inputPathObj, inputPath);
@@ -708,11 +737,13 @@ public:
static void CopyCache(Guid* dstId, Guid* srcId)
{
SCRIPTING_EXPORT("FlaxEditor.Editor::Internal_CopyCache")
ShaderCacheManager::CopyCache(*dstId, *srcId);
}
static void BakeLightmaps(bool cancel)
{
SCRIPTING_EXPORT("FlaxEditor.Editor::Internal_BakeLightmaps")
auto builder = ShadowsOfMordor::Builder::Instance();
if (cancel)
builder->CancelBuild();
@@ -722,6 +753,7 @@ public:
static MonoString* GetShaderAssetSourceCode(BinaryAsset* obj)
{
SCRIPTING_EXPORT("FlaxEditor.Editor::Internal_GetShaderAssetSourceCode")
INTERNAL_CALL_CHECK_RETURN(obj, nullptr);
if (obj->WaitForLoaded())
DebugLog::ThrowNullReference();
@@ -747,6 +779,7 @@ public:
static bool CookMeshCollision(MonoString* pathObj, CollisionDataType type, ModelBase* modelObj, int32 modelLodIndex, uint32 materialSlotsMask, ConvexMeshGenerationFlags convexFlags, int32 convexVertexLimit)
{
SCRIPTING_EXPORT("FlaxEditor.Editor::Internal_CookMeshCollision")
#if COMPILE_WITH_PHYSICS_COOKING
CollisionCooking::Argument arg;
String path;
@@ -767,6 +800,7 @@ public:
static void GetCollisionWires(CollisionData* collisionData, MonoArray** triangles, MonoArray** indices)
{
SCRIPTING_EXPORT("FlaxEditor.Editor::Internal_GetCollisionWires")
if (!collisionData || collisionData->WaitForLoaded() || collisionData->GetOptions().Type == CollisionDataType::None)
return;
@@ -792,32 +826,38 @@ public:
static void GetEditorBoxWithChildren(Actor* obj, BoundingBox* result)
{
SCRIPTING_EXPORT("FlaxEditor.Editor::Internal_GetEditorBoxWithChildren")
INTERNAL_CALL_CHECK(obj);
*result = obj->GetEditorBoxChildren();
}
static void SetOptions(ManagedEditor::InternalOptions* options)
{
SCRIPTING_EXPORT("FlaxEditor.Editor::Internal_SetOptions")
ManagedEditor::ManagedEditorOptions = *options;
}
static void DrawNavMesh()
{
SCRIPTING_EXPORT("FlaxEditor.Editor::Internal_DrawNavMesh")
Navigation::DrawNavMesh();
}
static bool GetIsEveryAssemblyLoaded()
{
SCRIPTING_EXPORT("FlaxEditor.Editor::Internal_GetIsEveryAssemblyLoaded")
return Scripting::IsEveryAssemblyLoaded();
}
static int32 GetLastProjectOpenedEngineBuild()
{
SCRIPTING_EXPORT("FlaxEditor.Editor::Internal_GetLastProjectOpenedEngineBuild")
return Editor::LastProjectOpenedEngineBuild;
}
static bool GetIsCSGActive()
{
SCRIPTING_EXPORT("FlaxEditor.Editor::Internal_GetIsCSGActive")
#if COMPILE_WITH_CSG_BUILDER
return CSG::Builder::IsActive();
#else
@@ -827,6 +867,7 @@ public:
static void RunVisualScriptBreakpointLoopTick(float deltaTime)
{
SCRIPTING_EXPORT("FlaxEditor.Editor::Internal_RunVisualScriptBreakpointLoopTick")
// Update
Platform::Tick();
Engine::HasFocus = (Engine::MainWindow && Engine::MainWindow->IsFocused()) || Platform::GetHasFocus();
@@ -921,6 +962,7 @@ public:
static MonoArray* GetVisualScriptLocals()
{
SCRIPTING_EXPORT("FlaxEditor.Editor::Internal_GetVisualScriptLocals")
MonoArray* result = nullptr;
const auto stack = VisualScripting::GetThreadStackTop();
if (stack && stack->Scope)
@@ -969,6 +1011,7 @@ public:
static MonoArray* GetVisualScriptStackFrames()
{
SCRIPTING_EXPORT("FlaxEditor.Editor::Internal_GetVisualScriptStackFrames")
MonoArray* result = nullptr;
const auto stack = VisualScripting::GetThreadStackTop();
if (stack)
@@ -1022,6 +1065,7 @@ public:
static bool EvaluateVisualScriptLocal(VisualScript* script, VisualScriptLocalManaged* local)
{
SCRIPTING_EXPORT("FlaxEditor.Editor::Internal_EvaluateVisualScriptLocal")
Variant v;
if (VisualScripting::Evaluate(script, VisualScripting::GetThreadStackTop()->Instance, local->NodeId, local->BoxId, v))
{
@@ -1034,6 +1078,7 @@ public:
static void DeserializeSceneObject(SceneObject* sceneObject, MonoString* jsonObj)
{
SCRIPTING_EXPORT("FlaxEditor.Editor::Internal_DeserializeSceneObject")
PROFILE_CPU_NAMED("DeserializeSceneObject");
StringAnsi json;
@@ -1062,11 +1107,13 @@ public:
static void LoadAsset(Guid* id)
{
SCRIPTING_EXPORT("FlaxEditor.Editor::Internal_LoadAsset")
Content::LoadAsync<Asset>(*id);
}
static bool CanSetToRoot(Prefab* prefab, Actor* targetActor)
{
SCRIPTING_EXPORT("FlaxEditor.Editor::Internal_CanSetToRoot")
// Reference: Prefab::ApplyAll(Actor* targetActor)
if (targetActor->GetPrefabID() != prefab->GetID())
return false;
@@ -1089,11 +1136,13 @@ public:
static float GetAnimationTime(AnimatedModel* animatedModel)
{
SCRIPTING_EXPORT("FlaxEditor.Editor::Internal_GetAnimationTime")
return animatedModel && animatedModel->GraphInstance.State.Count() == 1 ? animatedModel->GraphInstance.State[0].Animation.TimePosition : 0.0f;
}
static void SetAnimationTime(AnimatedModel* animatedModel, float time)
{
SCRIPTING_EXPORT("FlaxEditor.Editor::Internal_SetAnimationTime")
if (animatedModel && animatedModel->GraphInstance.State.Count() == 1)
animatedModel->GraphInstance.State[0].Animation.TimePosition = time;
}

View File

@@ -375,7 +375,9 @@ namespace FlaxEditor.Modules
Thread.Sleep(0);
_workerThread.Join(1000);
_workerThread.Abort();
#if !USE_NETCORE
_workerThread.Abort(); // Deprecated in .NET 7
#endif
_workerThread = null;
}

View File

@@ -257,7 +257,7 @@ namespace FlaxEditor.Modules.SourceCodeEditing
{
Profiler.BeginEvent("GetXmlDocs");
var uri = new UriBuilder(assembly.CodeBase);
var uri = new UriBuilder(Utils.GetAssemblyLocation(assembly));
var path = Uri.UnescapeDataString(uri.Path);
var name = assembly.GetName().Name;
var xmlFilePath = Path.Combine(Path.GetDirectoryName(path), name + ".xml");

View File

@@ -391,9 +391,27 @@ namespace FlaxEditor.Modules.SourceCodeEditing
private static bool HasAssemblyValidAnyTypes(Assembly assembly)
{
var codeBase = Utils.GetAssemblyLocation(assembly);
#if USE_NETCORE
if (assembly.ManifestModule.FullyQualifiedName == "<In Memory Module>")
return false;
if (string.IsNullOrEmpty(codeBase))
return true;
// Skip runtime related assemblies
string repositoryUrl = assembly.GetCustomAttributes<AssemblyMetadataAttribute>().FirstOrDefault(x => x.Key == "RepositoryUrl")?.Value ?? "";
if (repositoryUrl != "https://github.com/dotnet/runtime")
return true;
#else
if (string.IsNullOrEmpty(codeBase))
return true;
// Skip assemblies from in-build Mono directory
var codeBase = assembly.CodeBase;
return string.IsNullOrEmpty(codeBase) || !codeBase.Contains("/Mono/lib/mono/");
if (!codeBase.Contains("/Mono/lib/mono/"))
return true;
#endif
return false;
}
private static bool HasAssemblyValidScriptingTypes(Assembly a)

View File

@@ -218,6 +218,16 @@ namespace FlaxEditor.Options
return base.CanConvertFrom(context, sourceType);
}
/// <inheritdoc />
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
if (destinationType == typeof(string))
{
return false;
}
return base.CanConvertTo(context, destinationType);
}
/// <inheritdoc />
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{

View File

@@ -24,7 +24,7 @@ namespace FlaxEditor
var type = plugin.GetType();
var assembly = type.Assembly;
var assemblyPath = assembly.Location;
var assemblyPath = Utils.GetAssemblyLocation(assembly);
var assemblyName = assembly.GetName().Name;
var dotEditorPos = assemblyName.LastIndexOf(".Editor", StringComparison.OrdinalIgnoreCase);
if (dotEditorPos != -1)

View File

@@ -41,8 +41,10 @@ namespace FlaxEditor.Progress.Handlers
private void OnScriptsReload()
{
#if !USE_NETCORE
// Clear types cache
Newtonsoft.Json.JsonSerializer.ClearCache();
#endif
}
private void OnScriptsReloadEnd()

View File

@@ -8,6 +8,110 @@ using Newtonsoft.Json;
namespace FlaxEditor
{
/// <summary>
///
/// </summary>
public class FlaxVersionConverter : JsonConverter
{
// Original implementation is based on Newtonsoft.Json VersionConverter
/// <summary>
/// Writes the JSON representation of the object.
/// </summary>
/// <param name="writer">The <see cref="JsonWriter"/> to write to.</param>
/// <param name="value">The value.</param>
/// <param name="serializer">The calling serializer.</param>
public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
{
if (value == null)
{
writer.WriteNull();
}
else if (value is Version)
{
writer.WriteValue(value.ToString());
}
else
{
throw new JsonSerializationException("Expected Version object value");
}
}
/// <summary>
/// Reads the JSON representation of the object.
/// </summary>
/// <param name="reader">The <see cref="JsonReader"/> to read from.</param>
/// <param name="objectType">Type of the object.</param>
/// <param name="existingValue">The existing property value of the JSON that is being converted.</param>
/// <param name="serializer">The calling serializer.</param>
/// <returns>The object value.</returns>
public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
{
return null;
}
else
{
if (reader.TokenType == JsonToken.StartObject)
{
try
{
reader.Read();
Dictionary<string, int> values = new Dictionary<string, int>();
while (reader.TokenType == JsonToken.PropertyName)
{
var key = reader.Value as string;
reader.Read();
var val = (long)reader.Value;
reader.Read();
values.Add(key, (int)val);
}
int major = 0, minor = 0, build = 0;
values.TryGetValue("Major", out major);
values.TryGetValue("Minor", out minor);
values.TryGetValue("Build", out build);
Version v = new Version(major, minor, build);
return v;
}
catch (Exception ex)
{
throw new Exception(String.Format("Error parsing version string: {0}", reader.Value), ex);
}
}
else if (reader.TokenType == JsonToken.String)
{
try
{
Version v = new Version((string)reader.Value!);
return v;
}
catch (Exception ex)
{
throw new Exception(String.Format("Error parsing version string: {0}", reader.Value), ex);
}
}
else
{
throw new Exception(String.Format("Unexpected token or value when parsing version. Token: {0}, Value: {1}", reader.TokenType, reader.Value));
}
}
}
/// <summary>
/// Determines whether this instance can convert the specified object type.
/// </summary>
/// <param name="objectType">Type of the object.</param>
/// <returns>
/// <c>true</c> if this instance can convert the specified object type; otherwise, <c>false</c>.
/// </returns>
public override bool CanConvert(Type objectType)
{
return objectType == typeof(Version);
}
}
/// <summary>
/// Contains information about Flax project.
/// </summary>
@@ -154,7 +258,7 @@ namespace FlaxEditor
{
// Load
var contents = File.ReadAllText(path);
var project = JsonConvert.DeserializeObject<ProjectInfo>(contents);
var project = JsonConvert.DeserializeObject<ProjectInfo>(contents, new JsonSerializerSettings() { Converters = new[] { new FlaxVersionConverter() } });
project.ProjectPath = path;
project.ProjectFolderPath = StringUtils.NormalizePath(Path.GetDirectoryName(path));

View File

@@ -176,6 +176,7 @@ bool ScriptsBuilder::IsReady()
void ScriptsBuilder::MarkWorkspaceDirty()
{
SCRIPTING_EXPORT("FlaxEditor.ScriptsBuilder::Internal_MarkWorkspaceDirty")
ScopeLock scopeLock(_locker);
_lastSourceCodeEdited = DateTime::Now();
_wasProjectStructureChanged = true;
@@ -183,6 +184,7 @@ void ScriptsBuilder::MarkWorkspaceDirty()
void ScriptsBuilder::CheckForCompile()
{
SCRIPTING_EXPORT("FlaxEditor.ScriptsBuilder::Internal_CheckForCompile")
ScopeLock scopeLock(_locker);
if (IsSourceDirty())
Compile();
@@ -205,6 +207,7 @@ void ScriptsBuilderImpl::onScriptsReloadEnd()
void ScriptsBuilder::Compile()
{
SCRIPTING_EXPORT("FlaxEditor.ScriptsBuilder::Internal_Compile")
ScopeLock scopeLock(_locker);
// Request compile job

View File

@@ -6,7 +6,9 @@ using System.Linq;
using FlaxEditor.Options;
using FlaxEditor.SceneGraph.Actors;
using FlaxEngine;
using FlaxEditor.Utilities;
using FlaxEngine.Utilities;
using Utils = FlaxEngine.Utils;
namespace FlaxEditor.States
{

View File

@@ -1,6 +1,7 @@
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
using FlaxEngine;
using FlaxEditor.Utilities;
using FlaxEngine.Utilities;
namespace FlaxEditor.States

View File

@@ -1,6 +1,7 @@
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
using FlaxEngine;
using FlaxEditor.Utilities;
using FlaxEngine.Utilities;
namespace FlaxEditor.States

View File

@@ -12,6 +12,7 @@ using FlaxEditor.GUI.ContextMenu;
using FlaxEditor.Scripting;
using FlaxEditor.Surface.Elements;
using FlaxEditor.Windows.Assets;
using FlaxEngine.Utilities;
using FlaxEngine;
using FlaxEngine.GUI;
@@ -587,7 +588,7 @@ namespace FlaxEditor.Surface.Archetypes
for (int i = 0; i < _parameters.Length; i++)
{
writer.Write(_parameters[i].Name); // Parameter name
Utilities.VariantUtils.WriteVariantType(writer, TypeUtils.GetType(_parameters[i].Type)); // Box type
VariantUtils.WriteVariantType(writer, TypeUtils.GetType(_parameters[i].Type)); // Box type
}
SetValue(2, stream.ToArray());
}
@@ -605,7 +606,7 @@ namespace FlaxEditor.Surface.Archetypes
for (int i = 0; i < parametersCount; i++)
{
var parameterName = reader.ReadString(); // Parameter name
var boxType = Utilities.VariantUtils.ReadVariantType(reader); // Box type
var boxType = VariantUtils.ReadVariantType(reader); // Box type
MakeBox(i + 1, parameterName, boxType);
}
}
@@ -788,14 +789,14 @@ namespace FlaxEditor.Surface.Archetypes
{
reader.ReadByte(); // Version
signature.IsStatic = reader.ReadBoolean(); // Is Static
signature.ReturnType = Utilities.VariantUtils.ReadVariantScriptType(reader); // Return type
signature.ReturnType = VariantUtils.ReadVariantScriptType(reader); // Return type
var parametersCount = reader.ReadInt32(); // Parameters count
signature.Params = parametersCount != 0 ? new SignatureParamInfo[parametersCount] : Utils.GetEmptyArray<SignatureParamInfo>();
for (int i = 0; i < parametersCount; i++)
{
ref var param = ref signature.Params[i];
param.Name = Utilities.Utils.ReadStr(reader, 11); // Parameter name
param.Type = Utilities.VariantUtils.ReadVariantScriptType(reader); // Parameter type
param.Type = VariantUtils.ReadVariantScriptType(reader); // Parameter type
param.IsOut = reader.ReadByte() != 0; // Is parameter out
}
}
@@ -809,14 +810,14 @@ namespace FlaxEditor.Surface.Archetypes
{
reader.ReadByte(); // Version
signature.IsStatic = reader.ReadBoolean(); // Is Static
signature.ReturnType = Utilities.VariantUtils.ReadVariantScriptType(reader); // Return type
signature.ReturnType = VariantUtils.ReadVariantScriptType(reader); // Return type
var parametersCount = reader.ReadInt32(); // Parameters count
signature.Params = parametersCount != 0 ? new SignatureParamInfo[parametersCount] : Utils.GetEmptyArray<SignatureParamInfo>();
for (int i = 0; i < parametersCount; i++)
{
ref var param = ref signature.Params[i];
param.Name = reader.ReadString(); // Parameter name
param.Type = Utilities.VariantUtils.ReadVariantScriptType(reader); // Parameter type
param.Type = VariantUtils.ReadVariantScriptType(reader); // Parameter type
param.IsOut = reader.ReadByte() != 0; // Is parameter out
}
}
@@ -833,13 +834,13 @@ namespace FlaxEditor.Surface.Archetypes
{
writer.Write((byte)4); // Version
writer.Write(methodInfo.IsStatic); // Is Static
Utilities.VariantUtils.WriteVariantType(writer, methodInfo.ValueType); // Return type
VariantUtils.WriteVariantType(writer, methodInfo.ValueType); // Return type
writer.Write(parameters.Length); // Parameters count
for (int i = 0; i < parameters.Length; i++)
{
ref var param = ref parameters[i];
Utilities.Utils.WriteStr(writer, param.Name, 11); // Parameter name
Utilities.VariantUtils.WriteVariantType(writer, param.Type); // Parameter type
VariantUtils.WriteVariantType(writer, param.Type); // Parameter type
writer.Write((byte)(param.IsOut ? 1 : 0)); // Is parameter out
}
return stream.ToArray();
@@ -1461,14 +1462,14 @@ namespace FlaxEditor.Surface.Archetypes
if (_signature.IsVirtual)
flags |= Flags.Virtual;
writer.Write((byte)flags); // Flags
Utilities.VariantUtils.WriteVariantType(writer, _signature.ReturnType); // Return Type
VariantUtils.WriteVariantType(writer, _signature.ReturnType); // Return Type
var parametersCount = _signature.Parameters?.Length ?? 0;
writer.Write(parametersCount); // Parameters count
for (int i = 0; i < parametersCount; i++)
{
ref var param = ref _signature.Parameters[i];
Utilities.Utils.WriteStrAnsi(writer, param.Name, 13); // Parameter name
Utilities.VariantUtils.WriteVariantType(writer, param.Type); // Parameter type
VariantUtils.WriteVariantType(writer, param.Type); // Parameter type
writer.Write((byte)0); // Is parameter out
writer.Write((byte)0); // Has default value
}
@@ -1497,13 +1498,13 @@ namespace FlaxEditor.Surface.Archetypes
var flags = (Flags)reader.ReadByte(); // Flags
_signature.IsStatic = (flags & Flags.Static) == Flags.Static;
_signature.IsVirtual = (flags & Flags.Virtual) == Flags.Virtual;
_signature.ReturnType = Utilities.VariantUtils.ReadVariantScriptType(reader); // Return Type
_signature.ReturnType = VariantUtils.ReadVariantScriptType(reader); // Return Type
var parametersCount = reader.ReadInt32(); // Parameters count
_signature.Parameters = new Parameter[parametersCount];
for (int i = 0; i < parametersCount; i++)
{
var paramName = Utilities.Utils.ReadStrAnsi(reader, 13); // Parameter name
var paramType = Utilities.VariantUtils.ReadVariantScriptType(reader); // Parameter type
var paramType = VariantUtils.ReadVariantScriptType(reader); // Parameter type
var isOut = reader.ReadByte() != 0; // Is parameter out
var hasDefaultValue = reader.ReadByte() != 0; // Has default value
_signature.Parameters[i] = new Parameter

View File

@@ -6,6 +6,7 @@ using System.Linq;
using System.Reflection;
using FlaxEditor.Scripting;
using FlaxEditor.Surface.Elements;
using FlaxEngine.Utilities;
using FlaxEngine;
namespace FlaxEditor.Surface.Archetypes
@@ -167,7 +168,7 @@ namespace FlaxEditor.Surface.Archetypes
for (int i = 0; i < fieldsLength; i++)
{
Utilities.Utils.WriteStr(writer, fields[i].Name, 11); // Field type
Utilities.VariantUtils.WriteVariantType(writer, fields[i].ValueType); // Field type
VariantUtils.WriteVariantType(writer, fields[i].ValueType); // Field type
}
Values[1] = stream.ToArray();
}
@@ -184,7 +185,7 @@ namespace FlaxEditor.Surface.Archetypes
for (int i = 0; i < fieldsLength; i++)
{
var fieldName = Utilities.Utils.ReadStr(reader, 11); // Field name
var fieldType = Utilities.VariantUtils.ReadVariantType(reader); // Field type
var fieldType = VariantUtils.ReadVariantType(reader); // Field type
MakeBox(i + 1, fieldName, new ScriptType(fieldType));
}
}

View File

@@ -5,6 +5,7 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Loader;
using System.Runtime.Serialization.Formatters.Binary;
using FlaxEditor.CustomEditors;
using FlaxEditor.CustomEditors.Editors;
@@ -118,8 +119,13 @@ namespace FlaxEditor.Surface
using (var stream = new MemoryStream())
{
// Ensure we are in the correct load context (https://github.com/dotnet/runtime/issues/42041)
using var ctx = AssemblyLoadContext.EnterContextualReflection(typeof(Editor).Assembly);
var formatter = new BinaryFormatter();
#pragma warning disable SYSLIB0011
formatter.Serialize(stream, attributes);
#pragma warning restore SYSLIB0011
_oldData = stream.ToArray();
}
editor.Select(new Proxy
@@ -141,8 +147,13 @@ namespace FlaxEditor.Surface
}
using (var stream = new MemoryStream())
{
// Ensure we are in the correct load context (https://github.com/dotnet/runtime/issues/42041)
using var ctx = AssemblyLoadContext.EnterContextualReflection(typeof(Editor).Assembly);
var formatter = new BinaryFormatter();
#pragma warning disable SYSLIB0011
formatter.Serialize(stream, newValue);
#pragma warning restore SYSLIB0011
var newData = stream.ToArray();
if (!_oldData.SequenceEqual(newData))
{

View File

@@ -4,6 +4,8 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Loader;
using System.Runtime.Serialization.Formatters.Binary;
using FlaxEngine;
@@ -54,8 +56,13 @@ namespace FlaxEditor.Surface
{
try
{
// Ensure we are in the correct load context (https://github.com/dotnet/runtime/issues/42041)
using var ctx = AssemblyLoadContext.EnterContextualReflection(typeof(Editor).Assembly);
var formatter = new BinaryFormatter();
#pragma warning disable SYSLIB0011
return (Attribute[])formatter.Deserialize(stream);
#pragma warning restore SYSLIB0011
}
catch (Exception ex)
{
@@ -122,8 +129,13 @@ namespace FlaxEditor.Surface
}
using (var stream = new MemoryStream())
{
// Ensure we are in the correct load context (https://github.com/dotnet/runtime/issues/42041)
using var ctx = AssemblyLoadContext.EnterContextualReflection(typeof(Editor).Assembly);
var formatter = new BinaryFormatter();
#pragma warning disable SYSLIB0011
formatter.Serialize(stream, attributes);
#pragma warning restore SYSLIB0011
AddEntry(AttributeMetaTypeID, stream.ToArray());
}
}

View File

@@ -10,6 +10,7 @@ using FlaxEditor.CustomEditors;
using FlaxEditor.CustomEditors.Elements;
using FlaxEditor.Scripting;
using FlaxEditor.Utilities;
using FlaxEngine.Utilities;
using FlaxEngine;
namespace FlaxEditor.Surface

View File

@@ -8,6 +8,7 @@ using FlaxEditor.Scripting;
using FlaxEditor.Surface.Elements;
using FlaxEditor.Utilities;
using FlaxEngine;
using FlaxEngine.Utilities;
using Utils = FlaxEditor.Utilities.Utils;
namespace FlaxEditor.Surface

View File

@@ -1,6 +1,7 @@
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
using FlaxEngine;
using FlaxEditor.Utilities;
using FlaxEngine.Utilities;
namespace FlaxEditor.Tools.Terrain.Sculpt

View File

@@ -445,7 +445,7 @@ namespace FlaxEditor.Windows
int logCount;
do
{
logCount = Editor.Internal_ReadOutputLogs(_outMessages, _outLogTypes, _outLogTimes);
logCount = Editor.Internal_ReadOutputLogs(ref _outMessages, ref _outLogTypes, ref _outLogTimes, OutCapacity);
for (int i = 0; i < logCount; i++)
{

View File

@@ -19,16 +19,16 @@ namespace FlaxEngine
{
get
{
fixed (char* name = &Name0)
fixed (short* name = Name0)
{
return new string(name);
return new string((char*)name);
}
}
}
internal unsafe bool NameStartsWith(string prefix)
{
fixed (char* name = &Name0)
fixed (short* name = Name0)
{
fixed (char* p = prefix)
{

View File

@@ -3,6 +3,7 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;
namespace FlaxEngine
{
@@ -44,6 +45,7 @@ namespace FlaxEngine
/// The node evaluation context structure.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
[NativeMarshalling(typeof(ContextMarshaler))]
public struct Context
{
/// <summary>
@@ -92,6 +94,61 @@ namespace FlaxEngine
public AnimatedModel Instance;
}
[CustomMarshaller(typeof(Context), MarshalMode.Default, typeof(ContextMarshaler))]
internal static class ContextMarshaler
{
[StructLayout(LayoutKind.Sequential)]
public struct ContextNative
{
public IntPtr Graph;
public IntPtr GraphExecutor;
public IntPtr Node;
public uint NodeId;
public int BoxId;
public float DeltaTime;
public ulong CurrentFrameIndex;
public IntPtr BaseModel;
public IntPtr Instance;
}
internal static Context ConvertToManaged(ContextNative unmanaged) => ToManaged(unmanaged);
internal static ContextNative ConvertToUnmanaged(Context managed) => ToNative(managed);
internal static Context ToManaged(ContextNative managed)
{
return new Context()
{
Graph = managed.Graph,
GraphExecutor = managed.GraphExecutor,
Node = managed.Node,
NodeId = managed.NodeId,
BoxId = managed.BoxId,
DeltaTime = managed.DeltaTime,
CurrentFrameIndex = managed.CurrentFrameIndex,
BaseModel = SkinnedModelMarshaller.ConvertToManaged(managed.BaseModel),
Instance = AnimatedModelMarshaller.ConvertToManaged(managed.Instance),
};
}
internal static ContextNative ToNative(Context managed)
{
return new ContextNative()
{
Graph = managed.Graph,
GraphExecutor = managed.GraphExecutor,
Node = managed.Node,
NodeId = managed.NodeId,
BoxId = managed.BoxId,
DeltaTime = managed.DeltaTime,
CurrentFrameIndex = managed.CurrentFrameIndex,
BaseModel = SkinnedModelMarshaller.ConvertToUnmanaged(managed.BaseModel),
Instance = AnimatedModelMarshaller.ConvertToUnmanaged(managed.Instance),
};
}
internal static void Free(ContextNative unmanaged)
{
}
}
/// <summary>
/// The animation graph 'impulse' connections data container (the actual transfer is done via pointer as it gives better performance).
/// Container for skeleton nodes transformation hierarchy and any other required data.
@@ -203,14 +260,16 @@ namespace FlaxEngine
#region Internal Calls
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern bool Internal_HasConnection(ref CustomNode.Context context, int boxId);
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEngine.AnimationGraph::Internal_HasConnection")]
[return: MarshalAs(UnmanagedType.U1)]
internal static partial bool Internal_HasConnection(ref AnimationGraph.CustomNode.Context context, int boxId);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern object Internal_GetInputValue(ref CustomNode.Context context, int boxId);
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEngine.AnimationGraph::Internal_GetInputValue")]
[return: MarshalUsing(typeof(FlaxEngine.GCHandleMarshaller))]
internal static partial object Internal_GetInputValue(ref AnimationGraph.CustomNode.Context context, int boxId);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern IntPtr Internal_GetOutputImpulseData(ref CustomNode.Context context);
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEngine.AnimationGraph::Internal_GetOutputImpulseData")]
internal static partial IntPtr Internal_GetOutputImpulseData(ref AnimationGraph.CustomNode.Context context);
#endregion
}

View File

@@ -13,8 +13,8 @@
#include "Engine/Content/Assets/SkinnedModel.h"
#if USE_MONO
#include <ThirdParty/mono-2.0/mono/metadata/appdomain.h>
#endif
struct InternalInitData
{
@@ -52,6 +52,7 @@ namespace AnimGraphInternal
{
bool HasConnection(InternalContext* context, int32 boxId)
{
SCRIPTING_EXPORT("FlaxEngine.AnimationGraph::Internal_HasConnection")
const auto box = context->Node->TryGetBox(boxId);
if (box == nullptr)
DebugLog::ThrowArgumentOutOfRange("boxId");
@@ -60,6 +61,7 @@ namespace AnimGraphInternal
MonoObject* GetInputValue(InternalContext* context, int32 boxId)
{
SCRIPTING_EXPORT("FlaxEngine.AnimationGraph::Internal_GetInputValue")
const auto box = context->Node->TryGetBox(boxId);
if (box == nullptr)
DebugLog::ThrowArgumentOutOfRange("boxId");
@@ -77,14 +79,13 @@ namespace AnimGraphInternal
AnimGraphImpulse* GetOutputImpulseData(InternalContext* context)
{
SCRIPTING_EXPORT("FlaxEngine.AnimationGraph::Internal_GetOutputImpulseData")
const auto nodes = context->Node->GetNodes(context->GraphExecutor);
context->GraphExecutor->InitNodes(nodes);
return nodes;
}
}
#endif
void AnimGraphExecutor::initRuntime()
{
#if USE_MONO
@@ -122,7 +123,7 @@ void AnimGraphExecutor::ProcessGroupCustom(Box* boxBase, Node* nodeBase, Value&
internalContext.Instance = context.Data->Object ? context.Data->Object->GetOrCreateManagedInstance() : nullptr;
// Peek managed object
const auto obj = mono_gchandle_get_target(data.Handle);
const auto obj = MUtils::GetGCHandleTarget(data.Handle);
if (obj == nullptr)
{
LOG(Warning, "Custom node instance is null.");
@@ -166,7 +167,7 @@ void AnimGraph::ClearCustomNode(Node* node)
if (data.Handle)
{
#if USE_MONO
mono_gchandle_free(data.Handle);
MUtils::FreeGCHandle(data.Handle);
#endif
data.Handle = 0;
}
@@ -216,7 +217,7 @@ bool AnimGraph::InitCustomNode(Node* node)
// Allocate managed node object (create GC handle to prevent destruction)
const auto obj = type->CreateInstance();
const auto handleGC = mono_gchandle_new(obj, false);
const auto handleGC = MUtils::NewGCHandle(obj, false);
// Initialize node
InternalInitData initData;
@@ -228,7 +229,7 @@ bool AnimGraph::InitCustomNode(Node* node)
load->Invoke(obj, params, &exception);
if (exception)
{
mono_gchandle_free(handleGC);
MUtils::FreeGCHandle(handleGC);
MException ex(exception);
ex.Log(LogType::Warning, TEXT("AnimGraph"));

View File

@@ -516,7 +516,7 @@ public:
/// <summary>
/// The GC handle to the managed instance of the node object.
/// </summary>
uint32 Handle;
gchandle Handle;
};
struct CurveData

View File

@@ -13,6 +13,7 @@
#include "Engine/Threading/MainThreadTask.h"
#include "Engine/Threading/ConcurrentTaskQueue.h"
#if USE_MONO
#include "Engine/Scripting/ManagedCLR/MUtils.h"
#include <ThirdParty/mono-2.0/mono/metadata/mono-gc.h>
#endif
@@ -270,7 +271,7 @@ void Asset::OnManagedInstanceDeleted()
if (_gcHandle)
{
#if USE_MONO
mono_gchandle_free(_gcHandle);
MUtils::FreeGCHandle(_gcHandle);
#endif
_gcHandle = 0;
}

View File

@@ -36,7 +36,10 @@ namespace FlaxEngine
var dataTypeName = DataTypeName;
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
for (int i = 0; i < assemblies.Length; i++)
// Going through the assemblies in order will return collected assemblies first,
// use reverse order instead to find the type currently loaded assemblies instead.
for (int i = assemblies.Length-1; i >= 0; i--)
{
var assembly = assemblies[i];
if (assembly != null)

View File

@@ -32,6 +32,8 @@
#pragma clang diagnostic ignored "-Wnull-dereference"
#pragma clang diagnostic ignored "-Winvalid-noreturn"
#define SCRIPTING_EXPORT(name)
#elif defined(__GNUC__)
#define DLLEXPORT __attribute__ ((__visibility__ ("default")))
@@ -86,6 +88,8 @@
#pragma warning(disable: 4251)
#define SCRIPTING_EXPORT(name) __pragma(comment(linker, "/EXPORT:" #name "=" __FUNCDNAME__))
#else
#pragma error "Unknown compiler."

View File

@@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using FlaxEngine;
namespace FlaxEditor.Content.Settings
@@ -562,8 +563,8 @@ namespace FlaxEditor.Content.Settings
/// <summary>
/// Loads the current game settings asset and applies it to the engine runtime configuration.
/// </summary>
[MethodImpl(MethodImplOptions.InternalCall)]
public static extern void Apply();
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Content.Settings.GameSettings::Apply")]
public static partial void Apply();
#endif
}
}

View File

@@ -2,6 +2,8 @@
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;
using FlaxEngine;
namespace FlaxEditor.Content.Settings
@@ -24,14 +26,26 @@ namespace FlaxEditor.Content.Settings
/// Gets the current tags collection.
/// </summary>
/// <returns>The tags collection.</returns>
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern string[] GetCurrentTags();
internal static string[] GetCurrentTags()
{
return GetCurrentTags(out int _);
}
/// <summary>
/// Gets the current layer names (max 32 items but trims last empty items).
/// </summary>
/// <returns>The layers.</returns>
[MethodImpl(MethodImplOptions.InternalCall)]
public static extern string[] GetCurrentLayers();
public static string[] GetCurrentLayers()
{
return GetCurrentLayers(out int _);
}
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Content.Settings.LayersAndTagsSettings::GetCurrentTags", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
[return: MarshalUsing(typeof(FlaxEngine.ArrayMarshaller<,>), CountElementName = "tagCount")]
internal static partial string[] GetCurrentTags(out int tagCount);
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEditor.Content.Settings.LayersAndTagsSettings::GetCurrentLayers", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
[return: MarshalUsing(typeof(FlaxEngine.ArrayMarshaller<,>), CountElementName = "layerCount")]
internal static partial string[] GetCurrentLayers(out int layerCount);
}
}

View File

@@ -18,6 +18,16 @@ namespace FlaxEngine.TypeConverters
return base.CanConvertFrom(context, sourceType);
}
/// <inheritdoc />
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
if (destinationType == typeof(string))
{
return false;
}
return base.CanConvertTo(context, destinationType);
}
/// <inheritdoc />
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
@@ -25,9 +35,9 @@ namespace FlaxEngine.TypeConverters
{
string[] v = str.Split(',');
if (v.Length == 4)
return new Color(float.Parse(v[0]), float.Parse(v[1]), float.Parse(v[2]), float.Parse(v[3]));
return new Color(float.Parse(v[0], culture), float.Parse(v[1], culture), float.Parse(v[2], culture), float.Parse(v[3], culture));
if (v.Length == 3)
return new Color(float.Parse(v[0]), float.Parse(v[1]), float.Parse(v[2]), 1.0f);
return new Color(float.Parse(v[0], culture), float.Parse(v[1], culture), float.Parse(v[2], culture), 1.0f);
throw new FormatException("Invalid Color value format.");
}
return base.ConvertFrom(context, culture, value);
@@ -39,7 +49,7 @@ namespace FlaxEngine.TypeConverters
if (destinationType == typeof(string))
{
var v = (Color)value;
return v.R + "," + v.G + "," + v.B + "," + v.A;
return v.R.ToString(culture) + "," + v.G.ToString(culture) + "," + v.B.ToString(culture) + "," + v.A.ToString(culture);
}
return base.ConvertTo(context, culture, value, destinationType);
}

View File

@@ -18,13 +18,23 @@ namespace FlaxEngine.TypeConverters
return base.CanConvertFrom(context, sourceType);
}
/// <inheritdoc />
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
if (destinationType == typeof(string))
{
return false;
}
return base.CanConvertTo(context, destinationType);
}
/// <inheritdoc />
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value is string str)
{
string[] v = str.Split(',');
return new Double2(double.Parse(v[0]), double.Parse(v[1]));
return new Double2(double.Parse(v[0], culture), double.Parse(v[1], culture));
}
return base.ConvertFrom(context, culture, value);
}
@@ -35,7 +45,7 @@ namespace FlaxEngine.TypeConverters
if (destinationType == typeof(string))
{
var v = (Double2)value;
return v.X + "," + v.Y;
return v.X.ToString(culture) + "," + v.Y.ToString(culture);
}
return base.ConvertTo(context, culture, value, destinationType);
}

View File

@@ -18,13 +18,23 @@ namespace FlaxEngine.TypeConverters
return base.CanConvertFrom(context, sourceType);
}
/// <inheritdoc />
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
if (destinationType == typeof(string))
{
return false;
}
return base.CanConvertTo(context, destinationType);
}
/// <inheritdoc />
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value is string str)
{
string[] v = str.Split(',');
return new Double3(double.Parse(v[0]), double.Parse(v[1]), double.Parse(v[2]));
return new Double3(double.Parse(v[0], culture), double.Parse(v[1], culture), double.Parse(v[2], culture));
}
return base.ConvertFrom(context, culture, value);
}
@@ -35,7 +45,7 @@ namespace FlaxEngine.TypeConverters
if (destinationType == typeof(string))
{
var v = (Double3)value;
return v.X + "," + v.Y + "," + v.Z;
return v.X.ToString(culture) + "," + v.Y.ToString(culture) + "," + v.Z.ToString(culture);
}
return base.ConvertTo(context, culture, value, destinationType);
}

View File

@@ -18,13 +18,23 @@ namespace FlaxEngine.TypeConverters
return base.CanConvertFrom(context, sourceType);
}
/// <inheritdoc />
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
if (destinationType == typeof(string))
{
return false;
}
return base.CanConvertTo(context, destinationType);
}
/// <inheritdoc />
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value is string str)
{
string[] v = str.Split(',');
return new Double4(double.Parse(v[0]), double.Parse(v[1]), double.Parse(v[2]), double.Parse(v[3]));
return new Double4(double.Parse(v[0], culture), double.Parse(v[1], culture), double.Parse(v[2], culture), double.Parse(v[3], culture));
}
return base.ConvertFrom(context, culture, value);
}
@@ -35,7 +45,7 @@ namespace FlaxEngine.TypeConverters
if (destinationType == typeof(string))
{
var v = (Double4)value;
return v.X + "," + v.Y + "," + v.Z + "," + v.W;
return v.X.ToString(culture) + "," + v.Y.ToString(culture) + "," + v.Z.ToString(culture) + "," + v.W.ToString(culture);
}
return base.ConvertTo(context, culture, value, destinationType);
}

View File

@@ -18,13 +18,23 @@ namespace FlaxEngine.TypeConverters
return base.CanConvertFrom(context, sourceType);
}
/// <inheritdoc />
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
if (destinationType == typeof(string))
{
return false;
}
return base.CanConvertTo(context, destinationType);
}
/// <inheritdoc />
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value is string str)
{
string[] v = str.Split(',');
return new Float2(float.Parse(v[0]), float.Parse(v[1]));
return new Float2(float.Parse(v[0], culture), float.Parse(v[1], culture));
}
return base.ConvertFrom(context, culture, value);
}
@@ -35,7 +45,7 @@ namespace FlaxEngine.TypeConverters
if (destinationType == typeof(string))
{
var v = (Float2)value;
return v.X + "," + v.Y;
return v.X.ToString(culture) + "," + v.Y.ToString(culture);
}
return base.ConvertTo(context, culture, value, destinationType);
}

View File

@@ -18,13 +18,23 @@ namespace FlaxEngine.TypeConverters
return base.CanConvertFrom(context, sourceType);
}
/// <inheritdoc />
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
if (destinationType == typeof(string))
{
return false;
}
return base.CanConvertTo(context, destinationType);
}
/// <inheritdoc />
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value is string str)
{
string[] v = str.Split(',');
return new Float3(float.Parse(v[0]), float.Parse(v[1]), float.Parse(v[2]));
return new Float3(float.Parse(v[0], culture), float.Parse(v[1], culture), float.Parse(v[2], culture));
}
return base.ConvertFrom(context, culture, value);
}
@@ -35,7 +45,7 @@ namespace FlaxEngine.TypeConverters
if (destinationType == typeof(string))
{
var v = (Float3)value;
return v.X + "," + v.Y + "," + v.Z;
return v.X.ToString(culture) + "," + v.Y.ToString(culture) + "," + v.Z.ToString(culture);
}
return base.ConvertTo(context, culture, value, destinationType);
}

View File

@@ -18,13 +18,23 @@ namespace FlaxEngine.TypeConverters
return base.CanConvertFrom(context, sourceType);
}
/// <inheritdoc />
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
if (destinationType == typeof(string))
{
return false;
}
return base.CanConvertTo(context, destinationType);
}
/// <inheritdoc />
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value is string str)
{
string[] v = str.Split(',');
return new Float4(float.Parse(v[0]), float.Parse(v[1]), float.Parse(v[2]), float.Parse(v[3]));
return new Float4(float.Parse(v[0], culture), float.Parse(v[1], culture), float.Parse(v[2], culture), float.Parse(v[3], culture));
}
return base.ConvertFrom(context, culture, value);
}
@@ -35,7 +45,7 @@ namespace FlaxEngine.TypeConverters
if (destinationType == typeof(string))
{
var v = (Float4)value;
return v.X + "," + v.Y + "," + v.Z + "," + v.W;
return v.X.ToString(culture) + "," + v.Y.ToString(culture) + "," + v.Z.ToString(culture) + "," + v.W.ToString(culture);
}
return base.ConvertTo(context, culture, value, destinationType);
}

View File

@@ -18,13 +18,23 @@ namespace FlaxEngine.TypeConverters
return base.CanConvertFrom(context, sourceType);
}
/// <inheritdoc />
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
if (destinationType == typeof(string))
{
return false;
}
return base.CanConvertTo(context, destinationType);
}
/// <inheritdoc />
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value is string str)
{
string[] v = str.Split(',');
return new Int2(int.Parse(v[0]), int.Parse(v[1]));
return new Int2(int.Parse(v[0], culture), int.Parse(v[1], culture));
}
return base.ConvertFrom(context, culture, value);
}
@@ -35,7 +45,7 @@ namespace FlaxEngine.TypeConverters
if (destinationType == typeof(string))
{
var v = (Int2)value;
return v.X + "," + v.Y;
return v.X.ToString(culture) + "," + v.Y.ToString(culture);
}
return base.ConvertTo(context, culture, value, destinationType);
}

View File

@@ -18,13 +18,23 @@ namespace FlaxEngine.TypeConverters
return base.CanConvertFrom(context, sourceType);
}
/// <inheritdoc />
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
if (destinationType == typeof(string))
{
return false;
}
return base.CanConvertTo(context, destinationType);
}
/// <inheritdoc />
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value is string str)
{
string[] v = str.Split(',');
return new Int3(int.Parse(v[0]), int.Parse(v[1]), int.Parse(v[2]));
return new Int3(int.Parse(v[0], culture), int.Parse(v[1], culture), int.Parse(v[2], culture));
}
return base.ConvertFrom(context, culture, value);
}
@@ -35,7 +45,7 @@ namespace FlaxEngine.TypeConverters
if (destinationType == typeof(string))
{
var v = (Int3)value;
return v.X + "," + v.Y + "," + v.Z;
return v.X.ToString(culture) + "," + v.Y.ToString(culture) + "," + v.Z.ToString(culture);
}
return base.ConvertTo(context, culture, value, destinationType);
}

View File

@@ -18,13 +18,23 @@ namespace FlaxEngine.TypeConverters
return base.CanConvertFrom(context, sourceType);
}
/// <inheritdoc />
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
if (destinationType == typeof(string))
{
return false;
}
return base.CanConvertTo(context, destinationType);
}
/// <inheritdoc />
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value is string str)
{
string[] v = str.Split(',');
return new Int4(int.Parse(v[0]), int.Parse(v[1]), int.Parse(v[2]), int.Parse(v[3]));
return new Int4(int.Parse(v[0], culture), int.Parse(v[1], culture), int.Parse(v[2], culture), int.Parse(v[3], culture));
}
return base.ConvertFrom(context, culture, value);
}
@@ -35,7 +45,7 @@ namespace FlaxEngine.TypeConverters
if (destinationType == typeof(string))
{
var v = (Int4)value;
return v.X + "," + v.Y + "," + v.Z + "," + v.W;
return v.X.ToString(culture) + "," + v.Y.ToString(culture) + "," + v.Z.ToString(culture) + "," + v.W.ToString(culture);
}
return base.ConvertTo(context, culture, value, destinationType);
}

View File

@@ -18,13 +18,23 @@ namespace FlaxEngine.TypeConverters
return base.CanConvertFrom(context, sourceType);
}
/// <inheritdoc />
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
if (destinationType == typeof(string))
{
return false;
}
return base.CanConvertTo(context, destinationType);
}
/// <inheritdoc />
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value is string str)
{
string[] v = str.Split(',');
return new Quaternion(float.Parse(v[0]), float.Parse(v[1]), float.Parse(v[2]), float.Parse(v[3]));
return new Quaternion(float.Parse(v[0], culture), float.Parse(v[1], culture), float.Parse(v[2], culture), float.Parse(v[3], culture));
}
return base.ConvertFrom(context, culture, value);
}
@@ -35,7 +45,7 @@ namespace FlaxEngine.TypeConverters
if (destinationType == typeof(string))
{
var v = (Quaternion)value;
return v.X + "," + v.Y + "," + v.Z + "," + v.W;
return v.X.ToString(culture) + "," + v.Y.ToString(culture) + "," + v.Z.ToString(culture) + "," + v.W.ToString(culture);
}
return base.ConvertTo(context, culture, value, destinationType);
}

View File

@@ -18,13 +18,23 @@ namespace FlaxEngine.TypeConverters
return base.CanConvertFrom(context, sourceType);
}
/// <inheritdoc />
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
if (destinationType == typeof(string))
{
return false;
}
return base.CanConvertTo(context, destinationType);
}
/// <inheritdoc />
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value is string str)
{
string[] v = str.Split(',');
return new Vector2(float.Parse(v[0]), float.Parse(v[1]));
return new Vector2(float.Parse(v[0], culture), float.Parse(v[1], culture));
}
return base.ConvertFrom(context, culture, value);
}
@@ -35,7 +45,7 @@ namespace FlaxEngine.TypeConverters
if (destinationType == typeof(string))
{
var v = (Vector2)value;
return v.X + "," + v.Y;
return v.X.ToString(culture) + "," + v.Y.ToString(culture);
}
return base.ConvertTo(context, culture, value, destinationType);
}

View File

@@ -18,13 +18,23 @@ namespace FlaxEngine.TypeConverters
return base.CanConvertFrom(context, sourceType);
}
/// <inheritdoc />
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
if (destinationType == typeof(string))
{
return false;
}
return base.CanConvertTo(context, destinationType);
}
/// <inheritdoc />
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value is string str)
{
string[] v = str.Split(',');
return new Vector3(float.Parse(v[0]), float.Parse(v[1]), float.Parse(v[2]));
return new Vector3(float.Parse(v[0], culture), float.Parse(v[1], culture), float.Parse(v[2], culture));
}
return base.ConvertFrom(context, culture, value);
}
@@ -35,7 +45,7 @@ namespace FlaxEngine.TypeConverters
if (destinationType == typeof(string))
{
var v = (Vector3)value;
return v.X + "," + v.Y + "," + v.Z;
return v.X.ToString(culture) + "," + v.Y.ToString(culture) + "," + v.Z.ToString(culture);
}
return base.ConvertTo(context, culture, value, destinationType);
}

View File

@@ -18,13 +18,23 @@ namespace FlaxEngine.TypeConverters
return base.CanConvertFrom(context, sourceType);
}
/// <inheritdoc />
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
if (destinationType == typeof(string))
{
return false;
}
return base.CanConvertTo(context, destinationType);
}
/// <inheritdoc />
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value is string str)
{
string[] v = str.Split(',');
return new Vector4(float.Parse(v[0]), float.Parse(v[1]), float.Parse(v[2]), float.Parse(v[3]));
return new Vector4(float.Parse(v[0], culture), float.Parse(v[1], culture), float.Parse(v[2], culture), float.Parse(v[3], culture));
}
return base.ConvertFrom(context, culture, value);
}
@@ -35,7 +45,7 @@ namespace FlaxEngine.TypeConverters
if (destinationType == typeof(string))
{
var v = (Vector4)value;
return v.X + "," + v.Y + "," + v.Z + "," + v.W;
return v.X.ToString(culture) + "," + v.Y.ToString(culture) + "," + v.Z.ToString(culture) + "," + v.W.ToString(culture);
}
return base.ConvertTo(context, culture, value, destinationType);
}

View File

@@ -619,7 +619,11 @@ Variant::Variant(Asset* v)
Variant::Variant(_MonoObject* v)
: Type(VariantType::ManagedObject, v ? mono_object_get_class(v) : nullptr)
{
AsUint = v ? mono_gchandle_new(v, true) : 0;
#if USE_NETCORE
AsUint64 = v ? MUtils::NewGCHandle(v, true) : 0;
#else
AsUint = v ? MUtils::NewGCHandle(v, true) : 0;
#endif
}
#else
@@ -957,9 +961,13 @@ Variant::~Variant()
Delete(AsDictionary);
break;
case VariantType::ManagedObject:
#if USE_MONO
#if USE_NETCORE
if (AsUint64)
MUtils::FreeGCHandle(AsUint64);
break;
#elif USE_MONO
if (AsUint)
mono_gchandle_free(AsUint);
MUtils::FreeGCHandle(AsUint);
break;
#endif
default: ;
@@ -1088,8 +1096,10 @@ Variant& Variant::operator=(const Variant& other)
AsDictionary = New<Dictionary<Variant, Variant>>(*other.AsDictionary);
break;
case VariantType::ManagedObject:
#if USE_MONO
AsUint = other.AsUint ? mono_gchandle_new(mono_gchandle_get_target(other.AsUint), true) : 0;
#if USE_NETCORE
AsUint64 = other.AsUint64 ? MUtils::NewGCHandle(MUtils::GetGCHandleTarget(other.AsUint64), true) : 0;
#elif USE_MONO
AsUint = other.AsUint ? MUtils::NewGCHandle(MUtils::GetGCHandleTarget(other.AsUint), true) : 0;
#endif
break;
case VariantType::Null:
@@ -1217,7 +1227,7 @@ bool Variant::operator==(const Variant& other) const
case VariantType::ManagedObject:
#if USE_MONO
// TODO: invoke C# Equality logic?
return AsUint == other.AsUint || mono_gchandle_get_target(AsUint) == mono_gchandle_get_target(other.AsUint);
return AsUint == other.AsUint || MUtils::GetGCHandleTarget(AsUint) == MUtils::GetGCHandleTarget(other.AsUint);
#endif
default:
return false;
@@ -1308,8 +1318,10 @@ Variant::operator bool() const
case VariantType::Asset:
return AsAsset != nullptr;
case VariantType::ManagedObject:
#if USE_MONO
return AsUint != 0 && mono_gchandle_get_target(AsUint) != nullptr;
#if USE_NETCORE
return AsUint64 != 0 && MUtils::GetGCHandleTarget(AsUint64) != nullptr;
#elif USE_MONO
return AsUint != 0 && MUtils::GetGCHandleTarget(AsUint) != nullptr;
#endif
default:
return false;
@@ -1578,8 +1590,10 @@ Variant::operator void*() const
case VariantType::Blob:
return AsBlob.Data;
case VariantType::ManagedObject:
#if USE_MONO
return AsUint ? mono_gchandle_get_target(AsUint) : nullptr;
#if USE_NETCORE
return AsUint64 ? MUtils::GetGCHandleTarget(AsUint64) : nullptr;
#elif USE_MONO
return AsUint ? MUtils::GetGCHandleTarget(AsUint) : nullptr;
#endif
default:
return nullptr;
@@ -1623,8 +1637,10 @@ Variant::operator ScriptingObject*() const
Variant::operator _MonoObject*() const
{
#if USE_MONO
return Type.Type == VariantType::ManagedObject && AsUint ? mono_gchandle_get_target(AsUint) : nullptr;
#if USE_NETCORE
return Type.Type == VariantType::ManagedObject && AsUint64 ? MUtils::GetGCHandleTarget(AsUint64) : nullptr;
#elif USE_MONO
return Type.Type == VariantType::ManagedObject && AsUint ? MUtils::GetGCHandleTarget(AsUint) : nullptr;
#else
return nullptr;
#endif
@@ -2337,9 +2353,14 @@ void Variant::SetType(const VariantType& type)
Delete(AsDictionary);
break;
case VariantType::ManagedObject:
#if USE_MONO
#if USE_NETCORE
if (AsUint64)
MUtils::FreeGCHandle(AsUint64);
break;
#elif USE_MONO
if (AsUint)
mono_gchandle_free(AsUint);
MUtils::FreeGCHandle(AsUint);
break;
#endif
break;
default: ;
@@ -2447,9 +2468,14 @@ void Variant::SetType(VariantType&& type)
Delete(AsDictionary);
break;
case VariantType::ManagedObject:
#if USE_MONO
#if USE_NETCORE
if (AsUint64)
MUtils::FreeGCHandle(AsUint64);
break;
#elif USE_MONO
if (AsUint)
mono_gchandle_free(AsUint);
MUtils::FreeGCHandle(AsUint);
break;
#endif
break;
default: ;
@@ -2632,7 +2658,11 @@ void Variant::SetManagedObject(_MonoObject* object)
{
if (Type.Type != VariantType::ManagedObject)
SetType(VariantType(VariantType::ManagedObject, mono_object_get_class(object)));
AsUint = mono_gchandle_new(object, true);
#if USE_NETCORE
AsUint64 = MUtils::NewGCHandle(object, true);
#else
AsUint = MUtils::NewGCHandle(object, true);
#endif
}
else
{
@@ -2751,8 +2781,10 @@ String Variant::ToString() const
case VariantType::Typename:
return String((const char*)AsBlob.Data, AsBlob.Length ? AsBlob.Length - 1 : 0);
case VariantType::ManagedObject:
#if USE_MONO
return AsUint ? String(MUtils::ToString(mono_object_to_string(mono_gchandle_get_target(AsUint), nullptr))) : TEXT("null");
#if USE_NETCORE
return AsUint64 ? String(MUtils::ToString(mono_object_to_string(MUtils::GetGCHandleTarget(AsUint64), nullptr))) : TEXT("null");
#elif USE_MONO
return AsUint ? String(MUtils::ToString(mono_object_to_string(MUtils::GetGCHandleTarget(AsUint), nullptr))) : TEXT("null");
#endif
default:
return String::Empty;
@@ -3671,7 +3703,12 @@ void Variant::AllocStructure()
Platform::MemoryCopy(AsBlob.Data, data, AsBlob.Length);
#else
Type.Type = VariantType::ManagedObject;
AsUint = mono_gchandle_new(instance, true);
#if USE_NETCORE
AsUint64 = MUtils::NewGCHandle(instance, true);
#else
AsUint = MUtils::NewGCHandle(instance, true);
#endif
#endif
}
else
@@ -3763,8 +3800,10 @@ uint32 GetHash(const Variant& key)
case VariantType::Typename:
return GetHash((const char*)key.AsBlob.Data);
case VariantType::ManagedObject:
#if USE_MONO
return key.AsUint ? (uint32)mono_object_hash(mono_gchandle_get_target(key.AsUint)) : 0;
#if USE_NETCORE
return key.AsUint64 ? (uint32)mono_object_hash(MUtils::GetGCHandleTarget(key.AsUint64)) : 0;
#elif USE_MONO
return key.AsUint ? (uint32)mono_object_hash(MUtils::GetGCHandleTarget(key.AsUint)) : 0;
#endif
default:
return 0;

View File

@@ -4,10 +4,12 @@ using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Security;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;
namespace FlaxEngine
{
internal sealed class DebugLogHandler : ILogHandler
internal partial class DebugLogHandler : ILogHandler
{
/// <summary>
/// Occurs on sending a log message.
@@ -64,14 +66,14 @@ namespace FlaxEngine
Debug.Logger.LogException(exception);
}
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void Internal_LogWrite(LogType level, string msg);
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEngine.DebugLogHandler::Internal_LogWrite", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
internal static partial void Internal_LogWrite(LogType level, string msg);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void Internal_Log(LogType level, string msg, IntPtr obj, string stackTrace);
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEngine.DebugLogHandler::Internal_Log", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
internal static partial void Internal_Log(LogType level, string msg, IntPtr obj, string stackTrace);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void Internal_LogException(Exception exception, IntPtr obj);
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEngine.DebugLogHandler::Internal_LogException", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
internal static partial void Internal_LogException([MarshalUsing(typeof(FlaxEngine.ExceptionMarshaller))] Exception exception, IntPtr obj);
[SecuritySafeCritical]
public static string Internal_GetStackTrace()

File diff suppressed because it is too large Load Diff

View File

@@ -1277,6 +1277,7 @@ bool Level::SaveAllScenes()
void Level::SaveAllScenesAsync()
{
SCRIPTING_EXPORT("FlaxEngine.Level::Internal_SaveAllScenesAsync")
ScopeLock lock(_sceneActionsLocker);
for (int32 i = 0; i < Scenes.Count(); i++)
_sceneActions.Enqueue(New<SaveSceneAction>(Scenes[i]));
@@ -1381,6 +1382,7 @@ bool Level::UnloadAllScenes()
void Level::UnloadAllScenesAsync()
{
SCRIPTING_EXPORT("FlaxEngine.Level::Internal_UnloadAllScenesAsync")
ScopeLock lock(_sceneActionsLocker);
_sceneActions.Enqueue(New<UnloadScenesAction>());
}

View File

@@ -303,6 +303,7 @@ bool NetworkManager::StartHost()
void NetworkManager::Stop()
{
SCRIPTING_EXPORT("FlaxEngine.Networking.NetworkManager::Internal_Stop");
if (Mode == NetworkManagerMode::Offline && State == NetworkConnectionState::Offline)
return;
PROFILE_CPU();

View File

@@ -175,6 +175,7 @@ public:
/// <param name="size">Size of the memory to copy in bytes</param>
FORCE_INLINE static void MemoryCopy(void* dst, const void* src, uint64 size)
{
SCRIPTING_EXPORT("FlaxEngine.Utils::MemoryCopy")
memcpy(dst, src, static_cast<size_t>(size));
}
@@ -196,6 +197,7 @@ public:
/// <param name="size">Size of the memory to clear in bytes</param>
FORCE_INLINE static void MemoryClear(void* dst, uint64 size)
{
SCRIPTING_EXPORT("FlaxEngine.Utils::MemoryClear")
memset(dst, 0, static_cast<size_t>(size));
}
@@ -207,6 +209,7 @@ public:
/// <param name="size">Size of the memory to compare in bytes.</param>
FORCE_INLINE static int32 MemoryCompare(const void* buf1, const void* buf2, uint64 size)
{
SCRIPTING_EXPORT("FlaxEngine.Utils::MemoryCompare")
return memcmp(buf1, buf2, static_cast<size_t>(size));
}

View File

@@ -1658,6 +1658,7 @@ DragDropEffect LinuxWindow::DoDragDrop(const StringView& data)
void LinuxClipboard::Clear()
{
SCRIPTING_EXPORT("FlaxEngine.Clipboard::Internal_Clear");
SetText(StringView::Empty);
}

View File

@@ -110,6 +110,7 @@ Float2 MacUtils::GetScreensOrigin()
void MacClipboard::Clear()
{
SCRIPTING_EXPORT("FlaxEngine.Clipboard::Internal_Clear");
NSPasteboard* pasteboard = [NSPasteboard generalPasteboard];
[pasteboard clearContents];
}

View File

@@ -16,6 +16,7 @@ typedef struct _DROPFILES
void WindowsClipboard::Clear()
{
SCRIPTING_EXPORT("FlaxEngine.Clipboard::Internal_Clear");
OpenClipboard(nullptr);
EmptyClipboard();
CloseClipboard();

View File

@@ -699,6 +699,7 @@ void Render2D::Begin(GPUContext* context, GPUTextureView* output, GPUTextureView
void Render2D::End()
{
SCRIPTING_EXPORT("FlaxEngine.Render2D::Internal_End")
RENDER2D_CHECK_RENDERING_STATE;
ASSERT(Context != nullptr && Output != nullptr);
ASSERT(GUIShader != nullptr);
@@ -814,6 +815,7 @@ void Render2D::PeekTransform(Matrix3x3& transform)
void Render2D::PopTransform()
{
SCRIPTING_EXPORT("FlaxEngine.Render2D::Internal_PopTransform")
RENDER2D_CHECK_RENDERING_STATE;
ASSERT(TransformLayersStack.HasItems());
@@ -855,6 +857,7 @@ void Render2D::PeekClip(Rectangle& clipRect)
void Render2D::PopClip()
{
SCRIPTING_EXPORT("FlaxEngine.Render2D::Internal_PopClip")
RENDER2D_CHECK_RENDERING_STATE;
ClipLayersStack.Pop();
@@ -876,6 +879,7 @@ void Render2D::PeekTint(Color& tint)
void Render2D::PopTint()
{
SCRIPTING_EXPORT("FlaxEngine.Render2D::Internal_PopTint")
RENDER2D_CHECK_RENDERING_STATE;
TintLayersStack.Pop();

View File

@@ -64,8 +64,7 @@ namespace FlaxEngine
/// <param name="rect">The rectangle to draw.</param>
public static void DrawSprite(SpriteHandle spriteHandle, Rectangle rect)
{
var color = Color.White;
Internal_DrawSprite(ref spriteHandle, ref rect, ref color);
DrawSprite(spriteHandle, rect, Color.White);
}
/// <summary>
@@ -86,8 +85,7 @@ namespace FlaxEngine
/// <param name="rect">The rectangle to draw.</param>
public static void DrawSpritePoint(SpriteHandle spriteHandle, Rectangle rect)
{
var color = Color.White;
Internal_DrawSpritePoint(ref spriteHandle, ref rect, ref color);
DrawSpritePoint(spriteHandle, rect, Color.White);
}
/// <summary>

View File

@@ -15,7 +15,9 @@ namespace FlaxEngine
/// <param name="customActors">The custom set of actors to render. If empty, the loaded scenes will be rendered.</param>
public static void DrawSceneDepth(GPUContext context, SceneRenderTask task, GPUTexture output, List<Actor> customActors)
{
Internal_DrawSceneDepth(FlaxEngine.Object.GetUnmanagedPtr(context), FlaxEngine.Object.GetUnmanagedPtr(task), FlaxEngine.Object.GetUnmanagedPtr(output), Utils.ExtractArrayFromList(customActors));
var temp = Utils.ExtractArrayFromList(customActors);
var tempCount = temp.Length;
Internal_DrawSceneDepth(FlaxEngine.Object.GetUnmanagedPtr(context), FlaxEngine.Object.GetUnmanagedPtr(task), FlaxEngine.Object.GetUnmanagedPtr(output), ref temp, ref tempCount);
}
}
}

View File

@@ -820,22 +820,33 @@ MMethod* ManagedBinaryModule::FindMethod(MClass* mclass, const ScriptingTypeMeth
{
#if USE_MONO
MonoMethodSignature* sig = mono_method_signature(method->GetNative());
if (method->IsStatic() != signature.IsStatic ||
/*if (method->IsStatic() != signature.IsStatic ||
method->GetName() != signature.Name ||
(int32)mono_signature_get_param_count(sig) != signature.Params.Count())
continue;*/
if (method->IsStatic() != signature.IsStatic)
continue;
if (method->GetName() != signature.Name)
continue;
if ((int32)mono_signature_get_param_count(sig) != signature.Params.Count())
continue;
void* sigParams = nullptr;
mono_signature_get_params(sig, &sigParams);
MonoType* type = mono_signature_get_params(sig, &sigParams);
bool isValid = true;
for (int32 paramIdx = 0; paramIdx < signature.Params.Count(); paramIdx++)
int paramIdx = 0;
while (type != nullptr)
{
auto& param = signature.Params[paramIdx];
if (param.IsOut != (mono_signature_param_is_out(sig, paramIdx) != 0) ||
!VariantTypeEquals(param.Type, ((MonoType**)sigParams)[paramIdx]))
!VariantTypeEquals(param.Type, type))
{
auto asdf = VariantTypeEquals(param.Type, type);
isValid = false;
break;
}
type = mono_signature_get_params(sig, &sigParams);
paramIdx++;
}
if (isValid && VariantTypeEquals(signature.ReturnType, mono_signature_get_return_type(sig)))
return method;
@@ -1187,8 +1198,6 @@ bool ManagedBinaryModule::InvokeMethod(void* method, const Variant& instance, Sp
#if USE_MONO
const auto mMethod = (MMethod*)method;
MonoMethodSignature* signature = mono_method_signature(mMethod->GetNative());
void* signatureParams = nullptr;
mono_signature_get_params(signature, &signatureParams);
const int32 parametersCount = mono_signature_get_param_count(signature);
if (paramValues.Length() != parametersCount)
{
@@ -1222,20 +1231,25 @@ bool ManagedBinaryModule::InvokeMethod(void* method, const Variant& instance, Sp
void** params = (void**)alloca(parametersCount * sizeof(void*));
bool failed = false;
bool hasOutParams = false;
for (int32 paramIdx = 0; paramIdx < parametersCount; paramIdx++)
void* sigParams = nullptr;
MonoType* type = mono_signature_get_params(signature, &sigParams);
for (int paramIdx = 0; type != nullptr;)
{
auto& paramValue = paramValues[paramIdx];
const bool isOut = mono_signature_param_is_out(signature, paramIdx) != 0;
hasOutParams |= isOut;
// Marshal parameter for managed method
MType paramType(((MonoType**)signatureParams)[paramIdx]);
MType paramType(type);
params[paramIdx] = MUtils::VariantToManagedArgPtr(paramValue, paramType, failed);
if (failed)
{
LOG(Error, "Failed to marshal parameter {5}:{4} of method '{0}.{1}' (args count: {2}), value type: {6}, value: {3}", String(mMethod->GetParentClass()->GetFullName()), String(mMethod->GetName()), parametersCount, paramValue, paramType.ToString(), paramIdx, paramValue.Type);
return true;
}
type = mono_signature_get_params(signature, &sigParams);
paramIdx++;
}
// Invoke the method

View File

@@ -0,0 +1,151 @@
#include "CoreCLR.h"
#include "Engine/Core/Log.h"
#include "Engine/Platform/Platform.h"
#include "Engine/Platform/FileSystem.h"
#include "Engine/Core/Types/DateTime.h"
#include "Engine/Debug/DebugLog.h"
#include "Engine/Core/Collections/Dictionary.h"
#include <nethost.h>
#include <coreclr_delegates.h>
#include <hostfxr.h>
#if PLATFORM_WINDOWS
#include <combaseapi.h> // CoTask*
#undef SetEnvironmentVariable
#undef LoadLibrary
#endif
#if COMPILE_WITH_PROFILER
#endif
static Dictionary<String, void*> cachedFunctions;
static String assemblyName = TEXT("FlaxEngine.CSharp");
#if PLATFORM_WINDOWS
static const char_t* typeName = TEXT("FlaxEngine.NativeInterop, FlaxEngine.CSharp");
#else
static const char_t* typeName = "FlaxEngine.NativeInterop, FlaxEngine.CSharp";
#endif
hostfxr_initialize_for_runtime_config_fn hostfxr_initialize_for_runtime_config;
hostfxr_initialize_for_dotnet_command_line_fn hostfxr_initialize_for_dotnet_command_line;
hostfxr_get_runtime_delegate_fn hostfxr_get_runtime_delegate;
hostfxr_close_fn hostfxr_close;
load_assembly_and_get_function_pointer_fn load_assembly_and_get_function_pointer;
get_function_pointer_fn get_function_pointer;
hostfxr_set_error_writer_fn hostfxr_set_error_writer;
hostfxr_get_dotnet_environment_info_result_fn hostfxr_get_dotnet_environment_info_result;
hostfxr_run_app_fn hostfxr_run_app;
bool CoreCLR::LoadHostfxr(const String& library_path_)
{
const FLAX_CORECLR_STRING& library_path = FLAX_CORECLR_STRING(library_path_);
Platform::SetEnvironmentVariable(TEXT("DOTNET_TieredPGO"), TEXT("1"));
Platform::SetEnvironmentVariable(TEXT("DOTNET_TC_QuickJitForLoops"), TEXT("1"));
Platform::SetEnvironmentVariable(TEXT("DOTNET_ReadyToRun"), TEXT("0"));
char_t hostfxrPath[1024];
size_t hostfxrPathSize = sizeof(hostfxrPath) / sizeof(char_t);
get_hostfxr_parameters params;
params.size = sizeof(hostfxr_initialize_parameters);
params.assembly_path = library_path.Get();
params.dotnet_root = nullptr;//dotnetRoot.Get();
int rc = get_hostfxr_path(hostfxrPath, &hostfxrPathSize, &params);
if (rc != 0)
{
LOG(Error, "Failed to find hostfxr: {0:x}", (unsigned int)rc);
return false;
}
String path(hostfxrPath);
LOG(Info, "Found hostfxr in {0}", path);
void *hostfxr = Platform::LoadLibrary(path.Get());
hostfxr_initialize_for_runtime_config = (hostfxr_initialize_for_runtime_config_fn)Platform::GetProcAddress(hostfxr, "hostfxr_initialize_for_runtime_config");
hostfxr_initialize_for_dotnet_command_line = (hostfxr_initialize_for_dotnet_command_line_fn)Platform::GetProcAddress(hostfxr, "hostfxr_initialize_for_dotnet_command_line");
hostfxr_get_runtime_delegate = (hostfxr_get_runtime_delegate_fn)Platform::GetProcAddress(hostfxr, "hostfxr_get_runtime_delegate");
hostfxr_close = (hostfxr_close_fn)Platform::GetProcAddress(hostfxr, "hostfxr_close");
hostfxr_set_error_writer = (hostfxr_set_error_writer_fn)Platform::GetProcAddress(hostfxr, "hostfxr_set_error_writer");
hostfxr_get_dotnet_environment_info_result = (hostfxr_get_dotnet_environment_info_result_fn)Platform::GetProcAddress(hostfxr, "hostfxr_get_dotnet_environment_info_result");
hostfxr_run_app = (hostfxr_run_app_fn)Platform::GetProcAddress(hostfxr, "hostfxr_run_app");
return true;
}
bool CoreCLR::InitHostfxr(const String& config_path, const String& library_path_)
{
const FLAX_CORECLR_STRING& library_path = FLAX_CORECLR_STRING(library_path_);
const char_t* argv[1] = { library_path.Get() };
hostfxr_initialize_parameters params;
params.size = sizeof(hostfxr_initialize_parameters);
params.host_path = library_path.Get();
params.dotnet_root = nullptr;//dotnetRoot.Get(); // This probably must be set
hostfxr_handle handle = nullptr;
// Initialize hosting component, hostfxr_initialize_for_dotnet_command_line is used here
// to allow self-contained engine installation to be used when needed.
int rc = hostfxr_initialize_for_dotnet_command_line(1, argv, &params, &handle);
if (rc != 0 || handle == nullptr)
{
LOG(Error, "Failed to initialize hostfxr: {0:x}", (unsigned int)rc);
hostfxr_close(handle);
return false;
}
void* pget_function_pointer = nullptr;
rc = hostfxr_get_runtime_delegate(handle, hdt_get_function_pointer, &pget_function_pointer);
if (rc != 0 || pget_function_pointer == nullptr)
LOG(Error, "Failed to get runtime delegate hdt_get_function_pointer: {0:x}", (unsigned int)rc);
hostfxr_close(handle);
get_function_pointer = (get_function_pointer_fn)pget_function_pointer;
return true;
}
void* CoreCLR::GetStaticMethodPointer(const String& methodName)
{
void* fun;
if (cachedFunctions.TryGet(methodName, fun))
return fun;
int rc = get_function_pointer(typeName, FLAX_CORECLR_STRING(methodName).Get(), UNMANAGEDCALLERSONLY_METHOD, nullptr, nullptr, &fun);
if (rc != 0)
LOG(Fatal, "Failed to get unmanaged function pointer for method {0}: 0x{1:x}", methodName.Get(), (unsigned int)rc);
cachedFunctions.Add(String(methodName), fun);
return fun;
}
void* CoreCLR::Allocate(int size)
{
#if PLATFORM_WINDOWS
void* ptr = CoTaskMemAlloc(size);
#else
void* ptr = malloc(size);
#endif
#if COMPILE_WITH_PROFILER
Platform::OnMemoryAlloc(ptr, size);
#endif
return ptr;
}
void CoreCLR::Free(void* ptr)
{
#if COMPILE_WITH_PROFILER
Platform::OnMemoryFree(ptr);
#endif
#if PLATFORM_WINDOWS
CoTaskMemFree(ptr);
#else
free(ptr);
#endif
}

View File

@@ -0,0 +1,49 @@
#pragma once
// FIXME
#include <ThirdParty/mono-2.0/mono/metadata/blob.h>
#include "Engine/Core/Types/String.h"
#include "Engine/Scripting/Types.h"
#if defined(_WIN32)
#define CORECLR_DELEGATE_CALLTYPE __stdcall
#define FLAX_CORECLR_STRING String
#else
#define CORECLR_DELEGATE_CALLTYPE
#define FLAX_CORECLR_STRING StringAnsi
#endif
class CoreCLR
{
private:
public:
static bool LoadHostfxr(const String& library_path);
static bool InitHostfxr(const String& config_path, const String& library_path);
static void* GetStaticMethodPointer(const String& methodName);
template<typename RetType, typename ...Args>
static RetType CallStaticMethodInternal(const String& methodName, Args... args)
{
typedef RetType(CORECLR_DELEGATE_CALLTYPE* fun)(Args...);
fun function = (fun)GetStaticMethodPointer(methodName);
return function(args...);
}
template<typename RetType, typename ...Args>
static RetType CallStaticMethodInternalPointer(void* funPtr, Args... args)
{
typedef RetType(CORECLR_DELEGATE_CALLTYPE* fun)(Args...);
fun function = (fun)funPtr;
return function(args...);
}
static const char* GetClassFullname(void* klass);
static void* Allocate(int size);
static void Free(void* ptr);
static gchandle NewGCHandle(void* obj, bool pinned);
static gchandle NewGCHandleWeakref(void* obj, bool track_resurrection);
static void* GetGCHandleTarget(const gchandle& gchandle);
static void FreeGCHandle(const gchandle& gchandle);
};

File diff suppressed because it is too large Load Diff

View File

@@ -52,7 +52,9 @@ extern "C" FLAXENGINE_API void mono_add_internal_call(const char* name, const vo
#else
#define ADD_INTERNAL_CALL(fullName, method)
extern void DotNetAddInternalCall(const wchar_t* fullName, void* function);
#define ADD_INTERNAL_CALL(fullName, method) DotNetAddInternalCall(TEXT(fullName), (void*)(method))
#define INTERNAL_CALL_CHECK(obj)
#define INTERNAL_CALL_CHECK_EXP(expression)
#define INTERNAL_CALL_CHECK_RETURN(obj, defaultValue)

View File

@@ -6,17 +6,20 @@
#include "Engine/Scripting/MException.h"
#include "Engine/Scripting/ManagedCLR/MUtils.h"
#if USE_MONO
namespace UtilsInternal
{
MonoObject* ExtractArrayFromList(MonoObject* obj)
{
#if USE_MONO
auto klass = mono_object_get_class(obj);
auto field = mono_class_get_field_from_name(klass, "_items");
MonoObject* o;
mono_field_get_value(obj, field, &o);
return o;
#else
SCRIPTING_EXPORT("FlaxEngine.Utils::Internal_ExtractArrayFromList")
return nullptr;
#endif
}
}
@@ -24,6 +27,7 @@ namespace DebugLogHandlerInternal
{
void LogWrite(LogType level, MonoString* msgObj)
{
SCRIPTING_EXPORT("FlaxEngine.DebugLogHandler::Internal_LogWrite")
StringView msg;
MUtils::ToString(msgObj, msg);
Log::Logger::Write(level, msg);
@@ -31,6 +35,8 @@ namespace DebugLogHandlerInternal
void Log(LogType level, MonoString* msgObj, ScriptingObject* obj, MonoString* stackTrace)
{
SCRIPTING_EXPORT("FlaxEngine.DebugLogHandler::Internal_Log")
if (msgObj == nullptr)
return;
@@ -45,8 +51,11 @@ namespace DebugLogHandlerInternal
Log::Logger::Write(level, msg);
}
void LogException(MonoException* exception, ScriptingObject* obj)
{
SCRIPTING_EXPORT("FlaxEngine.DebugLogHandler::Internal_LogException")
#if USE_MONO
if (exception == nullptr)
return;
@@ -57,13 +66,16 @@ namespace DebugLogHandlerInternal
// Print exception including inner exceptions
// TODO: maybe option for build to threat warnings and errors as fatal errors?
ex.Log(LogType::Warning, objName.GetText());
#endif
}
}
namespace FlaxLogWriterInternal
{
void WriteStringToLog(MonoString* msgObj)
{
SCRIPTING_EXPORT("FlaxEngine.FlaxLogWriter::Internal_WriteStringToLog")
if (msgObj == nullptr)
return;
StringView msg;
@@ -72,8 +84,6 @@ namespace FlaxLogWriterInternal
}
}
#endif
void registerFlaxEngineInternalCalls()
{
AnimGraphExecutor::initRuntime();

View File

@@ -86,7 +86,11 @@ struct FLAXENGINE_API ManagedDictionary
CHECK_RETURN(makeGenericMethod, nullptr);
auto genericType = MUtils::GetType(StdTypesContainer::Instance()->DictionaryClass->GetNative());
#if USE_NETCORE
auto genericArgs = mono_array_new(domain, mono_get_intptr_class(), 2);
#else
auto genericArgs = mono_array_new(domain, mono_get_object_class(), 2);
#endif
mono_array_set(genericArgs, MonoReflectionType*, 0, mono_type_get_object(domain, keyType));
mono_array_set(genericArgs, MonoReflectionType*, 1, mono_type_get_object(domain, valueType));

View File

@@ -12,6 +12,10 @@
#include "Engine/Platform/Thread.h"
#include "Engine/Scripting/MException.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Platform/FileSystem.h"
#if USE_NETCORE
#include "Engine/Scripting/DotNet/CoreCLR.h"
#endif
#if USE_MONO
#ifdef USE_MONO_AOT_MODULE
#include "Engine/Core/Types/TimeSpan.h"
@@ -52,6 +56,9 @@ MDomain* MCore::GetActiveDomain()
MDomain* MCore::CreateDomain(const MString& domainName)
{
#if USE_NETCORE
return nullptr;
#else
#if USE_MONO_AOT
LOG(Fatal, "Scripts can run only in single domain mode with AOT mode enabled.");
return nullptr;
@@ -74,10 +81,13 @@ MDomain* MCore::CreateDomain(const MString& domainName)
#endif
MDomains.Add(domain);
return domain;
#endif
}
void MCore::UnloadDomain(const MString& domainName)
{
#if USE_NETCORE
#else
int32 i = 0;
for (; i < MDomains.Count(); i++)
{
@@ -103,9 +113,48 @@ void MCore::UnloadDomain(const MString& domainName)
#endif
Delete(domain);
MDomains.RemoveAtKeepOrder(i);
#endif
}
#if USE_MONO
#if USE_NETCORE
bool MCore::LoadEngine()
{
const String csharpLibraryPath = Globals::BinariesFolder / TEXT("FlaxEngine.CSharp.dll");
const String csharpRuntimeConfigPath = Globals::BinariesFolder / TEXT("FlaxEngine.CSharp.runtimeconfig.json");
if (!FileSystem::FileExists(csharpLibraryPath))
LOG(Fatal, "Failed to initialize managed runtime, FlaxEngine.CSharp.dll is missing.");
if (!FileSystem::FileExists(csharpRuntimeConfigPath))
LOG(Fatal, "Failed to initialize managed runtime, FlaxEngine.CSharp.runtimeconfig.json is missing.");
// Locate hostfxr and load it
if (!CoreCLR::LoadHostfxr(csharpLibraryPath))
return false;
// Initialize hosting component
if (!CoreCLR::InitHostfxr(csharpRuntimeConfigPath, csharpLibraryPath))
return false;
// Prepare managed side
const String hostExecutable = Platform::GetExecutableFilePath();
CoreCLR::CallStaticMethodInternal<void, const Char*>(TEXT("Init"), hostExecutable.Get());
MRootDomain = New<MDomain>("Root");
MDomains.Add(MRootDomain);
char* buildInfo = mono_get_runtime_build_info();
LOG(Info, ".NET runtime version: {0}", String(buildInfo));
mono_free(buildInfo);
return false;
}
void MCore::UnloadEngine()
{
CoreCLR::CallStaticMethodInternal<void>(TEXT("Exit"));
MDomains.ClearDelete();
MRootDomain = nullptr;
}
#elif USE_MONO
#if 0
@@ -518,7 +567,7 @@ bool MCore::LoadEngine()
}
#endif
// Init Mono
// Init managed runtime
#if PLATFORM_ANDROID
const char* monoVersion = "mobile";
#else
@@ -530,6 +579,7 @@ bool MCore::LoadEngine()
MRootDomain->_monoDomain = monoRootDomain;
MDomains.Add(MRootDomain);
#if !USE_NETCORE
auto exePath = Platform::GetExecutableFilePath();
auto configDir = StringUtils::GetDirectoryName(exePath).ToStringAnsi();
auto configFilename = StringUtils::GetFileName(exePath).ToStringAnsi() + ".config";
@@ -542,10 +592,11 @@ bool MCore::LoadEngine()
#endif
mono_domain_set_config(monoRootDomain, configDir.Get(), configFilename.Get());
mono_thread_set_main(mono_thread_current());
#endif
// Info
char* buildInfo = mono_get_runtime_build_info();
LOG(Info, "Mono version: {0}", String(buildInfo));
LOG(Info, "Mono runtime version: {0}", String(buildInfo));
mono_free(buildInfo);
return false;
@@ -672,7 +723,7 @@ void MCore::GC::WaitForPendingFinalizers()
#endif
}
#if USE_MONO && PLATFORM_WIN32 && !USE_MONO_DYNAMIC_LIB
#if USE_MONO && PLATFORM_WIN32 && !USE_MONO_DYNAMIC_LIB && !USE_NETCORE
// Export Mono functions
#pragma comment(linker, "/export:mono_add_internal_call")

View File

@@ -84,7 +84,7 @@ public:
/// </summary>
/// <remarks>
/// This is the fastest way of calling managed code.
/// Get thunk from class if you want to call static method. You to call it from method of a instance wrapper to call a specific instance.
/// Get thunk from class if you want to call static method. You need to call it from method of a instance wrapper to call a specific instance.
/// </remarks>
/// <returns>The method thunk pointer.</returns>
void* GetThunk();

View File

@@ -24,6 +24,10 @@
#include "Engine/Utilities/StringConverter.h"
#include "Engine/Content/Asset.h"
#if USE_NETCORE
#include "Engine/Scripting/DotNet/CoreCLR.h"
#endif
#if USE_MONO
// Inlined mono private types to access MonoType internals
@@ -356,11 +360,17 @@ Variant MUtils::UnboxVariant(MonoObject* value)
return Variant::Null;
const auto& stdTypes = *StdTypesContainer::Instance();
const auto klass = mono_object_get_class(value);
MonoType* monoType = mono_class_get_type(klass);
const MonoTypeEnum monoTypeId = (MonoTypeEnum)mono_type_get_type(monoType);
#if USE_NETCORE
void* unboxed = mono_object_unbox(value);
#else
void* unboxed = (byte*)value + sizeof(MonoObject);
const MonoType* monoType = mono_class_get_type(klass);
#endif
// Fast type detection for in-built types
switch (monoType->type)
switch (monoTypeId)
{
case MONO_TYPE_VOID:
return Variant(VariantType(VariantType::Void));
@@ -660,7 +670,11 @@ MonoObject* MUtils::BoxVariant(const Variant& value)
case VariantType::Guid:
return mono_value_box(mono_domain_get(), stdTypes.GuidClass->GetNative(), (void*)&value.AsData);
case VariantType::String:
#if USE_NETCORE
return (MonoObject*)MUtils::ToString((StringView)value);
#else
return (MonoObject*)MUtils::ToString((StringView)value);
#endif
case VariantType::Quaternion:
return mono_value_box(mono_domain_get(), stdTypes.QuaternionClass->GetNative(), (void*)&value.AsData);
case VariantType::BoundingSphere:
@@ -844,7 +858,11 @@ MonoObject* MUtils::BoxVariant(const Variant& value)
return nullptr;
}
case VariantType::ManagedObject:
return value.AsUint ? mono_gchandle_get_target(value.AsUint) : nullptr;
#if USE_NETCORE
return value.AsUint64 ? MUtils::GetGCHandleTarget(value.AsUint64) : nullptr;
#else
return value.AsUint ? MUtils::GetGCHandleTarget(value.AsUint) : nullptr;
#endif
case VariantType::Typename:
{
const auto klass = Scripting::FindClassNative((StringAnsiView)value);
@@ -869,6 +887,9 @@ void MUtils::GetClassFullname(MonoObject* obj, MString& fullname)
void MUtils::GetClassFullname(MonoClass* monoClass, MString& fullname)
{
#if USE_NETCORE
fullname = CoreCLR::GetClassFullname(monoClass);
#else
static MString plusStr("+");
static MString dotStr(".");
@@ -906,6 +927,7 @@ void MUtils::GetClassFullname(MonoClass* monoClass, MString& fullname)
}
fullname += ']';
}
#endif
}
void MUtils::GetClassFullname(MonoReflectionType* type, MString& fullname)
@@ -1138,7 +1160,8 @@ BytesContainer MUtils::LinkArray(MonoArray* arrayObj)
void* MUtils::VariantToManagedArgPtr(Variant& value, const MType& type, bool& failed)
{
// Convert Variant into matching managed type and return pointer to data for the method invocation
switch (type.GetNative()->type)
MonoTypeEnum monoType = (MonoTypeEnum)mono_type_get_type(type.GetNative());
switch (monoType)
{
case MONO_TYPE_BOOLEAN:
if (value.Type.Type != VariantType::Bool)
@@ -1183,7 +1206,11 @@ void* MUtils::VariantToManagedArgPtr(Variant& value, const MType& type, bool& fa
return MUtils::ToString((StringView)value);
case MONO_TYPE_VALUETYPE:
{
#if !USE_NETCORE
MonoClass* klass = type.GetNative()->data.klass;
#else
MonoClass* klass = mono_type_get_class(type.GetNative());
#endif
if (mono_class_is_enum(klass))
{
if (value.Type.Type != VariantType::Enum)
@@ -1305,15 +1332,52 @@ void* MUtils::VariantToManagedArgPtr(Variant& value, const MType& type, bool& fa
MonoObject* MUtils::ToManaged(const Version& value)
{
#if USE_NETCORE
auto scriptingClass = Scripting::GetStaticClass();
CHECK_RETURN(scriptingClass, nullptr);
auto versionToManaged = scriptingClass->GetMethod("VersionToManaged", 4);
CHECK_RETURN(versionToManaged, nullptr);
int major = value.Major();
int minor = value.Minor();
int build = value.Build();
int revision = value.Revision();
void* params[4];
params[0] = &major;
params[1] = &minor;
params[2] = &build;
params[3] = &revision;
auto obj = versionToManaged->Invoke(nullptr, params, nullptr);
#else
auto obj = mono_object_new(mono_domain_get(), Scripting::FindClassNative("System.Version"));
Platform::MemoryCopy((byte*)obj + sizeof(MonoObject), &value, sizeof(Version));
#endif
return obj;
}
Version MUtils::ToNative(MonoObject* value)
{
if (value)
#if USE_NETCORE
{
auto ver = Version();
auto scriptingClass = Scripting::GetStaticClass();
CHECK_RETURN(scriptingClass, ver);
auto versionToNative = scriptingClass->GetMethod("VersionToNative", 2);
CHECK_RETURN(versionToNative, ver);
void* params[2];
params[0] = value;
params[1] = &ver;
versionToNative->Invoke(nullptr, params, nullptr);
return ver;
}
#else
return *(Version*)((byte*)value + sizeof(MonoObject));
#endif
return Version();
}

View File

@@ -14,6 +14,11 @@
#include <ThirdParty/mono-2.0/mono/metadata/object.h>
#include <ThirdParty/mono-2.0/mono/metadata/appdomain.h>
#if USE_NETCORE
#include "Engine/Scripting/DotNet/CoreCLR.h"
#include "Engine/Core/Collections/BitArray.h"
#endif
struct Version;
namespace MUtils
@@ -53,6 +58,19 @@ struct MConverter
void ToNativeArray(Array<T, AllocationType>& result, MonoArray* data, int32 length);
};
#if USE_NETCORE
// Pass-through converter for ScriptingObjects (passed as GCHandles)
template<>
struct MConverter<void*>
{
void Unbox(void*& result, MonoObject* data)
{
CHECK(data);
result = data;
}
};
#endif
// Converter for POD types (that can use raw memory copy).
template<typename T>
struct MConverter<T, typename TEnableIf<TAnd<TIsPODType<T>, TNot<TIsBaseOf<class ScriptingObject, typename TRemovePointer<T>::Type>>>::Value>::Type>
@@ -86,12 +104,22 @@ struct MConverter<String>
{
MonoObject* Box(const String& data, MonoClass* klass)
{
#if USE_NETCORE
MonoString* str = MUtils::ToString(data);
return mono_value_box(nullptr, klass, str);
#else
return (MonoObject*)MUtils::ToString(data);
#endif
}
void Unbox(String& result, MonoObject* data)
{
#if USE_NETCORE
MonoString* str = (MonoString*)mono_object_unbox(data);
result = MUtils::ToString(str);
#else
result = MUtils::ToString((MonoString*)data);
#endif
}
void ToManagedArray(MonoArray* result, const Span<String>& data)
@@ -564,6 +592,72 @@ namespace MUtils
return ToArray(Span<String>(data.Get(), data.Count()), mono_get_string_class());
}
#if USE_NETCORE
/// <summary>
/// Allocates new boolean array and copies data from the given unmanaged data container.
/// The managed runtime is responsible for releasing the returned array data.
/// </summary>
/// <param name="data">The input data.</param>
/// <returns>The output array.</returns>
FORCE_INLINE bool* ToBoolArray(const Array<bool>& data)
{
bool* arr = (bool*)CoreCLR::Allocate(data.Count() * sizeof(bool));
memcpy(arr, data.Get(), data.Count() * sizeof(bool));
return arr;
}
/// <summary>
/// Allocates new boolean array and copies data from the given unmanaged data container.
/// The managed runtime is responsible for releasing the returned array data.
/// </summary>
/// <param name="data">The input data.</param>
/// <returns>The output array.</returns>
FORCE_INLINE bool* ToBoolArray(const BitArray<>& data)
{
bool* arr = (bool*)CoreCLR::Allocate(data.Count() * sizeof(bool));
//memcpy(arr, data.Get(), data.Count() * sizeof(bool));
for (int i = 0; i < data.Count(); i++)
arr[i] = data[i];
return arr;
}
#endif
FORCE_INLINE gchandle NewGCHandle(MonoObject* obj, bool pinned)
{
#if USE_NETCORE
return CoreCLR::NewGCHandle(obj, pinned);
#else
return mono_gchandle_new(obj, pinned);
#endif
}
FORCE_INLINE gchandle NewGCHandleWeakref(MonoObject* obj, bool track_resurrection)
{
#if USE_NETCORE
return CoreCLR::NewGCHandleWeakref(obj, track_resurrection);
#else
return mono_gchandle_new_weak_ref(obj, track_resurrection);
#endif
}
FORCE_INLINE MonoObject* GetGCHandleTarget(const gchandle& handle)
{
#if USE_NETCORE
return (MonoObject*)CoreCLR::GetGCHandleTarget(handle);
#else
return mono_gchandle_get_target(handle);
#endif
}
FORCE_INLINE void FreeGCHandle(const gchandle& handle)
{
#if USE_NETCORE
CoreCLR::FreeGCHandle(handle);
#else
mono_gchandle_free(handle);
#endif
}
extern void* VariantToManagedArgPtr(Variant& value, const MType& type, bool& failed);
extern MonoObject* ToManaged(const Version& value);
extern Version ToNative(MonoObject* value);

View File

@@ -2,6 +2,8 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;
// ReSharper disable UnassignedReadonlyField
// ReSharper disable InconsistentNaming
@@ -13,13 +15,14 @@ namespace FlaxEngine
/// Base class for all objects Flax can reference. Every object has unique identifier.
/// </summary>
[Serializable]
public abstract class Object
[NativeMarshalling(typeof(ObjectMarshaller))]
public abstract partial class Object
{
/// <summary>
/// The pointer to the unmanaged object (native C++ instance).
/// </summary>
[NonSerialized]
protected readonly IntPtr __unmanagedPtr;
internal readonly IntPtr __unmanagedPtr;
/// <summary>
/// The object unique identifier.
@@ -248,35 +251,35 @@ namespace FlaxEngine
#region Internal Calls
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern Object Internal_Create1(Type type);
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEngine.Object::Internal_Create1", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
internal static partial Object Internal_Create1([MarshalUsing(typeof(SystemTypeMarshaller))] Type type);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern Object Internal_Create2(string typeName);
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEngine.Object::Internal_Create2", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
internal static partial Object Internal_Create2(string typeName);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void Internal_ManagedInstanceCreated(Object managedInstance);
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEngine.Object::Internal_ManagedInstanceCreated", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
internal static partial void Internal_ManagedInstanceCreated(Object managedInstance);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void Internal_ManagedInstanceDeleted(IntPtr nativeInstance);
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEngine.Object::Internal_ManagedInstanceDeleted", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
internal static partial void Internal_ManagedInstanceDeleted(IntPtr nativeInstance);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void Internal_Destroy(IntPtr obj, float timeLeft);
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEngine.Object::Internal_Destroy", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
internal static partial void Internal_Destroy(IntPtr obj, float timeLeft);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern string Internal_GetTypeName(IntPtr obj);
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEngine.Object::Internal_GetTypeName", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
internal static partial string Internal_GetTypeName(IntPtr obj);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern Object Internal_FindObject(ref Guid id, Type type);
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEngine.Object::Internal_FindObject", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
internal static partial Object Internal_FindObject(ref Guid id, [MarshalUsing(typeof(SystemTypeMarshaller))] Type type);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern Object Internal_TryFindObject(ref Guid id, Type type);
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEngine.Object::Internal_TryFindObject", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
internal static partial Object Internal_TryFindObject(ref Guid id, [MarshalUsing(typeof(SystemTypeMarshaller))] Type type);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void Internal_ChangeID(IntPtr obj, ref Guid id);
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEngine.Object::Internal_ChangeID", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
internal static partial void Internal_ChangeID(IntPtr obj, ref Guid id);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern IntPtr Internal_GetUnmanagedInterface(IntPtr obj, Type type);
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEngine.Object::Internal_GetUnmanagedInterface", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
internal static partial IntPtr Internal_GetUnmanagedInterface(IntPtr obj, [MarshalUsing(typeof(SystemTypeMarshaller))] Type type);
#endregion
}

View File

@@ -15,7 +15,10 @@ public class Scripting : EngineModule
if (EngineConfiguration.WithCSharp(options))
{
options.PublicDependencies.Add("mono");
if (EngineConfiguration.UseDotNet)
options.PublicDependencies.Add("nethost");
else
options.PublicDependencies.Add("mono");
}
options.PrivateDependencies.Add("Utilities");

View File

@@ -40,6 +40,7 @@ namespace ProfilerInternal
void BeginEvent(MonoString* nameObj)
{
SCRIPTING_EXPORT("FlaxEngine.Profiler::BeginEvent")
#if COMPILE_WITH_PROFILER
const StringView name((const Char*)mono_string_chars(nameObj), mono_string_length(nameObj));
ProfilerCPU::BeginEvent(*name);
@@ -78,6 +79,7 @@ namespace ProfilerInternal
void EndEvent()
{
SCRIPTING_EXPORT("FlaxEngine.Profiler::EndEvent")
#if COMPILE_WITH_PROFILER
#if TRACY_ENABLE
tracy::ScopedZone::End();
@@ -88,6 +90,7 @@ namespace ProfilerInternal
void BeginEventGPU(MonoString* nameObj)
{
SCRIPTING_EXPORT("FlaxEngine.Profiler::BeginEventGPU")
#if COMPILE_WITH_PROFILER
const auto index = ProfilerGPU::BeginEvent((const Char*)mono_string_chars(nameObj));
ManagedEventsGPU.Push(index);
@@ -96,6 +99,7 @@ namespace ProfilerInternal
void EndEventGPU()
{
SCRIPTING_EXPORT("FlaxEngine.Profiler::EndEventGPU")
#if COMPILE_WITH_PROFILER
const auto index = ManagedEventsGPU.Pop();
ProfilerGPU::EndEvent(index);
@@ -111,16 +115,19 @@ public:
#if USE_MONO
static bool HasGameModulesLoaded()
{
SCRIPTING_EXPORT("FlaxEngine.Scripting::HasGameModulesLoaded")
return Scripting::HasGameModulesLoaded();
}
static bool IsTypeFromGameScripts(MonoReflectionType* type)
{
SCRIPTING_EXPORT("FlaxEngine.Scripting::IsTypeFromGameScripts")
return Scripting::IsTypeFromGameScripts(Scripting::FindClass(MUtils::GetClass(type)));
}
static void FlushRemovedObjects()
{
SCRIPTING_EXPORT("FlaxEngine.Scripting::FlushRemovedObjects")
ASSERT(IsInMainThread());
ObjectsRemovalService::Flush();
}

View File

@@ -903,6 +903,7 @@ ScriptingObject* Scripting::FindObject(const MObject* managedInstance)
void Scripting::OnManagedInstanceDeleted(ScriptingObject* obj)
{
SCRIPTING_EXPORT("FlaxEngine.Object::Internal_ManagedInstanceDeleted")
PROFILE_CPU();
ASSERT(obj);

View File

@@ -5,6 +5,8 @@ using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;
using System.Threading;
using System.Threading.Tasks;
using FlaxEngine.GUI;
@@ -65,7 +67,7 @@ namespace FlaxEngine
/// <summary>
/// C# scripting service.
/// </summary>
public static class Scripting
public static partial class Scripting
{
private static readonly List<Action> UpdateActions = new List<Action>();
private static readonly MainThreadTaskScheduler MainThreadTaskScheduler = new MainThreadTaskScheduler();
@@ -207,6 +209,21 @@ namespace FlaxEngine
return result;
}
internal static IntPtr VersionToManaged(int major, int minor, int build, int revision)
{
Version version = new Version(major, minor, Math.Max(build, 0), Math.Max(revision, 0));
return GCHandle.ToIntPtr(GCHandle.Alloc(version));
}
internal static void VersionToNative(IntPtr versionHandle, IntPtr nativePtr)
{
Version version = (Version)GCHandle.FromIntPtr(versionHandle).Target;
Marshal.WriteInt32(nativePtr, 0, version.Major);
Marshal.WriteInt32(nativePtr, 4, version.Minor);
Marshal.WriteInt32(nativePtr, 8, version.Build);
Marshal.WriteInt32(nativePtr, 12, version.Revision);
}
private static void CreateGuiStyle()
{
var style = new Style
@@ -283,21 +300,23 @@ namespace FlaxEngine
/// Returns true if game scripts assembly has been loaded.
/// </summary>
/// <returns>True if game scripts assembly is loaded, otherwise false.</returns>
[MethodImpl(MethodImplOptions.InternalCall)]
public static extern bool HasGameModulesLoaded();
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEngine.Scripting::HasGameModulesLoaded", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
[return: MarshalAs(UnmanagedType.U1)]
public static partial bool HasGameModulesLoaded();
/// <summary>
/// Returns true if given type is from one of the game scripts assemblies.
/// </summary>
/// <returns>True if the type is from game assembly, otherwise false.</returns>
[MethodImpl(MethodImplOptions.InternalCall)]
public static extern bool IsTypeFromGameScripts(Type type);
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEngine.Scripting::IsTypeFromGameScripts", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
[return: MarshalAs(UnmanagedType.U1)]
public static partial bool IsTypeFromGameScripts([MarshalUsing(typeof(SystemTypeMarshaller))] Type type);
/// <summary>
/// Flushes the removed objects (disposed objects using Object.Destroy).
/// </summary>
[MethodImpl(MethodImplOptions.InternalCall)]
public static extern void FlushRemovedObjects();
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEngine.Scripting::FlushRemovedObjects", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
public static partial void FlushRemovedObjects();
}
/// <summary>
@@ -306,32 +325,32 @@ namespace FlaxEngine
/// <remarks>
/// Profiler is available in the editor and Debug/Development builds. Release builds don't have profiling tools.
/// </remarks>
public static class Profiler
public static partial class Profiler
{
/// <summary>
/// Begins profiling a piece of code with a custom label.
/// </summary>
/// <param name="name">The name of the event.</param>
[MethodImpl(MethodImplOptions.InternalCall)]
public static extern void BeginEvent(string name);
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEngine.Profiler::BeginEvent", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
public static partial void BeginEvent(string name);
/// <summary>
/// Ends profiling an event.
/// </summary>
[MethodImpl(MethodImplOptions.InternalCall)]
public static extern void EndEvent();
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEngine.Profiler::EndEvent", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
public static partial void EndEvent();
/// <summary>
/// Begins GPU profiling a piece of code with a custom label.
/// </summary>
/// <param name="name">The name of the event.</param>
[MethodImpl(MethodImplOptions.InternalCall)]
public static extern void BeginEventGPU(string name);
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEngine.Profiler::BeginEventGPU", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
public static partial void BeginEventGPU(string name);
/// <summary>
/// Ends GPU profiling an event.
/// </summary>
[MethodImpl(MethodImplOptions.InternalCall)]
public static extern void EndEventGPU();
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEngine.Profiler::EndEventGPU", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]
public static partial void EndEventGPU();
}
}

View File

@@ -68,9 +68,13 @@ ScriptingObject* ScriptingObject::NewObject(const ScriptingTypeHandle& typeHandl
MObject* ScriptingObject::GetManagedInstance() const
{
#if USE_NETCORE
const gchandle handle = Platform::AtomicRead((int64*)&_gcHandle);
#elif USE_MONO
const gchandle handle = Platform::AtomicRead((int32*)&_gcHandle);
#endif
#if USE_MONO
const int32 handle = Platform::AtomicRead((int32*)&_gcHandle);
return handle ? mono_gchandle_get_target(handle) : nullptr;
return handle ? MUtils::GetGCHandleTarget(handle) : nullptr;
#else
return nullptr;
#endif
@@ -211,7 +215,7 @@ void ScriptingObject::OnManagedInstanceDeleted()
if (_gcHandle)
{
#if USE_MONO
mono_gchandle_free(_gcHandle);
MUtils::FreeGCHandle(_gcHandle);
#endif
_gcHandle = 0;
}
@@ -236,10 +240,16 @@ bool ScriptingObject::CreateManaged()
if (!managedInstance)
return true;
// Prevent form object GC destruction
auto handle = mono_gchandle_new(managedInstance, false);
// Prevent from object GC destruction
#if USE_NETCORE
auto handle = (gchandle)managedInstance;
auto oldHandle = Platform::InterlockedCompareExchange((int64*)&_gcHandle, *(int64*)&handle, 0);
if (*(uint64*)&oldHandle != 0)
#else
auto handle = MUtils::NewGCHandle(managedInstance, false);
auto oldHandle = Platform::InterlockedCompareExchange((int32*)&_gcHandle, *(int32*)&handle, 0);
if (*(uint32*)&oldHandle != 0)
#endif
{
// Other thread already created the object before
if (const auto monoClass = GetClass())
@@ -252,7 +262,7 @@ bool ScriptingObject::CreateManaged()
monoUnmanagedPtrField->SetValue(managedInstance, &param);
}
}
mono_gchandle_free(handle);
MUtils::FreeGCHandle(handle);
return true;
}
#endif
@@ -337,7 +347,7 @@ void ScriptingObject::DestroyManaged()
// Clear the handle
if (_gcHandle)
{
mono_gchandle_free(_gcHandle);
MUtils::FreeGCHandle(_gcHandle);
_gcHandle = 0;
}
#else
@@ -448,9 +458,15 @@ bool ManagedScriptingObject::CreateManaged()
return true;
// Cache the GC handle to the object (used to track the target object because it can be moved in a memory)
auto handle = mono_gchandle_new_weakref(managedInstance, false);
#if USE_NETCORE
auto handle = (gchandle)managedInstance;
auto oldHandle = Platform::InterlockedCompareExchange((int64*)&_gcHandle, *(int64*)&handle, 0);
if (*(uint64*)&oldHandle != 0)
#else
auto handle = MUtils::NewGCHandleWeakref(managedInstance, false);
auto oldHandle = Platform::InterlockedCompareExchange((int32*)&_gcHandle, *(int32*)&handle, 0);
if (*(uint32*)&oldHandle != 0)
#endif
{
// Other thread already created the object before
if (const auto monoClass = GetClass())
@@ -463,7 +479,7 @@ bool ManagedScriptingObject::CreateManaged()
monoUnmanagedPtrField->SetValue(managedInstance, &param);
}
}
mono_gchandle_free(handle);
MUtils::FreeGCHandle(handle);
return true;
}
#endif
@@ -487,6 +503,7 @@ public:
static MonoObject* Create1(MonoReflectionType* type)
{
SCRIPTING_EXPORT("FlaxEngine.Object::Internal_Create1")
// Peek class for that type (handle generic class cases)
if (!type)
DebugLog::ThrowArgumentNull("type");
@@ -550,6 +567,7 @@ public:
static MonoObject* Create2(MonoString* typeNameObj)
{
SCRIPTING_EXPORT("FlaxEngine.Object::Internal_Create2")
// Get typename
if (typeNameObj == nullptr)
DebugLog::ThrowArgumentNull("typeName");
@@ -587,6 +605,7 @@ public:
static void ManagedInstanceCreated(MonoObject* managedInstance)
{
SCRIPTING_EXPORT("FlaxEngine.Object::Internal_ManagedInstanceCreated")
MonoClass* typeClass = mono_object_get_class(managedInstance);
// Get the assembly with that class
@@ -625,12 +644,20 @@ public:
if (auto* managedScriptingObject = dynamic_cast<ManagedScriptingObject*>(obj))
{
// Managed
managedScriptingObject->_gcHandle = mono_gchandle_new_weakref(managedInstance, false);
#if USE_NETCORE
managedScriptingObject->_gcHandle = (gchandle)managedInstance;
#else
managedScriptingObject->_gcHandle = MUtils::NewGCHandleWeakref(managedInstance, false);
#endif
}
else
{
// Persistent
obj->_gcHandle = mono_gchandle_new(managedInstance, false);
#if USE_NETCORE
obj->_gcHandle = (gchandle)managedInstance;
#else
obj->_gcHandle = MUtils::NewGCHandle(managedInstance, false);
#endif
}
MClass* monoClass = obj->GetClass();
@@ -657,6 +684,7 @@ public:
static void Destroy(ScriptingObject* obj, float timeLeft)
{
SCRIPTING_EXPORT("FlaxEngine.Object::Internal_Destroy")
// Use scaled game time for removing actors/scripts by the user (maybe expose it to the api?)
const bool useGameTime = timeLeft > ZeroTolerance;
@@ -666,12 +694,14 @@ public:
static MonoString* GetTypeName(ScriptingObject* obj)
{
SCRIPTING_EXPORT("FlaxEngine.Object::Internal_GetTypeName")
INTERNAL_CALL_CHECK_RETURN(obj, nullptr);
return MUtils::ToString(obj->GetType().Fullname);
}
static MonoObject* FindObject(Guid* id, MonoReflectionType* type)
{
SCRIPTING_EXPORT("FlaxEngine.Object::Internal_FindObject")
if (!id->IsValid())
return nullptr;
auto klass = MUtils::GetClass(type);
@@ -701,6 +731,7 @@ public:
static MonoObject* TryFindObject(Guid* id, MonoReflectionType* type)
{
SCRIPTING_EXPORT("FlaxEngine.Object::Internal_TryFindObject")
ScriptingObject* obj = Scripting::TryFindObject(*id);
if (obj && !obj->Is(MUtils::GetClass(type)))
obj = nullptr;
@@ -709,12 +740,14 @@ public:
static void ChangeID(ScriptingObject* obj, Guid* id)
{
SCRIPTING_EXPORT("FlaxEngine.Object::Internal_ChangeID")
INTERNAL_CALL_CHECK(obj);
obj->ChangeID(*id);
}
static void* GetUnmanagedInterface(ScriptingObject* obj, MonoReflectionType* type)
{
SCRIPTING_EXPORT("FlaxEngine.Object::Internal_GetUnmanagedInterface")
if (obj && type)
{
auto typeClass = MUtils::GetClass(type);

View File

@@ -21,7 +21,7 @@ public:
protected:
uint32 _gcHandle;
gchandle _gcHandle;
ScriptingTypeHandle _type;
Guid _id;

View File

@@ -31,7 +31,7 @@ typedef void MObject;
#else
#define USE_MONO 1
#define USE_NETCORE 0
#define USE_NETCORE 1
// Enables using single (root) app domain for the user scripts
#define USE_SCRIPTING_SINGLE_DOMAIN 1
@@ -49,6 +49,17 @@ typedef void MObject;
#define USE_MONO_AOT_MODE MONO_AOT_MODE_NONE
#endif
#if USE_NETCORE
struct _MonoDomain {};
struct _MonoThread {};
#endif
#if USE_NETCORE
typedef unsigned long long gchandle;
#else
typedef uint32 gchandle;
#endif
// Mono types declarations
typedef struct _MonoClass MonoClass;
typedef struct _MonoDomain MonoDomain;

View File

@@ -37,7 +37,11 @@ namespace FlaxEngine.Json
{
// Skip serialization as reference id for the root object serialization (eg. Script)
var cache = JsonSerializer.Current.Value;
#if !USE_NETCORE
if (cache != null && cache.IsDuringSerialization && cache.SerializerWriter.SerializeStackSize == 0)
#else
if (cache != null && cache.IsDuringSerialization)
#endif
{
return false;
}
@@ -142,6 +146,7 @@ namespace FlaxEngine.Json
writer.WriteEndObject();
}
#if !USE_NETCORE
/// <inheritdoc />
public override void WriteJsonDiff(JsonWriter writer, object value, object other, Newtonsoft.Json.JsonSerializer serializer)
{
@@ -170,6 +175,7 @@ namespace FlaxEngine.Json
}
writer.WriteEndObject();
}
#endif
/// <inheritdoc />
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, Newtonsoft.Json.JsonSerializer serializer)
@@ -232,8 +238,10 @@ namespace FlaxEngine.Json
/// <inheritdoc />
public override bool CanWrite => true;
#if !USE_NETCORE
/// <inheritdoc />
public override bool CanWriteDiff => true;
#endif
}
/// <summary>

View File

@@ -1,6 +1,7 @@
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Runtime.CompilerServices;
@@ -11,7 +12,7 @@ using FlaxEngine.Json.JsonCustomSerializers;
using FlaxEngine.Utilities;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
using Newtonsoft.Json.Linq;
namespace FlaxEngine.Json
{
@@ -23,7 +24,11 @@ namespace FlaxEngine.Json
public StringBuilder StringBuilder;
public StringWriter StringWriter;
public JsonTextWriter JsonWriter;
#if !USE_NETCORE
public JsonSerializerInternalWriter SerializerWriter;
#else
public /*JsonSerializerInternalWriter*/ object SerializerWriter;
#endif
public UnmanagedMemoryStream MemoryStream;
public StreamReader Reader;
public bool IsDuringSerialization;
@@ -32,9 +37,18 @@ namespace FlaxEngine.Json
{
JsonSerializer = Newtonsoft.Json.JsonSerializer.CreateDefault(settings);
JsonSerializer.Formatting = Formatting.Indented;
#if USE_NETCORE
Type jsonSerializerInternalWriterType =
typeof(Newtonsoft.Json.Serialization.IValueProvider).Assembly.GetType(
"Newtonsoft.Json.Serialization.JsonSerializerInternalWriter");
System.Reflection.ConstructorInfo ctor = jsonSerializerInternalWriterType.GetConstructors
(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public)[0];
SerializerWriter = ctor.Invoke(new object[] { JsonSerializer });
#else
SerializerWriter = new JsonSerializerInternalWriter(JsonSerializer);
#endif
StringBuilder = new StringBuilder(256);
StringWriter = new StringWriter(StringBuilder, CultureInfo.InvariantCulture);
SerializerWriter = new JsonSerializerInternalWriter(JsonSerializer);
MemoryStream = new UnmanagedMemoryStream((byte*)0, 0);
Reader = new StreamReader(MemoryStream, Encoding.UTF8, false);
JsonWriter = new JsonTextWriter(StringWriter)
@@ -107,7 +121,72 @@ namespace FlaxEngine.Json
return sceneObjA.PrefabObjectID == sceneObjB.PrefabObjectID;
}*/
return Newtonsoft.Json.Utilities.MiscellaneousUtils.DefaultValueEquals(objA, objB);
// Based on Newtonsoft.Json MiscellaneousUtils-class ValueEquals-method
bool DefaultValueEquals(object objA_, object objB_)
{
bool IsInteger(object value)
{
var type = value.GetType();
return type == typeof(SByte) ||
type == typeof(Byte) ||
type == typeof(Int16) ||
type == typeof(UInt16) ||
type == typeof(Int32) ||
type == typeof(UInt32) ||
type == typeof(Int64) ||
type == typeof(SByte) ||
type == typeof(UInt64);
}
if (objA_ == objB_)
{
return true;
}
if (objA_ == null || objB_ == null)
{
return false;
}
// comparing an Int32 and Int64 both of the same value returns false
// make types the same then compare
if (objA_.GetType() != objB_.GetType())
{
if (IsInteger(objA_) && IsInteger(objB_))
{
return Convert.ToDecimal(objA_, CultureInfo.CurrentCulture).Equals(Convert.ToDecimal(objB_, CultureInfo.CurrentCulture));
}
else if ((objA_ is double || objA_ is float || objA_ is decimal) && (objB_ is double || objB_ is float || objB_ is decimal))
{
return Mathd.NearEqual(Convert.ToDouble(objA_, CultureInfo.CurrentCulture), Convert.ToDouble(objB_, CultureInfo.CurrentCulture));
}
else
{
return false;
}
}
// Diff on collections
if (objA_ is System.Collections.IList aList && objB_ is System.Collections.IList bList)
{
if (aList.Count != bList.Count)
return false;
}
if (objA_ is System.Collections.IEnumerable aEnumerable && objB_ is System.Collections.IEnumerable bEnumerable)
{
var aEnumerator = aEnumerable.GetEnumerator();
var bEnumerator = bEnumerable.GetEnumerator();
while (aEnumerator.MoveNext())
{
if (!bEnumerator.MoveNext() || !ValueEquals(aEnumerator.Current, bEnumerator.Current))
return false;
}
return !bEnumerator.MoveNext();
}
return objA_.Equals(objB_);
}
return /*Newtonsoft.Json.Utilities.MiscellaneousUtils.*/DefaultValueEquals(objA, objB);
}
/// <summary>
@@ -124,7 +203,17 @@ namespace FlaxEngine.Json
cache.StringBuilder.Clear();
cache.IsDuringSerialization = true;
#if !USE_NETCORE
cache.SerializerWriter.Serialize(cache.JsonWriter, obj, type);
#else
Type jsonSerializerInternalWriterType =
typeof(Newtonsoft.Json.Serialization.IValueProvider).Assembly.GetType(
"Newtonsoft.Json.Serialization.JsonSerializerInternalWriter");
System.Reflection.MethodInfo Serialize = jsonSerializerInternalWriterType.GetMethod("Serialize",
System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public);
Serialize.Invoke(cache.SerializerWriter, new object[] { cache.JsonWriter, obj, type });
#endif
return cache.StringBuilder.ToString();
}
@@ -143,7 +232,17 @@ namespace FlaxEngine.Json
cache.StringBuilder.Clear();
cache.IsDuringSerialization = true;
#if !USE_NETCORE
cache.SerializerWriter.Serialize(cache.JsonWriter, obj, type);
#else
Type jsonSerializerInternalWriterType =
typeof(Newtonsoft.Json.Serialization.IValueProvider).Assembly.GetType(
"Newtonsoft.Json.Serialization.JsonSerializerInternalWriter");
System.Reflection.MethodInfo Serialize = jsonSerializerInternalWriterType.GetMethod("Serialize",
System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public);
Serialize.Invoke(cache.SerializerWriter, new object[] { cache.JsonWriter, obj, type });
#endif
return cache.StringBuilder.ToString();
}
@@ -157,15 +256,25 @@ namespace FlaxEngine.Json
/// <returns>The output json string.</returns>
public static string SerializeDiff(object obj, object other, bool isManagedOnly = false)
{
Type type = obj.GetType();
var cache = isManagedOnly ? CacheManagedOnly.Value : Cache.Value;
Current.Value = cache;
cache.StringBuilder.Clear();
cache.IsDuringSerialization = true;
cache.SerializerWriter.SerializeDiff(cache.JsonWriter, obj, type, other);
return cache.StringBuilder.ToString();
JObject jObj = JObject.FromObject(obj, cache.JsonSerializer);
JObject jOther = JObject.FromObject(other, cache.JsonSerializer);
JObject diff = new JObject();
foreach (KeyValuePair<string, JToken> prop in jObj)
{
JProperty otherProp = jOther.Property(prop.Key);
if (JToken.DeepEquals(prop.Value, otherProp.Value))
continue;
diff.Add(prop.Key, prop.Value);
}
return diff.ToString();
}
/// <summary>

View File

@@ -6,7 +6,9 @@ using System.IO;
using System.Text;
using FlaxEngine.GUI;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
using System.Collections.Generic;
namespace FlaxEngine
{
@@ -340,10 +342,22 @@ namespace FlaxEngine
jsonWriter.StringEscapeHandling = jsonSerializer.StringEscapeHandling;
jsonWriter.Culture = jsonSerializer.Culture;
jsonWriter.DateFormatString = jsonSerializer.DateFormatString;
#if !USE_NETCORE
JsonSerializerInternalWriter serializerWriter = new JsonSerializerInternalWriter(jsonSerializer);
serializerWriter.Serialize(jsonWriter, _control, type);
#else
Type jsonSerializerInternalWriterType =
typeof(Newtonsoft.Json.Serialization.IValueProvider).Assembly.GetType(
"Newtonsoft.Json.Serialization.JsonSerializerInternalWriter");
System.Reflection.ConstructorInfo ctor = jsonSerializerInternalWriterType.GetConstructors
(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public)[0];
object serializerWriter = ctor.Invoke(new object[] { jsonSerializer });
System.Reflection.MethodInfo Serialize = jsonSerializerInternalWriterType.GetMethod("Serialize",
System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public);
Serialize.Invoke(serializerWriter, new object[] { jsonWriter, _control, type });
#endif
}
controlType = type.FullName;
@@ -380,10 +394,25 @@ namespace FlaxEngine
jsonWriter.StringEscapeHandling = jsonSerializer.StringEscapeHandling;
jsonWriter.Culture = jsonSerializer.Culture;
jsonWriter.DateFormatString = jsonSerializer.DateFormatString;
#if !USE_NETCORE
JsonSerializerInternalWriter serializerWriter = new JsonSerializerInternalWriter(jsonSerializer);
serializerWriter.SerializeDiff(jsonWriter, _control, type, other._control);
#else
JObject jObj = JObject.FromObject(_control, jsonSerializer);
JObject jOther = JObject.FromObject(other._control, jsonSerializer);
JObject diff = new JObject();
foreach (KeyValuePair<string, JToken> prop in jObj)
{
JProperty otherProp = jOther.Property(prop.Key);
if (JToken.DeepEquals(prop.Value, otherProp.Value))
continue;
diff.Add(prop.Key, prop.Value);
}
diff.WriteTo(jsonWriter);
#endif
}
controlType = string.Empty;

View File

@@ -6,13 +6,15 @@ using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;
namespace FlaxEngine
{
/// <summary>
/// Class with helper functions.
/// </summary>
public static class Utils
public static partial class Utils
{
/// <summary>
/// Copies data from one memory location to another using an unmanaged memory pointers.
@@ -35,8 +37,8 @@ namespace FlaxEngine
/// <param name="source">The source location.</param>
/// <param name="destination">The destination location.</param>
/// <param name="length">The length (amount of bytes to copy).</param>
[MethodImpl(MethodImplOptions.InternalCall)]
public static extern void MemoryCopy(IntPtr destination, IntPtr source, ulong length);
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEngine.Utils::MemoryCopy")]
public static partial void MemoryCopy(IntPtr destination, IntPtr source, ulong length);
/// <summary>
/// Clears the memory region with zeros.
@@ -44,8 +46,8 @@ namespace FlaxEngine
/// <remarks>Uses low-level platform impl.</remarks>
/// <param name="dst">Destination memory address</param>
/// <param name="size">Size of the memory to clear in bytes</param>
[MethodImpl(MethodImplOptions.InternalCall)]
public static extern void MemoryClear(IntPtr dst, ulong size);
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEngine.Utils::MemoryClear")]
public static partial void MemoryClear(IntPtr dst, ulong size);
/// <summary>
/// Compares two blocks of the memory.
@@ -54,8 +56,8 @@ namespace FlaxEngine
/// <param name="buf1">The first buffer address.</param>
/// <param name="buf2">The second buffer address.</param>
/// <param name="size">Size of the memory to compare in bytes.</param>
[MethodImpl(MethodImplOptions.InternalCall)]
public static extern int MemoryCompare(IntPtr buf1, IntPtr buf2, ulong size);
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEngine.Utils::MemoryCompare")]
public static partial int MemoryCompare(IntPtr buf1, IntPtr buf2, ulong size);
/// <summary>
/// Rounds the floating point value up to 1 decimal place.
@@ -94,7 +96,11 @@ namespace FlaxEngine
/// <returns>The empty array object.</returns>
public static T[] GetEmptyArray<T>()
{
#if USE_NETCORE
return Array.Empty<T>();
#else
return Enumerable.Empty<T>() as T[];
#endif
}
/// <summary>
@@ -209,10 +215,50 @@ namespace FlaxEngine
return result;
}
/// <summary>
/// Gets the location of the assembly.
/// </summary>
/// <param name="assembly">The assembly.</param>
/// <returns>Path in the filesystem</returns>
public static string GetAssemblyLocation(Assembly assembly)
{
#if USE_NETCORE
if (!string.IsNullOrEmpty(assembly.Location))
return assembly.Location;
if (NativeInterop.AssemblyLocations.TryGetValue(assembly.FullName, out string assemblyLocation))
return assemblyLocation;
return null;
#else
return assembly.Location;
#endif
}
#if USE_MONO
internal static T[] ExtractArrayFromList<T>(List<T> list)
{
return list != null ? (T[])Internal_ExtractArrayFromList(list) : null;
}
#else
private class ExtractArrayFromListContext<T>
{
public static FieldInfo? itemsField;
}
internal static T[] ExtractArrayFromList<T>(List<T> list)
{
if (list == null)
return null;
if (ExtractArrayFromListContext<T>.itemsField == null)
{
Type listType = typeof(List<T>);
ExtractArrayFromListContext<T>.itemsField = listType.GetField("_items", BindingFlags.NonPublic | BindingFlags.Instance);
}
return (T[])ExtractArrayFromListContext<T>.itemsField.GetValue(list); // boxing is slower;
}
#endif
internal static Float2[] ConvertCollection(Vector2[] v)
{
@@ -295,8 +341,9 @@ namespace FlaxEngine
return result;
}
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern Array Internal_ExtractArrayFromList(object list);
[LibraryImport("FlaxEngine", EntryPoint = "FlaxEngine.Utils::Internal_ExtractArrayFromList")]
[return: MarshalUsing(typeof(FlaxEngine.SystemArrayMarshaller))]
internal static partial Array Internal_ExtractArrayFromList([MarshalUsing(typeof(FlaxEngine.GCHandleMarshaller))] object list);
/// <summary>
/// Reads the color from the binary stream.

View File

@@ -4,11 +4,14 @@ using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
#if FLAX_EDITOR
using FlaxEditor.Scripting;
using FlaxEditor.Utilities;
#endif
using FlaxEngine;
using Newtonsoft.Json;
namespace FlaxEditor.Utilities
namespace FlaxEngine.Utilities
{
/// <summary>
/// Editor utilities and helper functions for Variant type.
@@ -76,6 +79,7 @@ namespace FlaxEditor.Utilities
#endif
}
#if FLAX_EDITOR
internal static VariantType ToVariantType(this Type type)
{
VariantType variantType;
@@ -107,7 +111,9 @@ namespace FlaxEditor.Utilities
variantType = VariantType.Pointer;
else if (type == typeof(string))
variantType = VariantType.String;
else if (type == typeof(Type) || type == typeof(ScriptType))
else if (type == typeof(Type))
variantType = VariantType.Typename;
else if (type == typeof(ScriptType))
variantType = VariantType.Typename;
else if (typeof(Asset).IsAssignableFrom(type))
variantType = VariantType.Asset;
@@ -1286,5 +1292,6 @@ namespace FlaxEditor.Utilities
stream.WriteEndObject();
}
#endif
}
}

View File

@@ -0,0 +1,6 @@
#include "VisjectScript.h"
VisjectScript::VisjectScript(const SpawnParams& params)
: Script(params)
{
}

View File

@@ -0,0 +1,14 @@
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
#pragma once
#include "Engine/Scripting/Script.h"
/// <summary>
///
/// </summary>
API_CLASS(Namespace = "FlaxEngine.Visject") class FLAXENGINE_API VisjectScript : public Script
{
API_AUTO_SERIALIZATION();
DECLARE_SCRIPTING_TYPE(VisjectScript);
};

View File

@@ -0,0 +1,9 @@
{
"runtimeOptions": {
"tfm": "net7.0",
"framework": {
"name": "Microsoft.NETCore.App",
"version": "7.0.0-rc.2.22472.3"
}
}
}

23
Source/ThirdParty/nethost/LICENSE.TXT vendored Normal file
View File

@@ -0,0 +1,23 @@
The MIT License (MIT)
Copyright (c) .NET Foundation and Contributors
All rights reserved.
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.

View File

@@ -0,0 +1,112 @@
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
using System.Collections.Generic;
using System.IO;
using System;
using Flax.Build;
using Flax.Build.NativeCpp;
using Flax.Build.Platforms;
using Microsoft.Win32;
using System.Linq;
/// <summary>
/// Module for nethost (.NET runtime host library)
/// </summary>
public class nethost : ThirdPartyModule
{
/// <inheritdoc />
public override void Init()
{
base.Init();
LicenseType = LicenseTypes.MIT;
LicenseFilePath = "LICENSE.TXT";
// Merge third-party modules into engine binary
BinaryModuleName = "FlaxEngine";
}
/// <inheritdoc />
private static Version ParseVersion(string version)
{
// Give precedence to final releases over release candidate / beta releases
int rev = 9999;
if (version.Contains("-")) // e.g. 7.0.0-rc.2.22472.3
{
version = version.Substring(0, version.IndexOf("-"));
rev = 0;
}
Version ver = new Version(version);
return new Version(ver.Major, ver.Minor, ver.Build, rev);
}
/// <inheritdoc />
public override void Setup(BuildOptions options)
{
base.Setup(options);
options.SourceFiles.Clear();
string arch = "x64"; //options.Architecture == TargetArchitecture.x64 ? "x64" : "x86";
string dotnetVersion;
string appHostRuntimePath;
// NOTE: nethost is bundled with SDK, not runtime. Should C# scripting have a hard requirement for SDK to be installed?
if (options.Platform.Target == TargetPlatform.Windows)
{
string os = $"win-{arch}";
using RegistryKey baseKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64);
using RegistryKey hostKey = baseKey.OpenSubKey(@$"SOFTWARE\dotnet\Setup\InstalledVersions\{arch}\sharedhost");
string dotnetPath = (string)hostKey.GetValue("Path");
using RegistryKey runtimeKey = baseKey.OpenSubKey(@$"SOFTWARE\WOW6432Node\dotnet\Setup\InstalledVersions\{arch}\sharedfx\Microsoft.NETCore.App");
string[] versions = runtimeKey.GetValueNames();
dotnetVersion = versions.OrderByDescending(x => ParseVersion(x)).FirstOrDefault();
if (string.IsNullOrEmpty(dotnetPath))
dotnetPath = Environment.GetEnvironmentVariable("DOTNET_ROOT");
if (string.IsNullOrEmpty(dotnetPath) || string.IsNullOrEmpty(dotnetVersion))
throw new Exception("Failed to find dotnet installation");
int majorVersion = int.Parse(dotnetVersion.Substring(0, dotnetVersion.IndexOf(".")));
if (majorVersion < 7)
throw new Exception($"Unsupported dotnet version found, minimum version required is .NET 7 (found {dotnetVersion})");
appHostRuntimePath = String.Format("{0}packs\\Microsoft.NETCore.App.Host.{1}\\{2}\\runtimes\\{1}\\native", dotnetPath, os, dotnetVersion);
options.OutputFiles.Add(Path.Combine(appHostRuntimePath, "nethost.lib"));
options.DependencyFiles.Add(Path.Combine(appHostRuntimePath, "nethost.dll"));
options.PublicIncludePaths.Add(appHostRuntimePath);
}
else if (options.Platform.Target == TargetPlatform.Linux)
{
// TODO: Support /etc/dotnet/install_location
string dotnetPath = "/usr/share/dotnet/";
string os = $"linux-{arch}";
string[] versions = Directory.GetDirectories($"{dotnetPath}host/fxr/").Select(x => Path.GetFileName(x)).ToArray();
dotnetVersion = versions.OrderByDescending(x => ParseVersion(x)).FirstOrDefault();
int majorVersion = int.Parse(dotnetVersion.Substring(0, dotnetVersion.IndexOf(".")));
if (majorVersion < 7)
throw new Exception($"Unsupported dotnet version found, minimum version required is .NET 7 (found {dotnetVersion})");
appHostRuntimePath = String.Format("{0}packs/Microsoft.NETCore.App.Host.{1}/{2}/runtimes/{1}/native", dotnetPath, os, dotnetVersion);
options.OutputFiles.Add(Path.Combine(appHostRuntimePath, "libnethost.a"));
options.DependencyFiles.Add(Path.Combine(appHostRuntimePath, "libnethost.so"));
options.PublicIncludePaths.Add(appHostRuntimePath);
}
else
throw new InvalidPlatformException(options.Platform.Target);
options.PublicIncludePaths.Add(appHostRuntimePath);
options.ScriptingAPI.Defines.Add("USE_NETCORE");
options.DependencyFiles.Add(Path.Combine(FolderPath, "FlaxEngine.CSharp.runtimeconfig.json"));
}
}

View File

@@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using BuildData = Flax.Build.Builder.BuildData;
namespace Flax.Build.Bindings
@@ -171,7 +172,11 @@ namespace Flax.Build.Bindings
// Skip for collections
if ((typeInfo.Type == "Array" || typeInfo.Type == "Span" || typeInfo.Type == "DataContainer" || typeInfo.Type == "Dictionary" || typeInfo.Type == "HashSet") && typeInfo.GenericArgs != null)
#if !USE_NETCORE
return false;
#else
return true;
#endif
// Skip for special types
if (typeInfo.GenericArgs == null)
@@ -219,6 +224,40 @@ namespace Flax.Build.Bindings
return false;
}
#if USE_NETCORE
/// <summary>
/// Check if structure contains unblittable types that would require custom marshaller for the structure.
/// </summary>
public static bool UseCustomMarshalling(BuildData buildData, StructureInfo structureInfo, ApiTypeInfo caller)
{
if (structureInfo.Fields.Any(x => !x.IsStatic &&
(x.Type.IsObjectRef || x.Type.Type == "Dictionary" || x.Type.Type == "Version")
&& x.Type.Type != "uint8" && x.Type.Type != "byte"))
{
return true;
}
foreach (var field in structureInfo.Fields)
{
if (field.Type.Type == structureInfo.FullNameNative)
continue;
if (field.IsStatic)
continue;
if (field.Type.Type == "String")
return true;
var fieldApiType = FindApiTypeInfo(buildData, field.Type, caller);
if (fieldApiType is StructureInfo fieldStructureInfo && UseCustomMarshalling(buildData, fieldStructureInfo, caller))
return true;
else if (fieldApiType is ClassInfo)
return true;
}
return false;
}
#endif
/// <summary>
/// Finds the API type information.
/// </summary>

View File

@@ -278,15 +278,15 @@ namespace Flax.Build.Bindings
}
// Object reference property
if ((typeInfo.Type == "ScriptingObjectReference" ||
typeInfo.Type == "AssetReference" ||
typeInfo.Type == "WeakAssetReference" ||
typeInfo.Type == "SoftAssetReference" ||
typeInfo.Type == "SoftObjectReference") && typeInfo.GenericArgs != null)
if (typeInfo.IsObjectRef)
return GenerateCSharpNativeToManaged(buildData, typeInfo.GenericArgs[0], caller);
// Array or Span or DataContainer
#if USE_NETCORE
if ((typeInfo.Type == "Array" || typeInfo.Type == "Span" || typeInfo.Type == "DataContainer" || typeInfo.Type == "MonoArray") && typeInfo.GenericArgs != null)
#else
if ((typeInfo.Type == "Array" || typeInfo.Type == "Span" || typeInfo.Type == "DataContainer") && typeInfo.GenericArgs != null)
#endif
return GenerateCSharpNativeToManaged(buildData, typeInfo.GenericArgs[0], caller) + "[]";
// Dictionary
@@ -361,11 +361,7 @@ namespace Flax.Build.Bindings
}
// Object reference property
if ((typeInfo.Type == "ScriptingObjectReference" ||
typeInfo.Type == "AssetReference" ||
typeInfo.Type == "WeakAssetReference" ||
typeInfo.Type == "SoftAssetReference" ||
typeInfo.Type == "SoftObjectReference") && typeInfo.GenericArgs != null)
if (typeInfo.IsObjectRef)
return "IntPtr";
// Function
@@ -419,11 +415,7 @@ namespace Flax.Build.Bindings
}
// Object reference property
if ((typeInfo.Type == "ScriptingObjectReference" ||
typeInfo.Type == "AssetReference" ||
typeInfo.Type == "WeakAssetReference" ||
typeInfo.Type == "SoftAssetReference" ||
typeInfo.Type == "SoftObjectReference") && typeInfo.GenericArgs != null)
if (typeInfo.IsObjectRef)
return "FlaxEngine.Object.GetUnmanagedPtr({0})";
// Default
@@ -443,8 +435,38 @@ namespace Flax.Build.Bindings
returnValueType = GenerateCSharpNativeToManaged(buildData, functionInfo.ReturnType, caller);
}
#if USE_NETCORE
string returnMarshalType = "";
if (returnValueType == "bool")
returnMarshalType = "MarshalAs(UnmanagedType.U1)";
else if (returnValueType == "System.Type")
returnMarshalType = "MarshalUsing(typeof(FlaxEngine.SystemTypeMarshaller))";
else if (returnValueType == "CultureInfo")
returnMarshalType = "MarshalUsing(typeof(FlaxEngine.CultureInfoMarshaller))";
else if (functionInfo.ReturnType.Type == "Variant")
returnMarshalType = "MarshalUsing(typeof(FlaxEngine.GCHandleMarshaller))";
else if (FindApiTypeInfo(buildData, functionInfo.ReturnType, caller)?.IsInterface ?? false)
{
// Interfaces are not supported by NativeMarshallingAttribute, marshal the parameter
returnMarshalType = $"MarshalUsing(typeof({returnValueType}Marshaller))";
}
else if (returnValueType == "byte[]")
returnMarshalType = $"MarshalUsing(typeof(FlaxEngine.ArrayMarshaller<,>), CountElementName = \"returnCount\")";
else if (returnValueType == "bool[]")
{
// Boolean arrays does not support custom marshalling for some unkown reason...
returnMarshalType = $"MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U1, SizeParamIndex = {functionInfo.Parameters.Count + (functionInfo.Glue.CustomParameters?.Count ?? 0)})";
}
#endif
#if !USE_NETCORE
contents.AppendLine().Append(indent).Append("[MethodImpl(MethodImplOptions.InternalCall)]");
contents.AppendLine().Append(indent).Append("internal static extern ");
contents.AppendLine().Append(indent).Append("internal static partial ");
#else
contents.AppendLine().Append(indent).Append($"[LibraryImport(\"FlaxEngine\", EntryPoint = \"{caller.FullNameManaged}::Internal_{functionInfo.UniqueName}\", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]");
if (!string.IsNullOrEmpty(returnMarshalType))
contents.AppendLine().Append(indent).Append($"[return: {returnMarshalType}]");
contents.AppendLine().Append(indent).Append("internal static partial ");
#endif
contents.Append(returnValueType).Append(" Internal_").Append(functionInfo.UniqueName).Append('(');
var separator = false;
@@ -461,6 +483,30 @@ namespace Flax.Build.Bindings
separator = true;
var nativeType = GenerateCSharpManagedToNativeType(buildData, parameterInfo.Type, caller);
#if USE_NETCORE
string parameterMarshalType = "";
if (nativeType == "System.Type")
parameterMarshalType = "MarshalUsing(typeof(FlaxEngine.SystemTypeMarshaller))";
else if (parameterInfo.Type.Type == "CultureInfo")
parameterMarshalType = "MarshalUsing(typeof(FlaxEngine.CultureInfoMarshaller))";
else if (parameterInfo.Type.Type == "Variant") // object
parameterMarshalType = "MarshalUsing(typeof(FlaxEngine.GCHandleMarshaller))";
else if (parameterInfo.Type.Type == "MonoArray")
parameterMarshalType = "MarshalUsing(typeof(FlaxEngine.SystemArrayMarshaller))";
else if (parameterInfo.Type.Type == "Array" && parameterInfo.Type.GenericArgs.Count > 0 && parameterInfo.Type.GenericArgs[0].Type == "bool")
parameterMarshalType = $"MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U1, SizeParamIndex = {(!functionInfo.IsStatic ? 1 : 0) + functionInfo.Parameters.Count + (functionInfo.Glue.CustomParameters.FindIndex(x => x.Name == parameterInfo.Name + "Count"))})";
else if (parameterInfo.Type.Type == "Array" || parameterInfo.Type.Type == "Span" || parameterInfo.Type.Type == "DataContainer" || parameterInfo.Type.Type == "BytesContainer" || nativeType == "Array")
parameterMarshalType = $"MarshalUsing(typeof(FlaxEngine.ArrayMarshaller<,>), CountElementName = \"{parameterInfo.Name}Count\")";
else if (parameterInfo.Type.Type == "Dictionary")
parameterMarshalType = $"MarshalUsing(typeof(FlaxEngine.DictionaryMarshaller<,>), ConstantElementCount = 0)";
else if (nativeType == "bool")
parameterMarshalType = "MarshalAs(UnmanagedType.U1)";
else if (nativeType == "char")
parameterMarshalType = "MarshalAs(UnmanagedType.I2)";
if (!string.IsNullOrEmpty(parameterMarshalType))
contents.Append($"[{parameterMarshalType}] ");
#endif
if (parameterInfo.IsOut)
contents.Append("out ");
else if (parameterInfo.IsRef || UsePassByReference(buildData, parameterInfo.Type, caller))
@@ -484,6 +530,21 @@ namespace Flax.Build.Bindings
separator = true;
var nativeType = GenerateCSharpManagedToNativeType(buildData, parameterInfo.Type, caller);
#if USE_NETCORE
string parameterMarshalType = "";
if (parameterInfo.IsOut && parameterInfo.DefaultValue == "var resultAsRef")
{
if (functionInfo.Glue.UseResultReferenceCount)
parameterMarshalType = $"MarshalUsing(typeof(FlaxEngine.ArrayMarshaller<,>), CountElementName = \"{parameterInfo.Name}Count\")";
else if (parameterInfo.Type.Type == "Dictionary")
parameterMarshalType = $"MarshalUsing(typeof(FlaxEngine.DictionaryMarshaller<,>), ConstantElementCount = 0)";
}
if (nativeType == "System.Type")
parameterMarshalType = "MarshalUsing(typeof(FlaxEngine.SystemTypeMarshaller))";
if (!string.IsNullOrEmpty(parameterMarshalType))
contents.Append($"[{parameterMarshalType}] ");
#endif
if (parameterInfo.IsOut)
contents.Append("out ");
else if (parameterInfo.IsRef || UsePassByReference(buildData, parameterInfo.Type, caller))
@@ -500,6 +561,17 @@ namespace Flax.Build.Bindings
private static void GenerateCSharpWrapperFunctionCall(BuildData buildData, StringBuilder contents, ApiTypeInfo caller, FunctionInfo functionInfo, bool isSetter = false)
{
#if USE_NETCORE
for (var i = 0; i < functionInfo.Parameters.Count; i++)
{
var parameterInfo = functionInfo.Parameters[i];
if (parameterInfo.Type.IsArray || parameterInfo.Type.Type == "Array" || parameterInfo.Type.Type == "Span" || parameterInfo.Type.Type == "BytesContainer" || parameterInfo.Type.Type == "DataContainer" || parameterInfo.Type.Type == "BitArray")
{
if (!parameterInfo.IsOut)
contents.Append($"var {parameterInfo.Name}Count = {(isSetter ? "value" : parameterInfo.Name)}?.Length ?? 0; ");
}
}
#endif
if (functionInfo.Glue.UseReferenceForResult)
{
}
@@ -613,6 +685,9 @@ namespace Flax.Build.Bindings
{
// Write attribute for C++ calling code
contents.Append(indent).AppendLine("[Unmanaged]");
// Skip boilerplate code when using debugger
//contents.Append(indent).AppendLine("[System.Diagnostics.DebuggerStepThrough]");
}
if (isDeprecated || apiTypeInfo.IsDeprecated)
{
@@ -698,6 +773,15 @@ namespace Flax.Build.Bindings
// Class begin
GenerateCSharpAttributes(buildData, contents, indent, classInfo, useUnmanaged);
#if USE_NETCORE
string marshallerName = "";
if (!classInfo.IsStatic)
{
marshallerName = classInfo.Name + "Marshaller";
contents.Append(indent).AppendLine($"[NativeMarshalling(typeof({marshallerName}))]");
//contents.Append(indent).AppendLine($"[NativeMarshalling(typeof(FlaxEngine.GCHandleMarshaller2<{classInfo.Name}>.GCHandleMarshaller3<{classInfo.Name}>))]");
}
#endif
contents.Append(indent);
GenerateCSharpAccessLevel(contents, classInfo.Access);
if (classInfo.IsStatic)
@@ -862,11 +946,19 @@ namespace Flax.Build.Bindings
contents.Append(indent).Append('}').AppendLine();
contents.AppendLine();
#if !USE_NETCORE
contents.Append(indent).Append("[MethodImpl(MethodImplOptions.InternalCall)]").AppendLine();
contents.Append(indent).Append($"internal static extern void Internal_{eventInfo.Name}_Bind(");
if (!eventInfo.IsStatic)
contents.Append("IntPtr obj, ");
contents.Append("bool bind);");
#else
contents.Append(indent).Append($"[LibraryImport(\"FlaxEngine\", EntryPoint = \"{classInfo.FullNameManaged}::Internal_{eventInfo.Name}_Bind\", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(FlaxEngine.StringMarshaller))]").AppendLine();
contents.Append(indent).Append($"internal static partial void Internal_{eventInfo.Name}_Bind(");
if (!eventInfo.IsStatic)
contents.Append("IntPtr obj, ");
contents.Append("[MarshalAs(UnmanagedType.U1)] bool bind);");
#endif
contents.AppendLine();
}
@@ -1100,6 +1192,51 @@ namespace Flax.Build.Bindings
indent = indent.Substring(0, indent.Length - 4);
contents.AppendLine(indent + "}");
#if USE_NETCORE
if (!string.IsNullOrEmpty(marshallerName))
{
string marshallerDefinition = $$"""
[CustomMarshaller(typeof({{classInfo.Name}}), MarshalMode.ManagedToUnmanagedIn, typeof({{marshallerName}}.ManagedToNative))]
[CustomMarshaller(typeof({{classInfo.Name}}), MarshalMode.UnmanagedToManagedOut, typeof({{marshallerName}}.ManagedToNative))]
[CustomMarshaller(typeof({{classInfo.Name}}), MarshalMode.ElementIn, typeof({{marshallerName}}.ManagedToNative))]
[CustomMarshaller(typeof({{classInfo.Name}}), MarshalMode.ManagedToUnmanagedOut, typeof({{marshallerName}}.NativeToManaged))]
[CustomMarshaller(typeof({{classInfo.Name}}), MarshalMode.UnmanagedToManagedIn, typeof({{marshallerName}}.NativeToManaged))]
[CustomMarshaller(typeof({{classInfo.Name}}), MarshalMode.ElementOut, typeof({{marshallerName}}.NativeToManaged))]
[CustomMarshaller(typeof({{classInfo.Name}}), MarshalMode.ManagedToUnmanagedRef, typeof({{marshallerName}}.Bidirectional))]
[CustomMarshaller(typeof({{classInfo.Name}}), MarshalMode.UnmanagedToManagedRef, typeof({{marshallerName}}.Bidirectional))]
[CustomMarshaller(typeof({{classInfo.Name}}), MarshalMode.ElementRef, typeof({{marshallerName}}))]
internal static class {{marshallerName}}
{
public static class NativeToManaged
{
public static {{classInfo.Name}} ConvertToManaged(IntPtr unmanaged) => ({{classInfo.Name}})GCHandleMarshaller.NativeToManaged.ConvertToManaged(unmanaged);
public static void Free(IntPtr unmanaged) => GCHandleMarshaller.NativeToManaged.Free(unmanaged);
}
public static class ManagedToNative
{
public static IntPtr ConvertToUnmanaged({{classInfo.Name}} managed) => GCHandleMarshaller.ManagedToNative.ConvertToUnmanaged(managed);
public static void Free(IntPtr unmanaged) => GCHandleMarshaller.ManagedToNative.Free(unmanaged);
}
public struct Bidirectional
{
GCHandleMarshaller.Bidirectional marsh;
public void FromManaged({{classInfo.Name}} managed) => marsh.FromManaged(managed);
public IntPtr ToUnmanaged() => marsh.ToUnmanaged();
public void FromUnmanaged(IntPtr unmanaged) => marsh.FromUnmanaged(unmanaged);
public {{classInfo.Name}} ToManaged() => ({{classInfo.Name}})marsh.ToManaged();
public void Free() => marsh.Free();
}
internal static {{classInfo.Name}} ConvertToManaged(IntPtr unmanaged) => ({{classInfo.Name}})GCHandleMarshaller.ConvertToManaged(unmanaged);
internal static IntPtr ConvertToUnmanaged({{classInfo.Name}} managed) => GCHandleMarshaller.ConvertToUnmanaged(managed);
internal static void Free(IntPtr unmanaged) => GCHandleMarshaller.Free(unmanaged);
internal static {{classInfo.Name}} ToManaged(IntPtr managed) => ({{classInfo.Name}})GCHandleMarshaller.ToManaged(managed);
internal static IntPtr ToNative({{classInfo.Name}} managed) => GCHandleMarshaller.ToNative(managed);
}
""";
contents.AppendLine(marshallerDefinition);
}
#endif
// Namespace end
if (!string.IsNullOrEmpty(classInfo.Namespace))
{
@@ -1119,13 +1256,330 @@ namespace Flax.Build.Bindings
contents.AppendLine("{");
indent += " ";
}
#if USE_NETCORE
// Generate blittable structure
string structNativeMarshaling = "";
if (UseCustomMarshalling(buildData, structureInfo, structureInfo))
{
string marshallerName = structureInfo.Name + "Marshaller";
structNativeMarshaling = $"[NativeMarshalling(typeof({marshallerName}))]";
contents.Append(indent).AppendLine($"/// <summary>");
contents.Append(indent).AppendLine($"/// Marshaller for unblittable type <see cref=\"{structureInfo.Name}\"/>.");
contents.Append(indent).AppendLine($"/// </summary>");
contents.Append(indent).AppendLine($"[CustomMarshaller(typeof({structureInfo.Name}), MarshalMode.ManagedToUnmanagedIn, typeof({marshallerName}.ManagedToNative))]");
contents.Append(indent).AppendLine($"[CustomMarshaller(typeof({structureInfo.Name}), MarshalMode.UnmanagedToManagedOut, typeof({marshallerName}.ManagedToNative))]");
contents.Append(indent).AppendLine($"[CustomMarshaller(typeof({structureInfo.Name}), MarshalMode.ElementIn, typeof({marshallerName}.ManagedToNative))]");
contents.Append(indent).AppendLine($"[CustomMarshaller(typeof({structureInfo.Name}), MarshalMode.ManagedToUnmanagedOut, typeof({marshallerName}.NativeToManaged))]");
contents.Append(indent).AppendLine($"[CustomMarshaller(typeof({structureInfo.Name}), MarshalMode.UnmanagedToManagedIn, typeof({marshallerName}.NativeToManaged))]");
contents.Append(indent).AppendLine($"[CustomMarshaller(typeof({structureInfo.Name}), MarshalMode.ElementOut, typeof({marshallerName}.NativeToManaged))]");
contents.Append(indent).AppendLine($"[CustomMarshaller(typeof({structureInfo.Name}), MarshalMode.ManagedToUnmanagedRef, typeof({marshallerName}.Bidirectional))]");
contents.Append(indent).AppendLine($"[CustomMarshaller(typeof({structureInfo.Name}), MarshalMode.UnmanagedToManagedRef, typeof({marshallerName}.Bidirectional))]");
contents.Append(indent).AppendLine($"[CustomMarshaller(typeof({structureInfo.Name}), MarshalMode.ElementRef, typeof({marshallerName}))]");
contents.Append(indent).AppendLine($"internal static unsafe class {marshallerName}");
contents.Append(indent).AppendLine("{");
indent += " ";
StringBuilder toManagedContent = new StringBuilder();
StringBuilder toNativeContent = new StringBuilder();
StringBuilder freeContents = new StringBuilder();
StringBuilder freeContents2 = new StringBuilder();
{
// Native struct begin
GenerateCSharpAttributes(buildData, contents, indent, structureInfo, true);
contents.Append(indent).AppendLine("[StructLayout(LayoutKind.Sequential)]");
contents.Append(indent);
contents.Append("internal struct ").Append(structureInfo.Name).Append("Internal");
if (structureInfo.BaseType != null && structureInfo.IsPod)
contents.Append(" : ").Append(GenerateCSharpNativeToManaged(buildData, new TypeInfo { Type = structureInfo.BaseType.Name }, structureInfo));
contents.AppendLine();
contents.Append(indent + "{");
indent += " ";
toNativeContent.Append($"return new {structureInfo.Name}Internal() {{ ");
toManagedContent.Append($"return new {structureInfo.Name}() {{ ");
bool useSeparator = false;
foreach (var fieldInfo in structureInfo.Fields)
{
if (fieldInfo.IsStatic || fieldInfo.IsConstexpr)
continue;
contents.AppendLine();
string type, originalType;
if (fieldInfo.Type.IsArray && (fieldInfo.NoArray || structureInfo.IsPod))
{
// Fixed-size array that needs to be inlined into structure instead of passing it as managed array
fieldInfo.Type.IsArray = false;
originalType = type = GenerateCSharpNativeToManaged(buildData, fieldInfo.Type, structureInfo);
fieldInfo.Type.IsArray = true;
}
else
originalType = type = GenerateCSharpNativeToManaged(buildData, fieldInfo.Type, structureInfo);
contents.Append(indent);
if (fieldInfo.Access == AccessLevel.Public)
contents.Append("public ");
else if (fieldInfo.Access == AccessLevel.Protected)
contents.Append("protected ");
else if (fieldInfo.Access == AccessLevel.Private)
contents.Append("private ");
if (fieldInfo.IsConstexpr)
contents.Append("const ");
else if (fieldInfo.IsStatic)
contents.Append("static ");
var apiType = FindApiTypeInfo(buildData, fieldInfo.Type, structureInfo);
bool internalType = apiType is StructureInfo fieldStructureInfo && UseCustomMarshalling(buildData, fieldStructureInfo, structureInfo);
string internalTypeMarshaler = "";
if (fieldInfo.Type.IsArray && (fieldInfo.NoArray || structureInfo.IsPod))
{
contents.Append(type).Append(' ').Append(fieldInfo.Name + "0;").AppendLine();
for (int i = 1; i < fieldInfo.Type.ArraySize; i++)
{
contents.AppendLine();
GenerateCSharpAttributes(buildData, contents, indent, structureInfo, fieldInfo, fieldInfo.IsStatic);
contents.Append(indent);
if (fieldInfo.Access == AccessLevel.Public)
contents.Append("public ");
else if (fieldInfo.Access == AccessLevel.Protected)
contents.Append("protected ");
else if (fieldInfo.Access == AccessLevel.Private)
contents.Append("private ");
if (fieldInfo.IsStatic)
contents.Append("static ");
contents.Append(type).Append(' ').Append(fieldInfo.Name + i).Append(';').AppendLine();
}
}
else
{
if (fieldInfo.Type.IsObjectRef || fieldInfo.Type.Type == "Dictionary")
type = "IntPtr";
else if (fieldInfo.Type.IsPtr && !originalType.EndsWith("*"))
type = "IntPtr";
else if (fieldInfo.Type.Type == "Array")
{
type = "IntPtr";
apiType = FindApiTypeInfo(buildData, fieldInfo.Type.GenericArgs[0], structureInfo);
internalType = apiType is StructureInfo elementStructureInfo && UseCustomMarshalling(buildData, elementStructureInfo, structureInfo);
}
else if (fieldInfo.Type.Type == "Version")
type = "IntPtr";
else if (type == "string")
type = "IntPtr";
else if (type == "bool")
type = "byte";
else if (type == "object")
type = "VariantNative";
else if (internalType)
{
internalTypeMarshaler = type + "Marshaller";
type = $"{internalTypeMarshaler}.{type}Internal";
}
//else if (type == "Guid")
// type = "GuidNative";
contents.Append(type).Append(' ').Append(fieldInfo.Name);
contents.Append(';').AppendLine();
}
// Generate struct constructor/getter and deconstructor/setter function
if (fieldInfo.NoArray && fieldInfo.Type.IsArray)
continue;
if (type == "VariantNative")
continue; // FIXME
if (useSeparator)
{
toManagedContent.Append(", ");
toNativeContent.Append(", ");
freeContents2.Append("");
freeContents.Append("");
}
useSeparator = true;
toManagedContent.Append(fieldInfo.Name);
toManagedContent.Append(" = ");
toNativeContent.Append(fieldInfo.Name);
toNativeContent.Append(" = ");
if (fieldInfo.Type.IsObjectRef)
{
toManagedContent.Append($"managed.{fieldInfo.Name} != IntPtr.Zero ? ({fieldInfo.Type.GenericArgs[0].Type})GCHandle.FromIntPtr(managed.{fieldInfo.Name}).Target : null");
toNativeContent.Append($"managed.{fieldInfo.Name} != null ? GCHandle.ToIntPtr(GCHandle.Alloc(managed.{fieldInfo.Name}, GCHandleType.Weak)) : IntPtr.Zero");
freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ GCHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}");
// Permanent ScriptingObject handle is passed from native side, do not release it
//freeContents2.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ GCHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}");
}
else if (fieldInfo.Type.Type == "ScriptingObject")
{
toManagedContent.Append($"managed.{fieldInfo.Name} != IntPtr.Zero ? (FlaxEngine.Object)GCHandle.FromIntPtr(managed.{fieldInfo.Name}).Target : null");
toNativeContent.Append($"managed.{fieldInfo.Name} != null ? GCHandle.ToIntPtr(GCHandle.Alloc(managed.{fieldInfo.Name}, GCHandleType.Weak)) : IntPtr.Zero");
freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ GCHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}");
// Permanent ScriptingObject handle is passed from native side, do not release it
//freeContents2.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ GCHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}");
}
else if (fieldInfo.Type.IsPtr && originalType != "IntPtr" && !originalType.EndsWith("*"))
{
toManagedContent.Append($"managed.{fieldInfo.Name} != IntPtr.Zero ? ({originalType})GCHandle.FromIntPtr(managed.{fieldInfo.Name}).Target : null");
toNativeContent.Append($"managed.{fieldInfo.Name} != null ? GCHandle.ToIntPtr(GCHandle.Alloc(managed.{fieldInfo.Name}, GCHandleType.Weak)) : IntPtr.Zero");
freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ GCHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}");
// Permanent ScriptingObject handle is passed from native side, do not release it
//freeContents2.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ GCHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}");
}
else if (fieldInfo.Type.Type == "Dictionary")
{
string dictionaryArgs = String.Join(", ",
fieldInfo.Type.GenericArgs.Select(x => CSharpNativeToManagedBasicTypes.ContainsKey(x.Type) ? CSharpNativeToManagedBasicTypes[x.Type] : x.Type).ToArray());
toManagedContent.Append(
$"managed.{fieldInfo.Name} != IntPtr.Zero ? (System.Collections.Generic.{fieldInfo.Type.Type}<{dictionaryArgs}>)GCHandle.FromIntPtr(managed.{fieldInfo.Name}).Target : null");
toNativeContent.Append(
$"GCHandle.ToIntPtr(GCHandle.Alloc(managed.{fieldInfo.Name}, GCHandleType.Weak))");
freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ GCHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}");
freeContents2.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ GCHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}");
}
else if (fieldInfo.Type.Type == "Array")
{
if (internalType)
{
// Marshal blittable array elements back to original non-blittable elements
string originalElementType = originalType.Substring(0, originalType.Length - 2);
string originalElementTypeMarshaller = originalElementType + "Marshaller";
string internalElementType = $"{originalElementTypeMarshaller}.{originalElementType}Internal";
toManagedContent.Append($"managed.{fieldInfo.Name} != IntPtr.Zero ? NativeInterop.NativeArrayToManagedArray<{originalElementType}, {internalElementType}>(((ManagedArray)GCHandle.FromIntPtr(managed.{fieldInfo.Name}).Target).array as {internalElementType}[], {originalElementTypeMarshaller}.ToManaged) : null");
toNativeContent.Append($"GCHandle.ToIntPtr(GCHandle.Alloc(ManagedArray.Get(managed.{fieldInfo.Name}), GCHandleType.Weak))");
freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ GCHandle handle = GCHandle.FromIntPtr(unmanaged.{fieldInfo.Name}); {internalElementType}[] values = ({internalElementType}[])(((ManagedArray)handle.Target).array); foreach (var value in values) {{ {originalElementTypeMarshaller}.Free(value); }} ((ManagedArray)handle.Target).Release(); handle.Free(); }}");
freeContents2.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ GCHandle handle = GCHandle.FromIntPtr(unmanaged.{fieldInfo.Name}); {internalElementType}[] values = ({internalElementType}[])(((ManagedArray)handle.Target).array); foreach (var value in values) {{ {originalElementTypeMarshaller}.Free(value); }} ((ManagedArray)handle.Target).Release(); handle.Free(); }}");
}
else if (fieldInfo.Type.GenericArgs[0].IsObjectRef)
{
// Array elements passed as GCHandles
string originalElementType = originalType.Substring(0, originalType.Length - 2);
toManagedContent.Append($"managed.{fieldInfo.Name} != IntPtr.Zero ? NativeInterop.GCHandleArrayToManagedArray<{originalElementType}>((ManagedArray)GCHandle.FromIntPtr(managed.{fieldInfo.Name}).Target) : null");
toNativeContent.Append($"managed.{fieldInfo.Name}?.Length > 0 ? GCHandle.ToIntPtr(GCHandle.Alloc(ManagedArray.Get(NativeInterop.ManagedArrayToGCHandleArray(managed.{fieldInfo.Name})), GCHandleType.Weak)) : IntPtr.Zero");
freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ GCHandle handle = GCHandle.FromIntPtr(unmanaged.{fieldInfo.Name}); IntPtr[] ptrs = (IntPtr[])(((ManagedArray)handle.Target).array); foreach (var ptr in ptrs) {{ if (ptr != IntPtr.Zero) {{ GCHandle.FromIntPtr(ptr).Free(); }} }} ((ManagedArray)handle.Target).Release(); handle.Free(); }}");
freeContents2.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ GCHandle handle = GCHandle.FromIntPtr(unmanaged.{fieldInfo.Name}); IntPtr[] ptrs = (IntPtr[])(((ManagedArray)handle.Target).array); foreach (var ptr in ptrs) {{ if (ptr != IntPtr.Zero) {{ GCHandle.FromIntPtr(ptr).Free(); }} }} ((ManagedArray)handle.Target).Release(); handle.Free(); }}");
}
else
{
// Blittable array elements
toManagedContent.Append($"managed.{fieldInfo.Name} != IntPtr.Zero ? ({originalType})(((ManagedArray)GCHandle.FromIntPtr(managed.{fieldInfo.Name}).Target).array) : null");
toNativeContent.Append($"managed.{fieldInfo.Name}?.Length > 0 ? GCHandle.ToIntPtr(GCHandle.Alloc(ManagedArray.Get(managed.{fieldInfo.Name}), GCHandleType.Weak)) : IntPtr.Zero");
freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ GCHandle handle = GCHandle.FromIntPtr(unmanaged.{fieldInfo.Name}); ((ManagedArray)handle.Target).Release(); handle.Free(); }}");
freeContents2.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ GCHandle handle = GCHandle.FromIntPtr(unmanaged.{fieldInfo.Name}); ((ManagedArray)handle.Target).Release(); handle.Free(); }}");
}
}
else if (fieldInfo.Type.Type == "Version")
{
toManagedContent.Append($"managed.{fieldInfo.Name} != IntPtr.Zero ? ({originalType})GCHandle.FromIntPtr(managed.{fieldInfo.Name}).Target : null");
toNativeContent.Append($"GCHandle.ToIntPtr(GCHandle.Alloc(managed.{fieldInfo.Name}, GCHandleType.Weak))");
freeContents.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ GCHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}");
freeContents2.AppendLine($"if (unmanaged.{fieldInfo.Name} != IntPtr.Zero) {{ GCHandle.FromIntPtr(unmanaged.{fieldInfo.Name}).Free(); }}");
}
else if (originalType == "string")
{
toManagedContent.Append($"ManagedString.ToManaged(managed.{fieldInfo.Name})");
toNativeContent.Append($"ManagedString.ToNative(managed.{fieldInfo.Name})");
freeContents.AppendLine($"ManagedString.Free(unmanaged.{fieldInfo.Name});");
freeContents2.AppendLine($"ManagedString.Free(unmanaged.{fieldInfo.Name});");
}
else if (originalType == "bool")
{
toManagedContent.Append($"managed.{fieldInfo.Name} != 0");
toNativeContent.Append($"managed.{fieldInfo.Name} ? (byte)1 : (byte)0");
}
else if (internalType)
{
toManagedContent.Append($"{internalTypeMarshaler}.ToManaged(managed.{fieldInfo.Name})");
toNativeContent.Append($"{internalTypeMarshaler}.ToNative(managed.{fieldInfo.Name})");
freeContents.AppendLine($"{internalTypeMarshaler}.Free(unmanaged.{fieldInfo.Name});");
freeContents2.AppendLine($"{internalTypeMarshaler}.Free(unmanaged.{fieldInfo.Name});");
}
/*else if (originalType == "Guid")
{
toManagedContent.Append("(Guid)managed.").Append(fieldInfo.Name);
toNativeContent.Append("(GuidNative)managed.").Append(fieldInfo.Name);
}*/
else
{
toManagedContent.Append("managed.").Append(fieldInfo.Name);
toNativeContent.Append("managed.").Append(fieldInfo.Name);
}
}
// Native struct end
indent = indent.Substring(0, indent.Length - 4);
contents.AppendLine(indent + "}").AppendLine();
toManagedContent.AppendLine(" };");
toNativeContent.AppendLine(" };");
}
var indent2 = indent + " ";
var indent3 = indent2 + " ";
// NativeToManaged stateless shape
// NOTE: GCHandles of FlaxEngine.Object must not be released in this case
contents.Append(indent).AppendLine($"public static class NativeToManaged").Append(indent).AppendLine("{");
contents.Append(indent2).AppendLine($"public static {structureInfo.Name} ConvertToManaged({structureInfo.Name}Internal unmanaged) => {marshallerName}.ToManaged(unmanaged);");
contents.Append(indent2).AppendLine($"public static void Free({structureInfo.Name}Internal unmanaged)");
contents.Append(indent2).AppendLine("{").Append(indent3).AppendLine(freeContents2.Replace("\n", "\n" + indent3).ToString().TrimEnd()).Append(indent2).AppendLine("}");
contents.Append(indent).AppendLine("}");
// ManagedToNative stateless shape
contents.Append(indent).AppendLine($"public static class ManagedToNative").Append(indent).AppendLine("{");
contents.Append(indent2).AppendLine($"public static {structureInfo.Name}Internal ConvertToUnmanaged({structureInfo.Name} managed) => {marshallerName}.ToNative(managed);");
contents.Append(indent2).AppendLine($"public static void Free({structureInfo.Name}Internal unmanaged) => {marshallerName}.Free(unmanaged);");
contents.Append(indent).AppendLine("}");
// Bidirectional stateful shape
// NOTE: GCHandles of FlaxEngine.Object must not be released unless they were allocated by this marshaller
contents.Append(indent).AppendLine($"public struct Bidirectional").Append(indent).AppendLine("{");
contents.Append(indent2).AppendLine($"{structureInfo.Name} managed;");
contents.Append(indent2).AppendLine($"{structureInfo.Name}Internal? unmanaged;");
contents.Append(indent2).AppendLine($"public void FromManaged({structureInfo.Name} managed) {{ this.managed = managed; }}");
contents.Append(indent2).AppendLine($"public {structureInfo.Name}Internal ToUnmanaged() {{ unmanaged = {marshallerName}.ToNative(managed); return unmanaged.Value; }}");
//contents.Append(indent2).AppendLine($"public void FromUnmanaged({structureInfo.Name}Internal unmanaged) {{ {marshallerName}.Free(this.unmanaged.Value); this.unmanaged = unmanaged; }}");
contents.Append(indent2).AppendLine($"public void FromUnmanaged({structureInfo.Name}Internal unmanaged) {{ this.unmanaged = unmanaged; }}");
contents.Append(indent2).AppendLine($"public {structureInfo.Name} ToManaged() {{ managed = {marshallerName}.ToManaged(unmanaged.Value); return managed; }}");
contents.Append(indent2).AppendLine($"public void Free() {{ if (unmanaged.HasValue) {{ NativeToManaged.Free(unmanaged.Value); unmanaged = null; }} }}");
contents.Append(indent).AppendLine("}");
// Bidirectional stateless shape
contents.Append(indent).AppendLine($"internal static {structureInfo.Name} ConvertToManaged({structureInfo.Name}Internal unmanaged) => ToManaged(unmanaged);");
contents.Append(indent).AppendLine($"internal static {structureInfo.Name}Internal ConvertToUnmanaged({structureInfo.Name} managed) => ToNative(managed);");
contents.Append(indent).AppendLine($"internal static void Free({structureInfo.Name}Internal unmanaged)");
contents.Append(indent).AppendLine("{").Append(indent2).AppendLine(freeContents.Replace("\n", "\n" + indent2).ToString().TrimEnd()).Append(indent).AppendLine("}");
// Managed/native converters
contents.Append(indent).AppendLine($"internal static {structureInfo.Name} ToManaged({structureInfo.Name}Internal managed)");
contents.Append(indent).AppendLine("{").Append(indent2).AppendLine(toManagedContent.Replace("\n", "\n" + indent2).ToString().TrimEnd()).Append(indent).AppendLine("}");
contents.Append(indent).AppendLine($"internal static {structureInfo.Name}Internal ToNative({structureInfo.Name} managed)");
contents.Append(indent).AppendLine("{").Append(indent2).AppendLine(toNativeContent.Replace("\n", "\n" + indent2).ToString().TrimEnd()).Append(indent).AppendLine("}");
indent = indent.Substring(0, indent.Length - 4);
contents.Append(indent).AppendLine("}");
}
#endif
// Struct docs
GenerateCSharpComment(contents, indent, structureInfo.Comment);
// Struct begin
GenerateCSharpAttributes(buildData, contents, indent, structureInfo, true);
contents.Append(indent).AppendLine("[StructLayout(LayoutKind.Sequential)]");
#if USE_NETCORE
if (!string.IsNullOrEmpty(structNativeMarshaling))
contents.Append(indent).AppendLine(structNativeMarshaling);
#endif
contents.Append(indent);
GenerateCSharpAccessLevel(contents, structureInfo.Access);
contents.Append("unsafe partial struct ").Append(structureInfo.Name);
@@ -1142,6 +1596,12 @@ namespace Flax.Build.Bindings
contents.AppendLine();
GenerateCSharpComment(contents, indent, fieldInfo.Comment);
GenerateCSharpAttributes(buildData, contents, indent, structureInfo, fieldInfo, fieldInfo.IsStatic, fieldInfo.DefaultValue, fieldInfo.Type);
#if USE_NETCORE
if (fieldInfo.Type.Type == "String")
contents.Append(indent).AppendLine("[MarshalAs(UnmanagedType.LPWStr)]");
else if (fieldInfo.Type.Type == "bool")
contents.Append(indent).AppendLine("[MarshalAs(UnmanagedType.U1)]");
#endif
contents.Append(indent);
GenerateCSharpAccessLevel(contents, fieldInfo.Access);
if (fieldInfo.IsConstexpr)
@@ -1153,21 +1613,32 @@ namespace Flax.Build.Bindings
if (fieldInfo.Type.IsArray && (fieldInfo.NoArray || structureInfo.IsPod))
{
// Fixed-size array that needs to be inlined into structure instead of passing it as managed array
fieldInfo.Type.IsArray = false;
type = GenerateCSharpNativeToManaged(buildData, fieldInfo.Type, structureInfo);
fieldInfo.Type.IsArray = true;
contents.Append(type).Append(' ').Append(fieldInfo.Name + "0;").AppendLine();
for (int i = 1; i < fieldInfo.Type.ArraySize; i++)
#if USE_NETCORE
// Use fixed statement with primitive types of buffers
if (type == "char")
{
contents.AppendLine();
GenerateCSharpComment(contents, indent, fieldInfo.Comment);
GenerateCSharpAttributes(buildData, contents, indent, structureInfo, fieldInfo, fieldInfo.IsStatic);
contents.Append(indent);
GenerateCSharpAccessLevel(contents, fieldInfo.Access);
if (fieldInfo.IsStatic)
contents.Append("static ");
contents.Append(type).Append(' ').Append(fieldInfo.Name + i).Append(';').AppendLine();
// char's are not blittable, store as short instead
contents.Append($"fixed short {fieldInfo.Name}0[{fieldInfo.Type.ArraySize}]; // {type}*").AppendLine();
}
else
#endif
{
// Padding in structs for fixed-size array
contents.Append(type).Append(' ').Append(fieldInfo.Name + "0;").AppendLine();
for (int i = 1; i < fieldInfo.Type.ArraySize; i++)
{
contents.AppendLine();
GenerateCSharpComment(contents, indent, fieldInfo.Comment);
GenerateCSharpAttributes(buildData, contents, indent, structureInfo, fieldInfo, fieldInfo.IsStatic);
contents.Append(indent);
GenerateCSharpAccessLevel(contents, fieldInfo.Access);
if (fieldInfo.IsStatic)
contents.Append("static ");
contents.Append(type).Append(' ').Append(fieldInfo.Name + i).Append(';').AppendLine();
}
}
continue;
}
@@ -1393,6 +1864,23 @@ namespace Flax.Build.Bindings
contents.AppendLine(indent + "}");
if (!string.IsNullOrEmpty(interfaceInfo.Namespace))
contents.AppendLine("}");
#if USE_NETCORE
{
string marshallerName = interfaceInfo.Name + "Marshaller";
contents.AppendLine();
contents.Append(indent).AppendLine("/// <summary>");
contents.Append(indent).AppendLine("/// ");
contents.Append(indent).AppendLine("/// </summary>");
contents.Append(indent).AppendLine($"[CustomMarshaller(typeof({interfaceInfo.Name}), MarshalMode.Default, typeof({marshallerName}))]");
contents.Append(indent).AppendLine($"internal static class {marshallerName}");
contents.Append(indent).AppendLine("{");
contents.Append(indent + " ").AppendLine($"internal static {interfaceInfo.Name} ConvertToManaged(IntPtr unmanaged) => ({interfaceInfo.Name})GCHandleMarshaller.ConvertToManaged(unmanaged);");
contents.Append(indent + " ").AppendLine($"internal static IntPtr ConvertToUnmanaged({interfaceInfo.Name} managed) => GCHandleMarshaller.ConvertToUnmanaged(managed);");
contents.Append(indent + " ").AppendLine("internal static void Free(IntPtr unmanaged) => GCHandleMarshaller.Free(unmanaged);");
contents.Append(indent).AppendLine("}");
}
#endif
}
private static bool GenerateCSharpType(BuildData buildData, StringBuilder contents, string indent, object type)
@@ -1456,6 +1944,9 @@ namespace Flax.Build.Bindings
CSharpUsedNamespaces.Add("System.Globalization");
CSharpUsedNamespaces.Add("System.Runtime.CompilerServices");
CSharpUsedNamespaces.Add("System.Runtime.InteropServices");
#if USE_NETCORE
CSharpUsedNamespaces.Add("System.Runtime.InteropServices.Marshalling");
#endif
CSharpUsedNamespaces.Add("FlaxEngine");
// Process all API types from the file

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