From 8c53f2169064e402a7c96a35da321dea97a99012 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20L=C3=BCbe=C3=9F?= Date: Tue, 26 Apr 2022 22:36:27 +0200 Subject: [PATCH] Even more DotNet stuff, help --- GlitchyEditor/src/Editor.bf | 71 ++-- GlitchyEngine/src/World/Components.bf | 31 ++ GlitchyEngine/src/World/Scene.bf | 10 + GlitchyEngineHelper/.gitignore | 398 ++++++++++++++++++ GlitchyEngineHelper/BeefProj.toml | 2 +- GlitchyEngineHelper/DotNetRuntime.cpp | 62 ++- .../DotNetScriptingHelper.csproj | 15 + .../DotNetScriptingHelper.sln | 25 ++ .../DotNetScriptingHelper/EcsEntity.cs | 23 + .../DotNetScriptingHelper/Entity.cs | 16 + .../DotNetScriptingHelper/InteropHelper.cs | 91 ++++ .../DotNetScriptingHelper/ScriptableEntity.cs | 59 +++ .../DotNetScriptingHelper/TestEntityScript.cs | 13 + GlitchyEngineHelper/src/DotNetRuntime.bf | 68 ++- 14 files changed, 821 insertions(+), 63 deletions(-) create mode 100644 GlitchyEngineHelper/.gitignore create mode 100644 GlitchyEngineHelper/DotNetScriptingHelper/DotNetScriptingHelper.csproj create mode 100644 GlitchyEngineHelper/DotNetScriptingHelper/DotNetScriptingHelper.sln create mode 100644 GlitchyEngineHelper/DotNetScriptingHelper/EcsEntity.cs create mode 100644 GlitchyEngineHelper/DotNetScriptingHelper/Entity.cs create mode 100644 GlitchyEngineHelper/DotNetScriptingHelper/InteropHelper.cs create mode 100644 GlitchyEngineHelper/DotNetScriptingHelper/ScriptableEntity.cs create mode 100644 GlitchyEngineHelper/DotNetScriptingHelper/TestEntityScript.cs diff --git a/GlitchyEditor/src/Editor.bf b/GlitchyEditor/src/Editor.bf index 82ec8bd..9f94ae5 100644 --- a/GlitchyEditor/src/Editor.bf +++ b/GlitchyEditor/src/Editor.bf @@ -5,6 +5,7 @@ using System.Collections; using GlitchyEngine.Collections; using GlitchyEditor.EditWindows; using GlitchyEngineHelper; +using System.IO; namespace GlitchyEditor { @@ -53,51 +54,40 @@ namespace GlitchyEditor /// Creates a new editor for the given world public this(Scene scene) { - String str = @"D:\Development\Projects\Beef\GlitchyEngine\build\Debug_Win64\GlitchyEditor\"; - //char16* ptr = str.ToScopedNativeWChar!(); + String exePath = Environment.GetExecutableFilePath(.. scope String()); - String runtimeConfig = scope String(str, "DotNetTest.runtimeconfig.json"); - String assemblyPath = scope String(str, "DotNetTest.dll"); + String exeDir = Path.GetDirectoryPath(exePath, .. scope String()); + + String runtimeConfig = scope String(exeDir, "/DotNetScriptingHelper.runtimeconfig.json"); + String assemblyPath = scope String(exeDir, "/DotNetScriptingHelper.dll"); DotNetRuntime.Init(); DotNetRuntime.LoadRuntime(runtimeConfig); - - - DotNetRuntime.LoadAssemblyAndGetFunctionPointer(assemblyPath, "DotNetTest.Lib, DotNetTest", "Hello", let hello); - DotNetRuntime.LoadAssemblyAndGetFunctionPointer(assemblyPath, "DotNetTest.ScriptableEntity, DotNetTest", "Hello2", let hello2); - - - DotNetRuntime.LoadAssemblyAndGetFunctionPointer(assemblyPath, "DotNetTest.InteropHelper, DotNetTest", "Test", let test); - CreateInstanceDelegate createInstance; - //DotNetRuntime.LoadAssemblyAndGetFunctionPointer(assemblyPath, "DotNetTest.InteropHelper, DotNetTest", "CreateInstance", "DotNetTest.InteropHelper+CreateInstanceEntryPoint, DotNetTest", out createInstance); - DotNetRuntime.LoadAssemblyAndGetFunctionPointer(assemblyPath, "DotNetTest.InteropHelper, DotNetTest", "CreateInstance", out createInstance); - - FreeInstanceDelegate freeInstance; - //DotNetRuntime.LoadAssemblyAndGetFunctionPointer(assemblyPath, "DotNetTest.InteropHelper, DotNetTest", "FreeInstance", "DotNetTest.InteropHelper+FreeInstanceEntryPoint, DotNetTest", out freeInstance); - DotNetRuntime.LoadAssemblyAndGetFunctionPointer(assemblyPath, "DotNetTest.InteropHelper, DotNetTest", "FreeInstance", out freeInstance); + DotNetRuntime.LoadAssemblyAndGetFunctionPointerUnmanagedCallersOnly(assemblyPath, "DotNetScriptingHelper.InteropHelper, DotNetScriptingHelper", "CreateInstance", out createInstance); + + DotNetRuntime.ManagedDelegate someDel1; + DotNetRuntime.ManagedDelegate someDel2; + + DotNetRuntime.LoadAssemblyAndGetFunctionPointer(assemblyPath, "DotNetScriptingHelper.InteropHelper, DotNetScriptingHelper", "SomeMethod", out someDel1); + + someDel1(null, 0); // System.Runtime.InteropServices.Marshal, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e + + int32 iy = DotNetRuntime.GetFunctionPointer("DotNetScriptingHelper.InteropHelper, DotNetScriptingHelper, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null", "SomeMethod", out someDel2); + //DotNetRuntime.GetFunctionPointer("System", DotNetScriptingHelper", "SomeMethod", out someDel2); + /* + FreeInstanceDelegate freeInstance = null; + //DotNetRuntime.LoadAssemblyAndGetFunctionPointer(assemblyPath, "DotNetScriptingHelper.InteropHelper, DotNetScriptingHelper", "FreeInstance", "DotNetScriptingHelper.InteropHelper+FreeInstanceEntryPoint, DotNetScriptingHelper", out freeInstance); + DotNetRuntime.GetFunctionPointer("DotNetScriptingHelper.InteropHelper, DotNetScriptingHelper", "FreeInstance", "DotNetScriptingHelper.InteropHelper+FreeInstanceEntryPoint, DotNetScriptingHelper", out freeInstance); InstanceMethodDelegate update; - //DotNetRuntime.LoadAssemblyAndGetFunctionPointer(assemblyPath, "DotNetTest.ScriptableEntity, DotNetTest", "InvokeUpdate", "DotNetTest.ScriptableEntity+InstanceMethodEntryPoint, DotNetTest", out update); - DotNetRuntime.LoadAssemblyAndGetFunctionPointer(assemblyPath, "DotNetTest.ScriptableEntity, DotNetTest", "InvokeUpdate", out update); + DotNetRuntime.GetFunctionPointerUnmanagedCallersOnly("DotNetScriptingHelper.ScriptableEntity, DotNetScriptingHelper", "UpdateEntity", out update); + + DotNetRuntime.GetFunctionPointerUnmanagedCallersOnly("DotNetScriptingHelper.ScriptableEntity, DotNetScriptingHelper", "CreateInstance", out DotNetScriptComponent.DelCreateInstance); - //DotNetRuntime.TestRunDotNet(1, &ptr); - - lib_args args = . - { - Message = "from host!".ToScopedNativeWChar!(), - number = 1337 - }; - - hello(&args, sizeof(lib_args)); - - hello2(&args, sizeof(lib_args)); - - test(null, 0); - - String typeName = "DotNetTest.ScriptableEntity, DotNetTest"; + String typeName = "DotNetScriptingHelper.TestEntityScript, DotNetScriptingHelper"; CreateInstanceArgs instanceArgs = . { @@ -107,15 +97,26 @@ namespace GlitchyEditor ManagedReference* instance = createInstance(&instanceArgs, sizeof(CreateInstanceArgs)); + void* reffy = DotNetScriptComponent.DelCreateInstance(typeName.Ptr, (int32)typeName.Length, EcsEntity.[Friend]CreateEntityID(1337, 420)); + for (int i < 10) { update(instance); + update((.)reffy); } freeInstance(instance); + freeInstance((.)reffy); DotNetRuntime.Deinit(); + */ + + while(true) + { + + } + _scene = scene; _ecsWorld = _scene.[Friend]_ecsWorld; diff --git a/GlitchyEngine/src/World/Components.bf b/GlitchyEngine/src/World/Components.bf index 28e6b6b..2765b9e 100644 --- a/GlitchyEngine/src/World/Components.bf +++ b/GlitchyEngine/src/World/Components.bf @@ -288,4 +288,35 @@ namespace GlitchyEngine.World DestroyInstanceFunction(&this); } } + + struct DotNetScriptComponent : IDisposableComponent + { + public void* InstanceHandlePtr = null; + + private String TypeName = null; + + typealias CreateInstanceDelegate = function void*(void* typeNamePtr, int32 typeNameLength, EcsEntity entity); + + public static CreateInstanceDelegate DelCreateInstance; + + public void Bind(StringView dotNetAssemblyQualifiedTypeName) mut + { + TypeName = new String(dotNetAssemblyQualifiedTypeName); + } + + internal void CreateInstance(EcsEntity entity) mut + { + //IntPtr CreateInstance(IntPtr typeNamePtr, int typeNameLength, EcsEntity entity) + InstanceHandlePtr = DelCreateInstance(TypeName.Ptr, (int32)TypeName.Length, entity); + } + + internal void UpdateInstance() + { + } + + public void Dispose() mut + { + delete TypeName; + } + } } \ No newline at end of file diff --git a/GlitchyEngine/src/World/Scene.bf b/GlitchyEngine/src/World/Scene.bf index f0ea702..9c38dac 100644 --- a/GlitchyEngine/src/World/Scene.bf +++ b/GlitchyEngine/src/World/Scene.bf @@ -49,6 +49,16 @@ namespace GlitchyEngine.World script.Instance.[Friend]OnUpdate(gameTime); } + + for (var (entity, script) in _ecsWorld.Enumerate()) + { + if (script.InstanceHandlePtr == null) + { + script.[Friend]CreateInstance(entity); + } + + script.[Friend]UpdateInstance(); // TODO: Gametime + } Camera* primaryCamera = null; Matrix primaryCameraTransform = default; diff --git a/GlitchyEngineHelper/.gitignore b/GlitchyEngineHelper/.gitignore new file mode 100644 index 0000000..8a30d25 --- /dev/null +++ b/GlitchyEngineHelper/.gitignore @@ -0,0 +1,398 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.tlog +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio 6 auto-generated project file (contains which files were open etc.) +*.vbp + +# Visual Studio 6 workspace and project file (working project files containing files to include in project) +*.dsw +*.dsp + +# Visual Studio 6 technical files +*.ncb +*.aps + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# Visual Studio History (VSHistory) files +.vshistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd + +# VS Code files for those working on multiple tools +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +# Local History for Visual Studio Code +.history/ + +# Windows Installer files from build outputs +*.cab +*.msi +*.msix +*.msm +*.msp + +# JetBrains Rider +*.sln.iml diff --git a/GlitchyEngineHelper/BeefProj.toml b/GlitchyEngineHelper/BeefProj.toml index cf2ed81..143d38b 100644 --- a/GlitchyEngineHelper/BeefProj.toml +++ b/GlitchyEngineHelper/BeefProj.toml @@ -10,4 +10,4 @@ StartupObject = "GlitchyEngineHelper.Program" BuildCommandsOnCompile = "IfFilesChanged" BuildCommandsOnRun = "IfFilesChanged" LibPaths = ["$(ProjectDir)/out/build/x64-debug/GlitchyEngineHelper.lib"] -PreBuildCmds = ["$(ProjectDir)\\..\\bin\\vscmake.bat x64 $(ProjectDir) x64-debug", "CopyToDependents(\"$(ProjectDir)/out/build/x64-debug/*.dll\")"] +PreBuildCmds = ["$(ProjectDir)\\..\\bin\\vscmake.bat x64 $(ProjectDir) x64-debug", "CopyToDependents(\"$(ProjectDir)/out/build/x64-debug/*.dll\")", "dotnet build $(ProjectDir)/DotNetScriptingHelper/DotNetScriptingHelper.csproj -c Debug", "CopyToDependents(\"$(ProjectDir)/DotNetScriptingHelper/bin/Debug/*\")"] diff --git a/GlitchyEngineHelper/DotNetRuntime.cpp b/GlitchyEngineHelper/DotNetRuntime.cpp index d5798df..5f008a2 100644 --- a/GlitchyEngineHelper/DotNetRuntime.cpp +++ b/GlitchyEngineHelper/DotNetRuntime.cpp @@ -49,6 +49,7 @@ namespace hostfxr_close_fn close_fptr; load_assembly_and_get_function_pointer_fn load_assembly_and_get_function_pointer; + get_function_pointer_fn get_function_pointer; hostfxr_handle hostContext; @@ -81,12 +82,6 @@ custom_entry_point_fn GetFunctionPointer(load_assembly_and_get_function_pointer_ return custom; } -void LoadRuntime(const char_t* configPath) -{ - load_assembly_and_get_function_pointer = get_dotnet_load_assembly(configPath); - assert(load_assembly_and_get_function_pointer != nullptr && "Failure: get_dotnet_load_assembly()"); -} - GE_EXPORT int GE_CALLTYPE TestRunDotNet(int argc, wchar_t* argv[]) { // Get the current executable's directory @@ -279,6 +274,46 @@ namespace // } +// Load and initialize .NET Core and get desired function pointer for scenario +void InitDotNet(const char_t* config_path) +{ + // Load .NET Core + load_assembly_and_get_function_pointer = nullptr; + hostContext = nullptr; + int rc = init_fptr(config_path, nullptr, &hostContext); + if (rc != 0 || hostContext == nullptr) + { + std::cerr << "Init failed: " << std::hex << std::showbase << rc << std::endl; + close_fptr(hostContext); + return; + } + + // Get the load assembly function pointer + rc = get_delegate_fptr( + hostContext, + hdt_load_assembly_and_get_function_pointer, + (void**)&load_assembly_and_get_function_pointer); + + if (rc != 0 || load_assembly_and_get_function_pointer == nullptr) + std::cerr << "Get delegate failed: " << std::hex << std::showbase << rc << std::endl; + + rc = get_delegate_fptr( + hostContext, + hdt_get_function_pointer, + (void**)&get_function_pointer); + + if (rc != 0 || get_function_pointer == nullptr) + std::cerr << "Get delegate failed: " << std::hex << std::showbase << rc << std::endl; + + // TODO: close_fptr(hostContext); ??? +} + +void LoadRuntime(const char_t* configPath) +{ + InitDotNet(configPath); + + assert(load_assembly_and_get_function_pointer != nullptr && get_function_pointer != nullptr && "Failure: get_dotnet_load_assembly()"); +} GE_EXPORT int GE_CALLTYPE DotNet_Init() { @@ -318,3 +353,18 @@ GE_EXPORT int GE_CALLTYPE DotNet_LoadAssemblyAndGetFunctionPointer(const char_t* return rc; } + +GE_EXPORT int GE_CALLTYPE DotNet_GetFunctionPointer(const char_t* typeName, const char_t* methodName, const char_t* delegateTypeName, void** outDelegate) +{ + int rc = get_function_pointer( + typeName, + methodName, + delegateTypeName, + nullptr, + nullptr, + outDelegate); + + //assert(rc == 0 && outDelegate != nullptr && "Failure: DotNet_GetFunctionPointer()"); + + return rc; +} diff --git a/GlitchyEngineHelper/DotNetScriptingHelper/DotNetScriptingHelper.csproj b/GlitchyEngineHelper/DotNetScriptingHelper/DotNetScriptingHelper.csproj new file mode 100644 index 0000000..13ca085 --- /dev/null +++ b/GlitchyEngineHelper/DotNetScriptingHelper/DotNetScriptingHelper.csproj @@ -0,0 +1,15 @@ + + + + net6.0 + true + + enable + enable + True + + embedded + false + + + diff --git a/GlitchyEngineHelper/DotNetScriptingHelper/DotNetScriptingHelper.sln b/GlitchyEngineHelper/DotNetScriptingHelper/DotNetScriptingHelper.sln new file mode 100644 index 0000000..d088a18 --- /dev/null +++ b/GlitchyEngineHelper/DotNetScriptingHelper/DotNetScriptingHelper.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.1.32328.378 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetScriptingHelper", "DotNetScriptingHelper.csproj", "{4B439162-2FA7-494E-A459-8F6D34FED80C}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {4B439162-2FA7-494E-A459-8F6D34FED80C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4B439162-2FA7-494E-A459-8F6D34FED80C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4B439162-2FA7-494E-A459-8F6D34FED80C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4B439162-2FA7-494E-A459-8F6D34FED80C}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {968CFBCF-61D9-404C-83CC-0F616E89DA10} + EndGlobalSection +EndGlobal diff --git a/GlitchyEngineHelper/DotNetScriptingHelper/EcsEntity.cs b/GlitchyEngineHelper/DotNetScriptingHelper/EcsEntity.cs new file mode 100644 index 0000000..0a6fd2f --- /dev/null +++ b/GlitchyEngineHelper/DotNetScriptingHelper/EcsEntity.cs @@ -0,0 +1,23 @@ +namespace DotNetScriptingHelper; + +public readonly struct EcsEntity +{ + // Binary Format: + // Bits: [0 - 31] [32 - 64] + // Data: Version Index + + private readonly ulong _id; + + internal uint Version => (uint)_id; + + internal uint Index => (uint)(_id >> 32); + + public EcsEntity(uint index, uint version) + { + _id = ((ulong)index << 32) | version; + } + + public bool IsValid => Index != InvalidEntity.Index; + + public static readonly EcsEntity InvalidEntity = new(uint.MaxValue, 0); +} diff --git a/GlitchyEngineHelper/DotNetScriptingHelper/Entity.cs b/GlitchyEngineHelper/DotNetScriptingHelper/Entity.cs new file mode 100644 index 0000000..f54eac1 --- /dev/null +++ b/GlitchyEngineHelper/DotNetScriptingHelper/Entity.cs @@ -0,0 +1,16 @@ +namespace DotNetScriptingHelper; + +public struct Entity +{ + public EcsEntity Handle { get; private set; } + // TODO: Scene + + public bool IsValid => Handle.IsValid; + + public Entity(EcsEntity handle) + { + Handle = handle; + } + + // TODO: Get Parent, Children +} diff --git a/GlitchyEngineHelper/DotNetScriptingHelper/InteropHelper.cs b/GlitchyEngineHelper/DotNetScriptingHelper/InteropHelper.cs new file mode 100644 index 0000000..05d7602 --- /dev/null +++ b/GlitchyEngineHelper/DotNetScriptingHelper/InteropHelper.cs @@ -0,0 +1,91 @@ +using System.Diagnostics; +using System.Reflection; +using System.Runtime.InteropServices; + +namespace DotNetScriptingHelper; + +public class InteropHelper +{ + struct CreateArgs + { + public IntPtr Ptr; + public int Length; + + public string ManagedCopy => Marshal.PtrToStringUTF8(Ptr, Length); + } + + //[UnmanagedCallersOnly] + public static int SomeMethod(IntPtr args, int argLength) + { + Debug.WriteLine(typeof(InteropHelper).AssemblyQualifiedName); + + return 5; + } + + [UnmanagedCallersOnly] + public static IntPtr CreateInstance(IntPtr args, int argLength) + { + try + { + Debug.Assert(Marshal.SizeOf() == argLength, "Provided argument has wrong size."); + + CreateArgs createArgs = Marshal.PtrToStructure(args); + + string str = createArgs.ManagedCopy; + + Console.WriteLine($"Trying to get type \"{str}\""); + + Type? type = Type.GetType(createArgs.ManagedCopy, true); + + if (type == null) + { + Console.WriteLine("Type was null."); + return IntPtr.Zero; + } + + object? instance = Activator.CreateInstance(type); + + if (instance == null) + return IntPtr.Zero; + + GCHandle handle = GCHandle.Alloc(instance); + + return GCHandle.ToIntPtr(handle); + } + catch (Exception e) + { + Console.WriteLine(e); + throw; + } + } + + [UnmanagedCallersOnly] + public static void CallMethod(IntPtr instance, IntPtr methodName, int methodNameLength) + { + GCHandle handle = GCHandle.FromIntPtr(instance); + + if (handle.Target == null) + throw new Exception("Handle has no target?"); + + Type type = handle.Target.GetType(); + + string name = Marshal.PtrToStringUTF8(methodName, methodNameLength); + + MethodInfo? mi = type.GetMethod(name); + + if (mi == null) + throw new Exception("MethodInfo is null."); + + mi.Invoke(handle.Target, null); + } + + + public delegate void FreeInstanceEntryPoint(IntPtr instance); + + [UnmanagedCallersOnly] + public static void FreeInstance(IntPtr instance) + { + GCHandle handle = GCHandle.FromIntPtr(instance); + handle.Free(); + } +} diff --git a/GlitchyEngineHelper/DotNetScriptingHelper/ScriptableEntity.cs b/GlitchyEngineHelper/DotNetScriptingHelper/ScriptableEntity.cs new file mode 100644 index 0000000..be1d8a4 --- /dev/null +++ b/GlitchyEngineHelper/DotNetScriptingHelper/ScriptableEntity.cs @@ -0,0 +1,59 @@ +using System.Diagnostics; +using System.Runtime.InteropServices; + +namespace DotNetScriptingHelper; + +public abstract class ScriptableEntity +{ + internal Entity Entity; + + public virtual void OnCreate() { } + protected virtual void OnDestroy() { } + protected virtual void OnUpdate() { } + + [UnmanagedCallersOnly] + public static void UpdateEntity(IntPtr entityPtr) + { + GCHandle entityHandle = GCHandle.FromIntPtr(entityPtr); + + if (entityHandle.Target is ScriptableEntity entity) + { + entity.OnUpdate(); + } + } + + [UnmanagedCallersOnly] + public static IntPtr CreateInstance(IntPtr typeNamePtr, int typeNameLength, EcsEntity entity) + { + try + { + string typeName = Marshal.PtrToStringUTF8(typeNamePtr, typeNameLength); + + Type? type = Type.GetType(typeName, true); + + if (type == null) + return IntPtr.Zero; + + object? instance = Activator.CreateInstance(type); + + if (type == null) + return IntPtr.Zero; + + Debug.Assert(instance is ScriptableEntity, "Activated instance doesn't inherit from ScriptableEntity"); + + if (instance is not ScriptableEntity scriptableEntity) + return IntPtr.Zero; + + scriptableEntity.Entity = new Entity(entity); + scriptableEntity.OnCreate(); + + GCHandle handle = GCHandle.Alloc(instance); + return GCHandle.ToIntPtr(handle); + } + catch (Exception e) + { + Console.WriteLine(e); + throw; + } + } +} diff --git a/GlitchyEngineHelper/DotNetScriptingHelper/TestEntityScript.cs b/GlitchyEngineHelper/DotNetScriptingHelper/TestEntityScript.cs new file mode 100644 index 0000000..2f3bdac --- /dev/null +++ b/GlitchyEngineHelper/DotNetScriptingHelper/TestEntityScript.cs @@ -0,0 +1,13 @@ +using System.Diagnostics; + +namespace DotNetScriptingHelper; + +public class TestEntityScript : ScriptableEntity +{ + public int Inty = 1337; + + protected override void OnUpdate() + { + Debug.WriteLine($"My Number is {Inty++}"); + } +} diff --git a/GlitchyEngineHelper/src/DotNetRuntime.bf b/GlitchyEngineHelper/src/DotNetRuntime.bf index d32e0a0..af1b5e7 100644 --- a/GlitchyEngineHelper/src/DotNetRuntime.bf +++ b/GlitchyEngineHelper/src/DotNetRuntime.bf @@ -14,13 +14,17 @@ namespace GlitchyEngineHelper [LinkName("DotNet_Deinit"), CallingConvention(.Cdecl)] public static extern c_int Deinit(); - [LinkName("DotNet_LoadRuntime"), CallingConvention(.Cdecl)] #if BF_PLATFORM_WINDOWS - private static extern void Internal_LoadRuntime(char16* configPath); + private typealias char_t = char16; #else - private static extern void Internal_LoadRuntime(char8* configPath); + private typealias char_t = char8; #endif + [LinkName("DotNet_LoadRuntime"), CallingConvention(.Cdecl)] + private static extern void Internal_LoadRuntime(char_t* configPath); + + private static char_t* UNMANAGEDCALLERSONLY_METHOD = (char_t*)(void*)-1; + public static void LoadRuntime(StringView configPath) { #if BF_PLATFORM_WINDOWS @@ -31,44 +35,66 @@ namespace GlitchyEngineHelper } [LinkName("DotNet_LoadAssemblyAndGetFunctionPointer"), CallingConvention(.Cdecl)] -#if BF_PLATFORM_WINDOWS - private static extern c_int DotNet_LoadAssemblyAndGetFunctionPointer(char16* assemblyPath, char16* typeName, char16* methodName, char16* delegateName, void** outDelegate); -#else - private static extern c_int DotNet_LoadAssemblyAndGetFunctionPointer(char8* assemblyPath, char8* typeName, char8* methodName, char8* delegateName, void** outDelegate); -#endif + private static extern c_int DotNet_LoadAssemblyAndGetFunctionPointer(char_t* assemblyPath, char_t* typeName, char_t* methodName, char_t* delegateName, void** outDelegate); public typealias ManagedDelegate = function int32(void *arg, int32 arg_size_in_bytes); + public static c_int LoadAssemblyAndGetFunctionPointer(StringView assemblyPath, StringView typeName, StringView methodName, StringView? delegateName, out T outDelegate) where T : var + { + outDelegate = null; + +#if BF_PLATFORM_WINDOWS + return DotNet_LoadAssemblyAndGetFunctionPointer(assemblyPath.ToScopedNativeWChar!(), typeName.ToScopedNativeWChar!(), methodName.ToScopedNativeWChar!(), delegateName?.ToScopedNativeWChar!(), (void**)&outDelegate); +#else + Runtime.NotImplemented(); +#endif + } + public static c_int LoadAssemblyAndGetFunctionPointer(StringView assemblyPath, StringView typeName, StringView methodName, out ManagedDelegate outDelegate) { - outDelegate = ?; + return LoadAssemblyAndGetFunctionPointer(assemblyPath, typeName, methodName, null, out outDelegate); + } + + public static c_int LoadAssemblyAndGetFunctionPointerUnmanagedCallersOnly(StringView assemblyPath, StringView typeName, StringView methodName, out T outDelegate) where T : var + { + outDelegate = null; #if BF_PLATFORM_WINDOWS - return DotNet_LoadAssemblyAndGetFunctionPointer(assemblyPath.ToScopedNativeWChar!(), typeName.ToScopedNativeWChar!(), methodName.ToScopedNativeWChar!(), null, (void**)&outDelegate); + return DotNet_LoadAssemblyAndGetFunctionPointer(assemblyPath.ToScopedNativeWChar!(), typeName.ToScopedNativeWChar!(), methodName.ToScopedNativeWChar!(), UNMANAGEDCALLERSONLY_METHOD, (void**)&outDelegate); #else - + Runtime.NotImplemented(); #endif } - public static c_int LoadAssemblyAndGetFunctionPointer(StringView assemblyPath, StringView typeName, StringView methodName, StringView delegateName, out T outDelegate) where T : var - { - outDelegate = ?; + + + [LinkName("DotNet_GetFunctionPointer"), CallingConvention(.Cdecl)] + private static extern c_int DotNet_GetFunctionPointer(char_t* typeName, char_t* methodName, char_t* delegateName, void** outDelegate); + + public static c_int GetFunctionPointer(StringView typeName, StringView methodName, StringView delegateName, out T outDelegate) where T : var + { + outDelegate = null; #if BF_PLATFORM_WINDOWS - return DotNet_LoadAssemblyAndGetFunctionPointer(assemblyPath.ToScopedNativeWChar!(), typeName.ToScopedNativeWChar!(), methodName.ToScopedNativeWChar!(), delegateName.ToScopedNativeWChar!(), (void**)&outDelegate); + return DotNet_GetFunctionPointer(typeName.ToScopedNativeWChar!(), methodName.ToScopedNativeWChar!(), delegateName.ToScopedNativeWChar!(), (void**)&outDelegate); #else - + Runtime.NotImplemented(); #endif } - public static c_int LoadAssemblyAndGetFunctionPointer(StringView assemblyPath, StringView typeName, StringView methodName, out T outDelegate) where T : var - { - outDelegate = ?; + public static c_int GetFunctionPointer(StringView typeName, StringView methodName, out ManagedDelegate outDelegate) + { + return GetFunctionPointer(typeName, methodName, null, out outDelegate); + } + + public static c_int GetFunctionPointerUnmanagedCallersOnly(StringView typeName, StringView methodName, out T outDelegate) where T : var + { + outDelegate = null; #if BF_PLATFORM_WINDOWS - return DotNet_LoadAssemblyAndGetFunctionPointer(assemblyPath.ToScopedNativeWChar!(), typeName.ToScopedNativeWChar!(), methodName.ToScopedNativeWChar!(), (char16*)(void*)-1, (void**)&outDelegate); + return DotNet_GetFunctionPointer(typeName.ToScopedNativeWChar!(), methodName.ToScopedNativeWChar!(), UNMANAGEDCALLERSONLY_METHOD, (void**)&outDelegate); #else - + Runtime.NotImplemented(); #endif } }