Added ScriptStore
This commit is contained in:
parent
216a9ac7cb
commit
5a6ff60fe1
|
@ -30,6 +30,11 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SHADE_Engine", "SHADE_Engin
|
|||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SHADE_Managed", "SHADE_Managed\SHADE_Managed.vcxproj", "{16DB1400-829B-9036-4BD6-D9B3B755D512}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{88F1A057-74BE-FB62-9DD7-E90A890331F1} = {88F1A057-74BE-FB62-9DD7-E90A890331F1}
|
||||
{C0FF640D-2C14-8DBE-F595-301E616989EF} = {C0FF640D-2C14-8DBE-F595-301E616989EF}
|
||||
{8EAD431C-7A4F-6EF2-630A-82464F4BF542} = {8EAD431C-7A4F-6EF2-630A-82464F4BF542}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
|
|
|
@ -34,6 +34,9 @@
|
|||
<ClInclude Include="src\Scripts\Script.hxx">
|
||||
<Filter>Scripts</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\Scripts\ScriptStore.hxx">
|
||||
<Filter>Scripts</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\Utility\Convert.hxx">
|
||||
<Filter>Utility</Filter>
|
||||
</ClInclude>
|
||||
|
@ -64,6 +67,9 @@
|
|||
<ClCompile Include="src\Scripts\Script.cxx">
|
||||
<Filter>Scripts</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\Scripts\ScriptStore.cxx">
|
||||
<Filter>Scripts</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\Utility\Convert.cxx">
|
||||
<Filter>Utility</Filter>
|
||||
</ClCompile>
|
||||
|
|
|
@ -0,0 +1,673 @@
|
|||
/************************************************************************************//*!
|
||||
\file ScriptStore.cxx
|
||||
\author Tng Kah Wei, kahwei.tng, 390009620
|
||||
\par email: kahwei.tng\@digipen.edu
|
||||
\date Oct 28, 2021
|
||||
\brief Contains the definition of the functions for the ScriptStore managed
|
||||
static class.
|
||||
|
||||
Note: This file is written in C++17/CLI.
|
||||
|
||||
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 "ScriptStore.hxx"
|
||||
// Standard Libraries
|
||||
#include <sstream>
|
||||
// Project Headers
|
||||
#include "Utility/Debug.hxx"
|
||||
#include "Utility/Convert.hxx"
|
||||
#include "Tools/SHLogger.h"
|
||||
#include "Script.hxx"
|
||||
#include "Engine/Entity.hxx"
|
||||
|
||||
namespace SHADE
|
||||
{
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
/* Scripts Manipulation Functions */
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
generic<typename T>
|
||||
T ScriptStore::AddScript(Entity entity)
|
||||
{
|
||||
// Check if entity exists and is a valid GameObject
|
||||
if (!EntityUtils::IsValid(entity) /*|| !GameObjectLibrary::Contains(entity)*/)
|
||||
throw gcnew System::ArgumentException("Invalid Entity provided to add a Script to.");
|
||||
|
||||
System::Collections::Generic::List<Script^> ^ entityScriptList;
|
||||
|
||||
// Check if storage for scripts of this entity exists
|
||||
if (!scripts.ContainsKey(entity))
|
||||
{
|
||||
// Create a new list for this set of scripts
|
||||
entityScriptList = gcnew System::Collections::Generic::List<Script^>();
|
||||
scripts.Add(entity, entityScriptList);
|
||||
}
|
||||
else
|
||||
{
|
||||
entityScriptList = scripts[entity];
|
||||
}
|
||||
|
||||
// Create the script and add it in
|
||||
array<Object^>^ params = gcnew array<Object^>{GameObject(entity)};
|
||||
Script^ script = safe_cast<Script^>(System::Activator::CreateInstance(T::typeid, params));
|
||||
entityScriptList->Add(script);
|
||||
awakeList.Add(script);
|
||||
startList.Add(script);
|
||||
script->OnAttached();
|
||||
|
||||
return safe_cast<T>(script);
|
||||
}
|
||||
|
||||
bool ScriptStore::AddScriptViaName(Entity entity, System::String^ scriptName)
|
||||
{
|
||||
SAFE_NATIVE_CALL_BEGIN
|
||||
Script^ script;
|
||||
return AddScriptViaNameWithRef(entity, scriptName, script);
|
||||
SAFE_NATIVE_CALL_END_N("SHADE.ScriptStore")
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ScriptStore::AddScriptViaNameWithRef(Entity entity, System::String^ scriptName, Script^% createdScript)
|
||||
{
|
||||
// Check if we are set up to get scripts
|
||||
if (addScriptMethod == nullptr)
|
||||
{
|
||||
Debug::LogError("[ScriptStore] Native AddScript<T>() was not loaded. Unable to add scripts.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the script if it exists
|
||||
System::Type^ scriptType = getScriptType(scriptName);
|
||||
if (scriptType == nullptr)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << "[ScriptStore] No Script named "
|
||||
<< Convert::ToNative(scriptName)
|
||||
<< " found!";
|
||||
SHLOG_ERROR(oss.str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Otherwise, add the script
|
||||
System::Reflection::MethodInfo^ method = addScriptMethod->MakeGenericMethod(scriptType);
|
||||
try
|
||||
{
|
||||
array<Object^>^ params = gcnew array<Object^>{entity};
|
||||
createdScript = safe_cast<Script^>(method->Invoke(nullptr, params));
|
||||
}
|
||||
catch (System::Exception^ e)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << "[ScriptStore] Failed to add Script named \"" << Convert::ToNative(scriptName)
|
||||
<< "\" to Entity #" << entity << "! (" << Convert::ToNative(e->GetType()->Name) << ")";
|
||||
SHLOG_ERROR(oss.str());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
generic<typename T>
|
||||
T ScriptStore::GetScript(Entity entity)
|
||||
{
|
||||
// Check if entity exists and is a valid GameObject
|
||||
if (!EntityUtils::IsValid(entity) /*|| !GameObjectLibrary::Contains(entity)*/)
|
||||
{
|
||||
throw gcnew System::ArgumentException("Invalid Entity provided to get a Script from.");
|
||||
}
|
||||
|
||||
|
||||
// Check if entity exists in the script storage
|
||||
if (!scripts.ContainsKey(entity))
|
||||
{
|
||||
return T();
|
||||
}
|
||||
|
||||
// Search for and obtain
|
||||
for each (Script^ script in scripts[entity])
|
||||
{
|
||||
try
|
||||
{
|
||||
T actualScript = safe_cast<T>(script);
|
||||
return actualScript;
|
||||
}
|
||||
catch (System::InvalidCastException^)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return T();
|
||||
}
|
||||
|
||||
generic <typename T>
|
||||
T ScriptStore::GetScriptInChildren(Entity entity)
|
||||
{
|
||||
// Check if entity exists and is a valid GameObject
|
||||
if (!EntityUtils::IsValid(entity) /*|| !GameObjectLibrary::Contains(entity)*/)
|
||||
{
|
||||
throw gcnew System::ArgumentException("Invalid Entity provided to get a Script from.");
|
||||
}
|
||||
|
||||
|
||||
// Check if entity exists in the script storage
|
||||
if (!scripts.ContainsKey(entity))
|
||||
{
|
||||
return T();
|
||||
}
|
||||
|
||||
// Get Transform component and get the children list
|
||||
throw gcnew System::NotImplementedException;
|
||||
//Pls::Transform* tf = Pls::ECS::GetComponent<Pls::Transform>(Convert::ToNative(entity));
|
||||
//if (tf == nullptr)
|
||||
// return T();
|
||||
|
||||
//// Search direct children first
|
||||
//for (const auto& child : tf->GetChildren())
|
||||
//{
|
||||
// T script = GetScript<T>(Convert::ToCLI(child));
|
||||
// if (script != nullptr)
|
||||
// return script;
|
||||
//}
|
||||
|
||||
//// Search their children
|
||||
//for (const auto& child : tf->GetChildren())
|
||||
//{
|
||||
// T script = GetScriptInChildren<T>(Convert::ToCLI(child));
|
||||
// if (script != nullptr)
|
||||
// return script;
|
||||
//}
|
||||
|
||||
// None here
|
||||
return T();
|
||||
}
|
||||
|
||||
generic <typename T>
|
||||
System::Collections::Generic::IEnumerable<T>^ ScriptStore::GetScripts(Entity entity)
|
||||
{
|
||||
// Check if entity exists and is a valid GameObject
|
||||
if (!EntityUtils::IsValid(entity) /*|| !GameObjectLibrary::Contains(entity)*/)
|
||||
{
|
||||
throw gcnew System::ArgumentException("Invalid Entity provided to get a Script from.");
|
||||
}
|
||||
|
||||
// Create a list to store entries
|
||||
System::Collections::Generic::List<T>^ foundScripts = gcnew System::Collections::Generic::List<T>();
|
||||
|
||||
// Check if entity exists in the script storage
|
||||
if (!scripts.ContainsKey(entity))
|
||||
{
|
||||
return foundScripts;
|
||||
}
|
||||
|
||||
// Search for and obtain
|
||||
for each (Script^ script in scripts[entity])
|
||||
{
|
||||
try
|
||||
{
|
||||
T actualScript = safe_cast<T>(script);
|
||||
foundScripts->Add(actualScript);
|
||||
}
|
||||
catch (System::InvalidCastException^)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return foundScripts;
|
||||
}
|
||||
System::Collections::Generic::IEnumerable<Script^>^ ScriptStore::GetAllScripts(Entity entity)
|
||||
{
|
||||
// Check if entity exists and is a valid GameObject
|
||||
if (!EntityUtils::IsValid(entity) /*|| !GameObjectLibrary::Contains(entity)*/)
|
||||
return nullptr;
|
||||
|
||||
// Check if entity exists in the script storage
|
||||
if (scripts.ContainsKey(entity))
|
||||
{
|
||||
return scripts[entity];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
generic<typename T>
|
||||
void ScriptStore::RemoveScript(Entity entity)
|
||||
{
|
||||
// Check if entity exists and is a valid GameObject
|
||||
if (!EntityUtils::IsValid(entity) /*|| !GameObjectLibrary::Contains(entity)*/)
|
||||
throw gcnew System::ArgumentException("Invalid Entity provided to remove a Script from.");
|
||||
|
||||
|
||||
// Check if entity exists in the script storage
|
||||
if (!scripts.ContainsKey(entity))
|
||||
{
|
||||
Debug::LogError("[ScriptStore] Attempted to remove a Script that does not belong to the specified Entity!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Search for and obtain
|
||||
for each (Script^ script in scripts[entity])
|
||||
{
|
||||
try
|
||||
{
|
||||
safe_cast<T>(script);
|
||||
removeScript(script);
|
||||
}
|
||||
catch (System::InvalidCastException^)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
bool ScriptStore::RemoveScript(Entity entity, Script^ script)
|
||||
{
|
||||
// Check if entity exists and is a valid GameObject
|
||||
if (!EntityUtils::IsValid(entity) /*|| !GameObjectLibrary::Contains(entity)*/)
|
||||
{
|
||||
Debug::LogError("[ScriptStore] Attempted to remove a Script from an invalid Entity!");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Check if entity exists in the script storage
|
||||
if (!scripts.ContainsKey(entity))
|
||||
{
|
||||
Debug::LogError("[ScriptStore] Attempted to remove a Script that does not belong to the specified Entity!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if the script exists to begin with
|
||||
if (!scripts[entity]->Contains(script))
|
||||
{
|
||||
Debug::LogError("[ScriptStore] Attempted to remove a Script that does not belong to the specified Entity!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Script found, queue it for deletion
|
||||
removeScript(script);
|
||||
return true;
|
||||
}
|
||||
void ScriptStore::RemoveAllScripts(Entity entity)
|
||||
{
|
||||
SAFE_NATIVE_CALL_BEGIN
|
||||
// Check if entity exists and is a valid GameObject
|
||||
if (!EntityUtils::IsValid(entity) /*|| !GameObjectLibrary::Contains(entity)*/)
|
||||
{
|
||||
Debug::LogError("[ScriptStore] Attempted to remove Scripts from an invalid Entity!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if entity exists in the script storage
|
||||
if (!scripts.ContainsKey(entity))
|
||||
return;
|
||||
|
||||
// Search for and clear
|
||||
System::Collections::Generic::List<Script^>^ scriptList = scripts[entity];
|
||||
for each (Script^ script in scriptList)
|
||||
{
|
||||
removeScript(script);
|
||||
}
|
||||
scriptList->Clear();
|
||||
SAFE_NATIVE_CALL_END_N("SHADE.ScriptStore")
|
||||
}
|
||||
void ScriptStore::RemoveAllScriptsImmediately(Entity entity, bool callOnDestroy)
|
||||
{
|
||||
SAFE_NATIVE_CALL_BEGIN
|
||||
// Check if entity exists and is a valid GameObject
|
||||
if (!EntityUtils::IsValid(entity) /*|| !GameObjectLibrary::Contains(entity)*/)
|
||||
{
|
||||
Debug::LogError("[ScriptStore] Attempted to remove Scripts from an invalid Entity!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if entity exists in the script storage
|
||||
if (!scripts.ContainsKey(entity))
|
||||
return;
|
||||
|
||||
// Clear all
|
||||
System::Collections::Generic::List<Script^>^ scriptList = scripts[entity];
|
||||
for each (Script ^ script in scriptList)
|
||||
{
|
||||
// Call OnDestroy only if indicated and also in play mode
|
||||
if (callOnDestroy)
|
||||
{
|
||||
script->OnDestroy();
|
||||
}
|
||||
script->OnDetached();
|
||||
|
||||
// Remove scripts from awakening if they were not woken up to begin with
|
||||
awakeList.Remove(script);
|
||||
startList.Remove(script);
|
||||
}
|
||||
scriptList->Clear();
|
||||
SAFE_NATIVE_CALL_END_N("SHADE.ScriptStore")
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
/* Lifecycle Functions */
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
void ScriptStore::Init()
|
||||
{
|
||||
// Create an enumerable list of script types
|
||||
refreshScriptTypeList();
|
||||
// Get stored methods for interop variants of functions
|
||||
getGenericMethods();
|
||||
}
|
||||
void ScriptStore::FrameSetUp()
|
||||
{
|
||||
SAFE_NATIVE_CALL_BEGIN
|
||||
// Clear the awake queue
|
||||
for each (Script^ script in awakeList)
|
||||
{
|
||||
script->Awake();
|
||||
}
|
||||
awakeList.Clear();
|
||||
|
||||
// Clear the start queue
|
||||
for each (Script^ script in startList)
|
||||
{
|
||||
if (script->Owner.IsActiveInHierarchy)
|
||||
{
|
||||
script->Start();
|
||||
}
|
||||
else
|
||||
{
|
||||
inactiveStartList.Add(script);
|
||||
}
|
||||
}
|
||||
startList.Clear();
|
||||
startList.AddRange(%inactiveStartList);
|
||||
inactiveStartList.Clear();
|
||||
|
||||
SAFE_NATIVE_CALL_END_N("SHADE.ScriptStore")
|
||||
}
|
||||
void ScriptStore::FrameCleanUp()
|
||||
{
|
||||
SAFE_NATIVE_CALL_BEGIN
|
||||
// Clear the queue
|
||||
while (disposalQueue.Count > 0)
|
||||
{
|
||||
Script^ script = disposalQueue.Dequeue();
|
||||
/*if (Application::IsPlaying)
|
||||
{
|
||||
script->OnDestroy();
|
||||
|
||||
}*/
|
||||
auto entity = script->Owner.GetEntity();
|
||||
auto scriptList = scripts[script->Owner.GetEntity()];
|
||||
scriptList->Remove(script);
|
||||
if (scriptList->Count <= 0)
|
||||
{
|
||||
scripts.Remove(entity);
|
||||
}
|
||||
}
|
||||
SAFE_NATIVE_CALL_END_N("SHADE.ScriptStore")
|
||||
}
|
||||
void ScriptStore::Exit()
|
||||
{
|
||||
SAFE_NATIVE_CALL_BEGIN
|
||||
// Run the deinit all scripts if needed
|
||||
//if (Application::IsPlaying)
|
||||
{
|
||||
Debug::Log("Running OnDestroy() for scripts.");
|
||||
for each (System::Collections::Generic::KeyValuePair<Entity, ScriptList^> entity in scripts)
|
||||
{
|
||||
for each (Script^ script in entity.Value)
|
||||
{
|
||||
script->OnDestroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clear Script Storage
|
||||
scripts.Clear();
|
||||
awakeList.Clear();
|
||||
startList.Clear();
|
||||
disposalQueue.Clear();
|
||||
scriptTypeList = nullptr;
|
||||
SAFE_NATIVE_CALL_END_N("SHADE.ScriptStore")
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
/* Script Information Functions */
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
System::Collections::Generic::IEnumerable<System::Type^>^ ScriptStore::GetAvailableScriptList()
|
||||
{
|
||||
return scriptTypeList;
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
/* Script Execution Functions */
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
void ScriptStore::ExecuteFixedUpdate()
|
||||
{
|
||||
SAFE_NATIVE_CALL_BEGIN
|
||||
for each (System::Collections::Generic::KeyValuePair<Entity, ScriptList^> entity in scripts)
|
||||
{
|
||||
// Check active state
|
||||
if (!isEntityActive(entity.Key))
|
||||
continue;
|
||||
|
||||
// Update each script
|
||||
for each (Script^ script in entity.Value)
|
||||
{
|
||||
script->FixedUpdate();
|
||||
}
|
||||
}
|
||||
SAFE_NATIVE_CALL_END_N("SHADE.ScriptStore")
|
||||
}
|
||||
void ScriptStore::ExecuteUpdate()
|
||||
{
|
||||
SAFE_NATIVE_CALL_BEGIN
|
||||
for each (System::Collections::Generic::KeyValuePair<Entity, ScriptList^> entity in scripts)
|
||||
{
|
||||
// Check active state
|
||||
if (!isEntityActive(entity.Key))
|
||||
continue;
|
||||
|
||||
// Update each script
|
||||
for each (Script^ script in entity.Value)
|
||||
{
|
||||
script->Update();
|
||||
}
|
||||
}
|
||||
SAFE_NATIVE_CALL_END_N("SHADE.ScriptStore")
|
||||
}
|
||||
void ScriptStore::ExecuteLateUpdate()
|
||||
{
|
||||
SAFE_NATIVE_CALL_BEGIN
|
||||
for each (System::Collections::Generic::KeyValuePair<Entity, ScriptList^> entity in scripts)
|
||||
{
|
||||
// Check active state
|
||||
if (!isEntityActive(entity.Key))
|
||||
continue;
|
||||
|
||||
// Update each script
|
||||
for each (Script^ script in entity.Value)
|
||||
{
|
||||
script->LateUpdate();
|
||||
}
|
||||
}
|
||||
SAFE_NATIVE_CALL_END_N("SHADE.ScriptStore")
|
||||
}
|
||||
bool ScriptStore::SerialiseScripts(Entity entity, System::Text::StringBuilder^ buffer, int bufferSize)
|
||||
{
|
||||
SAFE_NATIVE_CALL_BEGIN
|
||||
// Create a buffer that we can work with temporarily
|
||||
System::Text::StringBuilder^ jsonString = gcnew System::Text::StringBuilder();
|
||||
|
||||
// Check if entity exists and is a valid GameObject, otherwise nothing
|
||||
if (!EntityUtils::IsValid(entity) /*|| !GameObjectLibrary::Contains(entity)*/)
|
||||
return true;
|
||||
|
||||
|
||||
// Check if entity exists in the script storage
|
||||
if (!scripts.ContainsKey(entity))
|
||||
return true;
|
||||
|
||||
// Serialise each script
|
||||
System::Collections::Generic::List<Script^>^ scriptList = scripts[entity];
|
||||
for (int i = 0; i < scriptList->Count; ++i)
|
||||
{
|
||||
throw gcnew System::NotFiniteNumberException;
|
||||
//jsonString->Append(ReflectionUtilities::Serialise(scriptList[i]));
|
||||
|
||||
// Only add separator if is not last script
|
||||
if (i != scriptList->Count - 1)
|
||||
{
|
||||
jsonString->Append(",\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the size is too big
|
||||
if (jsonString->Length > bufferSize)
|
||||
return false;
|
||||
|
||||
// Otherwise we copy it over
|
||||
buffer->Clear();
|
||||
buffer->Append(jsonString->ToString());
|
||||
return true;
|
||||
SAFE_NATIVE_CALL_END_N("SHADE.ScriptStore")
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ScriptStore::DeserialiseScript(Entity entity, System::String^ json)
|
||||
{
|
||||
SAFE_NATIVE_CALL_BEGIN
|
||||
// Check if entity exists and is a valid GameObject, otherwise nothing
|
||||
if (!EntityUtils::IsValid(entity)/* || !GameObjectLibrary::Contains(entity)*/)
|
||||
return false;
|
||||
|
||||
// Get the name of the script
|
||||
const int FIRST_QUOTE = json->IndexOf('\"');
|
||||
const int FIRST_COLON = json->IndexOf(':');
|
||||
if (FIRST_QUOTE < 0 || FIRST_COLON < 0) // No script name, it's invalid
|
||||
return false;
|
||||
const int SCRIPT_NAME_START = FIRST_QUOTE + 1;
|
||||
const int SCRIPT_NAME_END = FIRST_COLON - 1;
|
||||
System::String^ typeName = json->Substring(SCRIPT_NAME_START, SCRIPT_NAME_END - SCRIPT_NAME_START);
|
||||
|
||||
// Create the script
|
||||
Script^ script;
|
||||
if (AddScriptViaNameWithRef(entity, typeName, script))
|
||||
{
|
||||
// Copy the data in
|
||||
throw gcnew System::NotImplementedException;
|
||||
//ReflectionUtilities::Deserialise(json, script);
|
||||
return true;
|
||||
}
|
||||
|
||||
SAFE_NATIVE_CALL_END_N("SHADE.ScriptStore")
|
||||
return false;
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
/* Helper Functions */
|
||||
/*---------------------------------------------------------------------------------*/
|
||||
void ScriptStore::removeScript(Script^ script)
|
||||
{
|
||||
// Prepare for disposal
|
||||
disposalQueue.Enqueue(script);
|
||||
|
||||
// Also remove it fromm awake and start queues if they were created but not initialised
|
||||
awakeList.Remove(script);
|
||||
startList.Remove(script);
|
||||
script->OnDetached();
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
/* Select Many */
|
||||
ref struct Pair
|
||||
{
|
||||
System::Reflection::Assembly^ assembly;
|
||||
System::Type^ type;
|
||||
};
|
||||
|
||||
System::Collections::Generic::IEnumerable<System::Type^>^ selectorFunc(System::Reflection::Assembly^ assembly)
|
||||
{
|
||||
return assembly->GetExportedTypes();
|
||||
}
|
||||
Pair^ resultSelectorFunc(System::Reflection::Assembly^ assembly, System::Type^ type)
|
||||
{
|
||||
Pair^ p = gcnew Pair();
|
||||
p->assembly = assembly;
|
||||
p->type = type;
|
||||
return p;
|
||||
}
|
||||
|
||||
/* Where */
|
||||
bool predicateFunc(Pair^ pair)
|
||||
{
|
||||
return pair->type->IsSubclassOf(PlushieScript::typeid) && !pair->type->IsAbstract;
|
||||
}
|
||||
|
||||
/* Select */
|
||||
System::Type^ selectorFunc(Pair^ pair)
|
||||
{
|
||||
return pair->type;
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptStore::refreshScriptTypeList()
|
||||
{
|
||||
using namespace System;
|
||||
using namespace System::Reflection;
|
||||
using namespace System::Linq;
|
||||
using namespace System::Collections::Generic;
|
||||
|
||||
/* Select Many: Types in Loaded Assemblies */
|
||||
IEnumerable<Assembly^>^ assemblies = AppDomain::CurrentDomain->GetAssemblies();
|
||||
Func<Assembly^, IEnumerable<Type^>^>^ collectionSelector = gcnew Func<Assembly^, IEnumerable<Type^>^>(selectorFunc);
|
||||
Func<Assembly^, Type^, Pair^>^ resultSelector = gcnew Func<Assembly^, Type^, Pair^>(resultSelectorFunc);
|
||||
IEnumerable<Pair^>^ selectManyResult = Enumerable::SelectMany(assemblies, collectionSelector, resultSelector);
|
||||
|
||||
/* Where: Are concrete PlushieScripts */
|
||||
Func<Pair^, bool>^ predicate = gcnew Func<Pair^, bool>(predicateFunc);
|
||||
IEnumerable<Pair^>^ whereResult = Enumerable::Where(selectManyResult, predicate);
|
||||
|
||||
/* Select: Select them all */
|
||||
Func<Pair^, Type^>^ selector = gcnew Func<Pair^, Type^>(selectorFunc);
|
||||
scriptTypeList = Enumerable::Select(whereResult, selector);
|
||||
|
||||
// Log
|
||||
std::ostringstream oss;
|
||||
oss << "[ScriptStore] Successfully retrieved references to " << Enumerable::Count(scriptTypeList)
|
||||
<< " Script(s) from currently loaded assemblies.";
|
||||
SHLOG_INFO(oss.str());
|
||||
}
|
||||
|
||||
void ScriptStore::getGenericMethods()
|
||||
{
|
||||
addScriptMethod = ScriptStore::typeid->GetMethod("AddScript");
|
||||
if (addScriptMethod == nullptr)
|
||||
{
|
||||
SHLOG_ERROR("[ScriptStore] Failed to get MethodInfo of \"AddScript<T>()\". Adding of scripts from native code will fail.");
|
||||
}
|
||||
}
|
||||
|
||||
System::Type^ ScriptStore::getScriptType(System::String^ scriptName)
|
||||
{
|
||||
// Remove any whitespaces just in case
|
||||
scriptName = scriptName->Trim();
|
||||
|
||||
// Look for the correct script
|
||||
for each (System::Type^ type in scriptTypeList)
|
||||
{
|
||||
if (type->FullName == scriptName || type->Name == scriptName)
|
||||
{
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool ScriptStore::isEntityActive(Entity entity)
|
||||
{
|
||||
// Check active state
|
||||
return Convert::ToNative(entity).isActive;
|
||||
}
|
||||
} // namespace SHADE
|
|
@ -0,0 +1,305 @@
|
|||
/************************************************************************************//*!
|
||||
\file ScriptStore.hxx
|
||||
\author Tng Kah Wei, kahwei.tng, 390009620
|
||||
\par email: kahwei.tng\@digipen.edu
|
||||
\date Oct 28, 2021
|
||||
\brief Contains the definitions of the GameObject managed class which define an
|
||||
abstraction for working with Entities in managed code.
|
||||
|
||||
Note: This file is written in C++17/CLI.
|
||||
|
||||
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
|
||||
|
||||
// Project Includes
|
||||
#include "Engine/Entity.hxx"
|
||||
#include "Script.hxx"
|
||||
|
||||
namespace SHADE
|
||||
{
|
||||
/// <summary>
|
||||
/// Responsible for managing all scripts attached to Entities as well as executing
|
||||
/// all lifecycle functions of scripts.
|
||||
/// </summary>
|
||||
public ref class ScriptStore abstract sealed
|
||||
{
|
||||
public:
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
/* Scripts Manipulation Functions */
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
/// <summary>
|
||||
/// Adds a Script to a specified Entity.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">
|
||||
/// Type of script to add.
|
||||
/// This needs to be a default constructable PlushieScript.
|
||||
/// </typeparam>
|
||||
/// <param name="entity">The entity to add a script to.</param>
|
||||
/// <returns>Reference to the script added.</returns>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// If the specified Entity is invalid.
|
||||
/// </exception>
|
||||
generic<typename T> where T : ref class, Script
|
||||
static T AddScript(Entity entity);
|
||||
/// <summary>
|
||||
/// Adds a Script to a specified Entity.
|
||||
/// <br/>
|
||||
/// This function is meant for consumption from native code. If you are writing
|
||||
/// in C# or C++/CLI, use AddScript<T>() instead as it is faster.
|
||||
/// </summary>
|
||||
/// <param name="entity">The entity to add a script to.</param>
|
||||
/// <param name="scriptName">The entity to add a script to.</param>
|
||||
/// <returns>
|
||||
/// True if successfully added. False otherwise with the error logged to the
|
||||
/// console.
|
||||
/// </returns>
|
||||
static bool AddScriptViaName(Entity entity, System::String^ scriptName);
|
||||
/// <summary>
|
||||
/// Adds a Script to a specified Entity.
|
||||
/// <br/>
|
||||
/// This function is meant for consumption from native code or for serialisation
|
||||
/// purposes. If you are writing in C# or C++/CLI and not doing serialisation,
|
||||
/// use AddScript<T>() instead as it is faster.
|
||||
/// </summary>
|
||||
/// <param name="entity">The entity to add a script to.</param>
|
||||
/// <param name="scriptName">The entity to add a script to.</param>
|
||||
/// <param name="createdScript">
|
||||
/// Out parameter handle to the Script that was created.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// True if successfully added. False otherwise with the error logged to the
|
||||
/// console.
|
||||
/// </returns>
|
||||
static bool AddScriptViaNameWithRef(Entity entity, System::String^ scriptName, [Out] Script^% createdScript);
|
||||
/// <summary>
|
||||
/// Retrieves the first Script from the specified Entity that matches the
|
||||
/// specified type.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">
|
||||
/// Type of script to get.
|
||||
/// This needs to be a default constructable Script.
|
||||
/// </typeparam>
|
||||
/// <param name="entity">
|
||||
/// The entity which the script to retrieve is attached.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Reference to the script. This can be null if no script of the specified
|
||||
/// type is attached.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// If the specified Entity is invalid.
|
||||
/// </exception>
|
||||
generic<typename T> where T : ref class, Script
|
||||
static T GetScript(Entity entity);
|
||||
/// <summary>
|
||||
/// Retrieves the first Script from the specified Entity's children that matches
|
||||
/// the specified type.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">
|
||||
/// Type of script to get.
|
||||
/// This needs to be a default constructable Script.
|
||||
/// </typeparam>
|
||||
/// <param name="entity">
|
||||
/// The entity which the script to retrieve is attached.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Reference to the script. This can be null if no script of the specified
|
||||
/// type is attached.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// If the specified Entity is invalid.
|
||||
/// </exception>
|
||||
generic<typename T> where T : ref class, Script
|
||||
static T GetScriptInChildren(Entity entity);
|
||||
/// <summary>
|
||||
/// Retrieves a immutable list of scripts from the specified Entity that
|
||||
/// matches the specified type.
|
||||
/// <br/>
|
||||
/// Note that this function allocates. It should be used sparingly.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">
|
||||
/// Type of scripts to get.
|
||||
/// This needs to be a default constructable Script.
|
||||
/// </typeparam>
|
||||
/// <param name="entity">
|
||||
/// The entity which the scripts to retrieve are attached.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Immutable list of references to scripts of the specified type.
|
||||
/// </returns>
|
||||
generic<typename T> where T : ref class, Script
|
||||
static System::Collections::Generic::IEnumerable<T> ^ GetScripts(Entity entity);
|
||||
/// <summary>
|
||||
/// Retrieves an immutable list of all scripts attached to a specified Entity.
|
||||
/// </summary>
|
||||
/// <param name="entity">
|
||||
/// The entity which the scripts to retrieve are attached.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// Immutable list of references to scripts attached to the specified Entity.
|
||||
/// This can also be null if there are no scripts at all or an invalid Entity
|
||||
/// was specified.
|
||||
/// </returns>
|
||||
static System::Collections::Generic::IEnumerable<Script^>^ GetAllScripts(Entity entity);
|
||||
/// <summary>
|
||||
/// Removes all Scripts of the specified type from the specified Entity.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">
|
||||
/// Type of script to remove.
|
||||
/// This needs to be a default constructable Script.
|
||||
/// </typeparam>
|
||||
/// <param name="entity">The entity to remove the script from.</param>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// If the specified Entity is invalid.
|
||||
/// </exception>
|
||||
generic<typename T> where T : ref class, Script
|
||||
static void RemoveScript(Entity entity);
|
||||
/// <summary>
|
||||
/// Removes a specific script from the
|
||||
/// </summary>
|
||||
/// <param name="entity">The entity to remove the script from.</param>
|
||||
/// <param name="script">The script to remove.</param>
|
||||
/// <returns>True if successfully removed. False otherwise.</returns>
|
||||
static bool RemoveScript(Entity entity, Script^ script);
|
||||
/// <summary>
|
||||
/// Removes all Scripts attached to the specified Entity. Does not do anything
|
||||
/// if the specified Entity is invalid or does not have any Scripts
|
||||
/// attached.
|
||||
/// </summary>
|
||||
/// <param name="entity">The entity to remove the scripts from.</param>
|
||||
static void RemoveAllScripts(Entity entity);
|
||||
/// <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
|
||||
/// Scripts 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>
|
||||
static void RemoveAllScriptsImmediately(Entity entity, bool callOnDestroy);
|
||||
|
||||
internal:
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
/* Lifecycle Functions */
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
/// <summary>
|
||||
/// Initializes the ScriptStore to allocate and pre-populate reflection data.
|
||||
/// </summary>
|
||||
static void Init();
|
||||
/// <summary>
|
||||
/// Sets up scripts that were marked for initialization. This calls the Awake()
|
||||
/// and Start() for Scripts that have yet to have done so.
|
||||
/// </summary>
|
||||
static void FrameSetUp();
|
||||
/// <summary>
|
||||
/// Cleans up scripts that were marked for deletion. This calls the OnDestroy()
|
||||
/// for these Scripts.
|
||||
/// </summary>
|
||||
static void FrameCleanUp();
|
||||
/// <summary>
|
||||
/// Cleans up data stored in the ScriptStore to free up memory for garbage
|
||||
/// collection.
|
||||
/// </summary>
|
||||
static void Exit();
|
||||
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
/* Script Information Functions */
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
/// <summary>
|
||||
/// Retrieves a immutable list of available scripts that can be added.
|
||||
/// </summary>
|
||||
/// <returns>Immutable list of available scripts that can be added.</returns>
|
||||
static System::Collections::Generic::IEnumerable<System::Type^>^ GetAvailableScriptList();
|
||||
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
/* Script Execution Functions */
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
/// <summary>
|
||||
/// Executes FixedUpdate() for all scripts.
|
||||
/// </summary>
|
||||
static void ExecuteFixedUpdate();
|
||||
/// <summary>
|
||||
/// Executes Update() for all scripts.
|
||||
/// </summary>
|
||||
static void ExecuteUpdate();
|
||||
/// <summary>
|
||||
/// Executes LateUpdate() for all scripts.
|
||||
/// </summary>
|
||||
static void ExecuteLateUpdate();
|
||||
/// <summary>
|
||||
/// Executes OnTrigger functions for all scripts.
|
||||
/// </summary>
|
||||
static void ExecuteOnTrigger();
|
||||
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
/* Serialisation Functions */
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
/// <summary>
|
||||
/// Generates a JSON string that represents the set of Scripts attached
|
||||
/// to the specified Entity.
|
||||
/// <br/> <br/>
|
||||
/// This function should only be called from native unmanaged code.
|
||||
/// </summary>
|
||||
/// <param name="entity">The Entity to Serialise.</param>
|
||||
/// <param name="buffer">
|
||||
/// StringBuilder handle that maps to a native char array that will contain the
|
||||
/// serialised string.
|
||||
/// </param>
|
||||
/// <param name="bufferSize">
|
||||
/// The size of the char array.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// True if serialisation is successful. False if the buffer is too small for
|
||||
/// the serialised output.
|
||||
/// </returns>
|
||||
static bool SerialiseScripts(Entity entity, System::Text::StringBuilder^ buffer, int bufferSize);
|
||||
/// <summary>
|
||||
/// Processes a JSON string that represents a single Script and attaches
|
||||
/// it onto the specified Entity.
|
||||
/// <br/> <br/>
|
||||
/// This function should only be called from native unmanaged code.
|
||||
/// </summary>
|
||||
/// <param name="entity">
|
||||
/// The Entity to attach the deserialised Scripts to.
|
||||
/// </param>
|
||||
/// <param name="json">
|
||||
/// JSON string that describes the Script to serialise.
|
||||
/// </param>
|
||||
/// <returns></returns>
|
||||
static bool DeserialiseScript(Entity entity, System::String^ json);
|
||||
|
||||
private:
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
/* Type Definition */
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
using ScriptList = System::Collections::Generic::List<Script^>;
|
||||
using ScriptDictionary = System::Collections::Generic::Dictionary<Entity, ScriptList^>;
|
||||
using ScriptQueue = System::Collections::Generic::Queue<Script^>;
|
||||
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
/* Static Data Members */
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
static ScriptDictionary scripts;
|
||||
static ScriptList awakeList;
|
||||
static ScriptList startList;
|
||||
static ScriptList inactiveStartList;
|
||||
static ScriptQueue disposalQueue;
|
||||
static System::Collections::Generic::IEnumerable<System::Type^>^ scriptTypeList;
|
||||
static System::Reflection::MethodInfo^ addScriptMethod;
|
||||
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
/* Helper Functions */
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
static void removeScript(Script^ script);
|
||||
static void refreshScriptTypeList();
|
||||
static void getGenericMethods();
|
||||
static System::Type^ getScriptType(System::String^ scriptName);
|
||||
static bool isEntityActive(Entity entity);
|
||||
};
|
||||
} // namespace PlushieAPI
|
Loading…
Reference in New Issue