C# generator now working with bindings.json

This commit is contained in:
antopilo
2025-04-03 23:06:54 -04:00
parent c9dfe2706e
commit 2908429b68
10 changed files with 257 additions and 8 deletions

View File

@@ -212,7 +212,7 @@ ProjectSettingsModuleWindow::ProjectSettingsModuleWindow(const std::string& inMo
void ProjectSettingsModuleWindow::Draw()
{
auto meta = entt::resolve(entt::hashed_string(Name.c_str()));
auto meta = entt::resolve(entt::hashed_string(("class " + Name).c_str()));
auto instance = ModuleDB::Get().GetBaseImpl(Name).instance;
for (auto [id, data] : meta.data())
{

View File

@@ -12,7 +12,7 @@ json ModuleDB::GenerateModuleAPI()
json moduleData;
moduleData["Name"] = name;
auto meta = entt::resolve(entt::hashed_string(name.c_str()));
auto meta = entt::resolve(entt::hashed_string(("class " + name).c_str()));
int j = 0;
for (auto [id, func] : meta.func())

View File

@@ -8,6 +8,7 @@
#include "Nuake/Core/GameState.h"
#include "Nuake/Core/MulticastDelegate.h"
#include "Nuake/Core/Object/Object.h"
#include "Nuake/Core/String.h"
#include "Nuake/Scene/Scene.h"
#include "Nuake/Scene/SceneSystems.h"
@@ -110,7 +111,6 @@ class moduleName : public ModuleInstance \
{ \
public: \
std::string Description = "No Description"; \
entt::meta_factory<moduleName> ModuleFactory = entt::meta<moduleName>(); \
TypeNameMap TypeNames; \
\
std::map<entt::id_type, std::vector<std::string>> FuncArgNames; \
@@ -205,22 +205,24 @@ namespace Nuake
T& RegisterModule()
{
T* newInstance = new T();
std::string unmangledName = Nuake::String::Split(typeid(T).name(), ' ')[1];
newInstance->instance = entt::resolve<T>().construct();
Modules[typeid(T).name()] = (ModuleInstance*)newInstance;
return *(T*)std::any_cast<ModuleInstance*>(Modules[typeid(T).name()]);
Modules[unmangledName] = (ModuleInstance*)newInstance;
return *(T*)std::any_cast<ModuleInstance*>(Modules[unmangledName]);
}
template<typename T>
T& GetModule()
{
const std::string_view typeName = typeid(T).name();
if (!Modules.contains(typeName))
std::string unmangledName = Nuake::String::Split(typeName)[1];
if (!Modules.contains(unmangledName))
{
assert(false && "Module not found.");
return;
}
return *(T*)std::any_cast<ModuleInstance*>(Modules[typeName]);
return *(T*)std::any_cast<ModuleInstance*>(Modules[unmangledName]);
}
ModuleInstance& GetBaseImpl(const std::string& moduleName)

View File

@@ -217,7 +217,7 @@ void VkRenderer::SelectGPU()
std::string errMessage = "No GPU found who supports the required Vulkan extension: " + std::string(extension);
errMessage += "\nConsider updating drivers.";
Logger::Log(errMessage, "vulkan", CRITICAL);
OS::ShowMessageBox("Vulkan Error", errMessage);
//OS::ShowMessageBox("Vulkan Error", errMessage);
}
}

View File

@@ -0,0 +1,21 @@
using System;
namespace Nuake
{
public static class AudioModule
{
public static void SetVolume(float volume)
{
unsafe
{
Internals.AudioModuleSetVolumeICall(volume);
}
}
public static void SetMuted(bool muted)
{
unsafe
{
Internals.AudioModuleSetMutedICall(muted);
}
}
}
}

View File

@@ -0,0 +1,21 @@
using System;
namespace Nuake
{
public static class ExampleModule
{
public static void ExampleFunction()
{
unsafe
{
Internals.ExampleModuleExampleFunctionICall();
}
}
public static void ExampleModuleLog(string hi)
{
unsafe
{
Internals.ExampleModuleExampleModuleLogICall(hi);
}
}
}
}

View File

@@ -0,0 +1,12 @@
using Coral.Managed.Interop;
public class Internals
{
// AudioModule
public static unsafe delegate*<float,void> AudioModuleSetVolumeICall;
public static unsafe delegate*<bool,void> AudioModuleSetMutedICall;
// ExampleModule
public static unsafe delegate*<void> ExampleModuleExampleFunctionICall;
public static unsafe delegate*<NativeString,void> ExampleModuleExampleModuleLogICall;
}

View File

@@ -0,0 +1,170 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
struct Arg
{
public string Name { get; set; }
public string Type { get; set; }
}
struct Function
{
public string Name { get; set; }
public int NumArgs { get; set; }
public Arg[] Args { get; set; }
public string ReturnType { get; set; }
}
struct Module
{
public string Name { get; set; }
public Function[] Functions { get; set; }
}
struct Bindings
{
public Module[] Modules { get; set; }
}
class Generator
{
static string ConvertTypes(string type)
{
return type switch
{
"float" => "float",
"int" => "int",
"bool" => "bool",
"void" => "void",
"char" => "byte",
"string" => "NativeString"
};
}
static string GenerateInternals(Bindings bindings)
{
string generatedInternals = string.Empty;
generatedInternals += "using Coral.Managed.Interop;\n";
generatedInternals += "public class Internals\n";
generatedInternals += "{\n";
foreach (var module in bindings.Modules)
{
generatedInternals += $" // {module.Name}\n";
foreach (var function in module.Functions)
{
generatedInternals += $" public static unsafe delegate*<";
if (function.NumArgs > 0)
{
generatedInternals += $"{string.Join(", ", function.Args.Select(a => ConvertTypes(a.Type)))},";
}
generatedInternals += $"{function.ReturnType}>{module.Name}{function.Name}ICall;\n";
}
generatedInternals += "\n";
}
generatedInternals += "}\n";
return generatedInternals;
}
static string GenerateModuleAPI(Module module)
{
string moduleApi = string.Empty;
moduleApi += "using System;\n";
moduleApi += "namespace Nuake\n";
moduleApi += "{\n";
moduleApi += " public static class " + module.Name + "\n";
moduleApi += " {\n";
foreach (Function func in module.Functions)
{
moduleApi += " public static " + func.ReturnType + " " + func.Name + "(";
if (func.NumArgs > 0)
{
moduleApi += string.Join(", ", func.Args.Select(a => a.Type + " " + a.Name));
}
moduleApi += ")\n";
moduleApi += " {\n";
moduleApi += " unsafe\n";
moduleApi += " {\n";
if (func.ReturnType != "void")
{
moduleApi += $" return ";
}
moduleApi += $" Internals.{module.Name}{func.Name}ICall(";
if (func.NumArgs > 0)
{
moduleApi += string.Join(", ", func.Args.Select(a => a.Name));
}
moduleApi += ");\n";
moduleApi += " }\n";
moduleApi += " }\n";
}
moduleApi += " }\n";
moduleApi += "}\n";
return moduleApi;
}
struct FileToWrite
{
public string FileName { get; set; }
public string Content { get; set; }
}
static void Main()
{
// Read file bindings.json
string json = File.ReadAllText("../../../../Editor/Build/Debug/Binaries/bindings.json");
// parse the json
Bindings bindings = System.Text.Json.JsonSerializer.Deserialize<Bindings>(json);
List<FileToWrite> filesToWrite = new List<FileToWrite>();
string internals = GenerateInternals(bindings);
filesToWrite.Add(new FileToWrite
{
FileName = "Internals.cs",
Content = internals
});
Console.WriteLine("Generated Internals:\n");
Console.WriteLine(internals);
foreach (var module in bindings.Modules)
{
string fileName = module.Name + ".cs";
Console.WriteLine("Generated " + fileName);
string api = GenerateModuleAPI(module);
Console.WriteLine(api);
filesToWrite.Add(new FileToWrite
{
FileName = fileName,
Content = api
});
}
// WriteFiles
foreach (var module in filesToWrite)
{
string filePath = Path.Combine("../../../../NuakeNet/Source/Generated", module.FileName);
File.WriteAllText(filePath, module.Content);
Console.WriteLine($"Wrote {module.FileName} to {filePath}");
}
}
}

View File

@@ -0,0 +1,22 @@
project "NuakeNetGenerator"
language "C#"
dotnetframework "net8.0"
kind "ConsoleApp"
clr "Unsafe"
targetdir (binaryOutputDir)
objdir (intBinaryOutputDir)
debugdir (binaryOutputDir)
-- Don't specify architecture here. (see https://github.com/premake/premake-core/issues/1758)
vsprops {
AppendTargetFrameworkToOutputPath = "false",
Nullable = "enable",
CopyLocalLockFileAssemblies = "true",
EnableDynamicLoading = "true"
}
files
{
"Source/**.cs"
}

View File

@@ -83,4 +83,5 @@ include "Nuake/premake5.lua"
include "Editor/premake5.lua"
include "Runtime/premake5.lua"
include "NuakeNet/premake5.lua"
include "NuakeNetGenerator/premake5.lua"
include "EditorNet/premake5.lua"