Static variables in scripts are now reset when leaving play mode #351

Merged
Pycorax merged 7 commits from SP3-6-CSharpStaticReset into main 2023-02-20 11:12:09 +08:00
11 changed files with 177 additions and 24 deletions

View File

@ -0,0 +1,37 @@
using SHADE;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SHADE_Scripting
{
public class StaticTest
{
public static int x;
static StaticTest()
{
x = 5;
Debug.Log("Static Constructor!");
}
}
public class ImplicitStaticTest : Script
{
public static int x = 5;
static ImplicitStaticTest()
{
Debug.Log("Static Constructor!");
}
protected override void awake()
{
Debug.LogWarning($"Before Add: x = {x}");
++x;
Debug.LogWarning($"After Add: x = {x}");
}
}
}

View File

@ -0,0 +1,3 @@
Name: StaticTest
ID: 159057282
Type: 9

View File

@ -387,6 +387,7 @@ namespace SHADE
{
auto eventData = reinterpret_cast<const SHEventSpec<SHEditorStateChangeEvent>*>(eventPtr.get());
csScriptRemoveAllForAllNow(true);
csEngineReloadScripts();
return eventData->handle;
}
@ -569,7 +570,7 @@ namespace SHADE
SHEventManager::SubscribeTo(SH_ENTITY_DESTROYED_EVENT, std::dynamic_pointer_cast<SHEventReceiver>(destroyedEventReceiver));
/* Editor */
// Register for editor state change event
// Register for editor state change events
std::shared_ptr<SHEventReceiverSpec<SHScriptEngine>> destroyedSceneEventReceiver
{
std::make_shared<SHEventReceiverSpec<SHScriptEngine>>(this, &SHScriptEngine::onSceneDestroyed)

View File

@ -233,6 +233,10 @@ namespace SHADE
/// </summary>
/// <param name="path"></param>
void OpenFile(const std::filesystem::path& path);
/// <summary>
/// Resets all static data in the loaded assemblies to their default values.
/// </summary>
static void ResetStaticDataInLoadedAssembly();
private:
/*-----------------------------------------------------------------------------*/

View File

@ -81,7 +81,8 @@ namespace SHADE
// Add the script
Script^ script;
ScriptStore::AddScriptViaNameWithRef(entity, type->Name, script);
registerUndoScriptAddAction(entity, script);
// TODO: Re-enable when undo-redo is fixed
// registerUndoScriptAddAction(entity, script);
break;
}
}
@ -375,7 +376,8 @@ namespace SHADE
{
// Mark script for removal
ScriptStore::RemoveScript(entity, script);
registerUndoScriptRemoveAction(entity, script, scriptIndex);
// TODO: Re-enable when undo-redo is fixed
// registerUndoScriptRemoveAction(entity, script, scriptIndex);
}
SHEditorUI::EndPopup();
}

View File

@ -22,6 +22,7 @@ of DigiPen Institute of Technology is prohibited.
#include "Utility/Debug.hxx"
#include "Utility/Convert.hxx"
#include "Scripts/ScriptStore.hxx"
#include "Serialisation/SerialisationUtilities.hxx"
namespace SHADE
{
@ -267,7 +268,9 @@ namespace SHADE
/*---------------------------------------------------------------------------------*/
ScriptAddCommand::ScriptAddCommand(EntityID id, Script^ script)
: entity { id }
, addedScript { script }
, typeName { script->GetType()->FullName }
, serialisedScript { SerialisationUtilities::Serialise(script) }
, insertedIndex { ScriptStore::GetScriptIndex(script) }
{}
/*---------------------------------------------------------------------------------*/
@ -275,12 +278,20 @@ namespace SHADE
/*---------------------------------------------------------------------------------*/
bool ScriptAddCommand::Execute()
{
return ScriptStore::AddScript(entity, addedScript) != nullptr;
Script^ script = nullptr;
if (ScriptStore::AddScriptViaNameWithRef(entity, typeName, script))
{
SerialisationUtilities::Deserialise(script, serialisedScript);
insertedIndex = ScriptStore::GetScriptIndex(script);
return true;
}
return false;
}
bool ScriptAddCommand::Unexceute()
{
return ScriptStore::RemoveScript(entity, addedScript);
return ScriptStore::RemoveScript(entity, insertedIndex);
}
bool ScriptAddCommand::Merge(ICommand^)
@ -293,8 +304,9 @@ namespace SHADE
/* ScriptRemoveCommand - Constructor */
/*---------------------------------------------------------------------------------*/
ScriptRemoveCommand::ScriptRemoveCommand(EntityID id, Script^ script, int index)
: entity { id }
, removedScript { script }
: entity{ id }
, typeName{ script->GetType()->FullName }
, serialisedScript{ SerialisationUtilities::Serialise(script) }
, originalIndex { index }
{}
@ -303,12 +315,19 @@ namespace SHADE
/*---------------------------------------------------------------------------------*/
bool ScriptRemoveCommand::Execute()
{
return ScriptStore::RemoveScript(entity, removedScript);
return ScriptStore::RemoveScript(entity, originalIndex);
}
bool ScriptRemoveCommand::Unexceute()
{
return ScriptStore::AddScript(entity, removedScript, originalIndex) != nullptr;
Script^ script = nullptr;
if (ScriptStore::AddScriptViaNameWithRef(entity, typeName, script, originalIndex))
{
SerialisationUtilities::Deserialise(script, serialisedScript);
return true;
}
return false;
}
bool ScriptRemoveCommand::Merge(ICommand^)

View File

@ -114,7 +114,9 @@ namespace SHADE
private:
EntityID entity;
Script^ addedScript;
System::String^ typeName;
System::String^ serialisedScript;
int insertedIndex;
};
private ref class ScriptRemoveCommand sealed : public ICommand
@ -128,7 +130,8 @@ namespace SHADE
private:
EntityID entity;
Script^ removedScript;
System::String^ typeName;
System::String^ serialisedScript;
int originalIndex;
};

View File

@ -100,6 +100,11 @@ namespace SHADE
}
bool ScriptStore::AddScriptViaNameWithRef(Entity entity, System::String^ scriptName, Script^% createdScript)
{
return AddScriptViaNameWithRef(entity, scriptName, createdScript, System::Int32::MaxValue);
}
bool ScriptStore::AddScriptViaNameWithRef(Entity entity, System::String^ scriptName, [System::Runtime::InteropServices::Out] Script^% createdScript, int index)
{
// Check if we are set up to get scripts
if (addScriptMethod == nullptr)
@ -120,17 +125,18 @@ namespace SHADE
return false;
}
// Otherwise, add the script
// 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));
// Create the script and add it in
createdScript = safe_cast<Script^>(System::Activator::CreateInstance(scriptType));
AddScript(entity, createdScript, index);
}
catch (System::Exception^ e)
{
std::ostringstream oss;
oss << "[ScriptStore] Failed to add Script named \"" << Convert::ToNative(scriptName)
oss << "[ScriptStore] Failed to add Script named \"" << Convert::ToNative(scriptType->Name)
<< "\" to Entity #" << entity << "! (" << Convert::ToNative(e->GetType()->Name) << ")";
oss << Convert::ToNative(e->ToString());
Debug::LogError(oss.str());
@ -321,6 +327,19 @@ namespace SHADE
}
return nullptr;
}
int ScriptStore::GetScriptIndex(Script^ script)
{
// Check if entity exists in the script storage
if (!scripts.ContainsKey(script->Owner.EntityId))
{
Debug::LogError("[ScriptStore] Attempted to query a Script that does not belong to the ScriptStore.");
return -1;
}
return scripts[script->Owner.EntityId]->IndexOf(script);
}
generic<typename T>
void ScriptStore::RemoveScript(Entity entity)
{
@ -376,6 +395,35 @@ namespace SHADE
removeScript(script);
return true;
}
bool ScriptStore::RemoveScript(Entity entity, int index)
{
// Check if entity exists
if (!EntityUtils::IsValid(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 index is out of bounds
if (index < 0 || index >= scripts[entity]->Count)
{
Debug::LogError("[ScriptStore] Attempted to remove a Script from an out of range index!");
return false;
}
// Script found, queue it for deletion
removeScript((*scripts[entity])[index]);
return true;
}
void ScriptStore::RemoveAllScripts(Entity entity)
{
SAFE_NATIVE_CALL_BEGIN

View File

@ -82,9 +82,9 @@ namespace SHADE
/// <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.
/// This function is meant for deserialisation 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>
@ -96,6 +96,7 @@ namespace SHADE
/// console.
/// </returns>
static bool AddScriptViaNameWithRef(Entity entity, System::String^ scriptName, [System::Runtime::InteropServices::Out] Script^% createdScript);
static bool AddScriptViaNameWithRef(Entity entity, System::String^ scriptName, [System::Runtime::InteropServices::Out] Script^% createdScript, int index);
/// <summary>
/// Retrieves the first Script from the specified Entity that matches the
/// specified type.
@ -190,6 +191,12 @@ namespace SHADE
/// </returns>
static System::Collections::Generic::IEnumerable<Script^>^ GetAllScripts(Entity entity);
/// <summary>
/// Retrieves the index of a Script within the list of it's Entity's script list.
/// </summary>
/// <param name="script">Script to get the index of.</param>
/// <returns>Script index if valid. Otherwise -1.</returns>
static int GetScriptIndex(Script^ script);
/// <summary>
/// Removes all Scripts of the specified type from the specified Entity.
/// </summary>
/// <typeparam name="T">
@ -210,6 +217,13 @@ namespace SHADE
/// <returns>True if successfully removed. False otherwise.</returns>
static bool RemoveScript(Entity entity, Script^ script);
/// <summary>
/// Removes a script at a specified index from the specified entity.
/// </summary>
/// <param name="entity">The entity to remove the script from.</param>
/// <param name="index">Index of the script to remove.</param>
/// <returns>True if successfully removed. False otherwise.</returns>
static bool RemoveScript(Entity entity, int index);
/// <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.

View File

@ -22,6 +22,7 @@ of DigiPen Institute of Technology is prohibited.
#include "Assets/MaterialAsset.hxx"
#include "Assets/MeshAsset.hxx"
#include "Scripts/Script.hxx"
#include "Scripts/ScriptStore.hxx"
/*-------------------------------------------------------------------------------------*/
/* File-Level Constants */
@ -79,6 +80,19 @@ namespace SHADE
scriptListNode.push_back(scriptNode);
}
System::String^ SerialisationUtilities::Serialise(Script^ script)
{
YAML::Node node;
node.SetStyle(YAML::EmitterStyle::Block);
Serialise(script, node);
YAML::Emitter emitter;
emitter << YAML::BeginMap;
emitter << node;
emitter << YAML::EndMap;
return Convert::ToCLI(emitter.c_str());
}
void SerialisationUtilities::Deserialise(Object^ object, YAML::Node& yamlNode)
{
using namespace System::Reflection;
@ -135,6 +149,12 @@ namespace SHADE
}
}
}
void SerialisationUtilities::Deserialise(Script^ script, System::String^ yamlString)
{
Deserialise(script, YAML::Load(Convert::ToNative(yamlString)));
}
/*---------------------------------------------------------------------------------*/
/* Serialization Helper Functions */
/*---------------------------------------------------------------------------------*/

View File

@ -39,6 +39,7 @@ namespace SHADE
/// </summary>
/// <param name="object">The object to serialise.</param>
static void Serialise(System::Object^ object, YAML::Node& yamlNode);
static System::String^ Serialise(Script^ script);
/// <summary>
/// Deserialises a YAML node that contains a map of Scripts and copies the
/// deserialised data into the specified object if there are matching fields.
@ -48,6 +49,7 @@ namespace SHADE
/// </param>
/// <param name="object">The object to copy deserialised data into.</param>
static void Deserialise(System::Object^ object, YAML::Node& yamlNode);
static void Deserialise(Script^ script, System::String^ yamlString);
private:
/*-----------------------------------------------------------------------------*/