Skip to content

Commit

Permalink
Initial C# Script Engine work
Browse files Browse the repository at this point in the history
- Added mono binaries/includes
- Basic assembly loading and object creation/method calling in ScriptEngine
  • Loading branch information
TheCherno committed Jul 12, 2022
1 parent 49da3c1 commit b22a755
Show file tree
Hide file tree
Showing 613 changed files with 44,955 additions and 5 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Binaries
**/bin/
bin-int/
Intermediates/

# Hazel files
*.log
Expand All @@ -11,8 +12,9 @@ bin-int/
**.vcxproj
**.vcxproj.filters
**.vcxproj.user
**.csproj

# Directories
Hazel/vendor/VulkanSDK
Hazelnut/assets/cache
scripts/__pycache__
scripts/__pycache__
10 changes: 10 additions & 0 deletions Dependencies.lua
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,19 @@ IncludeDir["ImGui"] = "%{wks.location}/Hazel/vendor/ImGui"
IncludeDir["ImGuizmo"] = "%{wks.location}/Hazel/vendor/ImGuizmo"
IncludeDir["glm"] = "%{wks.location}/Hazel/vendor/glm"
IncludeDir["entt"] = "%{wks.location}/Hazel/vendor/entt/include"
IncludeDir["mono"] = "%{wks.location}/Hazel/vendor/mono/include"
IncludeDir["shaderc"] = "%{wks.location}/Hazel/vendor/shaderc/include"
IncludeDir["SPIRV_Cross"] = "%{wks.location}/Hazel/vendor/SPIRV-Cross"
IncludeDir["VulkanSDK"] = "%{VULKAN_SDK}/Include"

LibraryDir = {}

LibraryDir["VulkanSDK"] = "%{VULKAN_SDK}/Lib"
LibraryDir["mono"] = "%{wks.location}/Hazel/vendor/mono/lib/%{cfg.buildcfg}"

Library = {}
Library["mono"] = "%{LibraryDir.mono}/libmono-static-sgen.lib"

Library["Vulkan"] = "%{LibraryDir.VulkanSDK}/vulkan-1.lib"
Library["VulkanUtils"] = "%{LibraryDir.VulkanSDK}/VkLayer_utils.lib"

Expand All @@ -33,3 +37,9 @@ Library["SPIRV_Tools_Debug"] = "%{LibraryDir.VulkanSDK}/SPIRV-Toolsd.lib"
Library["ShaderC_Release"] = "%{LibraryDir.VulkanSDK}/shaderc_shared.lib"
Library["SPIRV_Cross_Release"] = "%{LibraryDir.VulkanSDK}/spirv-cross-core.lib"
Library["SPIRV_Cross_GLSL_Release"] = "%{LibraryDir.VulkanSDK}/spirv-cross-glsl.lib"

-- Windows
Library["WinSock"] = "Ws2_32.lib"
Library["WinMM"] = "Winmm.lib"
Library["WinVersion"] = "Version.lib"
Library["BCrypt"] = "Bcrypt.lib"
38 changes: 38 additions & 0 deletions Hazel-ScriptCore/Source/Main.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using System;

namespace Hazel
{

public class Main
{

public float FloatVar { get; set; }

public Main()
{
Console.WriteLine("Main constructor!");
}

public void PrintMessage()
{
Console.WriteLine("Hello World from C#!");
}

public void PrintInt(int value)
{
Console.WriteLine($"C# says: {value}");
}

public void PrintInts(int value1, int value2)
{
Console.WriteLine($"C# says: {value1} and {value2}");
}

public void PrintCustomMessage(string message)
{
Console.WriteLine($"C# says: {message}");
}

}

}
25 changes: 25 additions & 0 deletions Hazel-ScriptCore/premake5.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
project "Hazel-ScriptCore"
kind "SharedLib"
language "C#"
dotnetframework "4.7.2"

targetdir ("%{wks.location}/Hazelnut/Resources/Scripts")
objdir ("%{wks.location}/Hazelnut/Resources/Scripts/Intermediates")

files
{
"Source/**.cs",
"Properties/**.cs"
}

filter "configurations:Debug"
optimize "Off"
symbols "Default"

filter "configurations:Release"
optimize "On"
symbols "Default"

filter "configurations:Dist"
optimize "Full"
symbols "Off"
13 changes: 12 additions & 1 deletion Hazel/premake5.lua
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ project "Hazel"
"%{IncludeDir.glm}",
"%{IncludeDir.stb_image}",
"%{IncludeDir.entt}",
"%{IncludeDir.mono}",
"%{IncludeDir.yaml_cpp}",
"%{IncludeDir.ImGuizmo}",
"%{IncludeDir.VulkanSDK}"
Expand All @@ -52,7 +53,9 @@ project "Hazel"
"Glad",
"ImGui",
"yaml-cpp",
"opengl32.lib"
"opengl32.lib",

"%{Library.mono}",
}

filter "files:vendor/ImGuizmo/**.cpp"
Expand All @@ -65,6 +68,14 @@ project "Hazel"
{
}

links
{
"%{Library.WinSock}",
"%{Library.WinMM}",
"%{Library.WinVersion}",
"%{Library.BCrypt}",
}

filter "configurations:Debug"
defines "HZ_DEBUG"
runtime "Debug"
Expand Down
3 changes: 3 additions & 0 deletions Hazel/src/Hazel/Core/Application.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "Hazel/Core/Log.h"

#include "Hazel/Renderer/Renderer.h"
#include "Hazel/Scripting/ScriptEngine.h"

#include "Hazel/Core/Input.h"
#include "Hazel/Utils/PlatformUtils.h"
Expand All @@ -28,6 +29,7 @@ namespace Hazel {
m_Window->SetEventCallback(HZ_BIND_EVENT_FN(Application::OnEvent));

Renderer::Init();
ScriptEngine::Init();

m_ImGuiLayer = new ImGuiLayer();
PushOverlay(m_ImGuiLayer);
Expand All @@ -37,6 +39,7 @@ namespace Hazel {
{
HZ_PROFILE_FUNCTION();

ScriptEngine::Shutdown();
Renderer::Shutdown();
}

Expand Down
169 changes: 169 additions & 0 deletions Hazel/src/Hazel/Scripting/ScriptEngine.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
#include "hzpch.h"
#include "ScriptEngine.h"

#include "mono/jit/jit.h"
#include "mono/metadata/assembly.h"
#include "mono/metadata/object.h"

namespace Hazel {

struct ScriptEngineData
{
MonoDomain* RootDomain = nullptr;
MonoDomain* AppDomain = nullptr;

MonoAssembly* CoreAssembly = nullptr;
};

static ScriptEngineData* s_Data = nullptr;

void ScriptEngine::Init()
{
s_Data = new ScriptEngineData();

InitMono();
}

void ScriptEngine::Shutdown()
{
ShutdownMono();
delete s_Data;
}

char* ReadBytes(const std::string& filepath, uint32_t* outSize)
{
std::ifstream stream(filepath, std::ios::binary | std::ios::ate);

if (!stream)
{
// Failed to open the file
return nullptr;
}

std::streampos end = stream.tellg();
stream.seekg(0, std::ios::beg);
uint32_t size = end - stream.tellg();

if (size == 0)
{
// File is empty
return nullptr;
}

char* buffer = new char[size];
stream.read((char*)buffer, size);
stream.close();

*outSize = size;
return buffer;
}

MonoAssembly* LoadCSharpAssembly(const std::string& assemblyPath)
{
uint32_t fileSize = 0;
char* fileData = ReadBytes(assemblyPath, &fileSize);

// NOTE: We can't use this image for anything other than loading the assembly because this image doesn't have a reference to the assembly
MonoImageOpenStatus status;
MonoImage* image = mono_image_open_from_data_full(fileData, fileSize, 1, &status, 0);

if (status != MONO_IMAGE_OK)
{
const char* errorMessage = mono_image_strerror(status);
// Log some error message using the errorMessage data
return nullptr;
}

MonoAssembly* assembly = mono_assembly_load_from_full(image, assemblyPath.c_str(), &status, 0);
mono_image_close(image);

// Don't forget to free the file data
delete[] fileData;

return assembly;
}

void PrintAssemblyTypes(MonoAssembly* assembly)
{
MonoImage* image = mono_assembly_get_image(assembly);
const MonoTableInfo* typeDefinitionsTable = mono_image_get_table_info(image, MONO_TABLE_TYPEDEF);
int32_t numTypes = mono_table_info_get_rows(typeDefinitionsTable);

for (int32_t i = 0; i < numTypes; i++)
{
uint32_t cols[MONO_TYPEDEF_SIZE];
mono_metadata_decode_row(typeDefinitionsTable, i, cols, MONO_TYPEDEF_SIZE);

const char* nameSpace = mono_metadata_string_heap(image, cols[MONO_TYPEDEF_NAMESPACE]);
const char* name = mono_metadata_string_heap(image, cols[MONO_TYPEDEF_NAME]);

HZ_CORE_TRACE("{}.{}", nameSpace, name);
}
}

void ScriptEngine::InitMono()
{
mono_set_assemblies_path("mono/lib");

MonoDomain* rootDomain = mono_jit_init("HazelJITRuntime");
HZ_CORE_ASSERT(rootDomain);

// Store the root domain pointer
s_Data->RootDomain = rootDomain;

// Create an App Domain
s_Data->AppDomain = mono_domain_create_appdomain("HazelScriptRuntime", nullptr);
mono_domain_set(s_Data->AppDomain, true);

// Move this maybe
s_Data->CoreAssembly = LoadCSharpAssembly("Resources/Scripts/Hazel-ScriptCore.dll");
PrintAssemblyTypes(s_Data->CoreAssembly);

MonoImage* assemblyImage = mono_assembly_get_image(s_Data->CoreAssembly);
MonoClass* monoClass = mono_class_from_name(assemblyImage, "Hazel", "Main");

// 1. create an object (and call constructor)
MonoObject* instance = mono_object_new(s_Data->AppDomain, monoClass);
mono_runtime_object_init(instance);

// 2. call function
MonoMethod* printMessageFunc = mono_class_get_method_from_name(monoClass, "PrintMessage", 0);
mono_runtime_invoke(printMessageFunc, instance, nullptr, nullptr);

// 3. call function with param
MonoMethod* printIntFunc = mono_class_get_method_from_name(monoClass, "PrintInt", 1);

int value = 5;
void* param = &value;

mono_runtime_invoke(printIntFunc, instance, &param, nullptr);

MonoMethod* printIntsFunc = mono_class_get_method_from_name(monoClass, "PrintInts", 2);
int value2 = 508;
void* params[2] =
{
&value,
&value2
};
mono_runtime_invoke(printIntsFunc, instance, params, nullptr);

MonoString* monoString = mono_string_new(s_Data->AppDomain, "Hello World from C++!");
MonoMethod* printCustomMessageFunc = mono_class_get_method_from_name(monoClass, "PrintCustomMessage", 1);
void* stringParam = monoString;
mono_runtime_invoke(printCustomMessageFunc, instance, &stringParam, nullptr);

// HZ_CORE_ASSERT(false);
}

void ScriptEngine::ShutdownMono()
{
// NOTE(Yan): mono is a little confusing to shutdown, so maybe come back to this

// mono_domain_unload(s_Data->AppDomain);
s_Data->AppDomain = nullptr;

// mono_jit_cleanup(s_Data->RootDomain);
s_Data->RootDomain = nullptr;
}

}
15 changes: 15 additions & 0 deletions Hazel/src/Hazel/Scripting/ScriptEngine.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#pragma once

namespace Hazel {

class ScriptEngine
{
public:
static void Init();
static void Shutdown();
private:
static void InitMono();
static void ShutdownMono();
};

}
Loading

0 comments on commit b22a755

Please sign in to comment.