Added SHScriptEngine and SHDotNetRuntime for managed code execution
This commit is contained in:
parent
ba5bea9d53
commit
548b09df06
|
@ -187,15 +187,24 @@
|
|||
<ClInclude Include="src\Math\Vector\SHVec4.h" />
|
||||
<ClInclude Include="src\Meta\SHIsDetected.h" />
|
||||
<ClInclude Include="src\Resource\Handle.h" />
|
||||
<ClInclude Include="src\Resource\Handle.hpp" />
|
||||
<ClInclude Include="src\Resource\ResourceLibrary.h" />
|
||||
<ClInclude Include="src\Resource\ResourceLibrary.hpp" />
|
||||
<ClInclude Include="src\Resource\SparseSet.h" />
|
||||
<ClInclude Include="src\Resource\SparseSet.hpp" />
|
||||
<ClInclude Include="src\SHpch.h" />
|
||||
<ClInclude Include="src\Scene\SHScene.h" />
|
||||
<ClInclude Include="src\Scene\SHSceneManager.h" />
|
||||
<ClInclude Include="src\Scripting\SHDotNetRuntime.h" />
|
||||
<ClInclude Include="src\Scripting\SHDotNetRuntime.hpp" />
|
||||
<ClInclude Include="src\Scripting\SHScriptEngine.h" />
|
||||
<ClInclude Include="src\Tools\SHException.h" />
|
||||
<ClInclude Include="src\Tools\SHExceptionHandler.h" />
|
||||
<ClInclude Include="src\Tools\SHLogger.h" />
|
||||
<ClInclude Include="src\Tools\SHUtilities.h" />
|
||||
<ClInclude Include="src\Tools\SHUtilities.hpp" />
|
||||
<ClInclude Include="src\Tools\SHStringUtils.h" />
|
||||
<ClInclude Include="src\Tools\SHStringUtils.hpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="src\Engine\ECS_Base\Components\SHComponent.cpp" />
|
||||
|
@ -262,9 +271,12 @@
|
|||
<PrecompiledHeader>Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\Scene\SHSceneManager.cpp" />
|
||||
<ClCompile Include="src\Scripting\SHDotNetRuntime.cpp" />
|
||||
<ClCompile Include="src\Scripting\SHScriptEngine.cpp" />
|
||||
<ClCompile Include="src\Tools\SHException.cpp" />
|
||||
<ClCompile Include="src\Tools\SHExceptionHandler.cpp" />
|
||||
<ClCompile Include="src\Tools\SHLogger.cpp" />
|
||||
<ClCompile Include="src\Tools\SHStringUtils.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Dependencies\yamlcpp\yaml-cpp.vcxproj">
|
||||
|
|
|
@ -109,6 +109,9 @@
|
|||
<Filter Include="Scene">
|
||||
<UniqueIdentifier>{B3F7140E-1F0C-3DBF-E88D-E01E546139F0}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Scripting">
|
||||
<UniqueIdentifier>{985A7358-04C5-27CF-4D03-D974B9AC0524}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Tools">
|
||||
<UniqueIdentifier>{16CF2D0E-82E3-55BF-4B65-F91EB73852F0}</UniqueIdentifier>
|
||||
</Filter>
|
||||
|
@ -327,6 +330,9 @@
|
|||
<ClInclude Include="src\Math\SHMathHelpers.h">
|
||||
<Filter>Math</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\Math\SHMathHelpers.hpp">
|
||||
<Filter>Math</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\Math\SHMatrix.h">
|
||||
<Filter>Math</Filter>
|
||||
</ClInclude>
|
||||
|
@ -348,12 +354,21 @@
|
|||
<ClInclude Include="src\Resource\Handle.h">
|
||||
<Filter>Resource</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\Resource\Handle.hpp">
|
||||
<Filter>Resource</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\Resource\ResourceLibrary.h">
|
||||
<Filter>Resource</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\Resource\ResourceLibrary.hpp">
|
||||
<Filter>Resource</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\Resource\SparseSet.h">
|
||||
<Filter>Resource</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\Resource\SparseSet.hpp">
|
||||
<Filter>Resource</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\SHpch.h" />
|
||||
<ClInclude Include="src\Scene\SHScene.h">
|
||||
<Filter>Scene</Filter>
|
||||
|
@ -361,6 +376,15 @@
|
|||
<ClInclude Include="src\Scene\SHSceneManager.h">
|
||||
<Filter>Scene</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\Scripting\SHDotNetRuntime.h">
|
||||
<Filter>Scripting</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\Scripting\SHDotNetRuntime.hpp">
|
||||
<Filter>Scripting</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\Scripting\SHScriptEngine.h">
|
||||
<Filter>Scripting</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\Tools\SHException.h">
|
||||
<Filter>Tools</Filter>
|
||||
</ClInclude>
|
||||
|
@ -373,6 +397,15 @@
|
|||
<ClInclude Include="src\Tools\SHUtilities.h">
|
||||
<Filter>Tools</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\Tools\SHUtilities.hpp">
|
||||
<Filter>Tools</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\Tools\SHStringUtils.h">
|
||||
<Filter>Tools</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\Tools\SHStringUtils.hpp">
|
||||
<Filter>Tools</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="src\Engine\ECS_Base\Components\SHComponent.cpp">
|
||||
|
@ -559,6 +592,12 @@
|
|||
<ClCompile Include="src\Scene\SHSceneManager.cpp">
|
||||
<Filter>Scene</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\Scripting\SHDotNetRuntime.cpp">
|
||||
<Filter>Scripting</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\Scripting\SHScriptEngine.cpp">
|
||||
<Filter>Scripting</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\Tools\SHException.cpp">
|
||||
<Filter>Tools</Filter>
|
||||
</ClCompile>
|
||||
|
@ -568,5 +607,8 @@
|
|||
<ClCompile Include="src\Tools\SHLogger.cpp">
|
||||
<Filter>Tools</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\Tools\SHStringUtils.cpp">
|
||||
<Filter>Tools</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -28,7 +28,7 @@ namespace SHADE
|
|||
//SHEntityManager::RemoveEntity(this->entityID);
|
||||
}
|
||||
|
||||
EntityID SHEntity::GetEID() noexcept
|
||||
EntityID SHEntity::GetEID() const noexcept
|
||||
{
|
||||
return this->entityID;
|
||||
}
|
||||
|
|
|
@ -77,7 +77,7 @@ namespace SHADE
|
|||
* \return uint32_t
|
||||
* The entityID of this Entity object.
|
||||
***************************************************************************/
|
||||
EntityID GetEID() noexcept;
|
||||
EntityID GetEID() const noexcept;
|
||||
|
||||
/*!*************************************************************************
|
||||
* \brief Set the Active object
|
||||
|
|
|
@ -0,0 +1,198 @@
|
|||
/*************************************************************************************//*!
|
||||
\file SHDotNetRuntime.cpp
|
||||
\author Tng Kah Wei, kahwei.tng, 390009620
|
||||
\par email: kahwei.tng\@digipen.edu
|
||||
\date Oct 2, 2021
|
||||
\brief Contains the definition of the SHDotNetRuntime class.
|
||||
Implementation of code to set up code for SHDotNetRuntime is based on the
|
||||
following repository:
|
||||
https://github.com/mjrousos/SampleCoreCLRHost
|
||||
|
||||
Copyright (C) 2021 DigiPen Institute of Technology.
|
||||
Reproduction or disclosure of this file or its contents without the prior written consent
|
||||
of DigiPen Institute of Technology is prohibited.
|
||||
*//**************************************************************************************/
|
||||
// Precompiled Header
|
||||
#include <SHpch.h>
|
||||
// Primary Header
|
||||
#include "SHDotNetRuntime.h"
|
||||
// Standard Library
|
||||
#include <array>
|
||||
// External Dependencies
|
||||
#include <shlwapi.h> // PathRemoveFileSpecA
|
||||
#include "Tools/SHLogger.h"
|
||||
|
||||
namespace SHADE
|
||||
{
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
/* Constructors/Destructor */
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
SHDotNetRuntime::SHDotNetRuntime(bool autoInit)
|
||||
{
|
||||
if (autoInit)
|
||||
{
|
||||
Init();
|
||||
}
|
||||
}
|
||||
|
||||
SHDotNetRuntime::~SHDotNetRuntime()
|
||||
{
|
||||
if (IsLoaded())
|
||||
{
|
||||
try
|
||||
{
|
||||
Exit();
|
||||
}
|
||||
catch (std::runtime_error& e)
|
||||
{
|
||||
SHLOG_ERROR(e.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
/* Lifecycle Functions */
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
void SHDotNetRuntime::Init()
|
||||
{
|
||||
// State checking, in case there was an unload before, we must ensure that the state is valid
|
||||
if (initialised)
|
||||
throw std::runtime_error("[DotNetRuntime] Failed to initialise as it was already initialised or was deinitialised into an invalid state.");
|
||||
|
||||
// Get the current executable directory
|
||||
std::string runtimePath(MAX_PATH, '\0');
|
||||
GetModuleFileNameA(nullptr, runtimePath.data(), MAX_PATH);
|
||||
PathRemoveFileSpecA(runtimePath.data());
|
||||
// Since PathRemoveFileSpecA() removes from data(), the size is not updated, so we must manually update it
|
||||
runtimePath.resize(std::strlen(runtimePath.data()));
|
||||
|
||||
// Do not need to load the library if it was previously loaded
|
||||
if (coreClr == nullptr)
|
||||
{
|
||||
// Construct the CoreCLR path
|
||||
std::string coreClrPath(runtimePath); // Works
|
||||
coreClrPath += "\\coreclr.dll";
|
||||
|
||||
// Load the CoreCLR DLL
|
||||
coreClr = LoadLibraryExA(coreClrPath.c_str(), nullptr, 0);
|
||||
if (!coreClr)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << "[DotNetRuntime] Error #" << GetLastError() << " Failed to load CoreCLR from \"" << coreClrPath << "\"\n";
|
||||
throw std::runtime_error(oss.str());
|
||||
}
|
||||
|
||||
// Step 2: Get CoreCLR hosting functions
|
||||
initializeCoreClr = getCoreClrFunctionPtr<coreclr_initialize_ptr>("coreclr_initialize");
|
||||
createManagedDelegate = getCoreClrFunctionPtr<coreclr_create_delegate_ptr>("coreclr_create_delegate");
|
||||
shutdownCoreClr = getCoreClrFunctionPtr<coreclr_shutdown_ptr>("coreclr_shutdown");
|
||||
}
|
||||
|
||||
// Step 3: Construct AppDomain properties used when starting the runtime
|
||||
// Construct the trusted platform assemblies (TPA) list
|
||||
// This is the list of assemblies that .NET Core can load as
|
||||
// trusted system assemblies (similar to the .NET Framework GAC).
|
||||
// For this host (as with most), assemblies next to CoreCLR will
|
||||
// be included in the TPA list
|
||||
std::string tpaList = buildTpaList(runtimePath);
|
||||
|
||||
// Define CoreCLR properties
|
||||
std::array propertyKeys =
|
||||
{
|
||||
"TRUSTED_PLATFORM_ASSEMBLIES", // Trusted assemblies (like the GAC)
|
||||
"APP_PATHS", // Directories to probe for application assemblies
|
||||
// "APP_NI_PATHS", // Directories to probe for application native images (not used in this sample)
|
||||
// "NATIVE_DLL_SEARCH_DIRECTORIES", // Directories to probe for native dlls (not used in this sample)
|
||||
};
|
||||
std::array propertyValues =
|
||||
{
|
||||
tpaList.c_str(),
|
||||
runtimePath.c_str()
|
||||
};
|
||||
|
||||
// Step 4: Start the CoreCLR runtime
|
||||
int result = initializeCoreClr
|
||||
(
|
||||
runtimePath.c_str(), // AppDomain base path
|
||||
"SHADEHost", // AppDomain friendly name
|
||||
propertyKeys.size(), // Property count
|
||||
propertyKeys.data(), // Property names
|
||||
propertyValues.data(), // Property values
|
||||
&hostHandle, // Host handle
|
||||
&domainId // AppDomain ID
|
||||
);
|
||||
|
||||
// Check if intiialization of CoreCLR failed
|
||||
throwIfFailed("[DotNetRuntime] Failed to initialize CoreCLR.", result);
|
||||
|
||||
initialised = true;
|
||||
SHLOG_INFO("[DotNetRuntime] Successfully loaded the .NET 5.0 Runtime.");
|
||||
}
|
||||
|
||||
void SHDotNetRuntime::Exit()
|
||||
{
|
||||
// State checking, in case there was an unload before, we must ensure that the state is valid
|
||||
if (!initialised)
|
||||
throw std::runtime_error("[DotNetRuntime] Failed to deinitialise as it was not initialised before.");
|
||||
|
||||
// Shutdown CoreCLR
|
||||
int result = shutdownCoreClr(hostHandle, domainId);
|
||||
throwIfFailed("[DotNetRuntime] Failed to shut down CoreCLR.", result);
|
||||
|
||||
// Unset pointers
|
||||
hostHandle = nullptr;
|
||||
domainId = 0;
|
||||
initialised = false;
|
||||
|
||||
SHLOG_INFO("[DotNetRuntime] Successfully shut down the .NET 5.0 Runtime.");
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
/* Helper Functions */
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
std::string SHDotNetRuntime::buildTpaList(const std::string& directory)
|
||||
{
|
||||
// Constants
|
||||
static const std::string SEARCH_PATH = directory + "\\*.dll";
|
||||
static constexpr char PATH_DELIMITER = ';';
|
||||
|
||||
// Create a osstream object to compile the string
|
||||
std::ostringstream tpaList;
|
||||
|
||||
// Search the current directory for the TPAs (.DLLs)
|
||||
WIN32_FIND_DATAA findData;
|
||||
HANDLE fileHandle = FindFirstFileA(SEARCH_PATH.c_str(), &findData);
|
||||
if (fileHandle != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
do
|
||||
{
|
||||
// Append the assembly to the list
|
||||
tpaList << directory << '\\' << findData.cFileName << PATH_DELIMITER;
|
||||
|
||||
// Note that the CLR does not guarantee which assembly will be loaded if an assembly
|
||||
// is in the TPA list multiple times (perhaps from different paths or perhaps with different NI/NI.dll
|
||||
// extensions. Therefore, a real host should probably add items to the list in priority order and only
|
||||
// add a file if it's not already present on the list.
|
||||
//
|
||||
// For this simple sample, though, and because we're only loading TPA assemblies from a single path,
|
||||
// and have no native images, we can ignore that complication.
|
||||
}
|
||||
while (FindNextFileA(fileHandle, &findData));
|
||||
FindClose(fileHandle);
|
||||
}
|
||||
|
||||
return tpaList.str();
|
||||
}
|
||||
|
||||
void SHDotNetRuntime::throwIfFailed(const std::string& errMsg, int resultCode)
|
||||
{
|
||||
if (resultCode < 0)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << std::hex << std::setfill('0') << std::setw(8)
|
||||
<< errMsg
|
||||
<< " Error 0x" << resultCode << "\n";
|
||||
throw std::runtime_error(oss.str());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,149 @@
|
|||
/*************************************************************************************//*!
|
||||
\file SHDotNetRuntime.h
|
||||
\author Tng Kah Wei, kahwei.tng, 390009620
|
||||
\par email: kahwei.tng\@digipen.edu
|
||||
\date Oct 2, 2021
|
||||
\brief Contains the interface of a wrapper class for interfacing with the
|
||||
.NET 5 Runtime.
|
||||
|
||||
Copyright (C) 2021 DigiPen Institute of Technology.
|
||||
Reproduction or disclosure of this file or its contents without the prior written consent
|
||||
of DigiPen Institute of Technology is prohibited.
|
||||
*//**************************************************************************************/
|
||||
#pragma once
|
||||
|
||||
// Standard Libraries
|
||||
#include <iomanip> // std::setfill, std::setw
|
||||
#include <stdexcept> // std::runtime_error
|
||||
#include <string> // std::string
|
||||
#include <sstream> // std::ostringstream
|
||||
// External Dependencies
|
||||
#include <Windows.h> // HMODULE
|
||||
#include <coreclrhost.h> // coreclr_*
|
||||
|
||||
namespace SHADE
|
||||
{
|
||||
/********************************************************************************//*!
|
||||
@brief Class that encapsulates the state of the .NET Core Runtime lifecycle.
|
||||
*//*********************************************************************************/
|
||||
class SHDotNetRuntime
|
||||
{
|
||||
public:
|
||||
/*----------------------------------------------------------------------------------*/
|
||||
/* Constructors/Destructor */
|
||||
/*----------------------------------------------------------------------------------*/
|
||||
/****************************************************************************//*!
|
||||
@brief Default constructor that immediately initializes the CoreCLR.
|
||||
|
||||
@param[in] autoInit
|
||||
If true, loads the CoreCLR by calling Init().
|
||||
*//*****************************************************************************/
|
||||
SHDotNetRuntime(bool autoInit = true);
|
||||
/****************************************************************************//*!
|
||||
@brief Destructor that unloads the CoreCLR if it has not been unloaded
|
||||
yet.
|
||||
*//*****************************************************************************/
|
||||
~SHDotNetRuntime();
|
||||
|
||||
// Disallow copy and moving
|
||||
SHDotNetRuntime(const SHDotNetRuntime&) = delete;
|
||||
SHDotNetRuntime(SHDotNetRuntime&&) = delete;
|
||||
|
||||
/*----------------------------------------------------------------------------------*/
|
||||
/* Lifecycle Functions */
|
||||
/*----------------------------------------------------------------------------------*/
|
||||
/****************************************************************************//*!
|
||||
@brief Loads the CoreCLR and grabs pointers to bootstrapping functions and
|
||||
kickstarts the CoreCLR.
|
||||
|
||||
@throws SystemExitException
|
||||
Thrown if there is a failure in loading the CLR and related functions.
|
||||
*//*****************************************************************************/
|
||||
void Init();
|
||||
/****************************************************************************//*!
|
||||
@brief Unloads the CoreCLR.
|
||||
|
||||
@throws SystemExitException
|
||||
Thrown if there is a failure in unloading the CLR.
|
||||
*//*****************************************************************************/
|
||||
void Exit();
|
||||
|
||||
/*----------------------------------------------------------------------------------*/
|
||||
/* Usage Functions */
|
||||
/*----------------------------------------------------------------------------------*/
|
||||
/****************************************************************************//*!
|
||||
@brief Checks if the DotNetRuntime has successfully been initialised.
|
||||
|
||||
@return True if this DotNetRuntime has been initialised.
|
||||
*//*****************************************************************************/
|
||||
inline bool IsLoaded() { return coreClr != nullptr; }
|
||||
/****************************************************************************//*!
|
||||
@brief Retrieves a function pointer from the a CLR assembly based on the
|
||||
specified assembly, type and function names.
|
||||
|
||||
@tparam FunctionType
|
||||
Type of the function pointer that the specified function name will
|
||||
provide.
|
||||
|
||||
@params[in] assemblyName
|
||||
Name of the CoreCLR assembly that contains the function.
|
||||
@params[in] typeName
|
||||
Name of the CoreCLR type in the assembly that contains the function.
|
||||
Nested types are separated by a period(.).
|
||||
@params[in] functionName
|
||||
Name of the CoreCLR function to get a pointer to.
|
||||
|
||||
@returns Pointer to the function in the assembly that was specified.
|
||||
*//*****************************************************************************/
|
||||
template<typename FunctionType>
|
||||
FunctionType GetFunctionPtr(const std::string_view& assemblyName,
|
||||
const std::string_view& typeName,
|
||||
const std::string_view& functionName);
|
||||
|
||||
private:
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
/* Data Members */
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
bool initialised = false;
|
||||
// References to CoreCLR key components
|
||||
HMODULE coreClr = nullptr;
|
||||
void* hostHandle = nullptr;
|
||||
unsigned int domainId = 0;
|
||||
// Function Pointers to CoreCLR functions
|
||||
coreclr_initialize_ptr initializeCoreClr = nullptr;
|
||||
coreclr_create_delegate_ptr createManagedDelegate = nullptr;
|
||||
coreclr_shutdown_ptr shutdownCoreClr = nullptr;
|
||||
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
/* Helper Functions */
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
/****************************************************************************//*!
|
||||
@brief Retrieves a function pointer from the CoreCLR based on the specified
|
||||
function name.
|
||||
|
||||
@tparam FunctionType
|
||||
Type of the function pointer that the specified function name will
|
||||
provide.
|
||||
|
||||
@params[in] functionName
|
||||
Name of the CoreCLR function to get a pointer to.
|
||||
|
||||
@returns Pointer to the function in the CoreCLR that was specified.
|
||||
*//*****************************************************************************/
|
||||
template<typename FunctionType>
|
||||
FunctionType getCoreClrFunctionPtr(const std::string& functionName);
|
||||
/****************************************************************************//*!
|
||||
@brief Compiles a semicolon separated string of trusted platform assemblies by
|
||||
searching the specified directory.
|
||||
|
||||
@params[in] directory
|
||||
Path to the directory where the trusted platform assemblies reside.
|
||||
|
||||
@returns Semicolon separated string of trusted platform assemblies.
|
||||
*//*****************************************************************************/
|
||||
static std::string buildTpaList(const std::string& directory);
|
||||
static void throwIfFailed(const std::string& errMsg, int resultCode);
|
||||
};
|
||||
} // namespace PlushieEngine::Scripts
|
||||
|
||||
#include "SHDotNetRuntime.hpp"
|
|
@ -0,0 +1,61 @@
|
|||
/*************************************************************************************//*!
|
||||
\file SHDotNetRuntime.hpp
|
||||
\author Tng Kah Wei, kahwei.tng, 390009620
|
||||
\par email: kahwei.tng\@digipen.edu
|
||||
\date Oct 2, 2021
|
||||
\brief Contains the implementation of the template functions of the
|
||||
DotNetRuntime class.
|
||||
|
||||
Copyright (C) 2021 DigiPen Institute of Technology.
|
||||
Reproduction or disclosure of this file or its contents without the prior written consent
|
||||
of DigiPen Institute of Technology is prohibited.
|
||||
*//**************************************************************************************/
|
||||
#pragma once
|
||||
|
||||
// Primary Include
|
||||
#include "SHDotNetRuntime.h"
|
||||
|
||||
namespace SHADE
|
||||
{
|
||||
template<typename FunctionType>
|
||||
FunctionType SHDotNetRuntime::GetFunctionPtr(const std::string_view & assemblyName,
|
||||
const std::string_view & typeName,
|
||||
const std::string_view & functionName)
|
||||
{
|
||||
FunctionType managedDelegate = nullptr;
|
||||
int result = createManagedDelegate
|
||||
(
|
||||
hostHandle,
|
||||
domainId,
|
||||
assemblyName.data(),
|
||||
typeName.data(),
|
||||
functionName.data(),
|
||||
reinterpret_cast<void**>(&managedDelegate)
|
||||
);
|
||||
|
||||
// Check if it failed
|
||||
if (result < 0)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << std::hex << std::setfill('0') << std::setw(8)
|
||||
<< "[DotNetRuntime] Failed to get pointer to function \""
|
||||
<< typeName << "." << functionName << "\" in assembly (" << assemblyName << "). "
|
||||
<< "Error 0x" << result << "\n";
|
||||
throw std::runtime_error(oss.str());
|
||||
}
|
||||
|
||||
return managedDelegate;
|
||||
}
|
||||
template<typename FunctionType>
|
||||
FunctionType SHDotNetRuntime::getCoreClrFunctionPtr(const std::string& functionName)
|
||||
{
|
||||
FunctionType fPtr = reinterpret_cast<FunctionType>(GetProcAddress(coreClr, functionName.c_str()));
|
||||
if (!fPtr)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << "[DotNetRuntime] Unable to get pointer to function: \"" << functionName << "\"";
|
||||
throw std::runtime_error(oss.str());
|
||||
}
|
||||
return fPtr;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,509 @@
|
|||
/************************************************************************************//*!
|
||||
\file SHScriptEngine.cpp
|
||||
\author Tng Kah Wei, kahwei.tng, 390009620
|
||||
\par email: kahwei.tng\@digipen.edu
|
||||
\date Sep 17, 2021
|
||||
\brief Contains the implementation for ScriptEngine class.
|
||||
|
||||
Copyright (C) 2021 DigiPen Institute of Technology.
|
||||
Reproduction or disclosure of this file or its contents without the prior written consent
|
||||
of DigiPen Institute of Technology is prohibited.
|
||||
*//*************************************************************************************/
|
||||
// Precompiled Headers
|
||||
#include <SHpch.h>
|
||||
// Primary Header
|
||||
#include "SHScriptEngine.h"
|
||||
// Standard Library
|
||||
#include <fstream> // std::fstream
|
||||
#include <filesystem> // std::filesystem::canonical, std::filesystem::remove
|
||||
// Project Headers
|
||||
#include "Tools/SHLogger.h"
|
||||
#include "Tools/SHStringUtils.h"
|
||||
|
||||
namespace SHADE
|
||||
{
|
||||
/*--------------------------------------------------------------------------------*/
|
||||
/* Static Definitions */
|
||||
/*--------------------------------------------------------------------------------*/
|
||||
const std::string SHScriptEngine::DEFAULT_CSHARP_NAMESPACE = std::string(DEFAULT_CSHARP_LIB_NAME);
|
||||
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
/* Constructors/Destructors */
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
SHScriptEngine::SHScriptEngine()
|
||||
{}
|
||||
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
/* Lifecycle Functions */
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
void SHScriptEngine::Init()
|
||||
{
|
||||
// Do not allow initialization if already initialised
|
||||
if (dotNet.IsLoaded())
|
||||
{
|
||||
SHLOG_ERROR("[ScriptEngine] Attempted to initialise an already loaded DotNetRuntime.");
|
||||
return;
|
||||
}
|
||||
|
||||
dotNet.Init();
|
||||
|
||||
// Load all the helpers
|
||||
loadFunctions();
|
||||
|
||||
// Generate script assembly if it hasn't been before
|
||||
if (!fileExists(std::string(MANAGED_SCRIPT_LIB_NAME) + ".dll"))
|
||||
{
|
||||
BuildScriptAssembly();
|
||||
}
|
||||
|
||||
// Initialise the CSharp Engine
|
||||
csEngineInit();
|
||||
|
||||
// Link events
|
||||
// - Entity Creation
|
||||
/*onEntityCreate = [this](const SHEntity& e)
|
||||
{
|
||||
csGOLibNotifyNewEntity(e.GetEID());
|
||||
};
|
||||
ECS::OnEntityCreated += onEntityCreate;*/
|
||||
// - Entity Destruction
|
||||
/*onEntityDestroy = [this](const SHEntity& e)
|
||||
{
|
||||
csScriptsRemoveAll(e.GetEID());
|
||||
csGOLibNotifyDestroyEntity(e.GetEID());
|
||||
};
|
||||
ECS::OnEntityDestroy += onEntityDestroy;*/
|
||||
}
|
||||
void SHScriptEngine::UnloadScriptAssembly()
|
||||
{
|
||||
csEngineUnloadScripts();
|
||||
}
|
||||
void SHScriptEngine::LoadScriptAssembly()
|
||||
{
|
||||
csEngineLoadScripts();
|
||||
}
|
||||
void SHScriptEngine::ReloadScriptAssembly()
|
||||
{
|
||||
csEngineReloadScripts();
|
||||
}
|
||||
void SHScriptEngine::ExecuteFixedUpdates()
|
||||
{
|
||||
csScriptsExecuteFixedUpdate();
|
||||
}
|
||||
|
||||
void SHScriptEngine::ExecuteOnTrigger()
|
||||
{
|
||||
csScriptsExecuteOnTrigger();
|
||||
}
|
||||
|
||||
void SHScriptEngine::Exit()
|
||||
{
|
||||
// Do not allow deinitialization if not initialised
|
||||
if (!dotNet.IsLoaded())
|
||||
{
|
||||
SHLOG_ERROR("[ScriptEngine] Attempted to clean up an unloaded DotNetRuntime.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Unlink events
|
||||
/*ECS::OnEntityCreated -= onEntityCreate;
|
||||
ECS::OnEntityDestroy -= onEntityDestroy;*/
|
||||
|
||||
// Clean up the CSharp Engine
|
||||
csEngineExit();
|
||||
|
||||
// Shut down the CLR
|
||||
dotNet.Exit();
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
/* Script Manipulation Functions */
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
bool SHScriptEngine::AddScript(const SHEntity& entity, const std::string_view& scriptName) const
|
||||
{
|
||||
return csScriptsAdd(entity.GetEID(), scriptName.data());
|
||||
}
|
||||
void SHScriptEngine::RemoveAllScripts(const SHEntity& entity) const
|
||||
{
|
||||
csScriptsRemoveAll(entity.GetEID());
|
||||
}
|
||||
void SHScriptEngine::RemoveAllScriptsImmediately(const SHEntity& entity, bool callOnDestroy) const
|
||||
{
|
||||
csScriptsRemoveAllImmediately(entity.GetEID(), callOnDestroy);
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
/* Script Serialisation Functions */
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
std::string SHScriptEngine::SerialiseScripts(const SHEntity& entity) const
|
||||
{
|
||||
// Create buffer needed to store serialised script data
|
||||
constexpr int BUFFER_SIZE = 10240;
|
||||
std::unique_ptr<char> buffer { new char[BUFFER_SIZE] };
|
||||
std::memset(buffer.get(), 0, BUFFER_SIZE);
|
||||
|
||||
// Attempt to serialise the script
|
||||
std::string result;
|
||||
if (csScriptsSerialise(entity.GetEID(), buffer.get(), BUFFER_SIZE))
|
||||
{
|
||||
result = std::string(buffer.get());
|
||||
}
|
||||
else
|
||||
{
|
||||
SHLOG_ERROR("[ScriptEngine] Failed to serialise scripts as string buffer is too small!");
|
||||
}
|
||||
|
||||
// Return an empty string since we failed to serialise
|
||||
return result;
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
/* Script Serialisation Functions */
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
void SHScriptEngine::DeserialiseScript(const SHEntity& entity, const std::string& yaml) const
|
||||
{
|
||||
csScriptDeserialise(entity.GetEID(), yaml.c_str());
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
/* Script Editor Functions */
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
void SHScriptEngine::RenderScriptsInInspector(const SHEntity& entity) const
|
||||
{
|
||||
csEditorRenderScripts(entity.GetEID());
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
/* Static Utility Functions */
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
bool SHScriptEngine::BuildScriptAssembly(bool debug)
|
||||
{
|
||||
constexpr std::string_view BUILD_LOG_PATH = "../Build.log";
|
||||
|
||||
// Prepare directory (delete useless files)
|
||||
deleteFolder("net5.0");
|
||||
deleteFolder("ref");
|
||||
deleteFolder("../PlushieGameManaged");
|
||||
deleteFolder("../obj");
|
||||
|
||||
// Attempt to build the assembly
|
||||
std::ostringstream oss;
|
||||
oss << "[ScriptEngine] Building " << (debug ? " debug " : "") << "Managed Script Assembly (" << MANAGED_SCRIPT_LIB_NAME << ")!";
|
||||
SHLOG_INFO(oss.str());
|
||||
oss.str("");
|
||||
const bool BUILD_SUCCESS = execProcess
|
||||
(
|
||||
L"C:\\Windows\\system32\\cmd.exe",
|
||||
L"/K \"dotnet build \"../PlushieGameManaged.csproj\" -c Debug -o \"./tmp/\" -fl -flp:LogFile=build.log;Verbosity=quiet & exit\""
|
||||
) == 0;
|
||||
if (BUILD_SUCCESS)
|
||||
{
|
||||
// Copy to built dll to the working directory and replace
|
||||
std::filesystem::copy_file("./tmp/PlushieGameManaged.dll", "PlushieGameManaged.dll", std::filesystem::copy_options::overwrite_existing);
|
||||
|
||||
oss << "[ScriptEngine] Successfully built Managed Script Assembly (" << MANAGED_SCRIPT_LIB_NAME << ")!";
|
||||
SHLOG_INFO(oss.str());
|
||||
}
|
||||
else
|
||||
{
|
||||
oss << "[ScriptEngine] Failed to build Managed Script Assembly (" << MANAGED_SCRIPT_LIB_NAME << ")!";
|
||||
SHLOG_ERROR(oss.str());
|
||||
}
|
||||
|
||||
// Clean up built files
|
||||
deleteFolder("./tmp");
|
||||
|
||||
// Read the build log and output to the console
|
||||
dumpBuildLog(BUILD_LOG_PATH);
|
||||
// Delete the build log file since we no longer need it
|
||||
deleteFile(BUILD_LOG_PATH);
|
||||
|
||||
return BUILD_SUCCESS;
|
||||
}
|
||||
|
||||
void SHScriptEngine::GenerateScriptsCsProjFile(const std::filesystem::path& path)
|
||||
{
|
||||
// Sample
|
||||
static std::string_view FILE_CONTENTS =
|
||||
"<Project Sdk=\"Microsoft.NET.Sdk\">\n\
|
||||
<PropertyGroup>\n\
|
||||
<TargetFramework>net5.0</TargetFramework>\n\
|
||||
<Platforms>x64</Platforms>\n\
|
||||
<Configurations>Release;Debug</Configurations>\n\
|
||||
</PropertyGroup>\n\
|
||||
<PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n\
|
||||
<OutputPath>.\\bin_Release-x64</OutputPath>\n\
|
||||
<PlatformTarget>x64</PlatformTarget>\n\
|
||||
</PropertyGroup>\n\
|
||||
<PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\"> \n\
|
||||
<OutputPath>.\\bin_Debug-x64</OutputPath>\n\
|
||||
<PlatformTarget>x64</PlatformTarget>\n\
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>\n\
|
||||
<Optimize>false</Optimize>\n\
|
||||
<DebugType>full</DebugType>\n\
|
||||
<DebugSymbols>true</DebugSymbols>\n\
|
||||
</PropertyGroup>\n\
|
||||
<ItemGroup>\n\
|
||||
<Compile Remove=\"bin\**\" />\n\
|
||||
<EmbeddedResource Remove=\"Assets\**\" />\n\
|
||||
<EmbeddedResource Remove=\"bin\**\" />\n\
|
||||
<None Remove=\"bin\**\" />\n\
|
||||
</ItemGroup>\n\
|
||||
<ItemGroup>\n\
|
||||
<None Remove=\".gitignore\" />\n\
|
||||
<None Remove=\".gitmodules\" />\n\
|
||||
</ItemGroup>\n\
|
||||
<ItemGroup>\n\
|
||||
<Reference Include=\"PlushieAPI\">\n\
|
||||
<HintPath>.\\bin\\PlushieAPI.dll</HintPath>\n\
|
||||
</Reference>\n\
|
||||
</ItemGroup>\n\
|
||||
</Project>";
|
||||
|
||||
// Attempt to create the file
|
||||
std::ofstream file(path);
|
||||
if (!file.is_open())
|
||||
throw std::runtime_error("Unable to create CsProj file!");
|
||||
|
||||
// Fill the file
|
||||
file << FILE_CONTENTS;
|
||||
|
||||
// Close
|
||||
file.close();
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
/* Helper Functions */
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
void SHScriptEngine::loadFunctions()
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << "[ScriptEngine] Loading \"" << DEFAULT_CSHARP_LIB_NAME << "\" CLR library.";
|
||||
SHLOG_INFO(oss.str());
|
||||
|
||||
// Load functions
|
||||
csEngineInit = dotNet.GetFunctionPtr<CsFuncPtr>
|
||||
(
|
||||
DEFAULT_CSHARP_LIB_NAME,
|
||||
DEFAULT_CSHARP_NAMESPACE + ".EngineInterface",
|
||||
"Init"
|
||||
);
|
||||
csEngineLoadScripts = dotNet.GetFunctionPtr<CsFuncPtr>
|
||||
(
|
||||
DEFAULT_CSHARP_LIB_NAME,
|
||||
DEFAULT_CSHARP_NAMESPACE + ".EngineInterface",
|
||||
"LoadScriptAssembly"
|
||||
);
|
||||
csEngineUnloadScripts = dotNet.GetFunctionPtr<CsFuncPtr>
|
||||
(
|
||||
DEFAULT_CSHARP_LIB_NAME,
|
||||
DEFAULT_CSHARP_NAMESPACE + ".EngineInterface",
|
||||
"UnloadScriptAssembly"
|
||||
);
|
||||
csEngineReloadScripts = dotNet.GetFunctionPtr<CsFuncPtr>
|
||||
(
|
||||
DEFAULT_CSHARP_LIB_NAME,
|
||||
DEFAULT_CSHARP_NAMESPACE + ".EngineInterface",
|
||||
"ReloadScriptAssembly"
|
||||
);
|
||||
csEngineExit = dotNet.GetFunctionPtr<CsFuncPtr>
|
||||
(
|
||||
DEFAULT_CSHARP_LIB_NAME,
|
||||
DEFAULT_CSHARP_NAMESPACE + ".EngineInterface",
|
||||
"Exit"
|
||||
);
|
||||
csScriptsFrameSetUp = dotNet.GetFunctionPtr<CsFuncPtr>
|
||||
(
|
||||
DEFAULT_CSHARP_LIB_NAME,
|
||||
DEFAULT_CSHARP_NAMESPACE + ".ScriptStore",
|
||||
"FrameSetUp"
|
||||
);
|
||||
csScriptsExecuteOnTrigger = dotNet.GetFunctionPtr<CsFuncPtr>
|
||||
(
|
||||
DEFAULT_CSHARP_LIB_NAME,
|
||||
DEFAULT_CSHARP_NAMESPACE + ".ScriptStore",
|
||||
"ExecuteOnTrigger"
|
||||
);
|
||||
csScriptsExecuteFixedUpdate = dotNet.GetFunctionPtr<CsFuncPtr>
|
||||
(
|
||||
DEFAULT_CSHARP_LIB_NAME,
|
||||
DEFAULT_CSHARP_NAMESPACE + ".ScriptStore",
|
||||
"ExecuteFixedUpdate"
|
||||
);
|
||||
csScriptsExecuteUpdate = dotNet.GetFunctionPtr<CsFuncPtr>
|
||||
(
|
||||
DEFAULT_CSHARP_LIB_NAME,
|
||||
DEFAULT_CSHARP_NAMESPACE + ".ScriptStore",
|
||||
"ExecuteUpdate"
|
||||
);
|
||||
csScriptsExecuteLateUpdate = dotNet.GetFunctionPtr<CsFuncPtr>
|
||||
(
|
||||
DEFAULT_CSHARP_LIB_NAME,
|
||||
DEFAULT_CSHARP_NAMESPACE + ".ScriptStore",
|
||||
"ExecuteLateUpdate"
|
||||
);
|
||||
csScriptsFrameCleanUp = dotNet.GetFunctionPtr<CsFuncPtr>
|
||||
(
|
||||
DEFAULT_CSHARP_LIB_NAME,
|
||||
DEFAULT_CSHARP_NAMESPACE + ".ScriptStore",
|
||||
"FrameCleanUp"
|
||||
);
|
||||
csScriptsAdd = dotNet.GetFunctionPtr<CsScriptManipFuncPtr>
|
||||
(
|
||||
DEFAULT_CSHARP_LIB_NAME,
|
||||
DEFAULT_CSHARP_NAMESPACE + ".ScriptStore",
|
||||
"AddScriptViaName"
|
||||
);
|
||||
csScriptsRemoveAll = dotNet.GetFunctionPtr<CsScriptBasicFuncPtr>
|
||||
(
|
||||
DEFAULT_CSHARP_LIB_NAME,
|
||||
DEFAULT_CSHARP_NAMESPACE + ".ScriptStore",
|
||||
"RemoveAllScripts"
|
||||
);
|
||||
csScriptsRemoveAllImmediately = dotNet.GetFunctionPtr<CsScriptOptionalFuncPtr>
|
||||
(
|
||||
DEFAULT_CSHARP_LIB_NAME,
|
||||
DEFAULT_CSHARP_NAMESPACE + ".ScriptStore",
|
||||
"RemoveAllScriptsImmediately"
|
||||
);
|
||||
csScriptsSerialise = dotNet.GetFunctionPtr<CsScriptSerialiseFuncPtr>
|
||||
(
|
||||
DEFAULT_CSHARP_LIB_NAME,
|
||||
DEFAULT_CSHARP_NAMESPACE + ".ScriptStore",
|
||||
"SerialiseScripts"
|
||||
);
|
||||
csScriptsSerialiseJson = dotNet.GetFunctionPtr<CsScriptSerialiseJsonFuncPtr>
|
||||
(
|
||||
DEFAULT_CSHARP_LIB_NAME,
|
||||
DEFAULT_CSHARP_NAMESPACE + ".ScriptStore",
|
||||
"SerialiseScriptsJson"
|
||||
);
|
||||
csScriptDeserialise = dotNet.GetFunctionPtr<CsScriptDeserialiseFuncPtr>
|
||||
(
|
||||
DEFAULT_CSHARP_LIB_NAME,
|
||||
DEFAULT_CSHARP_NAMESPACE + ".ScriptStore",
|
||||
"DeserialiseScript"
|
||||
);
|
||||
csGOLibNotifyNewEntity = dotNet.GetFunctionPtr<CsScriptBasicFuncPtr>
|
||||
(
|
||||
DEFAULT_CSHARP_LIB_NAME,
|
||||
DEFAULT_CSHARP_NAMESPACE + ".GameObjectLibrary",
|
||||
"NotifyNewGameObject"
|
||||
);
|
||||
csGOLibNotifyDestroyEntity = dotNet.GetFunctionPtr<CsScriptBasicFuncPtr>
|
||||
(
|
||||
DEFAULT_CSHARP_LIB_NAME,
|
||||
DEFAULT_CSHARP_NAMESPACE + ".GameObjectLibrary",
|
||||
"NotifyDestroyGameObject"
|
||||
);
|
||||
csEditorRenderScripts = dotNet.GetFunctionPtr<CsScriptEditorFuncPtr>
|
||||
(
|
||||
DEFAULT_CSHARP_LIB_NAME,
|
||||
DEFAULT_CSHARP_NAMESPACE + ".Editor",
|
||||
"RenderScriptsInInspector"
|
||||
);
|
||||
}
|
||||
|
||||
void SHScriptEngine::dumpBuildLog(const std::string_view& buildLogPath)
|
||||
{
|
||||
std::ifstream buildLog(buildLogPath);
|
||||
|
||||
// Fail to open
|
||||
if (!buildLog.is_open())
|
||||
return;
|
||||
|
||||
// Process line by line
|
||||
std::string line;
|
||||
while (std::getline(buildLog, line))
|
||||
{
|
||||
if (line.find("error") != line.npos)
|
||||
{
|
||||
SHLOG_ERROR(line);
|
||||
}
|
||||
else
|
||||
{
|
||||
SHLOG_WARNING(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
void SHScriptEngine::deleteFile(const std::string_view& filePath)
|
||||
{
|
||||
try
|
||||
{
|
||||
std::filesystem::remove(std::filesystem::canonical(filePath));
|
||||
}
|
||||
catch (...) {} // Ignore deletion failures
|
||||
}
|
||||
|
||||
void SHScriptEngine::deleteFolder(const std::string_view& filePath)
|
||||
{
|
||||
try
|
||||
{
|
||||
std::filesystem::remove_all(std::filesystem::canonical(filePath));
|
||||
}
|
||||
catch (...) {} // Ignore deletion failures
|
||||
}
|
||||
|
||||
bool SHScriptEngine::fileExists(const std::string_view& filePath)
|
||||
{
|
||||
std::error_code error;
|
||||
if (std::filesystem::exists(filePath, error))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
DWORD SHScriptEngine::execProcess(const std::wstring& path, const std::wstring& args)
|
||||
{
|
||||
STARTUPINFOW startInfo;
|
||||
PROCESS_INFORMATION procInfo;
|
||||
ZeroMemory(&startInfo, sizeof(startInfo));
|
||||
ZeroMemory(&procInfo, sizeof(procInfo));
|
||||
startInfo.cb = sizeof(startInfo);
|
||||
|
||||
std::wstring argsWstr = args;
|
||||
|
||||
// Start Process
|
||||
const auto SUCCESS = CreateProcess
|
||||
(
|
||||
path.data(), argsWstr.data(),
|
||||
nullptr, nullptr, false, NULL, nullptr, nullptr,
|
||||
&startInfo, &procInfo
|
||||
);
|
||||
|
||||
// Error Check
|
||||
if (!SUCCESS)
|
||||
{
|
||||
auto err = GetLastError();
|
||||
std::ostringstream oss;
|
||||
oss << "[ScriptEngine] Failed to launch process. Error code: " << std::hex << err
|
||||
<< " (" << SHStringUtils::GetWin32ErrorMessage(err) << ")";
|
||||
throw std::runtime_error(oss.str());
|
||||
}
|
||||
|
||||
// Wait for execution to end
|
||||
DWORD status;
|
||||
while (true)
|
||||
{
|
||||
const auto SUCCESS = GetExitCodeProcess(procInfo.hProcess, &status);
|
||||
if (!SUCCESS)
|
||||
{
|
||||
auto err = GetLastError();
|
||||
std::ostringstream oss;
|
||||
oss << "[ScriptEngine] Failed to query process. Error code: " << std::hex << err
|
||||
<< " (" << SHStringUtils::GetWin32ErrorMessage(err) << ")";
|
||||
throw std::runtime_error(oss.str());
|
||||
}
|
||||
|
||||
// Break only if process ends
|
||||
if (status != STILL_ACTIVE)
|
||||
{
|
||||
CloseHandle(procInfo.hProcess);
|
||||
CloseHandle(procInfo.hThread);
|
||||
return status;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,258 @@
|
|||
/************************************************************************************//*!
|
||||
\file ScriptEngine.h
|
||||
\author Tng Kah Wei, kahwei.tng, 390009620
|
||||
\par email: kahwei.tng\@digipen.edu
|
||||
\date Sep 17, 2021
|
||||
\brief Contains the interface for ScriptEngine class.
|
||||
|
||||
Copyright (C) 2021 DigiPen Institute of Technology.
|
||||
Reproduction or disclosure of this file or its contents without the prior written consent
|
||||
of DigiPen Institute of Technology is prohibited.
|
||||
*//*************************************************************************************/
|
||||
#pragma once
|
||||
|
||||
// STL Includes
|
||||
#include <filesystem>
|
||||
|
||||
// Project Headers
|
||||
#include "SHDotNetRuntime.h"
|
||||
#include "Engine/ECS_Base/SHECSMacros.h"
|
||||
#include "Engine/ECS_Base/Entity/SHEntity.h"
|
||||
|
||||
namespace SHADE
|
||||
{
|
||||
/// <summary>
|
||||
/// Manages initialisation of the DotNetRuntime and interfacing with CLR code written
|
||||
/// and executed on .NET.
|
||||
/// </summary>
|
||||
class SHScriptEngine
|
||||
{
|
||||
public:
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
/* Constructors & Destructors */
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
/// <summary>
|
||||
/// Default Constructor
|
||||
/// </summary>
|
||||
SHScriptEngine();
|
||||
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
/* Lifecycle Functions */
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
/// <summary>
|
||||
/// Initialises the DotNetRuntime and retrieves function pointers to all
|
||||
/// functions on the CLR used to interface with the engine.
|
||||
/// </summary>
|
||||
void Init();
|
||||
/// <summary>
|
||||
/// Loads the managed script assembly. Ensure this is only called after
|
||||
/// UnloadScriptAssembly() has been called.
|
||||
/// </summary>
|
||||
void UnloadScriptAssembly();
|
||||
/// <summary>
|
||||
/// Unloads the managed script assembly.
|
||||
/// Take note that this will clear all existing scripts, ensure that the scene
|
||||
/// is saved before doing so.
|
||||
/// </summary>
|
||||
void LoadScriptAssembly();
|
||||
/// <summary>
|
||||
/// Reloads the managed script assembly.
|
||||
/// Take note that this will clear all existing scripts, ensure that the scene
|
||||
/// is saved before doing so.
|
||||
/// </summary>
|
||||
void ReloadScriptAssembly();
|
||||
/// <summary>
|
||||
/// Executes the FixedUpdate()s of the PlushieScripts that are attached to
|
||||
/// Entities.
|
||||
/// </summary>
|
||||
void ExecuteFixedUpdates();
|
||||
/// <summary>
|
||||
/// Executes the OnTrigger() family of functions of the PlushieScripts that are
|
||||
/// attached to Entities.
|
||||
/// </summary>
|
||||
void ExecuteOnTrigger();
|
||||
/// <summary>
|
||||
/// Shuts down the DotNetRuntime.
|
||||
/// </summary>
|
||||
void Exit();
|
||||
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
/* Script Manipulation Functions */
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
/// <summary>
|
||||
/// Adds a Script to a specified Entity. Note that while you can call this
|
||||
/// multiple times on a specified Entity, it will work for all intents and
|
||||
/// purposes but GetScript<T>() (C# only) currently only
|
||||
/// gives you the first PlushieScript added of the specified type.
|
||||
/// </summary>
|
||||
/// <param name="entity">The entity to add a script to.</param>
|
||||
/// <param name="scriptName">Type name of the script to add.</param>
|
||||
/// <returns>
|
||||
/// True if successfully added. False otherwise with the error logged to the
|
||||
/// console.
|
||||
/// </returns>
|
||||
bool AddScript(const SHEntity& entity, const std::string_view& scriptName) const;
|
||||
/// <summary>
|
||||
/// Removes all Scripts attached to the specified Entity. Does not do anything
|
||||
/// if the specified Entity is invalid or does not have any PlushieScripts
|
||||
/// attached.
|
||||
/// </summary>
|
||||
/// <param name="entity">The entity to remove the scripts from.</param>
|
||||
void RemoveAllScripts(const SHEntity& entity) const;
|
||||
/// <summary>
|
||||
/// Removes all Scripts attached to the specified Entity. Unlike
|
||||
/// RemoveAllScripts(), this removes all the scripts immediately.
|
||||
/// Does not do anything if the specified Entity is invalid or does not have any
|
||||
/// PlushieScripts attached.
|
||||
/// </summary>
|
||||
/// <param name="entity">The entity to remove the scripts from.</param>
|
||||
/// <param name="callOnDestroy">
|
||||
/// Whether or not to call OnDestroy on the scripts. This is ignored if not in
|
||||
/// play mode.
|
||||
/// </param>
|
||||
void RemoveAllScriptsImmediately(const SHEntity& entity, bool callOnDestroy) const;
|
||||
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
/* Script Serialisation Functions */
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
/// <summary>
|
||||
/// Generates a JSON string that represents the set of Scripts attached to the
|
||||
/// specified Entity.
|
||||
/// </summary>
|
||||
/// <param name="entity"> The Entity to Serialise.</param>
|
||||
/// <returns>
|
||||
/// String that represents the set of scripts attached to the specified Entity.
|
||||
/// </returns>
|
||||
std::string SerialiseScripts(const SHEntity& entity) const;
|
||||
/// <summary>
|
||||
/// Loads the specified JSON string and creates a Script for the specified Entity
|
||||
/// based on the specified JSON string.
|
||||
/// </summary>
|
||||
/// <param name="entity">The Entity to deserialise a Script on to.</param>
|
||||
/// <param name="yaml">
|
||||
/// The JSON string that represents the Script to load into the Entity.
|
||||
/// </param>
|
||||
void DeserialiseScript(const SHEntity& entity, const std::string& yaml) const;
|
||||
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
/* Script Editor Functions */
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
/// <summary>
|
||||
/// Renders the set of attached PlushieScripts for the specified Entity into the
|
||||
/// inspector.
|
||||
/// <br/>
|
||||
/// This function is meant for consumption from native code in the inspector
|
||||
/// rendering code.
|
||||
/// </summary>
|
||||
/// <param name="entity">The Entity to render the PlushieScripts of.</param>
|
||||
void RenderScriptsInInspector(const SHEntity& entity) const;
|
||||
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
/* Static Utility Functions */
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
/// <summary>
|
||||
/// Utilises execution of a external batch file for invoking the dotnet build
|
||||
/// tool to compile C# scripts in the Assets folder into the PlushieGameManaged
|
||||
/// C# assembly DLL.
|
||||
/// </summary>
|
||||
/// <param name="debug">
|
||||
/// Whether or not a debug build will be built. Only debug built C# assemblies
|
||||
/// can be debugged.
|
||||
/// </param>
|
||||
/// <returns>Whether or not the build succeeded.</returns>
|
||||
static bool BuildScriptAssembly(bool debug = false);
|
||||
/// <summary>
|
||||
/// Generates a .csproj file for editing and compiling the C# scripts.
|
||||
/// </summary>
|
||||
/// <param name="path">File path to the generated file.</param>
|
||||
static void GenerateScriptsCsProjFile(const std::filesystem::path& path);
|
||||
|
||||
private:
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
/* Type Definitions */
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
using CsFuncPtr = void(*)(void);
|
||||
using CsScriptManipFuncPtr = bool(*)(EntityID, const char*);
|
||||
using CsScriptBasicFuncPtr = void(*)(EntityID);
|
||||
using CsScriptOptionalFuncPtr = void(*)(EntityID, bool);
|
||||
using CsScriptSerialiseFuncPtr = bool(*)(EntityID, char*, int);
|
||||
using CsScriptDeserialiseFuncPtr = bool(*)(EntityID, const char*);
|
||||
using CsScriptSerialiseJsonFuncPtr = bool(*)(EntityID, void*);
|
||||
using CsScriptEditorFuncPtr = void(*)(EntityID);
|
||||
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
/* Constants */
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
static constexpr std::string_view DEFAULT_CSHARP_LIB_NAME = "SHADEAPI";
|
||||
static constexpr std::string_view MANAGED_SCRIPT_LIB_NAME = "SHADEManaged";
|
||||
static const std::string DEFAULT_CSHARP_NAMESPACE;
|
||||
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
/* Data Members */
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
SHDotNetRuntime dotNet {false};
|
||||
// Function Pointers to CLR Code
|
||||
// - Engine Init
|
||||
CsFuncPtr csEngineInit = nullptr;
|
||||
CsFuncPtr csEngineLoadScripts = nullptr;
|
||||
CsFuncPtr csEngineUnloadScripts = nullptr;
|
||||
CsFuncPtr csEngineReloadScripts = nullptr;
|
||||
CsFuncPtr csEngineExit = nullptr;
|
||||
// - Scripts Store
|
||||
CsFuncPtr csScriptsFrameSetUp = nullptr;
|
||||
CsFuncPtr csScriptsExecuteOnTrigger = nullptr;
|
||||
CsFuncPtr csScriptsExecuteFixedUpdate = nullptr;
|
||||
CsFuncPtr csScriptsExecuteUpdate = nullptr;
|
||||
CsFuncPtr csScriptsExecuteLateUpdate = nullptr;
|
||||
CsFuncPtr csScriptsFrameCleanUp = nullptr;
|
||||
CsScriptManipFuncPtr csScriptsAdd = nullptr;
|
||||
CsScriptBasicFuncPtr csScriptsRemoveAll = nullptr;
|
||||
CsScriptOptionalFuncPtr csScriptsRemoveAllImmediately = nullptr;
|
||||
CsScriptSerialiseFuncPtr csScriptsSerialise = nullptr;
|
||||
CsScriptDeserialiseFuncPtr csScriptDeserialise = nullptr;
|
||||
CsScriptSerialiseJsonFuncPtr csScriptsSerialiseJson = nullptr;
|
||||
CsScriptSerialiseJsonFuncPtr csScriptDeserialiseJson = nullptr;
|
||||
// - GameObject Library
|
||||
CsScriptBasicFuncPtr csGOLibNotifyNewEntity = nullptr;
|
||||
CsScriptBasicFuncPtr csGOLibNotifyDestroyEntity = nullptr;
|
||||
// - Editor
|
||||
CsScriptEditorFuncPtr csEditorRenderScripts = nullptr;
|
||||
// Delegates
|
||||
/*ECS::EntityEvent::Delegate onEntityCreate;
|
||||
ECS::EntityEvent::Delegate onEntityDestroy;*/
|
||||
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
/* Helper Functions */
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
/// <summary>
|
||||
/// Loads all the function pointers to CLR code that we need to execute.
|
||||
/// </summary>
|
||||
void loadFunctions();
|
||||
/// <summary>
|
||||
/// Reads the file via the specified path that represents a build log of error
|
||||
/// and warning messages.
|
||||
/// </summary>
|
||||
/// <param name="buildLogPath">
|
||||
/// File path to the build log of script builds done by BuildScriptAssembly() to
|
||||
/// dump and process.
|
||||
/// </param>
|
||||
static void dumpBuildLog(const std::string_view& buildLogPath);
|
||||
/// <summary>
|
||||
/// Deletes the file as specified by the file path.
|
||||
/// </summary>
|
||||
/// <param name="filePath">File path to the file to delete.</param>
|
||||
static void deleteFile(const std::string_view& filePath);
|
||||
/// <summary>
|
||||
/// Deletes the folder and all files in it as specified by the file path.
|
||||
/// </summary>
|
||||
/// <param name="filePath">File path to the file to delete.</param>
|
||||
static void deleteFolder(const std::string_view& filePath);
|
||||
/// <summary>
|
||||
/// Checks if a specified file exists.
|
||||
/// </summary>
|
||||
/// <param name="filePath">File path to the file to check.</param>
|
||||
/// <returns> True if the file exists </returns>
|
||||
static bool fileExists(const std::string_view& filePath);
|
||||
static DWORD execProcess(const std::wstring& path, const std::wstring& args);
|
||||
};
|
||||
} // namespace PlushieEngine
|
|
@ -0,0 +1,52 @@
|
|||
/************************************************************************************//*!
|
||||
\file StringUtilities.cpp
|
||||
\author Tng Kah Wei, kahwei.tng, 390009620
|
||||
\par email: kahwei.tng\@digipen.edu
|
||||
\date Nov 29, 2021
|
||||
\brief Contains the definition of functions for working with strings.
|
||||
|
||||
Copyright (C) 2021 DigiPen Institute of Technology.
|
||||
Reproduction or disclosure of this file or its contents without the prior written consent
|
||||
of DigiPen Institute of Technology is prohibited.
|
||||
*//*************************************************************************************/
|
||||
// Precompiled Header
|
||||
#include <SHpch.h>
|
||||
// Primary Header
|
||||
#include "SHStringUtils.h"
|
||||
|
||||
namespace SHADE
|
||||
{
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
/* Utility Functions */
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
std::vector<std::string> SHStringUtils::Split(const std::string& str, const char& delim)
|
||||
{
|
||||
return Split<char>(str, delim);
|
||||
}
|
||||
std::vector<std::wstring> SHStringUtils::Split(const std::wstring& str, const wchar_t& delim)
|
||||
{
|
||||
return Split<wchar_t>(str, delim);
|
||||
}
|
||||
std::string SHStringUtils::WstrToStr(const std::wstring& wstr)
|
||||
{
|
||||
static std::vector<char> buffer;
|
||||
const int STR_SIZE = WideCharToMultiByte(CP_UTF8, 0, wstr.data(), static_cast<int>(wstr.size()), nullptr, 0, nullptr, nullptr) + 1 /* Null Terminator */;
|
||||
buffer.resize(STR_SIZE);
|
||||
WideCharToMultiByte(CP_UTF8, 0, wstr.data(), static_cast<int>(wstr.size()), buffer.data(), MAX_PATH, nullptr, nullptr);
|
||||
return std::string(buffer.data());
|
||||
}
|
||||
std::wstring SHStringUtils::StrToWstr(const std::string& str)
|
||||
{
|
||||
static std::vector<wchar_t> buffer;
|
||||
const int WSTR_SIZE = MultiByteToWideChar(CP_UTF8, 0, str.data(), static_cast<int>(str.size()), nullptr, 0) + 1 /* Null Terminator */;
|
||||
buffer.resize(WSTR_SIZE);
|
||||
MultiByteToWideChar(CP_UTF8, 0, str.data(), static_cast<int>(str.size()), buffer.data(), WSTR_SIZE);
|
||||
return std::wstring(buffer.data());
|
||||
}
|
||||
|
||||
std::string SHStringUtils::GetWin32ErrorMessage(unsigned long errorCode)
|
||||
{
|
||||
return std::system_category().message(errorCode);
|
||||
}
|
||||
|
||||
} // namespace PlushieEngine
|
|
@ -0,0 +1,81 @@
|
|||
/************************************************************************************//*!
|
||||
\file StringUtilities.h
|
||||
\author Tng Kah Wei, kahwei.tng, 390009620
|
||||
\par email: kahwei.tng\@digipen.edu
|
||||
\date Nov 29, 2021
|
||||
\brief Contains the declaration of functions for working with files and folders.
|
||||
|
||||
Copyright (C) 2021 DigiPen Institute of Technology.
|
||||
Reproduction or disclosure of this file or its contents without the prior written consent
|
||||
of DigiPen Institute of Technology is prohibited.
|
||||
*//*************************************************************************************/
|
||||
#pragma once
|
||||
// Standard Libraries
|
||||
#include <string> // std::basic_string
|
||||
#include <vector> // std::vector
|
||||
|
||||
namespace SHADE
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains useful functions for operating on strings.
|
||||
/// </summary>
|
||||
class SHStringUtils
|
||||
{
|
||||
public:
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
/* Utility Functions */
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
|
||||
/// <summary>
|
||||
/// Splits a string separated by a specified delimiter into a vector of strings.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Internal type of each element in the string.</typeparam>
|
||||
/// <param name="str">Read only reference to the string to split.</param>
|
||||
/// <param name="delim">Read only reference to the delimiter.</param>
|
||||
/// <returns>Vector of strings that have been split.</returns>
|
||||
template<typename T>
|
||||
static std::vector<std::basic_string<T>> Split(const std::basic_string<T>& str, const T& delim);
|
||||
/// <summary>
|
||||
/// Splits a string separated by a specified delimiter into a vector of strings.
|
||||
/// Overload of Split<T>() to allow for string literals to be accepted.
|
||||
/// </summary>
|
||||
/// <param name="str">Read only reference to the string to split.</param>
|
||||
/// <param name="delim">Read only reference to the delimiter.</param>
|
||||
/// <returns>Vector of strings that have been split.</returns>
|
||||
static std::vector<std::string> Split(const std::string& str, const char& delim);
|
||||
/// <summary>
|
||||
/// Splits a string separated by a specified delimiter into a vector of strings.
|
||||
/// Overload of Split<T>() to allow for wide string literals to be accepted.
|
||||
/// </summary>
|
||||
/// <param name="str">Read only reference to the string to split.</param>
|
||||
/// <param name="delim">Read only reference to the delimiter.</param>
|
||||
/// <returns>Vector of strings that have been split.</returns>
|
||||
static std::vector<std::wstring> Split(const std::wstring& str, const wchar_t& delim);
|
||||
/// <summary>
|
||||
/// Converts a wstring to a string.
|
||||
/// </summary>
|
||||
/// <param name="wstr">wstring to convert.</param>
|
||||
/// <returns>The converted wstring in string form.</returns>
|
||||
static std::string WstrToStr(const std::wstring& wstr);
|
||||
/// <summary>
|
||||
/// Converts a string to a wstring.
|
||||
/// </summary>
|
||||
/// <param name="str">string to convert.</param>
|
||||
/// <returns>The converted string in wstring form.</returns>
|
||||
static std::wstring StrToWstr(const std::string& str);
|
||||
/// <summary>
|
||||
/// Retrieves the error message associated with a Win32 error code.
|
||||
/// </summary>
|
||||
/// <param name="errorCode">Win32 error code to decode.</param>
|
||||
/// <returns>String that represents the Win32 error.</returns>
|
||||
static std::string GetWin32ErrorMessage(unsigned long errorCode);
|
||||
|
||||
private:
|
||||
/*-------------------------------------------------------------------------------*/
|
||||
/* Constructors/Destructors */
|
||||
/*-------------------------------------------------------------------------------*/
|
||||
SHStringUtils() = delete;
|
||||
};
|
||||
} // namespace PlushieEngine
|
||||
|
||||
#include "SHStringUtils.hpp"
|
|
@ -0,0 +1,46 @@
|
|||
/************************************************************************************//*!
|
||||
\file StringUtilities.hpp
|
||||
\author Tng Kah Wei, kahwei.tng, 390009620
|
||||
\par email: kahwei.tng\@digipen.edu
|
||||
\date Nov 29, 2021
|
||||
\brief Contains the implementation of template functions for working with files
|
||||
and folders.
|
||||
|
||||
Copyright (C) 2021 DigiPen Institute of Technology.
|
||||
Reproduction or disclosure of this file or its contents without the prior written consent
|
||||
of DigiPen Institute of Technology is prohibited.
|
||||
*//*************************************************************************************/
|
||||
#pragma once
|
||||
// Primary Header
|
||||
#include "SHStringUtils.h"
|
||||
|
||||
namespace SHADE
|
||||
{
|
||||
/*-------------------------------------------------------------------------------*/
|
||||
/* Template Function Definitions */
|
||||
/*-------------------------------------------------------------------------------*/
|
||||
template<typename T>
|
||||
inline std::vector<std::basic_string<T>> SHStringUtils::Split(const std::basic_string<T>& str, const T& delim)
|
||||
{
|
||||
std::vector<std::basic_string<T>> results;
|
||||
std::basic_string<T> remaining = str;
|
||||
|
||||
// Go through looking for delimiters
|
||||
while (true)
|
||||
{
|
||||
const size_t DELIM_POS = remaining.find_first_of(delim);
|
||||
results.emplace_back(remaining.substr(0, DELIM_POS));
|
||||
|
||||
// Check if we hit the end of the string
|
||||
if (DELIM_POS == remaining.npos)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// Otherwise, cut the remainder
|
||||
remaining = remaining.substr(DELIM_POS + 1);
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
} // namespace PlushieEngine
|
Loading…
Reference in New Issue