Add scripting API events in Visual Script

This commit is contained in:
Wojtek Figat
2021-01-22 10:55:13 +01:00
parent 4042d466d3
commit cbbfb16628
11 changed files with 708 additions and 38 deletions

View File

@@ -6,6 +6,7 @@
#include "Engine/Content/Factories/BinaryAssetFactory.h"
#include "Engine/Scripting/MException.h"
#include "Engine/Scripting/Scripting.h"
#include "Engine/Scripting/Events.h"
#include "Engine/Scripting/ManagedCLR/MClass.h"
#include "Engine/Scripting/ManagedCLR/MMethod.h"
#include "Engine/Scripting/ManagedCLR/MField.h"
@@ -347,7 +348,7 @@ void VisualScriptExecutor::ProcessGroupParameters(Box* box, Node* node, Value& v
const auto instanceParams = stack.Stack->Script->_instances.Find(stack.Stack->Instance->GetID());
if (param && instanceParams)
{
value = instanceParams->Value[paramIndex];
value = instanceParams->Value.Params[paramIndex];
}
else
{
@@ -371,7 +372,7 @@ void VisualScriptExecutor::ProcessGroupParameters(Box* box, Node* node, Value& v
const auto instanceParams = stack.Stack->Script->_instances.Find(stack.Stack->Instance->GetID());
if (param && instanceParams)
{
instanceParams->Value[paramIndex] = tryGetValue(node->GetBox(1), 1, Value::Zero);
instanceParams->Value.Params[paramIndex] = tryGetValue(node->GetBox(1), 1, Value::Zero);
}
else
{
@@ -1004,6 +1005,125 @@ void VisualScriptExecutor::ProcessGroupFunction(Box* boxBase, Node* node, Value&
if (returnedImpulse && returnedImpulse->HasConnection())
eatBox(node, returnedImpulse->FirstConnection());
break;
}
// Bind/Unbind
case 9:
case 10:
{
const bool bind = node->TypeID == 9;
auto& stack = ThreadStacks.Get();
if (!stack.Stack->Instance)
{
// TODO: add support for binding to events in static Visual Script
LOG(Error, "Cannot bind to event in static Visual Script.");
PrintStack(LogType::Error);
break;
}
const auto object = stack.Stack->Instance;
// Find method to bind
VisualScriptGraphNode* methodNode = nullptr;
const auto graph = stack.Stack && stack.Stack->Script ? &stack.Stack->Script->Graph : nullptr;
if (graph)
methodNode = graph->GetNode((uint32)node->Values[2]);
if (!methodNode)
{
LOG(Error, "Missing function handler to bind to the event.");
PrintStack(LogType::Error);
break;
}
VisualScript::Method* method = nullptr;
for (auto& m : stack.Stack->Script->_methods)
{
if (m.Node == methodNode)
{
method = &m;
break;
}
}
if (!method)
{
LOG(Error, "Missing method to bind to the event.");
PrintStack(LogType::Error);
break;
}
// Find event
const StringView eventTypeName(node->Values[0]);
const StringView eventName(node->Values[1]);
const StringAsANSI<100> eventTypeNameAnsi(eventTypeName.Get(), eventTypeName.Length());
const ScriptingTypeHandle eventType = Scripting::FindScriptingType(StringAnsiView(eventTypeNameAnsi.Get(), eventTypeName.Length()));
// Find event binding callback
auto eventBinder = ScriptingEvents::EventsTable.TryGet(Pair<ScriptingTypeHandle, StringView>(eventType, eventName));
if (!eventBinder)
{
LOG(Error, "Cannot bind to missing event {0} from type {1}.", eventName, eventTypeName);
PrintStack(LogType::Error);
break;
}
// Evaluate object instance
const auto box = node->GetBox(1);
Variant instance;
if (box->HasConnection())
instance = eatBox(node, box->FirstConnection());
else
instance.SetObject(object);
if (!instance.AsObject)
{
LOG(Error, "Cannot bind event to null object.");
PrintStack(LogType::Error);
break;
}
// TODO: check if instance is of event type (including inheritance)
// Add Visual Script method to the event bindings table
const auto& type = object->GetType();
Guid id;
if (Guid::Parse(type.Fullname, id))
break;
if (const auto visualScript = (VisualScript*)Content::GetAsset(id))
{
if (auto i = visualScript->GetScriptInstance(object))
{
VisualScript::EventBinding* eventBinding = nullptr;
for (auto& b : i->EventBindings)
{
if (b.Type == eventType && b.Name == eventName)
{
eventBinding = &b;
break;
}
}
if (bind)
{
// Bind to the event
if (!eventBinding)
{
eventBinding = &i->EventBindings.AddOne();
eventBinding->Type = eventType;
eventBinding->Name = eventName;
}
eventBinding->BindedMethods.Add(method);
if (eventBinding->BindedMethods.Count() == 1)
(*eventBinder)(instance.AsObject, object, true);
}
else if (eventBinding)
{
// Unbind from the event
if (eventBinding->BindedMethods.Count() == 1)
(*eventBinder)(instance.AsObject, object, false);
eventBinding->BindedMethods.Remove(method);
}
}
}
// Call graph further
const auto returnedImpulse = &node->Boxes[2];
if (returnedImpulse && returnedImpulse->HasConnection())
eatBox(node, returnedImpulse->FirstConnection());
break;
}
default:
break;
@@ -1304,7 +1424,7 @@ Asset::LoadResult VisualScript::load()
// Update instanced data from previous format to the current graph parameters scheme
for (auto& e : _instances)
{
auto& instanceParams = e.Value;
auto& instanceParams = e.Value.Params;
Array<Variant> valuesCache(MoveTemp(instanceParams));
instanceParams.Resize(count);
for (int32 i = 0; i < count; i++)
@@ -1319,7 +1439,7 @@ Asset::LoadResult VisualScript::load()
// Reset instances values to defaults
for (auto& e : _instances)
{
auto& instanceParams = e.Value;
auto& instanceParams = e.Value.Params;
instanceParams.Resize(count);
for (int32 i = 0; i < count; i++)
instanceParams[i] = Graph.Parameters[i].Value;
@@ -1421,13 +1541,17 @@ void VisualScript::CacheScriptingType()
_scriptingTypeHandle = ScriptingTypeHandle(&binaryModule, typeIndex);
binaryModule.Scripts.Add(this);
#if USE_EDITOR
// When first Visual Script gets loaded register for other modules unload to clear runtime execution cache
// Special initialization when the first Visual Script gets loaded
if (typeIndex == 0)
{
#if USE_EDITOR
// Register for other modules unload to clear runtime execution cache
Scripting::ScriptsReloading.Bind<VisualScriptingBinaryModule, &VisualScriptingBinaryModule::OnScriptsReloading>(&binaryModule);
}
#endif
// Register for scripting events
ScriptingEvents::Event.Bind(VisualScriptingBinaryModule::OnEvent);
}
}
auto& type = _scriptingTypeHandle.Module->Types[_scriptingTypeHandle.TypeIndex];
type.ManagedClass = baseType.GetType().ManagedClass;
@@ -1550,7 +1674,7 @@ ScriptingObject* VisualScriptingBinaryModule::VisualScriptObjectSpawn(const Scri
VisualScript* visualScript = VisualScriptingModule.Scripts[params.Type.TypeIndex];
// Initialize instance data
auto& instanceParams = visualScript->_instances[object->GetID()];
auto& instanceParams = visualScript->_instances[object->GetID()].Params;
instanceParams.Resize(visualScript->Graph.Parameters.Count());
for (int32 i = 0; i < instanceParams.Count(); i++)
instanceParams[i] = visualScript->Graph.Parameters[i].Value;
@@ -1608,6 +1732,56 @@ void VisualScriptingBinaryModule::OnScriptsReloading()
#endif
void VisualScriptingBinaryModule::OnEvent(ScriptingObject* object, Span<Variant>& parameters, const ScriptingTypeHandle& eventType, const StringView& eventName)
{
if (object)
{
// Object event
const auto& type = object->GetType();
Guid id;
if (Guid::Parse(type.Fullname, id))
return;
if (const auto visualScript = (VisualScript*)Content::GetAsset(id))
{
if (auto instance = visualScript->GetScriptInstance(object))
{
for (auto& b : instance->EventBindings)
{
if (b.Type != eventType || b.Name != eventName)
continue;
for (auto& m : b.BindedMethods)
{
VisualScripting::Invoke(m, object, parameters);
}
}
}
}
}
else
{
// Static event
for (auto& asset : Content::GetAssetsRaw())
{
if (const auto visualScript = ScriptingObject::Cast<VisualScript>(asset.Value))
{
for (auto& e : visualScript->_instances)
{
auto instance = &e.Value;
for (auto& b : instance->EventBindings)
{
if (b.Type != eventType || b.Name != eventName)
continue;
for (auto& m : b.BindedMethods)
{
VisualScripting::Invoke(m, object, parameters);
}
}
}
}
}
}
}
const StringAnsi& VisualScriptingBinaryModule::GetName() const
{
return _name;
@@ -1702,7 +1876,7 @@ bool VisualScriptingBinaryModule::GetFieldValue(void* field, const Variant& inst
LOG(Error, "Missing parameters for the object instance.");
return true;
}
result = instanceParams->Value[vsFiled->Index];
result = instanceParams->Value.Params[vsFiled->Index];
return false;
}
@@ -1721,7 +1895,7 @@ bool VisualScriptingBinaryModule::SetFieldValue(void* field, const Variant& inst
LOG(Error, "Missing parameters for the object instance.");
return true;
}
instanceParams->Value[vsFiled->Index] = value;
instanceParams->Value.Params[vsFiled->Index] = value;
return false;
}
@@ -1735,7 +1909,7 @@ void VisualScriptingBinaryModule::SerializeObject(JsonWriter& stream, ScriptingO
const auto instanceParams = asset->_instances.Find(object->GetID());
if (instanceParams)
{
auto& params = instanceParams->Value;
auto& params = instanceParams->Value.Params;
if (otherObj)
{
// Serialize parameters diff
@@ -1746,7 +1920,7 @@ void VisualScriptingBinaryModule::SerializeObject(JsonWriter& stream, ScriptingO
{
auto& param = asset->Graph.Parameters[paramIndex];
auto& value = params[paramIndex];
auto& otherValue = otherParams->Value[paramIndex];
auto& otherValue = otherParams->Value.Params[paramIndex];
if (value != otherValue)
{
param.Identifier.ToString(idName, Guid::FormatType::N);
@@ -1798,7 +1972,7 @@ void VisualScriptingBinaryModule::DeserializeObject(ISerializable::DeserializeSt
if (instanceParams)
{
// Deserialize all parameters
auto& params = instanceParams->Value;
auto& params = instanceParams->Value.Params;
for (auto i = stream.MemberBegin(); i != stream.MemberEnd(); ++i)
{
StringAnsiView idNameAnsi(i->name.GetString(), i->name.GetStringLength());
@@ -1865,6 +2039,11 @@ ScriptingObject* VisualScript::CreateInstance()
return scriptingTypeHandle ? scriptingTypeHandle.GetType().Script.Spawn(ScriptingObjectSpawnParams(Guid::New(), scriptingTypeHandle)) : nullptr;
}
VisualScript::Instance* VisualScript::GetScriptInstance(ScriptingObject* instance) const
{
return instance ? _instances.TryGet(instance->GetID()) : nullptr;
}
Variant VisualScript::GetScriptInstanceParameterValue(const StringView& name, ScriptingObject* instance) const
{
CHECK_RETURN(instance, Variant());
@@ -1874,7 +2053,7 @@ Variant VisualScript::GetScriptInstanceParameterValue(const StringView& name, Sc
{
const auto instanceParams = _instances.Find(instance->GetID());
if (instanceParams)
return instanceParams->Value[paramIndex];
return instanceParams->Value.Params[paramIndex];
LOG(Error, "Failed to access Visual Script parameter {1} for {0}.", instance->ToString(), name);
return Graph.Parameters[paramIndex].Value;
}
@@ -1893,7 +2072,7 @@ void VisualScript::SetScriptInstanceParameterValue(const StringView& name, Scrip
const auto instanceParams = _instances.Find(instance->GetID());
if (instanceParams)
{
instanceParams->Value[paramIndex] = value;
instanceParams->Value.Params[paramIndex] = value;
return;
}
LOG(Error, "Failed to access Visual Script parameter {1} for {0}.", instance->ToString(), name);
@@ -1913,7 +2092,7 @@ void VisualScript::SetScriptInstanceParameterValue(const StringView& name, Scrip
const auto instanceParams = _instances.Find(instance->GetID());
if (instanceParams)
{
instanceParams->Value[paramIndex] = MoveTemp(value);
instanceParams->Value.Params[paramIndex] = MoveTemp(value);
return;
}
}