Added SHScriptEngine and SHDotNetRuntime for managed code execution

This commit is contained in:
Kah Wei 2022-09-12 16:04:22 +08:00
parent ba5bea9d53
commit 548b09df06
12 changed files with 1410 additions and 2 deletions

View File

@ -187,15 +187,24 @@
<ClInclude Include="src\Math\Vector\SHVec4.h" /> <ClInclude Include="src\Math\Vector\SHVec4.h" />
<ClInclude Include="src\Meta\SHIsDetected.h" /> <ClInclude Include="src\Meta\SHIsDetected.h" />
<ClInclude Include="src\Resource\Handle.h" /> <ClInclude Include="src\Resource\Handle.h" />
<ClInclude Include="src\Resource\Handle.hpp" />
<ClInclude Include="src\Resource\ResourceLibrary.h" /> <ClInclude Include="src\Resource\ResourceLibrary.h" />
<ClInclude Include="src\Resource\ResourceLibrary.hpp" />
<ClInclude Include="src\Resource\SparseSet.h" /> <ClInclude Include="src\Resource\SparseSet.h" />
<ClInclude Include="src\Resource\SparseSet.hpp" />
<ClInclude Include="src\SHpch.h" /> <ClInclude Include="src\SHpch.h" />
<ClInclude Include="src\Scene\SHScene.h" /> <ClInclude Include="src\Scene\SHScene.h" />
<ClInclude Include="src\Scene\SHSceneManager.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\SHException.h" />
<ClInclude Include="src\Tools\SHExceptionHandler.h" /> <ClInclude Include="src\Tools\SHExceptionHandler.h" />
<ClInclude Include="src\Tools\SHLogger.h" /> <ClInclude Include="src\Tools\SHLogger.h" />
<ClInclude Include="src\Tools\SHUtilities.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>
<ItemGroup> <ItemGroup>
<ClCompile Include="src\Engine\ECS_Base\Components\SHComponent.cpp" /> <ClCompile Include="src\Engine\ECS_Base\Components\SHComponent.cpp" />
@ -262,9 +271,12 @@
<PrecompiledHeader>Create</PrecompiledHeader> <PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile> </ClCompile>
<ClCompile Include="src\Scene\SHSceneManager.cpp" /> <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\SHException.cpp" />
<ClCompile Include="src\Tools\SHExceptionHandler.cpp" /> <ClCompile Include="src\Tools\SHExceptionHandler.cpp" />
<ClCompile Include="src\Tools\SHLogger.cpp" /> <ClCompile Include="src\Tools\SHLogger.cpp" />
<ClCompile Include="src\Tools\SHStringUtils.cpp" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Dependencies\yamlcpp\yaml-cpp.vcxproj"> <ProjectReference Include="..\Dependencies\yamlcpp\yaml-cpp.vcxproj">

View File

@ -109,6 +109,9 @@
<Filter Include="Scene"> <Filter Include="Scene">
<UniqueIdentifier>{B3F7140E-1F0C-3DBF-E88D-E01E546139F0}</UniqueIdentifier> <UniqueIdentifier>{B3F7140E-1F0C-3DBF-E88D-E01E546139F0}</UniqueIdentifier>
</Filter> </Filter>
<Filter Include="Scripting">
<UniqueIdentifier>{985A7358-04C5-27CF-4D03-D974B9AC0524}</UniqueIdentifier>
</Filter>
<Filter Include="Tools"> <Filter Include="Tools">
<UniqueIdentifier>{16CF2D0E-82E3-55BF-4B65-F91EB73852F0}</UniqueIdentifier> <UniqueIdentifier>{16CF2D0E-82E3-55BF-4B65-F91EB73852F0}</UniqueIdentifier>
</Filter> </Filter>
@ -327,6 +330,9 @@
<ClInclude Include="src\Math\SHMathHelpers.h"> <ClInclude Include="src\Math\SHMathHelpers.h">
<Filter>Math</Filter> <Filter>Math</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="src\Math\SHMathHelpers.hpp">
<Filter>Math</Filter>
</ClInclude>
<ClInclude Include="src\Math\SHMatrix.h"> <ClInclude Include="src\Math\SHMatrix.h">
<Filter>Math</Filter> <Filter>Math</Filter>
</ClInclude> </ClInclude>
@ -348,12 +354,21 @@
<ClInclude Include="src\Resource\Handle.h"> <ClInclude Include="src\Resource\Handle.h">
<Filter>Resource</Filter> <Filter>Resource</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="src\Resource\Handle.hpp">
<Filter>Resource</Filter>
</ClInclude>
<ClInclude Include="src\Resource\ResourceLibrary.h"> <ClInclude Include="src\Resource\ResourceLibrary.h">
<Filter>Resource</Filter> <Filter>Resource</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="src\Resource\ResourceLibrary.hpp">
<Filter>Resource</Filter>
</ClInclude>
<ClInclude Include="src\Resource\SparseSet.h"> <ClInclude Include="src\Resource\SparseSet.h">
<Filter>Resource</Filter> <Filter>Resource</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="src\Resource\SparseSet.hpp">
<Filter>Resource</Filter>
</ClInclude>
<ClInclude Include="src\SHpch.h" /> <ClInclude Include="src\SHpch.h" />
<ClInclude Include="src\Scene\SHScene.h"> <ClInclude Include="src\Scene\SHScene.h">
<Filter>Scene</Filter> <Filter>Scene</Filter>
@ -361,6 +376,15 @@
<ClInclude Include="src\Scene\SHSceneManager.h"> <ClInclude Include="src\Scene\SHSceneManager.h">
<Filter>Scene</Filter> <Filter>Scene</Filter>
</ClInclude> </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"> <ClInclude Include="src\Tools\SHException.h">
<Filter>Tools</Filter> <Filter>Tools</Filter>
</ClInclude> </ClInclude>
@ -373,6 +397,15 @@
<ClInclude Include="src\Tools\SHUtilities.h"> <ClInclude Include="src\Tools\SHUtilities.h">
<Filter>Tools</Filter> <Filter>Tools</Filter>
</ClInclude> </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>
<ItemGroup> <ItemGroup>
<ClCompile Include="src\Engine\ECS_Base\Components\SHComponent.cpp"> <ClCompile Include="src\Engine\ECS_Base\Components\SHComponent.cpp">
@ -559,6 +592,12 @@
<ClCompile Include="src\Scene\SHSceneManager.cpp"> <ClCompile Include="src\Scene\SHSceneManager.cpp">
<Filter>Scene</Filter> <Filter>Scene</Filter>
</ClCompile> </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"> <ClCompile Include="src\Tools\SHException.cpp">
<Filter>Tools</Filter> <Filter>Tools</Filter>
</ClCompile> </ClCompile>
@ -568,5 +607,8 @@
<ClCompile Include="src\Tools\SHLogger.cpp"> <ClCompile Include="src\Tools\SHLogger.cpp">
<Filter>Tools</Filter> <Filter>Tools</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="src\Tools\SHStringUtils.cpp">
<Filter>Tools</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -28,7 +28,7 @@ namespace SHADE
//SHEntityManager::RemoveEntity(this->entityID); //SHEntityManager::RemoveEntity(this->entityID);
} }
EntityID SHEntity::GetEID() noexcept EntityID SHEntity::GetEID() const noexcept
{ {
return this->entityID; return this->entityID;
} }

View File

@ -77,7 +77,7 @@ namespace SHADE
* \return uint32_t * \return uint32_t
* The entityID of this Entity object. * The entityID of this Entity object.
***************************************************************************/ ***************************************************************************/
EntityID GetEID() noexcept; EntityID GetEID() const noexcept;
/*!************************************************************************* /*!*************************************************************************
* \brief Set the Active object * \brief Set the Active object

View File

@ -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());
}
}
}

View File

@ -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"

View File

@ -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;
}
}

View File

@ -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;
}
}
}
}

View File

@ -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&lt;T&gt;() (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

View File

@ -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

View File

@ -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"

View File

@ -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