GithubHelp home page GithubHelp logo

studiocherno / coral Goto Github PK

View Code? Open in Web Editor NEW
130.0 130.0 19.0 1.2 MB

Coral is a C++/C# wrapper around the .NET CoreCLR library, the purpose of Coral is to provide a native interface similar to Mono, but in a more modern style, and using .NET Core instead of .NET Framework

License: MIT License

C# 35.79% Lua 3.64% C++ 52.81% C 7.64% Shell 0.08% Batchfile 0.05%
cpp csharp dotnet dotnet-core interop

coral's People

Contributors

peter1745 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

coral's Issues

Invalid ID Error.

I am trying to make C# bindings for C++ for a game engine. This is the ScriptEngine.cpp file:

#include "ScriptEngine.h"

namespace Square
{
	Assembly* LoadAssembly(int argc, char** argv, const std::string& assemblyDLL)
	{
		auto exeDir = std::filesystem::path(argv[0]).parent_path();
		auto coralDir = exeDir.string();

		Coral::HostSettings settings =
		{
			.CoralDirectory = coralDir,
			.ExceptionCallback = ExceptionCallback
		};
		Coral::HostInstance hostInstance;
		hostInstance.Initialize(settings);

		auto loadContext = hostInstance.CreateAssemblyLoadContext("ExampleContext");

		auto assemblyPath = std::string(exeDir.string()) + std::string("/") + assemblyDLL;
		auto& assembly = loadContext.LoadAssembly(assemblyPath);

		assembly.AddInternalCall("SquareEngine.Entity", "LogInternal", reinterpret_cast<void*>(&Log));
		assembly.AddInternalCall("SquareEngine.Entity", "RandVectorInternal", reinterpret_cast<void*>(&RandVector));

		assembly.UploadInternalCalls();

		Assembly a = Assembly { assembly };

		return &a;
	}

	Script* LoadScript(Assembly* assembly, const std::string& name)
	{
		Coral::Type& type = assembly->coralAssembly.GetType(name);
		
		Script script = Script { name, type };

		return &script;
	}

	InstantiatedScript* CreateScript(Script* script)
	{
		Coral::ManagedObject instance = script->type.CreateInstance();
		InstantiatedScript sc = { script->name, &instance };
		return &sc;
	}

	void RunFunction(InstantiatedScript* script, const std::string& func)
	{
		script->obj->InvokeMethod(func);
	}
}

Header:

#pragma once
#include <iostream>
#include <string>
#include <vector>
#include <filesystem>
#include <chrono>
#include <functional>
#include <ranges>

#include <Coral/HostInstance.hpp>
#include <Coral/GC.hpp>
#include <Coral/Array.hpp>
#include <Coral/Attribute.hpp>

#include "Glue.h"

namespace Square
{
	struct Assembly
	{
		Coral::ManagedAssembly& coralAssembly;
	};

	struct Script
	{
		std::string_view name;
		Coral::Type& type;
	};

	struct InstantiatedScript
	{
		std::string_view name;
		Coral::ManagedObject* obj;
	};
	
	inline void ExceptionCallback(std::string_view InMessage) { std::cout << "Unhandled native exception: " << InMessage << std::endl; };

	Assembly* LoadAssembly(int argc, char** argv, const std::string& assemblyDLL);

	Script* LoadScript(Assembly* assembly, const std::string& name);
	
	InstantiatedScript* CreateScript(Script* script);

	void RunFunction(InstantiatedScript* script, const std::string& func);
}

I get the following error:

System.AccessViolationException: 'Attempted to read or write protected memory. This is often an indication that other memory is corrupt.'

Call Stack:
image

Console:

[Coral](Info): Loading assembly 'D:\dev\SquareEngine\scripting\Coral\Build\Release/Example.Managed.dll'
[Coral](Error): Failed to find type with id '1928776192'.
Fatal error. System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
   at Coral.Managed.ManagedObject.InvokeMethod(IntPtr, Coral.Managed.Interop.NativeString, IntPtr, Coral.Managed.ManagedType*, Int32)

Main.cpp:

#include "ScriptEngine.h"

int main(int argc, char** argv)
{
	Square::Assembly* mainAssembly = Square::LoadAssembly(argc, argv, "Example.Managed.dll");
	Square::Script* nonInstantiatedPlayerScript = Square::LoadScript(mainAssembly, "Example.PlayerScript");
	Square::InstantiatedScript* playerScript = Square::CreateScript(nonInstantiatedPlayerScript);

	Square::RunFunction(playerScript, "OnStart");
	Square::RunFunction(playerScript, "OnUpdate");
	Square::RunFunction(playerScript, "OnUpdate");
	Square::RunFunction(playerScript, "OnUpdate");

	return 0;
}

Invoking static method feature request

It would be quite nice to be able to invoke a static method of a class.

Example

The proposed API would be something like so, where the assembly type exposes a new method called invokeMethod as well, but would invoke static methods of the assembly type.

Managed source

public class Engine
{
    internal static unsafe delegate*<NativeString, void> LoggerLogIcall;
    
    public static void Log()
    {
         unsafe { LoggerLogICall("HelloWorld"); }
    }
}

Native source

...
auto& assembly = loadContext.LoadAssembly("Testing.dll");
assembly.AddInternalCall("Engine", "LoggerLogIcall", reinterpret_cast<void*>(&Log));
assembly.UploadInternalCalls();

auto& engineType = assembly.GetType("Engine");

// Here, instead of creating an instance of T and invoking the method of that instance
// We could instead call invokeMethod on the type directly to invoke static methods
// auto engineInstance = engineType.CreateInstance(); 

engineType.InvokeMethod("Log"); // Using the type instead of instance here.
...

Unable to unload and reload assembly when loading more than one

It used to not be possible to unload/load an assembly since the reflection improvements.

It works fine when you load 1 assembly and unload it. But doesnt seem to work properly when you load more than 1 assembly and unload them and reload them.

You get the same exception as before:

Unhandled native exception: System.Reflection.TargetException: Object does not match target type.
   at System.Reflection.MethodBase.ValidateInvokeTarget(Object target)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters)
   at Coral.Managed.ManagedObject.InvokeMethod(IntPtr InObjectHandle, NativeString InMethodName, IntPtr InParameters, ManagedType* InParameterTypes, Int32 InParameterCount)

This is how I am loading my assemblies:

_LoadContext = m_HostInstance->CreateAssemblyLoadContext(m_ContextName);

// Load Nuake assembly DLL
const std::string absoluteAssemblyPath = FileSystem::Root + m_NetDirectory + "/" + m_EngineAssemblyName;
m_NuakeAssembly = m_LoadContext.LoadAssembly(absoluteAssemblyPath);

// Upload internal calls for each module
// --------------------------------------------------
for (const auto& netModule : m_Modules)
{
	const std::string inClassName = m_Scope + '.' + netModule->GetModuleName();
	for (const auto& [methodName, methodPtr] : netModule->GetMethods())
	{
		m_NuakeAssembly.AddInternalCall(inClassName, methodName, methodPtr);
	}
}

m_NuakeAssembly.UploadInternalCalls();
		
m_GameAssembly = m_LoadContext.LoadAssembly(absoluteGameAssemblyPath);
// Detecting entity scripts
for (auto& type : m_GameAssembly.GetTypes())
{
	Logger::Log(std::string("Detected type: ") + std::string(type->GetName()), ".net");
	Logger::Log(std::string("Detected base type: ") + std::string(type->GetBaseType().GetName()), ".net");
                
	const std::string baseTypeName = std::string(type->GetBaseType().GetName());
	if (baseTypeName == "Entity")
	 {
		// We have found an entity script.
		m_GameEntityTypes[std::string(type->GetName())] = type;
	 }
}

This is how I am unloading:

for (auto& [entity, managedObject] : m_EntityToManagedObjects)
{
	managedObject.Destroy();
}

m_GameEntityTypes.clear();
Coral::GC::Collect();

m_HostInstance->UnloadAssemblyLoadContext(m_LoadContext);

m_EntityToManagedObjects.clear();

Assertion failed in HostInstance.cpp

My application fails the assertion at line 237 with the latest commit when switching projects where each project has it's own dynamic library:
[Coral.Native]: Assert Failed! Expression: status == StatusCode::Success && m_HostFXRContext != nullptr at path\to\engine\vendor\Coral\Coral.Native\Source\Coral\HostInstance.cpp:237
I can avoid the assertion failed by setting the && to || but i suspect that isn't the intended behaviour?

Error when having more than one instance

It seems when you create more than one instance of a type.

For example, if you have an EntityScript type and you create two classes that inherit from EntityScript with this simple setup:

public class EntityScript {
    internal unsafe static delegate*<uint, bool> EntityHasComponentIcall;
    
    public virtual void OnInit() { }
    public virtual void OnUpdate(float dt) { }
    public virtual void OnFixedUpdate(float dt) { }
    public virtual void OnDestroy() { }
}

And then have two scripts inheriting from this class:

public class FPSCamera : EntityScript {
    public override void OnInit() {}
}

public class Player : EntityScript {
    public override void OnInit() {}
}

Calling OnInit() from C++ works fine when you have 1 entity but as soon as you have more than one, the method invokations don't resolve.

The instances are creating by calling CreateInstance() on both types (FPSCamera, Player) and doing InvokeMethod on those instances.

This is a blocking issue preventing from creating multiple entity scripts and calling the necessary methods without managing all the callbacks manually in the C# side and tracking entities there, which is less than ideal for performance reasons and adds an extra layer of indirection.

Reflection data improvement

Few useful features:

  • Method return value
  • Method parameter reflection
  • Class, field and method attribute types

Thanks Peter, very cool

Coral.Manage build warnings

Branch: dev
Commit: 9f071d7

When building Coral.Managed within my project, some build warnings are given (mainly about null references) that should be handled e.g. by error reporting, establishing default handling, etc.

Used tools:

  • cmake 3.25.1
  • Visual Studio Community 2022 64-bit (17.9.2)

BuildWarningsCoralManaged.txt

Multiple inhertance crash when iterating over the types after loading the assembly

After adding more than one inheritance on a class(baseType + interface), Coral crashes after calling type->GetBaseType(); on said type.

C# Example:

public interface IRespawnable { 
    void Respawn() {}
}

public Player : Entity, IRespawnable { }

C++ crash:

m_GameAssembly = m_LoadContext.LoadAssembly(absoluteAssemblyPath);

for (auto& type : m_GameAssembly.GetTypes())
{
	auto baseType = type->GetBaseType(); // Crash here
	std::cout <<  std::string(type->GetBaseType().GetFullName()) << std::endl;
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.