Added CustomWidget through C# reflection with callbacks

This commit is contained in:
antopilo
2024-09-11 22:58:33 -04:00
parent 596e4a484c
commit c7621b31e0
15 changed files with 928 additions and 719 deletions

View File

@@ -44,19 +44,19 @@ namespace Nuake
.ExceptionCallback = ExceptionCallback,
};
m_HostInstance = new Coral::HostInstance();
m_HostInstance->Initialize(settings);
hostInstance = new Coral::HostInstance();
hostInstance->Initialize(settings);
// Initialize API modules
// ----------------------------------
m_Modules =
modules =
{
CreateRef<EngineNetAPI>(),
CreateRef<InputNetAPI>(),
CreateRef<SceneNetAPI>()
};
for (auto& m : m_Modules)
for (auto& m : modules)
{
m->RegisterMethods();
}
@@ -66,14 +66,14 @@ namespace Nuake
if (!FileSystem::FileExists(m_EngineAssemblyName, true))
{
m_IsInitialized = false;
isInitialized = false;
return;
}
}
ScriptingEngineNet::~ScriptingEngineNet()
{
m_HostInstance->Shutdown();
hostInstance->Shutdown();
}
std::vector<CompilationError> ScriptingEngineNet::ExtractErrors(const std::string& output)
@@ -149,7 +149,7 @@ namespace Nuake
// Upload internal calls for each module
// --------------------------------------------------
for (const auto& netModule : m_Modules)
for (const auto& netModule : modules)
{
for (const auto& [methodName, methodPtr] : netModule->GetMethods())
{
@@ -165,23 +165,31 @@ namespace Nuake
void ScriptingEngineNet::Initialize()
{
m_LoadContext = m_HostInstance->CreateAssemblyLoadContext(m_ContextName);
loadContext = hostInstance->CreateAssemblyLoadContext(m_ContextName);
m_NuakeAssembly = ReloadEngineAPI(m_LoadContext);
nuakeAssembly = ReloadEngineAPI(loadContext);
m_IsInitialized = true;
isInitialized = true;
m_GameEntityTypes.clear();
gameEntityTypes.clear();
brushEntityTypes.clear();
pointEntityTypes.clear();
uiWidgets.clear();
}
void ScriptingEngineNet::Uninitialize()
{
if (!m_IsInitialized)
if (!isInitialized)
{
return;
}
for (auto& [entity, managedObject] : m_EntityToManagedObjects)
for (auto& [entity, managedObject] : entityToManagedObjects)
{
managedObject.Destroy();
}
for (auto& [widgetUUID, managedObject] : widgetUUIDToManagedObjects)
{
managedObject.Destroy();
}
@@ -189,9 +197,10 @@ namespace Nuake
Coral::GC::Collect();
Coral::GC::WaitForPendingFinalizers();
GetHostInstance()->UnloadAssemblyLoadContext(m_LoadContext);
GetHostInstance()->UnloadAssemblyLoadContext(loadContext);
m_EntityToManagedObjects.clear();
entityToManagedObjects.clear();
widgetUUIDToManagedObjects.clear();
}
void ScriptingEngineNet::UpdateEntityWithExposedVar(Entity entity)
@@ -257,16 +266,59 @@ namespace Nuake
className = filePath;
}
if (m_GameEntityTypes.find(className) == m_GameEntityTypes.end())
if (gameEntityTypes.find(className) == gameEntityTypes.end())
{
// The class name parsed in the file was not found in the game's DLL.
const std::string& msg = "Skipped .net entity script: \n Class: " +
className + " not found in " + std::string(m_GameAssembly.GetName());
className + " not found in " + std::string(gameAssembly.GetName());
Logger::Log(msg, ".net", CRITICAL);
return std::vector<ExposedVar>();
}
return m_GameEntityTypes[className].exposedVars;
return gameEntityTypes[className].exposedVars;
}
bool ScriptingEngineNet::HasUIWidget(const std::string& widgetName)
{
return uiWidgets.contains(widgetName);
}
UIWidgetObject& ScriptingEngineNet::GetUIWidget(const std::string& widgetName)
{
ASSERT(HasUIWidget(widgetName));
return uiWidgets[widgetName];
}
void ScriptingEngineNet::RegisterCustomWidgetInstance(const UUID& uuid, const std::string& widgetTypeName)
{
if (uiWidgets.find(widgetTypeName) == uiWidgets.end())
{
// The class name parsed in the file was not found in the game's DLL.
const std::string& msg = "Skipped .net widget script: \n Class: " +
widgetTypeName + " not found in " + std::string(gameAssembly.GetName());
Logger::Log(msg, ".net", CRITICAL);
return;
}
auto classInstance = uiWidgets[widgetTypeName].coralType->CreateInstance();
classInstance.SetPropertyValue("ID", uuid);
widgetUUIDToManagedObjects[uuid] = classInstance;
}
bool ScriptingEngineNet::HasCustomWidgetInstance(const UUID& uuid)
{
return widgetUUIDToManagedObjects.contains(uuid);
}
Coral::ManagedObject ScriptingEngineNet::GetCustomWidgetInstance(const UUID& uuid)
{
if (!HasCustomWidgetInstance(uuid))
{
Logger::Log("Failed to get custom widget .Net script instance, doesn't exist", ".net", CRITICAL);
return Coral::ManagedObject();
}
return widgetUUIDToManagedObjects[uuid];
}
std::vector<CompilationError> ScriptingEngineNet::BuildProjectAssembly(Ref<Project> project)
@@ -293,7 +345,7 @@ namespace Nuake
void ScriptingEngineNet::LoadProjectAssembly(Ref<Project> project)
{
if (!m_IsInitialized)
if (!isInitialized)
{
return;
}
@@ -308,15 +360,17 @@ namespace Nuake
}
const std::string absoluteAssemblyPath = FileSystem::Root + assemblyPath;
m_GameAssembly = m_LoadContext.LoadAssembly(absoluteAssemblyPath);
gameAssembly = loadContext.LoadAssembly(absoluteAssemblyPath);
m_PrefabType = m_GameAssembly.GetType("Nuake.Net.Prefab");
auto entityScriptType = m_GameAssembly.GetType("Nuake.Net.Entity");
auto& exposedFieldAttributeType = m_GameAssembly.GetType("Nuake.Net.ExposedAttribute");
auto& brushScriptAttributeType = m_GameAssembly.GetType("Nuake.Net.BrushScript");
auto& pointScriptAttributeType = m_GameAssembly.GetType("Nuake.Net.PointScript");
prefabType = gameAssembly.GetType("Nuake.Net.Prefab");
auto entityScriptType = gameAssembly.GetType("Nuake.Net.Entity");
auto& exposedFieldAttributeType = gameAssembly.GetType("Nuake.Net.ExposedAttribute");
auto& brushScriptAttributeType = gameAssembly.GetType("Nuake.Net.BrushScript");
auto& pointScriptAttributeType = gameAssembly.GetType("Nuake.Net.PointScript");
auto& uiWidgetType = gameAssembly.GetType("Nuake.Net.UIWidget");
auto& uiWidgetExternalLayoutType = gameAssembly.GetType("Nuake.Net.ExternalHTML");
for (auto& type : m_GameAssembly.GetTypes())
for (auto& type : gameAssembly.GetTypes())
{
// Brush
bool isBrushScript = false;
@@ -352,7 +406,7 @@ namespace Nuake
continue;
}
m_BaseEntityType = type->GetBaseType();
baseEntityType = type->GetBaseType();
auto typeSplits = String::Split(type->GetFullName(), '.');
std::string shortenedTypeName = typeSplits[typeSplits.size() - 1];
@@ -437,22 +491,43 @@ namespace Nuake
{
gameScriptObject.Description = brushDescription;
gameScriptObject.isTrigger = isTrigger;
m_BrushEntityTypes[shortenedTypeName] = gameScriptObject;
brushEntityTypes[shortenedTypeName] = gameScriptObject;
}
if (isPointScript)
{
gameScriptObject.Description = pointDescription;
m_PointEntityTypes[shortenedTypeName] = gameScriptObject;
pointEntityTypes[shortenedTypeName] = gameScriptObject;
}
gameEntityTypes[shortenedTypeName] = gameScriptObject;
}
if (type->IsSubclassOf(uiWidgetType))
{
auto typeSplits = String::Split(type->GetFullName(), '.');
std::string shortenedTypeName = typeSplits[typeSplits.size() - 1];
for (auto& attribute : type->GetAttributes())
{
if (attribute.GetType() == uiWidgetExternalLayoutType)
{
Coral::String htmlPath = attribute.GetFieldValue<Coral::String>("HTMLPath");
if (FileSystem::FileExists(htmlPath))
{
uiWidgets[shortenedTypeName] = { type, htmlPath };
}
else
{
Logger::Log("Couldn't register UI Widget: " + shortenedTypeName + " \nExternal HTML file doesn't exist with path: " + std::string(htmlPath), "UI", WARNING);
}
}
}
m_GameEntityTypes[shortenedTypeName] = gameScriptObject;
}
}
}
void ScriptingEngineNet::RegisterEntityScript(Entity& entity)
{
if (!m_IsInitialized)
if (!isInitialized)
{
return;
}
@@ -483,16 +558,16 @@ namespace Nuake
className = filePath;
}
if(m_GameEntityTypes.find(className) == m_GameEntityTypes.end())
if(gameEntityTypes.find(className) == gameEntityTypes.end())
{
// The class name parsed in the file was not found in the game's DLL.
const std::string& msg = "Skipped .net entity script: \n Class: " +
className + " not found in " + std::string(m_GameAssembly.GetName());
className + " not found in " + std::string(gameAssembly.GetName());
Logger::Log(msg, ".net", CRITICAL);
return;
}
auto classInstance = m_GameEntityTypes[className].coralType->CreateInstance();
auto classInstance = gameEntityTypes[className].coralType->CreateInstance();
int handle = entity.GetHandle();
int id = entity.GetID();
@@ -505,9 +580,9 @@ namespace Nuake
}
std::vector<std::string> detectedExposedVar;
detectedExposedVar.reserve(std::size(m_GameEntityTypes[className].exposedVars));
detectedExposedVar.reserve(std::size(gameEntityTypes[className].exposedVars));
// Update new default values if they have changed in the code.
for (auto& exposedVar : m_GameEntityTypes[className].exposedVars)
for (auto& exposedVar : gameEntityTypes[className].exposedVars)
{
const std::string varName = exposedVar.Name;
detectedExposedVar.push_back(varName);
@@ -563,7 +638,7 @@ namespace Nuake
}
}
m_EntityToManagedObjects.emplace(entity.GetID(), classInstance);
entityToManagedObjects.emplace(entity.GetID(), classInstance);
// Override with user values set in the editor.
for (auto& exposedVarUserValue : component.ExposedVar)
@@ -597,7 +672,7 @@ namespace Nuake
else
{
// In the case where the entity doesnt have an instance, we create one
auto newEntity = m_BaseEntityType.CreateInstance(scriptEntity.GetHandle());
auto newEntity = baseEntityType.CreateInstance(scriptEntity.GetHandle());
classInstance.SetFieldValue<Coral::ManagedObject>(exposedVarUserValue.Name, newEntity);
}
}
@@ -615,7 +690,7 @@ namespace Nuake
if (FileSystem::FileExists(path))
{
// In the case where the entity doesnt have an instance, we create one
auto newPrefab = m_PrefabType.CreateInstance(Coral::String::New(path));
auto newPrefab = prefabType.CreateInstance(Coral::String::New(path));
classInstance.SetFieldValue<Coral::ManagedObject>(exposedVarUserValue.Name, newPrefab);
}
else
@@ -641,17 +716,17 @@ namespace Nuake
return Coral::ManagedObject();
}
return m_EntityToManagedObjects[entity.GetID()];
return entityToManagedObjects[entity.GetID()];
}
bool ScriptingEngineNet::HasEntityScriptInstance(const Entity& entity)
{
if (!m_IsInitialized)
if (!isInitialized)
{
return false;
}
return m_EntityToManagedObjects.find(entity.GetID()) != m_EntityToManagedObjects.end();
return entityToManagedObjects.find(entity.GetID()) != entityToManagedObjects.end();
}
void ScriptingEngineNet::CopyNuakeNETAssemblies(const std::string& path)