Added ScriptStore

This commit is contained in:
Kah Wei 2022-09-13 00:18:33 +08:00
parent 216a9ac7cb
commit 5a6ff60fe1
4 changed files with 989 additions and 0 deletions

View File

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

View File

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

View File

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

View File

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