孟宇 3 달 전
부모
커밋
aac1ac7e00
21개의 변경된 파일2626개의 추가작업 그리고 67개의 파일을 삭제
  1. 21 1
      Ability/Plugins/EzAbility/Source/EzAbility/EzAbility.Build.cs
  2. 712 0
      Ability/Plugins/EzAbility/Source/EzAbility/Private/Debugger/EzAbilityTrace.cpp
  3. 343 9
      Ability/Plugins/EzAbility/Source/EzAbility/Private/EzAbilityContext.cpp
  4. 16 0
      Ability/Plugins/EzAbility/Source/EzAbility/Private/EzAbilityEvents.cpp
  5. 8 0
      Ability/Plugins/EzAbility/Source/EzAbility/Private/EzAbilityExecutionTypes.cpp
  6. 48 0
      Ability/Plugins/EzAbility/Source/EzAbility/Private/EzAbilityIndexTypes.cpp
  7. 84 0
      Ability/Plugins/EzAbility/Source/EzAbility/Private/EzAbilityInstanceData.cpp
  8. 7 4
      Ability/Plugins/EzAbility/Source/EzAbility/Private/EzAbilityTypes.cpp
  9. 41 1
      Ability/Plugins/EzAbility/Source/EzAbility/Private/EzAbility_Replicate.cpp
  10. 101 0
      Ability/Plugins/EzAbility/Source/EzAbility/Public/Debugger/EzAbilityTrace.h
  11. 4 0
      Ability/Plugins/EzAbility/Source/EzAbility/Public/Evaluator/EzAbilityEvaluator.h
  12. 27 3
      Ability/Plugins/EzAbility/Source/EzAbility/Public/EzAbility.h
  13. 60 3
      Ability/Plugins/EzAbility/Source/EzAbility/Public/EzAbilityContext.h
  14. 59 0
      Ability/Plugins/EzAbility/Source/EzAbility/Public/EzAbilityEvents.h
  15. 504 0
      Ability/Plugins/EzAbility/Source/EzAbility/Public/EzAbilityExecutionTypes.h
  16. 104 0
      Ability/Plugins/EzAbility/Source/EzAbility/Public/EzAbilityIndexTypes.h
  17. 50 20
      Ability/Plugins/EzAbility/Source/EzAbility/Public/EzAbilityInstanceData.h
  18. 3 0
      Ability/Plugins/EzAbility/Source/EzAbility/Public/EzAbilityLog.h
  19. 15 0
      Ability/Plugins/EzAbility/Source/EzAbility/Public/EzAbilityNodeBase.h
  20. 413 24
      Ability/Plugins/EzAbility/Source/EzAbility/Public/EzAbilityTypes.h
  21. 6 2
      Ability/Plugins/EzAbility/Source/EzAbility/Public/Task/EzAbilityTask.h

+ 21 - 1
Ability/Plugins/EzAbility/Source/EzAbility/EzAbility.Build.cs

@@ -27,7 +27,9 @@ public class EzAbility : ModuleRules
 			{
 				"Core",
 				"StructUtils",
-				"DeveloperSettings", "NetCore",
+				"DeveloperSettings",
+				"NetCore",
+				"GameplayTags"
 			}
 		);
 			
@@ -49,5 +51,23 @@ public class EzAbility : ModuleRules
 					// ... add any modules that your module loads dynamically here ...
 			}
 		);
+		
+		if (Target.Platform == UnrealTargetPlatform.Win64 && 
+		    (Target.Configuration != UnrealTargetConfiguration.Shipping || Target.bBuildEditor))
+		{
+			PublicDefinitions.Add("WITH_EZABILITY_DEBUGGER=1");
+			PublicDependencyModuleNames.AddRange(
+				new string[]
+				{
+					"TraceLog",
+					"TraceServices",
+					"TraceAnalysis"
+				}
+			);
+		}
+		else
+		{
+			PublicDefinitions.Add("WITH_EZABILITY_DEBUGGER=0");
+		}
 	}
 }

+ 712 - 0
Ability/Plugins/EzAbility/Source/EzAbility/Private/Debugger/EzAbilityTrace.cpp

@@ -0,0 +1,712 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+
+#include "Debugger/EzAbilityTrace.h"
+
+#include "EzAbility.h"
+#include "EzAbilityExecutionTypes.h"
+#include "EzAbilityIndexTypes.h"
+#include "Exporters/Exporter.h"
+#include "Serialization/BufferArchive.h"
+#include "ObjectTrace.h"
+
+#if WITH_EDITOR
+#include "Editor.h"
+#endif // WITH_EDITOR
+
+UE_TRACE_CHANNEL_DEFINE(EzAbilityDebugChannel)
+
+UE_TRACE_EVENT_BEGIN(EzAbilityDebugger, WorldTimestampEvent)
+	UE_TRACE_EVENT_FIELD(double, WorldTime)
+UE_TRACE_EVENT_END()
+
+UE_TRACE_EVENT_BEGIN(EzAbilityDebugger, AssetDebugIdEvent)
+	UE_TRACE_EVENT_FIELD(uint64, Cycle)
+	UE_TRACE_EVENT_FIELD(UE::Trace::WideString, AbilityName)
+	UE_TRACE_EVENT_FIELD(UE::Trace::WideString, AbilityPath)
+	UE_TRACE_EVENT_FIELD(uint32, CompiledDataHash)
+	UE_TRACE_EVENT_FIELD(uint16, AssetDebugId)
+UE_TRACE_EVENT_END()
+
+UE_TRACE_EVENT_BEGIN(EzAbilityDebugger, InstanceEvent)
+	UE_TRACE_EVENT_FIELD(uint64, Cycle)
+	UE_TRACE_EVENT_FIELD(uint32, InstanceId)
+	UE_TRACE_EVENT_FIELD(uint32, InstanceSerial)
+	UE_TRACE_EVENT_FIELD(UE::Trace::WideString, InstanceName)
+	UE_TRACE_EVENT_FIELD(std::underlying_type_t<EEzAbilityTraceEventType>, EventType)
+	UE_TRACE_EVENT_FIELD(uint16, AssetDebugId)
+UE_TRACE_EVENT_END()
+
+UE_TRACE_EVENT_BEGIN(EzAbilityDebugger, InstanceFrameEvent)
+	UE_TRACE_EVENT_FIELD(uint64, Cycle)
+	UE_TRACE_EVENT_FIELD(uint32, InstanceId)
+	UE_TRACE_EVENT_FIELD(uint32, InstanceSerial)
+	UE_TRACE_EVENT_FIELD(uint16, AssetDebugId)
+UE_TRACE_EVENT_END()
+
+UE_TRACE_EVENT_BEGIN(EzAbilityDebugger, PhaseEvent)
+	UE_TRACE_EVENT_FIELD(uint64, Cycle)
+	UE_TRACE_EVENT_FIELD(uint32, InstanceId)
+	UE_TRACE_EVENT_FIELD(uint32, InstanceSerial)
+	UE_TRACE_EVENT_FIELD(std::underlying_type_t<EEzAbilityUpdatePhase>, Phase)
+	UE_TRACE_EVENT_FIELD(uint16, StateIndex)
+	UE_TRACE_EVENT_FIELD(std::underlying_type_t<EEzAbilityTraceEventType>, EventType)
+UE_TRACE_EVENT_END()
+
+UE_TRACE_EVENT_BEGIN(EzAbilityDebugger, LogEvent)
+	UE_TRACE_EVENT_FIELD(uint64, Cycle)
+	UE_TRACE_EVENT_FIELD(uint32, InstanceId)
+	UE_TRACE_EVENT_FIELD(uint32, InstanceSerial)
+	UE_TRACE_EVENT_FIELD(UE::Trace::WideString, Message)
+UE_TRACE_EVENT_END()
+
+UE_TRACE_EVENT_BEGIN(EzAbilityDebugger, StateEvent)
+	UE_TRACE_EVENT_FIELD(uint64, Cycle)
+	UE_TRACE_EVENT_FIELD(uint32, InstanceId)
+	UE_TRACE_EVENT_FIELD(uint32, InstanceSerial)
+	UE_TRACE_EVENT_FIELD(uint16, StateIndex)
+	UE_TRACE_EVENT_FIELD(std::underlying_type_t<EEzAbilityTraceEventType>, EventType)
+UE_TRACE_EVENT_END()
+
+UE_TRACE_EVENT_BEGIN(EzAbilityDebugger, TaskEvent)
+	UE_TRACE_EVENT_FIELD(uint64, Cycle)
+	UE_TRACE_EVENT_FIELD(uint32, InstanceId)
+	UE_TRACE_EVENT_FIELD(uint32, InstanceSerial)
+	UE_TRACE_EVENT_FIELD(uint16, NodeIndex)
+	UE_TRACE_EVENT_FIELD(uint8[], DataView)
+	UE_TRACE_EVENT_FIELD(std::underlying_type_t<EEzAbilityTraceEventType>, EventType)
+	UE_TRACE_EVENT_FIELD(uint8, Status)
+UE_TRACE_EVENT_END()
+
+UE_TRACE_EVENT_BEGIN(EzAbilityDebugger, EvaluatorEvent)
+	UE_TRACE_EVENT_FIELD(uint64, Cycle)
+	UE_TRACE_EVENT_FIELD(uint32, InstanceId)
+	UE_TRACE_EVENT_FIELD(uint32, InstanceSerial)
+	UE_TRACE_EVENT_FIELD(uint16, NodeIndex)
+	UE_TRACE_EVENT_FIELD(uint8[], DataView)
+	UE_TRACE_EVENT_FIELD(std::underlying_type_t<EEzAbilityTraceEventType>, EventType)
+UE_TRACE_EVENT_END()
+
+UE_TRACE_EVENT_BEGIN(EzAbilityDebugger, TransitionEvent)
+	UE_TRACE_EVENT_FIELD(uint64, Cycle)
+	UE_TRACE_EVENT_FIELD(uint32, InstanceId)
+	UE_TRACE_EVENT_FIELD(uint32, InstanceSerial)
+	UE_TRACE_EVENT_FIELD(uint8, SourceType)
+	UE_TRACE_EVENT_FIELD(uint16, TransitionIndex)
+	UE_TRACE_EVENT_FIELD(uint16, TargetStateIndex)
+	UE_TRACE_EVENT_FIELD(uint8, Priority)
+	UE_TRACE_EVENT_FIELD(std::underlying_type_t<EEzAbilityTraceEventType>, EventType)
+UE_TRACE_EVENT_END()
+
+UE_TRACE_EVENT_BEGIN(EzAbilityDebugger, ConditionEvent)
+	UE_TRACE_EVENT_FIELD(uint64, Cycle)
+	UE_TRACE_EVENT_FIELD(uint32, InstanceId)
+	UE_TRACE_EVENT_FIELD(uint32, InstanceSerial)
+	UE_TRACE_EVENT_FIELD(uint16, NodeIndex)
+	UE_TRACE_EVENT_FIELD(uint8[], DataView)
+	UE_TRACE_EVENT_FIELD(std::underlying_type_t<EEzAbilityTraceEventType>, EventType)
+UE_TRACE_EVENT_END()
+
+UE_TRACE_EVENT_BEGIN(EzAbilityDebugger, ActiveStatesEvent)
+	UE_TRACE_EVENT_FIELD(uint64, Cycle)
+	UE_TRACE_EVENT_FIELD(uint32, InstanceId)
+	UE_TRACE_EVENT_FIELD(uint32, InstanceSerial)
+	UE_TRACE_EVENT_FIELD(uint16[], ActiveStates)
+	UE_TRACE_EVENT_FIELD(uint16[], AssetDebugIds)
+UE_TRACE_EVENT_END()
+
+namespace UE::EzAbilityTrace
+{
+	FDelegateHandle GOnWorldTickStartDelegateHandle;
+	FDelegateHandle GTracingStateChangedDelegateHandle;
+
+	/** Struct to keep track if a given phase was traced or not. */
+	struct FPhaseTraceStatusPair
+	{
+		explicit FPhaseTraceStatusPair(const EEzAbilityUpdatePhase Phase, const FEzAbilityStateHandle StateHandle)
+		: Phase(Phase)
+		, StateHandle(StateHandle)
+		{
+		}
+
+		EEzAbilityUpdatePhase Phase = EEzAbilityUpdatePhase::Unset;
+		FEzAbilityStateHandle StateHandle = FEzAbilityStateHandle::Invalid; 
+		bool bTraced = false;
+	};
+
+	/** Struct to keep track of the list of stacked phases for a given ability instance. */
+	struct FPhaseStack
+	{
+		FEzAbilityInstanceDebugId InstanceId;
+		TArray<FPhaseTraceStatusPair> Stack;
+	};
+
+	/**
+	 * Struct to hold data for asset debug id events until we are ready to trace the events (i.e. traces are active and channel is enabled).
+	 */
+	struct FAssetDebugIdEventBufferedData
+	{
+		FAssetDebugIdEventBufferedData() = default;
+		explicit FAssetDebugIdEventBufferedData(const UEzAbility* Ability, const FEzAbilityIndex16 AssetDebugId) : WeakAbility(Ability), AssetDebugId(AssetDebugId)
+		{
+		}
+
+		void Trace() const
+		{
+			if (ensureMsgf(UE_TRACE_CHANNELEXPR_IS_ENABLED(EzAbilityDebugChannel), TEXT("Tracing a buffered data is expected only if channel is enabled.")))
+			{
+				if (const UEzAbility* Ability = WeakAbility.Get())
+				{
+					OutputAssetDebugIdEvent(Ability, AssetDebugId);
+				}
+			}
+		}
+
+		TWeakObjectPtr<const UEzAbility> WeakAbility;
+		FEzAbilityIndex16 AssetDebugId;
+	};
+
+	/**
+	 * Struct to hold data for active states events until we are ready to trace the events (i.e. traces are active and channel is enabled).
+	 */
+	struct FInstanceEventBufferedData
+	{
+		struct FActiveStates
+		{
+			FActiveStates() = default;
+			explicit FActiveStates(const TConstArrayView<FEzAbilityExecutionFrame> ActiveFrames)
+			{
+				for (const FEzAbilityExecutionFrame& Frame : ActiveFrames)
+				{
+					const FEzAbilityIndex16 AssetDebugId = FindOrAddDebugIdForAsset(Frame.Ability.Get());
+
+					const int32 RequiredSize = StatesIndices.Num() + Frame.ActiveStates.Num();
+					StatesIndices.Reserve(RequiredSize);
+					AssetDebugIds.Reserve(RequiredSize);
+
+					for (const FEzAbilityStateHandle StateHandle : Frame.ActiveStates)
+					{
+						StatesIndices.Add(StateHandle.Index);
+						AssetDebugIds.Add(AssetDebugId.Get());
+					}
+				}
+			}
+
+			bool IsValid() const { return StatesIndices.Num() > 0 && StatesIndices.Num() == AssetDebugIds.Num(); }
+			void Output(const FEzAbilityInstanceDebugId InInstanceId) const
+			{
+				UE_TRACE_LOG(EzAbilityDebugger, ActiveStatesEvent, EzAbilityDebugChannel)
+					<< ActiveStatesEvent.Cycle(FPlatformTime::Cycles64())
+					<< ActiveStatesEvent.InstanceId(InInstanceId.Id)
+					<< ActiveStatesEvent.InstanceSerial(InInstanceId.SerialNumber)
+					<< ActiveStatesEvent.ActiveStates(StatesIndices.GetData(), StatesIndices.Num())
+					<< ActiveStatesEvent.AssetDebugIds(AssetDebugIds.GetData(), AssetDebugIds.Num());
+			}
+
+			TArray<uint16> StatesIndices;
+			TArray<uint16> AssetDebugIds;
+		};
+
+		FInstanceEventBufferedData() = default;
+		explicit FInstanceEventBufferedData(
+			const double RecordingWorldTime,
+			const UEzAbility* Ability,
+			const FEzAbilityInstanceDebugId InstanceId,
+			const FString& InstanceName)
+			: InstanceName(InstanceName)
+			, WeakAbility(Ability)
+			, InstanceId(InstanceId)
+			, LifetimeRecordingWorldTime(RecordingWorldTime)
+		{
+		}
+
+		void Trace() const
+		{
+			if (ensureMsgf(UE_TRACE_CHANNELEXPR_IS_ENABLED(EzAbilityDebugChannel), TEXT("Tracing a buffered data is expected only if channel is enabled.")))
+			{
+				if (const UEzAbility* Ability = WeakAbility.Get())
+				{
+					// Force a world time update since we are tracing an event from the past
+					UE_TRACE_LOG(EzAbilityDebugger, WorldTimestampEvent, EzAbilityDebugChannel)
+						<< WorldTimestampEvent.WorldTime(LifetimeRecordingWorldTime);
+
+					OutputInstanceLifetimeEvent(InstanceId, Ability, *InstanceName, EEzAbilityTraceEventType::Push);
+
+					if (ActiveStates.IsValid())
+					{
+						// Force a world time update since we are tracing an event from the past
+						UE_TRACE_LOG(EzAbilityDebugger, WorldTimestampEvent, EzAbilityDebugChannel)
+							<< WorldTimestampEvent.WorldTime(ActiveStatesRecordingWorldTime);
+
+						ActiveStates.Output(InstanceId);
+					}
+				}
+			}
+		}
+
+		FActiveStates ActiveStates;
+		FString InstanceName;
+		TWeakObjectPtr<const UEzAbility> WeakAbility;
+		FEzAbilityInstanceDebugId InstanceId;
+		double LifetimeRecordingWorldTime = 0;
+		double ActiveStatesRecordingWorldTime = 0;
+	};
+	
+	/** Struct to keep track of the buffered event data and flush them. */
+	struct FBufferedDataList
+	{
+		double RecordingWorldTime = -1;
+		double TracedRecordingWorldTime = -1;
+		
+		/**
+		 * Stacks to keep track of all received phase events so other events will control when and if a given phase trace will be sent.
+		 * This is per thread since it is possible to update execution contexts on multiple threads.
+		 */
+		TArray<FPhaseStack> PhaseStacks;
+
+		/** List of asset debug ids events that will be output if channel gets enabled. */
+		TArray<FAssetDebugIdEventBufferedData> AssetDebugIdEvents;
+
+		/** List of lifetime events that will be output if channel gets enabled in the Push - Pop lifetime window of an instance. */
+		TMap<FEzAbilityInstanceDebugId, FInstanceEventBufferedData> InstanceLifetimeEvents;
+
+		/** Flag use to prevent reentrant calls */
+		bool bFlushing = false;
+
+		uint16 NextAssetDebugId = 1;
+		int32 CurrentVersion = 0;
+		int32 FlushedVersion = -1;
+
+		void Flush(const FEzAbilityInstanceDebugId InstanceId)
+		{
+			if (bFlushing)
+			{
+				return;
+			}
+
+			TGuardValue<bool> GuardReentry(bFlushing, true);
+
+			if (FlushedVersion != CurrentVersion)
+			{
+				FlushedVersion = CurrentVersion;
+
+				// Trace asset events first since they are required for instance lifetime event types.
+				// Events are preserved in case the trace session is stopped and then a new one gets started
+				// in the same game session. In which case we need to output the ids to that new trace.
+				for (const FAssetDebugIdEventBufferedData& AssetDebugIdEventData : AssetDebugIdEvents)
+				{
+					AssetDebugIdEventData.Trace();
+				}
+
+				// Then trace instance lifetime events since they are required for other event types.
+				// It is also associated to an older world time.
+				// Events are also preserved for the same reason as AssetDebugIdEvents.
+				for (TPair<FEzAbilityInstanceDebugId, FInstanceEventBufferedData>& Pair : InstanceLifetimeEvents)
+				{
+					Pair.Value.Trace();
+				}
+			}
+
+			TraceWorldTime();
+
+			if (InstanceId.IsValid())
+			{
+				TraceStackedPhases(InstanceId);
+			}
+		}
+
+		/**
+		 * Called by TraceBufferedEvents from the OutputXYZ methods to make sure we have the current world time was sent.
+		 */
+		void TraceWorldTime()
+		{
+			if (TracedRecordingWorldTime != RecordingWorldTime)
+			{
+				TracedRecordingWorldTime = RecordingWorldTime;
+				UE_TRACE_LOG(EzAbilityDebugger, WorldTimestampEvent, EzAbilityDebugChannel)
+					<< WorldTimestampEvent.WorldTime(RecordingWorldTime);
+			}
+		}
+
+		/**
+		 * Called by TraceBufferedEvents from the OutputXYZ methods to flush pending phase events.
+		 * Phases popped before TraceStackedPhases gets called will never produce any trace since
+		 * they will not be required for the analysis.
+		 */
+		void TraceStackedPhases(const FEzAbilityInstanceDebugId InstanceId)
+		{
+			for (FPhaseStack& PhaseStack : PhaseStacks)
+			{
+				if (PhaseStack.InstanceId == InstanceId)
+				{
+					for (FPhaseTraceStatusPair& StackEntry : PhaseStack.Stack)
+					{
+						// Trace push phase event and marked as traced only if not already traced and our channel is enabled.
+						// We need the pop phase event to be sent only in this case to enforce complementary events in case of
+						// late recording (e.g. recording started, or channel enabled, after simulation is running and instances are ticked)
+						if (StackEntry.bTraced == false && UE_TRACE_CHANNELEXPR_IS_ENABLED(EzAbilityDebugChannel))
+						{
+							UE_TRACE_LOG(EzAbilityDebugger, PhaseEvent, EzAbilityDebugChannel)
+								<< PhaseEvent.Cycle(FPlatformTime::Cycles64())
+								<< PhaseEvent.InstanceId(InstanceId.Id)
+								<< PhaseEvent.InstanceSerial(InstanceId.SerialNumber)
+								<< PhaseEvent.Phase(static_cast<std::underlying_type_t<EEzAbilityUpdatePhase>>(StackEntry.Phase))
+								<< PhaseEvent.StateIndex(StackEntry.StateHandle.Index)
+								<< PhaseEvent.EventType(static_cast<std::underlying_type_t<EEzAbilityTraceEventType>>(EEzAbilityTraceEventType::Push));
+
+							StackEntry.bTraced = true;
+						}
+					}
+					break;
+				}
+			}
+		}
+
+		void BumpTraceVersion()
+		{
+			// Bump version so shareable data will be flush in the next trace (e.g. Asset ids, instance lifetime events, etc.)
+			CurrentVersion++;
+
+			// Force world time to trace on the next event
+			RecordingWorldTime = -1;
+		}
+	};
+
+	/**
+	 * Buffered events (e.g. lifetime, active state, scoped phase) in case channel is not active yet or phase are empty and don't need to be traced.
+	 * This is per thread since it is possible to update execution contexts on multiple threads.
+	 * @note The current implementation of the lifetime events doesn't properly support same instance getting ticked in different threads.
+	 */
+	thread_local FBufferedDataList GBufferedEvents;
+
+	void TraceBufferedEvents(const FEzAbilityInstanceDebugId InstanceId)
+	{
+		GBufferedEvents.Flush(InstanceId);
+	}
+
+	void SerializeDataViewToArchive(FBufferArchive& Ar, const FEzAbilityDataView DataView)
+	{
+		constexpr uint32 PortFlags = 
+			PPF_PropertyWindow // limit to properties visible in Editor 
+			| PPF_ExportsNotFullyQualified
+			| PPF_Delimited // property data should be wrapped in quotes
+			| PPF_ExternalEditor // uses authored names instead of internal names and default values are always written out
+			| PPF_SimpleObjectText // object property values should be exported without the package or class information
+			| PPF_ForDiff; // do not emit object path
+
+		if (const UScriptStruct* ScriptStruct = Cast<const UScriptStruct>(DataView.GetStruct()))
+		{
+			TRACE_CPUPROFILER_EVENT_SCOPE(UE::StateTree::ExportStructAsText)
+			FString StructPath = ScriptStruct->GetPathName();
+			FString TextValue;
+
+			ScriptStruct->ExportText(TextValue, DataView.GetMemory(), DataView.GetMemory(), /*OwnerObject*/nullptr, PortFlags | PPF_SeparateDefine, /*ExportRootScope*/nullptr);
+
+			Ar << StructPath;
+			Ar << TextValue;
+		}
+		else if (const UClass* Class = Cast<const UClass>(DataView.GetStruct()))
+		{
+			TRACE_CPUPROFILER_EVENT_SCOPE(UE::StateTree::ExportObjectAsText)
+			FString StructPath = Class->GetPathName();
+			FStringOutputDevice OutputDevice;
+			UObject* Object = DataView.GetMutablePtr<UObject>();
+
+			// Not using on scope FExportObjectInnerContext since it is very costly to build.
+			// Passing a null context will make the export use an already built thread local context.
+			UExporter::ExportToOutputDevice(nullptr, Object, /*Exporter*/nullptr, OutputDevice, TEXT("copy"), 0, PortFlags, false, Object->GetOuter());
+
+			Ar << StructPath;
+			Ar << OutputDevice;
+		}
+	}
+	
+	void RegisterGlobalDelegates()
+	{
+		GOnWorldTickStartDelegateHandle = FWorldDelegates::OnWorldTickStart.AddLambda([&WorldTime=GBufferedEvents.RecordingWorldTime](const UWorld* TickedWorld, ELevelTick TickType, float DeltaTime)
+		{
+#if OBJECT_TRACE_ENABLED
+			WorldTime = FObjectTrace::GetWorldElapsedTime(TickedWorld);
+#endif// OBJECT_TRACE_ENABLED
+		});
+
+		// GTracingStateChangedDelegateHandle = UE::StateTree::Delegates::OnTracingStateChanged.AddLambda([](const bool bTracesEnabled)
+		// {
+		// 	// Bump trace version when disabling traces so next trace will flush buffered events that are still relevant.
+		// 	if (!bTracesEnabled)
+		// 	{
+		// 		GBufferedEvents.BumpTraceVersion();
+		// 	}
+		// });
+	}
+	
+	void UnregisterGlobalDelegates()
+	{
+		FWorldDelegates::OnWorldTickStart.Remove(GOnWorldTickStartDelegateHandle);
+		GOnWorldTickStartDelegateHandle.Reset();
+
+		// UE::StateTree::Delegates::OnTracingStateChanged.Remove(GTracingStateChangedDelegateHandle);
+		// GTracingStateChangedDelegateHandle.Reset();
+	}
+	
+	FEzAbilityIndex16 FindOrAddDebugIdForAsset(const UEzAbility* Ability)
+	{
+		FEzAbilityIndex16 AssetDebugId;
+        const FAssetDebugIdEventBufferedData* ExistingPair = GBufferedEvents.AssetDebugIdEvents.FindByPredicate([Ability](const FAssetDebugIdEventBufferedData& BufferedData)
+        {
+        	return BufferedData.WeakAbility == Ability;
+        });
+        
+		if (ExistingPair == nullptr)
+		{
+			if (ensure(Ability != nullptr))
+			{
+				AssetDebugId = FEzAbilityIndex16(GBufferedEvents.NextAssetDebugId++);
+				GBufferedEvents.AssetDebugIdEvents.Emplace(Ability, AssetDebugId);
+				OutputAssetDebugIdEvent(Ability, AssetDebugId);
+			}
+		}
+		else
+		{
+			AssetDebugId = ExistingPair->AssetDebugId;
+		}
+        
+		return AssetDebugId;
+	}
+	
+	void OutputPhaseScopeEvent(FEzAbilityInstanceDebugId InstanceId, EEzAbilityUpdatePhase Phase, EEzAbilityTraceEventType EventType, FEzAbilityStateHandle StateHandle)
+	{
+		TArray<FPhaseStack>& PhaseStacks = GBufferedEvents.PhaseStacks;
+		int32 ExistingStackIndex = PhaseStacks.IndexOfByPredicate([InstanceId](const FPhaseStack& PhaseStack){ return PhaseStack.InstanceId == InstanceId; });
+
+		if (EventType == EEzAbilityTraceEventType::Push)
+		{
+			if (ExistingStackIndex == INDEX_NONE)
+			{
+				ExistingStackIndex = PhaseStacks.AddDefaulted();
+			}
+			FPhaseStack& PhaseStack = PhaseStacks[ExistingStackIndex];
+			PhaseStack.InstanceId = InstanceId;
+			PhaseStack.Stack.Push(FPhaseTraceStatusPair(Phase, StateHandle));
+		}
+		else if (ensureMsgf(ExistingStackIndex != INDEX_NONE, TEXT("Not expected to pop phases for an instance that never pushed a phase.")))
+		{
+			FPhaseStack& PhaseStack = PhaseStacks[ExistingStackIndex];
+
+			if (ensureMsgf(PhaseStack.Stack.IsEmpty() == false, TEXT("Not expected to pop phases that never got pushed.")) &&
+				ensureMsgf(PhaseStack.InstanceId == InstanceId, TEXT("Not expected to pop phases for an instance that is not the one currently assigned to the stack.")))
+			{
+				const FPhaseTraceStatusPair RemovedPair = PhaseStack.Stack.Pop();
+				ensureMsgf(RemovedPair.Phase == Phase, TEXT("Not expected to pop a phase that is not on the top of the stack."));
+
+				// Clear associated InstanceId when removing last entry from the stack.
+				if (PhaseStack.Stack.IsEmpty())
+				{
+					PhaseStacks.RemoveAt(ExistingStackIndex, /*Count*/1, EAllowShrinking::No);
+				}
+
+				// Phase was previously traced (i.e. other events were traced in that scope so we need to trace the closing (i.e. Pop) event.
+				if (RemovedPair.bTraced)
+				{
+					UE_TRACE_LOG(EzAbilityDebugger, PhaseEvent, EzAbilityDebugChannel)
+					<< PhaseEvent.Cycle(FPlatformTime::Cycles64())
+					<< PhaseEvent.InstanceId(InstanceId.Id)
+					<< PhaseEvent.InstanceSerial(InstanceId.SerialNumber)
+					<< PhaseEvent.Phase(static_cast<std::underlying_type_t<EEzAbilityUpdatePhase>>(Phase))
+					<< PhaseEvent.StateIndex(StateHandle.Index)
+					<< PhaseEvent.EventType(static_cast<std::underlying_type_t<EEzAbilityTraceEventType>>(EEzAbilityTraceEventType::Pop));
+				}
+			}
+		}
+	}
+	
+	void OutputAssetDebugIdEvent(const UEzAbility* Ability, FEzAbilityIndex16 AssetDebugId)
+	{
+		if (UE_TRACE_CHANNELEXPR_IS_ENABLED(EzAbilityDebugChannel))
+		{
+			TraceBufferedEvents(FEzAbilityInstanceDebugId::Invalid);
+
+			check(Ability);
+			const FString AbilityName = Ability->GetName();
+			const FString AbilityPath = Ability->GetPathName();
+
+			UE_TRACE_LOG(EzAbilityDebugger, AssetDebugIdEvent, EzAbilityDebugChannel)
+				<< AssetDebugIdEvent.Cycle(FPlatformTime::Cycles64())
+				<< AssetDebugIdEvent.AbilityName(*AbilityName, AbilityName.Len())
+				<< AssetDebugIdEvent.AbilityPath(*AbilityPath, AbilityPath.Len())
+				<< AssetDebugIdEvent.CompiledDataHash(Ability->LastCompiledEditorDataHash)
+				<< AssetDebugIdEvent.AssetDebugId(AssetDebugId.Get());
+		}
+	}
+	
+	void OutputInstanceLifetimeEvent(FEzAbilityInstanceDebugId InstanceId, const UEzAbility* Ability, const TCHAR* InstanceName, EEzAbilityTraceEventType EventType)
+	{
+		if (UE_TRACE_CHANNELEXPR_IS_ENABLED(EzAbilityDebugChannel))
+		{
+			TraceBufferedEvents(InstanceId);
+
+			const FEzAbilityIndex16 AssetDebugId = FindOrAddDebugIdForAsset(Ability);
+
+			UE_TRACE_LOG(EzAbilityDebugger, InstanceEvent, EzAbilityDebugChannel)
+				<< InstanceEvent.Cycle(FPlatformTime::Cycles64())
+				<< InstanceEvent.InstanceId(InstanceId.Id)
+				<< InstanceEvent.InstanceSerial(InstanceId.SerialNumber)
+				<< InstanceEvent.InstanceName(InstanceName)
+				<< InstanceEvent.EventType(static_cast<std::underlying_type_t<EEzAbilityTraceEventType>>(EventType))
+				<< InstanceEvent.AssetDebugId(AssetDebugId.Get());
+		}
+
+		// Buffer these events regardless of the status of the channel since they will be used
+		// when flushing buffered event when a late recording is started or more than one trace are started
+		// during the same game session (i.e. Start Traces -> Stop Traces -> Start Traces).
+		if (!GBufferedEvents.bFlushing)
+		{
+			if (EventType == EEzAbilityTraceEventType::Push)
+			{
+				GBufferedEvents.InstanceLifetimeEvents.Emplace(InstanceId, FInstanceEventBufferedData(GBufferedEvents.RecordingWorldTime, Ability, InstanceId, InstanceName));
+			}
+			else if (EventType == EEzAbilityTraceEventType::Pop)
+			{
+				GBufferedEvents.InstanceLifetimeEvents.Remove(InstanceId);
+			}
+			else
+			{
+				ensureMsgf(false, TEXT("Unexpected EventType '%s' for instance lifetime event."), *UEnum::GetDisplayValueAsText(EventType).ToString());
+			}
+		}
+	}
+	
+	void OutputInstanceFrameEvent(FEzAbilityInstanceDebugId InstanceId, const FEzAbilityExecutionFrame* Frame)
+	{
+		check(Frame != nullptr);
+
+		if (UE_TRACE_CHANNELEXPR_IS_ENABLED(EzAbilityDebugChannel))
+		{
+			TraceBufferedEvents(InstanceId);
+
+			const FEzAbilityIndex16 AssetDebugId = FindOrAddDebugIdForAsset(Frame->Ability.Get());
+
+			UE_TRACE_LOG(EzAbilityDebugger, InstanceFrameEvent, EzAbilityDebugChannel)
+				<< InstanceFrameEvent.Cycle(FPlatformTime::Cycles64())
+				<< InstanceFrameEvent.InstanceId(InstanceId.Id)
+				<< InstanceFrameEvent.InstanceSerial(InstanceId.SerialNumber)
+				<< InstanceFrameEvent.AssetDebugId(AssetDebugId.Get());
+		}
+		// No need to buffer since frame event are sent each time a FrameScope is used by the execution context
+		// and we don't expect the trace channel to be enabled/disabled during a single execution context update.
+	}
+	
+	void OutputLogEventTrace(FEzAbilityInstanceDebugId InstanceId, const TCHAR* Fmt, ...)
+	{
+		static TCHAR TraceStaticBuffer[8192];
+		GET_TYPED_VARARGS(TCHAR, TraceStaticBuffer, UE_ARRAY_COUNT(TraceStaticBuffer), UE_ARRAY_COUNT(TraceStaticBuffer) - 1, Fmt, Fmt);
+
+		TraceBufferedEvents(InstanceId);
+
+		UE_TRACE_LOG(EzAbilityDebugger, LogEvent, EzAbilityDebugChannel)
+			<< LogEvent.Cycle(FPlatformTime::Cycles64())
+			<< LogEvent.InstanceId(InstanceId.Id)
+			<< LogEvent.InstanceSerial(InstanceId.SerialNumber)
+			<< LogEvent.Message(TraceStaticBuffer);
+	}
+	
+	void OutputStateEventTrace(FEzAbilityInstanceDebugId InstanceId, FEzAbilityStateHandle StateHandle, EEzAbilityTraceEventType EventType)
+	{
+		TraceBufferedEvents(InstanceId);
+
+		UE_TRACE_LOG(EzAbilityDebugger, StateEvent, EzAbilityDebugChannel)
+			<< StateEvent.Cycle(FPlatformTime::Cycles64())
+			<< StateEvent.InstanceId(InstanceId.Id)
+			<< StateEvent.InstanceSerial(InstanceId.SerialNumber)
+			<< StateEvent.StateIndex(StateHandle.Index)
+			<< StateEvent.EventType(static_cast<std::underlying_type_t<EEzAbilityTraceEventType>>(EventType));
+	}
+	
+	void OutputTaskEventTrace(FEzAbilityInstanceDebugId InstanceId, FEzAbilityIndex16 TaskIdx, FEzAbilityDataView DataView, EEzAbilityTraceEventType EventType, EAbilityRunStatus Status)
+	{
+		FBufferArchive Archive;
+		SerializeDataViewToArchive(Archive, DataView);
+
+		TraceBufferedEvents(InstanceId);
+
+		UE_TRACE_LOG(EzAbilityDebugger, TaskEvent, EzAbilityDebugChannel)
+			<< TaskEvent.Cycle(FPlatformTime::Cycles64())
+			<< TaskEvent.InstanceId(InstanceId.Id)
+			<< TaskEvent.InstanceSerial(InstanceId.SerialNumber)
+			<< TaskEvent.NodeIndex(TaskIdx.Get())
+			<< TaskEvent.DataView(Archive.GetData(), Archive.Num())
+			<< TaskEvent.EventType(static_cast<std::underlying_type_t<EEzAbilityTraceEventType>>(EventType))
+			<< TaskEvent.Status(static_cast<std::underlying_type_t<EAbilityRunStatus>>(Status));
+	}
+	
+	void OutputEvaluatorEventTrace(FEzAbilityInstanceDebugId InstanceId, FEzAbilityIndex16 EvaluatorIdx, FEzAbilityDataView DataView, EEzAbilityTraceEventType EventType)
+	{
+		FBufferArchive Archive;
+		SerializeDataViewToArchive(Archive, DataView);
+
+		TraceBufferedEvents(InstanceId);
+
+		UE_TRACE_LOG(EzAbilityDebugger, EvaluatorEvent, EzAbilityDebugChannel)
+			<< EvaluatorEvent.Cycle(FPlatformTime::Cycles64())
+			<< EvaluatorEvent.InstanceId(InstanceId.Id)
+			<< EvaluatorEvent.InstanceSerial(InstanceId.SerialNumber)
+			<< EvaluatorEvent.NodeIndex(EvaluatorIdx.Get())
+			<< EvaluatorEvent.DataView(Archive.GetData(), Archive.Num())
+			<< EvaluatorEvent.EventType(static_cast<std::underlying_type_t<EEzAbilityTraceEventType>>(EventType));
+	}
+	
+	void OutputConditionEventTrace(FEzAbilityInstanceDebugId InstanceId, FEzAbilityIndex16 ConditionIdx, FEzAbilityDataView DataView, EEzAbilityTraceEventType EventType)
+	{
+		FBufferArchive Archive;
+		SerializeDataViewToArchive(Archive, DataView);
+        
+		TraceBufferedEvents(InstanceId);
+        
+		UE_TRACE_LOG(EzAbilityDebugger, ConditionEvent, EzAbilityDebugChannel)
+		<< ConditionEvent.Cycle(FPlatformTime::Cycles64())
+		<< ConditionEvent.InstanceId(InstanceId.Id)
+		<< ConditionEvent.InstanceSerial(InstanceId.SerialNumber)
+		<< ConditionEvent.NodeIndex(ConditionIdx.Get())
+		<< ConditionEvent.DataView(Archive.GetData(), Archive.Num())
+		<< ConditionEvent.EventType(static_cast<std::underlying_type_t<EEzAbilityTraceEventType>>(EventType));
+	}
+	
+	void OutputTransitionEventTrace(FEzAbilityInstanceDebugId InstanceId, FEzAbilityTransitionSource Source, EEzAbilityTraceEventType EventType)
+	{
+		FBufferArchive Archive;
+		Archive << EventType;
+
+		TraceBufferedEvents(InstanceId);
+
+		UE_TRACE_LOG(EzAbilityDebugger, TransitionEvent, EzAbilityDebugChannel)
+		<< TransitionEvent.Cycle(FPlatformTime::Cycles64())
+		<< TransitionEvent.InstanceId(InstanceId.Id)
+		<< TransitionEvent.InstanceSerial(InstanceId.SerialNumber)
+		<< TransitionEvent.SourceType(static_cast<std::underlying_type_t<EEzAbilityTransitionSourceType>>(Source.SourceType))
+		<< TransitionEvent.TransitionIndex(Source.TransitionIndex.Get())
+		<< TransitionEvent.TargetStateIndex(Source.TargetState.Index)
+		<< TransitionEvent.Priority(static_cast<std::underlying_type_t<EEzAbilityTransitionPriority>>(Source.Priority))
+		<< TransitionEvent.EventType(static_cast<std::underlying_type_t<EEzAbilityTraceEventType>>(EventType));
+	}
+	
+	void OutputActiveStatesEventTrace(FEzAbilityInstanceDebugId InstanceId, TConstArrayView<FEzAbilityExecutionFrame> ActiveFrames)
+	{
+		if (UE_TRACE_CHANNELEXPR_IS_ENABLED(EzAbilityDebugChannel))
+		{
+			TraceBufferedEvents(InstanceId);
+
+			const FInstanceEventBufferedData::FActiveStates ActiveStates(ActiveFrames);
+			ActiveStates.Output(InstanceId);
+		}
+		else
+		{
+			// We keep only the most recent active states since this is all we need to know in which state was the instance
+			// when we start receiving the events once the channel is enabled.
+			if (FInstanceEventBufferedData* ExisingBufferedData = GBufferedEvents.InstanceLifetimeEvents.Find(InstanceId))
+			{
+				ExisingBufferedData->ActiveStates= FInstanceEventBufferedData::FActiveStates(ActiveFrames);
+				ExisingBufferedData->ActiveStatesRecordingWorldTime = GBufferedEvents.RecordingWorldTime;
+			}
+		}
+	}
+}

+ 343 - 9
Ability/Plugins/EzAbility/Source/EzAbility/Private/EzAbilityContext.cpp

@@ -6,7 +6,66 @@
 #include "EzAbility.h"
 #include "EzAbilityComponent.h"
 #include "EzAbilityTypes.h"
-
+#include "Evaluator/EzAbilityEvaluator.h"
+#include "Task/EzAbilityTask.h"
+#include "EzAbilityLog.h"
+#include "Debugger/EzAbilityTrace.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+///define
+#define EZ_ABILITY_LOG_AND_TRACE(Verbosity, Format, ...) \
+	UE_VLOG_UELOG(GetOwner(), LogEzAbility, Verbosity, TEXT("%s: ") Format, *GetInstanceDescription(), ##__VA_ARGS__); \
+	EZ_ABILITY_TRACE_LOG_EVENT(Format, ##__VA_ARGS__)
+
+#if WITH_EZABILITY_DEBUGGER
+	#define ID_NAME PREPROCESSOR_JOIN(InstanceId,__LINE__) \
+
+	#define EZ_ABILITY_TRACE_SCOPED_PHASE(Phase) \
+		FEzAbilityInstanceDebugId ID_NAME = GetInstanceDebugId(); \
+		TRACE_EZ_ABILITY_PHASE_EVENT(ID_NAME, Phase, EEzAbilityTraceEventType::Push, FStateTreeStateHandle::Invalid) \
+		ON_SCOPE_EXIT { TRACE_EZ_ABILITY_PHASE_EVENT(ID_NAME, Phase, EEzAbilityTraceEventType::Pop, FStateTreeStateHandle::Invalid) }
+
+	#define EZ_ABILITY_TRACE_SCOPED_STATE(StateHandle) \
+		FEzAbilityInstanceDebugId ID_NAME = GetInstanceDebugId(); \
+		TRACE_EZ_ABILITY_PHASE_EVENT(ID_NAME, EStateTreeUpdatePhase::Unset, EEzAbilityTraceEventType::Push, StateHandle) \
+		ON_SCOPE_EXIT { TRACE_EZ_ABILITY_PHASE_EVENT(ID_NAME, EStateTreeUpdatePhase::Unset, EEzAbilityTraceEventType::Pop, StateHandle) }
+
+	#define EZ_ABILITY_TRACE_SCOPED_STATE_PHASE(StateHandle, Phase) \
+		FEzAbilityInstanceDebugId ID_NAME = GetInstanceDebugId(); \
+		TRACE_EZ_ABILITY_PHASE_EVENT(ID_NAME, Phase, EEzAbilityTraceEventType::Push, StateHandle) \
+		ON_SCOPE_EXIT { TRACE_EZ_ABILITY_PHASE_EVENT(ID_NAME, Phase, EEzAbilityTraceEventType::Pop, StateHandle) }
+
+	#define EZ_ABILITY_TRACE_INSTANCE_EVENT(EventType)						TRACE_EZ_ABILITY_INSTANCE_EVENT(GetInstanceDebugId(), GetStateTree(), *GetInstanceDescription(), EventType);
+	#define EZ_ABILITY_TRACE_INSTANCE_FRAME_EVENT(InstanceDebugId, Frame)	TRACE_EZ_ABILITY_INSTANCE_FRAME_EVENT(InstanceDebugId, Frame);
+	#define EZ_ABILITY_TRACE_PHASE_BEGIN(Phase)								TRACE_EZ_ABILITY_PHASE_EVENT(GetInstanceDebugId(), Phase, EEzAbilityTraceEventType::Push, FStateTreeStateHandle::Invalid)
+	#define EZ_ABILITY_TRACE_PHASE_END(Phase)								TRACE_EZ_ABILITY_PHASE_EVENT(GetInstanceDebugId(), Phase, EEzAbilityTraceEventType::Pop, FStateTreeStateHandle::Invalid)
+	#define EZ_ABILITY_TRACE_ACTIVE_STATES_EVENT(ActiveFrames)				TRACE_EZ_ABILITY_ACTIVE_STATES_EVENT(GetInstanceDebugId(), ActiveFrames);
+	#define EZ_ABILITY_TRACE_LOG_EVENT(Format, ...)							TRACE_EZ_ABILITY_LOG_EVENT(GetInstanceDebugId(), Format, ##__VA_ARGS__)
+	#define EZ_ABILITY_TRACE_STATE_EVENT(StateHandle, EventType)			TRACE_EZ_ABILITY_STATE_EVENT(GetInstanceDebugId(), StateHandle, EventType);
+	#define EZ_ABILITY_TRACE_TASK_EVENT(Index, DataView, EventType, Status)	TRACE_EZ_ABILITY_TASK_EVENT(GetInstanceDebugId(), FEzAbilityIndex16(Index), DataView, EventType, Status);
+	#define EZ_ABILITY_TRACE_EVALUATOR_EVENT(Index, DataView, EventType)	TRACE_EZ_ABILITY_EVALUATOR_EVENT(GetInstanceDebugId(), FEzAbilityIndex16(Index), DataView, EventType);
+	#define EZ_ABILITY_TRACE_CONDITION_EVENT(Index, DataView, EventType)	TRACE_EZ_ABILITY_CONDITION_EVENT(GetInstanceDebugId(), FEzAbilityIndex16(Index), DataView, EventType);	
+	#define EZ_ABILITY_TRACE_TRANSITION_EVENT(Source, EventType)			TRACE_EZ_ABILITY_TRANSITION_EVENT(GetInstanceDebugId(), Source, EventType);
+#else
+	#define EZ_ABILITY_TRACE_SCOPED_PHASE(Phase)
+	#define EZ_ABILITY_TRACE_SCOPED_STATE(StateHandle)
+	#define EZ_ABILITY_TRACE_SCOPED_STATE_PHASE(StateHandle, Phase)
+	#define EZ_ABILITY_TRACE_INSTANCE_EVENT(EventType)
+	#define EZ_ABILITY_TRACE_INSTANCE_FRAME_EVENT(InstanceDebugId, Frame)
+	#define EZ_ABILITY_TRACE_PHASE_BEGIN(Phase)
+	#define EZ_ABILITY_TRACE_PHASE_END(Phase)
+	#define EZ_ABILITY_TRACE_ACTIVE_STATES_EVENT(ActiveFrames)
+	#define EZ_ABILITY_TRACE_LOG_EVENT(Format, ...)
+	#define EZ_ABILITY_TRACE_STATE_EVENT(StateHandle, EventType)
+	#define EZ_ABILITY_TRACE_TASK_EVENT(Index, DataView, EventType, Status)
+	#define EZ_ABILITY_TRACE_EVALUATOR_EVENT(Index, DataView, EventType)
+	#define EZ_ABILITY_TRACE_CONDITION_EVENT(Index, DataView, EventType)
+	#define EZ_ABILITY_TRACE_TRANSITION_EVENT(Source, EventType)
+#endif // WITH_EZABILITY_DEBUGGER
+
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+///Ability Context
 void FEzAbilityContext::InitContext(UEzAbilityComponent* Component)
 {
 	AbilityComponent = Component;
@@ -118,11 +177,12 @@ EAbilityRunStatus FEzAbilityContext::Start(UEzAbility* InAbility, const FEzAbili
 
 	SetGlobalParameters(Ability->GetDefaultParameters());
 	
-	FEzAbilityFrame InitFrame = State.ActiveFrames.AddDefaulted_GetRef();
+	FEzAbilityExecutionFrame InitFrame = State.ActiveFrames.AddDefaulted_GetRef();
 	InitFrame.Ability			= Ability;
-	InitFrame.bIsGlobalFrame	= true;
 	InitFrame.RootState			= FEzAbilityStateHandle::Root;
-
+	InitFrame.ActiveStates		= {};
+	InitFrame.bIsGlobalFrame	= true;
+	
 	UpdateInstanceData({}, State.ActiveFrames);
 	
 	//TODO 设置默认参数
@@ -155,18 +215,29 @@ void FEzAbilityContext::Reset()
 	InstanceData.Reset();
 }
 
-void FEzAbilityContext::UpdateInstanceData(TConstArrayView<FEzAbilityFrame> CurrentActiveFrames, TArrayView<FEzAbilityFrame> NextActiveFrames)
+FString FEzAbilityContext::GetInstanceDescription() const
+{
+	return FString::Printf(TEXT("%s"), *GetNameSafe(OwnerActor));
+}
+
+void FEzAbilityContext::UpdateInstanceData(TConstArrayView<FEzAbilityExecutionFrame> CurrentActiveFrames, TArrayView<FEzAbilityExecutionFrame> NextActiveFrames)
 {
 	int32 EstimatedNumStructs = 0;
-	for (FEzAbilityFrame& Frame : NextActiveFrames)
+	for (FEzAbilityExecutionFrame& Frame : NextActiveFrames)
 	{
 		if(Frame.bIsGlobalFrame)
 		{
 			EstimatedNumStructs += Frame.Ability->NumGlobalInstanceData;
 		}
-		
-	}
 
+		for (int32 StateIndex = 0; StateIndex < Frame.ActiveStates.Num(); ++StateIndex)
+		{
+			const FEzAbilityStateHandle StateHandle = Frame.ActiveStates[StateIndex];
+			const FCompactEzAbilityState& State = Frame.Ability->States[StateHandle.Index];
+			EstimatedNumStructs += State.InstanceDataNum;
+		}
+	}
+	
 	TArray<FConstStructView, TConcurrentLinearArrayAllocator<FDefaultBlockAllocationTag>> InstanceStructs;
 	InstanceStructs.Reserve(EstimatedNumStructs);
 
@@ -175,12 +246,174 @@ void FEzAbilityContext::UpdateInstanceData(TConstArrayView<FEzAbilityFrame> Curr
 
 	TArrayView<FEzAbilityTemporaryInstanceData> TempInstances = InstanceDataStorage->GetMutableTemporaryInstances();
 
+	auto FindInstanceTempData = [&TempInstances](const FEzAbilityExecutionFrame& Frame, FEzAbilityDataHandle DataHandle)
+	{
+		FEzAbilityTemporaryInstanceData* TempData = TempInstances.FindByPredicate([&Frame, &DataHandle](const FEzAbilityTemporaryInstanceData& Data)
+		{
+			return Data.Ability == Frame.Ability && Data.RootState == Frame.RootState && Data.DataHandle == DataHandle; 
+		});
+		return TempData ? &TempData->Instance : nullptr;
+	};
+	
 	int32 CurrentGlobalInstanceIndexBase = 0;
 	int32 NumCommonInstanceData = 0;
 
+	const UStruct* NextStateParameterDataStruct = nullptr;
+	FEzAbilityDataHandle NextStateParameterDataHandle = FEzAbilityDataHandle::Invalid;
+	
+	FEzAbilityDataHandle CurrentGlobalParameterDataHandle = FEzAbilityDataHandle(EEzAbilityDataSourceType::GlobalParameterData);
+
+	bool bAreCommon = true;
 	for (int32 FrameIndex = 0; FrameIndex < NextActiveFrames.Num(); FrameIndex++)
 	{
-		
+		const bool bIsCurrentFrameValid = CurrentActiveFrames.IsValidIndex(FrameIndex)
+						&& CurrentActiveFrames[FrameIndex].IsSameFrame(NextActiveFrames[FrameIndex]);
+
+		bAreCommon &= bIsCurrentFrameValid;
+
+		const FEzAbilityExecutionFrame* CurrentFrame = bIsCurrentFrameValid ? &CurrentActiveFrames[FrameIndex] : nullptr;
+		FEzAbilityExecutionFrame& NextFrame = NextActiveFrames[FrameIndex];
+
+		check(NextFrame.Ability);
+
+		if (NextFrame.bIsGlobalFrame)
+		{
+			// Handle global tree parameters
+			if (NextStateParameterDataHandle.IsValid())
+			{
+				// Point to the parameter block set by linked state.
+				check(NextStateParameterDataStruct == NextFrame.Ability->GetDefaultParameters().GetPropertyBagStruct());
+				CurrentGlobalParameterDataHandle = NextStateParameterDataHandle;
+				NextStateParameterDataHandle = FEzAbilityDataHandle::Invalid; // Mark as used.
+			}
+			
+			// Global Evals
+			const int32 BaseIndex = InstanceStructs.Num();
+			CurrentGlobalInstanceIndexBase = BaseIndex;
+			
+			InstanceStructs.AddDefaulted(NextFrame.Ability->NumGlobalInstanceData);
+			TempInstanceStructs.AddZeroed(NextFrame.Ability->NumGlobalInstanceData);
+			
+			for (int32 EvalIndex = NextFrame.Ability->EvaluatorsBegin; EvalIndex < (NextFrame.Ability->EvaluatorsBegin + NextFrame.Ability->EvaluatorsNum); EvalIndex++)
+			{
+				const FEzAbilityEvaluator& Eval =  NextFrame.Ability->Nodes[EvalIndex].Get<const FEzAbilityEvaluator>();
+				const FConstStructView EvalInstanceData = NextFrame.Ability->DefaultInstanceData.GetStruct(Eval.InstanceTemplateIndex.Get());
+				InstanceStructs[BaseIndex + Eval.InstanceDataHandle.GetIndex()] = EvalInstanceData;
+				if (!bAreCommon)
+				{
+					TempInstanceStructs[BaseIndex + Eval.InstanceDataHandle.GetIndex()] = FindInstanceTempData(NextFrame, Eval.InstanceDataHandle);
+				}
+			}
+
+			// Global tasks
+			for (int32 TaskIndex = NextFrame.Ability->GlobalTasksBegin; TaskIndex < (NextFrame.Ability->GlobalTasksBegin + NextFrame.Ability->GlobalTasksNum); TaskIndex++)
+			{
+				const FEzAbilityTask& Task =  NextFrame.Ability->Nodes[TaskIndex].Get<const FEzAbilityTask>();
+				const FConstStructView TaskInstanceData = NextFrame.Ability->DefaultInstanceData.GetStruct(Task.InstanceTemplateIndex.Get());
+				InstanceStructs[BaseIndex + Task.InstanceDataHandle.GetIndex()] = TaskInstanceData;
+				if (!bAreCommon)
+				{
+					TempInstanceStructs[BaseIndex + Task.InstanceDataHandle.GetIndex()] = FindInstanceTempData(NextFrame, Task.InstanceDataHandle);
+				}
+			}
+
+			if (bAreCommon)
+			{
+				NumCommonInstanceData = InstanceStructs.Num();
+			}
+		}
+
+		// States
+		const int32 BaseIndex = InstanceStructs.Num();
+
+		NextFrame.GlobalParameterDataHandle = CurrentGlobalParameterDataHandle;
+		NextFrame.GlobalInstanceIndexBase = FEzAbilityIndex16(CurrentGlobalInstanceIndexBase);
+		NextFrame.ActiveInstanceIndexBase = FEzAbilityIndex16(BaseIndex);
+
+		for (int32 StateIndex = 0; StateIndex < NextFrame.ActiveStates.Num(); StateIndex++)
+		{
+			// Check if the next state is still same as current state, GetStateSafe() will return invalid state if passed out of bounds index.
+			bAreCommon = bAreCommon && (CurrentFrame && CurrentFrame->ActiveStates.GetStateSafe(StateIndex) == NextFrame.ActiveStates[StateIndex]);
+
+			const FEzAbilityStateHandle StateHandle = NextFrame.ActiveStates[StateIndex];
+			const FCompactEzAbilityState& State = NextFrame.Ability->States[StateHandle.Index];
+
+			InstanceStructs.AddDefaulted(State.InstanceDataNum);
+			TempInstanceStructs.AddZeroed(State.InstanceDataNum);
+
+			bool bCanHaveTempData = false;
+
+			if (State.Type == EEzAbilityStateType::Subtree)
+			{
+				check(State.ParameterDataHandle.IsValid());
+				check(State.ParameterTemplateIndex.IsValid());
+				const FConstStructView ParamsInstanceData = NextFrame.Ability->DefaultInstanceData.GetStruct(State.ParameterTemplateIndex.Get());
+				if (!NextStateParameterDataHandle.IsValid())
+				{
+					// Parameters are not set by a linked state, create instance data.
+					InstanceStructs[BaseIndex + State.ParameterDataHandle.GetIndex()] = ParamsInstanceData;
+					NextFrame.StateParameterDataHandle = State.ParameterDataHandle;
+					bCanHaveTempData = true;
+				}
+				else
+				{
+					// Point to the parameter block set by linked state.
+					const FCompactEzAbilityParameters* Params = ParamsInstanceData.GetPtr<const FCompactEzAbilityParameters>();
+					const UStruct* StateParameterDataStruct = Params ? Params->Parameters.GetPropertyBagStruct() : nullptr;
+					check(NextStateParameterDataStruct == StateParameterDataStruct);
+					
+					NextFrame.StateParameterDataHandle = NextStateParameterDataHandle;
+					NextStateParameterDataHandle = FEzAbilityDataHandle::Invalid; // Mark as used.
+
+					// This state will not instantiate parameter data, so we don't care about the temp data either.
+					bCanHaveTempData = false;
+				}
+			}
+			else
+			{
+				if (State.ParameterTemplateIndex.IsValid())
+				{
+					// Linked state's instance data is the parameters.
+					check(State.ParameterDataHandle.IsValid());
+					const FConstStructView ParamsInstanceData = NextFrame.Ability->DefaultInstanceData.GetStruct(State.ParameterTemplateIndex.Get());
+					InstanceStructs[BaseIndex + State.ParameterDataHandle.GetIndex()] = ParamsInstanceData;
+					bCanHaveTempData = true;
+
+					if (State.Type == EEzAbilityStateType::Linked
+						|| State.Type == EEzAbilityStateType::LinkedAsset)
+					{
+						// Store the index of the parameter data, so that we can point the linked state to it.
+						check(State.ParameterDataHandle.GetSource() == EEzAbilityDataSourceType::StateParameterData);
+						checkf(!NextStateParameterDataHandle.IsValid(), TEXT("NextStateParameterDataIndex not should be set yet when we encounter a linked state."));
+						NextStateParameterDataHandle = State.ParameterDataHandle;
+					
+						const FCompactEzAbilityParameters* Params = ParamsInstanceData.GetPtr<const FCompactEzAbilityParameters>();
+						NextStateParameterDataStruct = Params ? Params->Parameters.GetPropertyBagStruct() : nullptr;
+					}
+				}
+			}
+			
+			if (!bAreCommon && bCanHaveTempData)
+			{
+				TempInstanceStructs[BaseIndex + State.ParameterDataHandle.GetIndex()] = FindInstanceTempData(NextFrame, State.ParameterDataHandle);
+			}
+
+			for (int32 TaskIndex = State.TasksBegin; TaskIndex < (State.TasksBegin + State.TasksNum); TaskIndex++)
+			{
+				const FEzAbilityTask& Task = NextFrame.Ability->Nodes[TaskIndex].Get<const FEzAbilityTask>();
+				const FConstStructView TaskInstanceData = NextFrame.Ability->DefaultInstanceData.GetStruct(Task.InstanceTemplateIndex.Get());
+				InstanceStructs[BaseIndex + Task.InstanceDataHandle.GetIndex()] = TaskInstanceData;
+				if (!bAreCommon)
+				{
+					TempInstanceStructs[BaseIndex + Task.InstanceDataHandle.GetIndex()] = FindInstanceTempData(NextFrame, Task.InstanceDataHandle);
+				}
+			}
+
+			if (bAreCommon)
+			{
+				NumCommonInstanceData = InstanceStructs.Num();
+			}
+		}
 	}
 	
 	for (int32 Index = 0; Index < NumCommonInstanceData; Index++)
@@ -206,6 +439,107 @@ void FEzAbilityContext::UpdateInstanceData(TConstArrayView<FEzAbilityFrame> Curr
 	InstanceData.Append(*OwnerActor,
 		MakeArrayView(InstanceStructs.GetData() + NumCommonInstanceData, InstanceStructs.Num() - NumCommonInstanceData),
 		MakeArrayView(TempInstanceStructs.GetData() + NumCommonInstanceData, TempInstanceStructs.Num() - NumCommonInstanceData));
+
+	// Stop any temporary global tasks or evals that were left over.
+	StopTemporaryEvaluatorsAndGlobalTasks(TempInstances);
 	
 	InstanceData.ResetTemporaryInstances();
 }
+
+void FEzAbilityContext::StopTemporaryEvaluatorsAndGlobalTasks(TArrayView<FEzAbilityTemporaryInstanceData> TempInstances)
+{
+	EZ_ABILITY_CLOG(!TempInstances.IsEmpty(), Verbose, TEXT("Stop Temporary Evaluators & Global tasks left over from previous state selection"));
+	
+	// Create temporary transition to stop the unused global tasks and evaluators.
+	constexpr  EAbilityRunStatus CompletionStatus = EAbilityRunStatus::Stopped; 
+	FEzAbilityTransitionResult Transition;
+	Transition.TargetState = FEzAbilityStateHandle::FromCompletionStatus(CompletionStatus);
+	Transition.CurrentRunStatus = CompletionStatus;
+
+	for (int32 Index = TempInstances.Num() - 1; Index >= 0; Index--)
+	{
+		FEzAbilityTemporaryInstanceData& TempInstance = TempInstances[Index];
+		if (TempInstance.OwnerNodeIndex.IsValid()
+			&& TempInstance.Instance.IsValid())
+		{
+			FEzAbilityExecutionFrame TempFrame;
+			TempFrame.Ability = TempInstance.Ability;
+			TempFrame.RootState = TempInstance.RootState;
+
+			FEzAbilityDataView NodeInstanceView;
+			if (FEzAbilityInstanceObjectWrapper* Wrapper = TempInstance.Instance.GetMutablePtr<FEzAbilityInstanceObjectWrapper>())
+			{
+				NodeInstanceView = FEzAbilityDataView(Wrapper->InstanceObject);
+			}
+			else
+			{
+				NodeInstanceView = FEzAbilityDataView(TempInstance.Instance);
+			}
+			
+			FCurrentlyProcessedFrameScope FrameScope(*this, nullptr, TempFrame);
+			FNodeInstanceDataScope DataScope(*this, TempInstance.DataHandle, NodeInstanceView);
+
+			FConstStructView NodeView = TempFrame.Ability->Nodes[TempInstance.OwnerNodeIndex.Get()];
+			if (const FEzAbilityTask* Task = NodeView.GetPtr<const FEzAbilityTask>())
+			{
+				EZ_ABILITY_LOG(Verbose, TEXT("  Stop: '%s'"), *Task->Name.ToString());
+				{
+					QUICK_SCOPE_CYCLE_COUNTER(EzAbility_Task_Stop);
+					Task->ExitState(*this, Transition);
+				}
+				EZ_ABILITY_TRACE_TASK_EVENT(TempInstance.OwnerNodeIndex.Get(), NodeInstanceView, EEzAbilityTraceEventType::OnExited, Transition.CurrentRunStatus);
+			}
+			else if (const FEzAbilityEvaluator* Eval = NodeView.GetPtr<const FEzAbilityEvaluator>())
+			{
+				EZ_ABILITY_LOG(Verbose, TEXT("  Stop: '%s'"), *Eval->Name.ToString());
+				{
+					QUICK_SCOPE_CYCLE_COUNTER(EzAbility_Eval_Stop);
+					Eval->Stop(*this);
+
+					EZ_ABILITY_TRACE_EVALUATOR_EVENT(TempInstance.OwnerNodeIndex.Get(), NodeInstanceView, EEzAbilityTraceEventType::OnAbilityStopped);
+				}
+			}
+		}
+	}
+}
+
+FEzAbilityContext::FCurrentlyProcessedFrameScope::FCurrentlyProcessedFrameScope(FEzAbilityContext& InContext, const FEzAbilityExecutionFrame* CurrentParentFrame, const FEzAbilityExecutionFrame& CurrentFrame)
+	: Context(InContext)
+{
+	check(CurrentFrame.Ability);
+	FEzAbilityInstanceStorage* SharedInstanceDataStorage = &CurrentFrame.Ability->GetSharedInstanceData()->GetMutableStorage();
+
+	SavedFrame = Context.CurrentlyProcessedFrame;
+	SavedParentFrame = Context.CurrentlyProcessedParentFrame;
+	SavedSharedInstanceDataStorage = Context.CurrentlyProcessedSharedInstanceStorage;
+	Context.CurrentlyProcessedFrame = &CurrentFrame;
+	Context.CurrentlyProcessedParentFrame = CurrentParentFrame;
+	Context.CurrentlyProcessedSharedInstanceStorage = SharedInstanceDataStorage;
+	
+	EZ_ABILITY_TRACE_INSTANCE_FRAME_EVENT(Context.GetInstanceDebugId(), Context.CurrentlyProcessedFrame);
+}
+
+FEzAbilityContext::FCurrentlyProcessedFrameScope::~FCurrentlyProcessedFrameScope()
+{
+	Context.CurrentlyProcessedFrame = SavedFrame;
+	Context.CurrentlyProcessedParentFrame = SavedParentFrame;
+	Context.CurrentlyProcessedSharedInstanceStorage = SavedSharedInstanceDataStorage;
+
+	if (Context.CurrentlyProcessedFrame)
+	{
+		EZ_ABILITY_TRACE_INSTANCE_FRAME_EVENT(Context.GetInstanceDebugId(), Context.CurrentlyProcessedFrame);
+	}
+}
+
+#if WITH_EZABILITY_DEBUGGER
+FEzAbilityInstanceDebugId FEzAbilityContext::GetInstanceDebugId() const
+{
+	FEzAbilityInstanceDebugId& InstanceDebugId = GetExecState().InstanceDebugId; 
+	if (!InstanceDebugId.IsValid())
+	{
+		static std::atomic<uint32> SerialNumber = 0;
+		InstanceDebugId = FEzAbilityInstanceDebugId(GetTypeHash(GetInstanceDescription()), ++SerialNumber); 
+	}
+	return InstanceDebugId;
+}
+#endif

+ 16 - 0
Ability/Plugins/EzAbility/Source/EzAbility/Private/EzAbilityEvents.cpp

@@ -0,0 +1,16 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+
+#include "EzAbilityEvents.h"
+#include "EzAbilityLog.h"
+
+void FEzAbilityEventQueue::SendEvent(const UObject* Owner, const FGameplayTag& Tag, const FConstStructView Payload, const FName Origin)
+{
+	if (Events.Num() >= MaxActiveEvents)
+	{
+		UE_VLOG_UELOG(Owner, LogEzAbility, Error, TEXT("%s: Too many events send on '%s'. Dropping event %s"), ANSI_TO_TCHAR(__FUNCTION__), *GetNameSafe(Owner), *Tag.ToString());
+		return;
+	}
+
+	Events.Emplace(Tag, Payload, Origin);
+}

+ 8 - 0
Ability/Plugins/EzAbility/Source/EzAbility/Private/EzAbilityExecutionTypes.cpp

@@ -0,0 +1,8 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+
+#include "EzAbilityExecutionTypes.h"
+
+#if WITH_EZABILITY_DEBUGGER
+const FEzAbilityInstanceDebugId FEzAbilityInstanceDebugId::Invalid = FEzAbilityInstanceDebugId();
+#endif 

+ 48 - 0
Ability/Plugins/EzAbility/Source/EzAbility/Private/EzAbilityIndexTypes.cpp

@@ -0,0 +1,48 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+
+#include "EzAbilityIndexTypes.h"
+
+bool FEzAbilityIndex16::SerializeFromMismatchedTag(const FPropertyTag& Tag, FStructuredArchive::FSlot Slot)
+{
+	// Support loading from Index8.
+	if (Tag.GetType().IsStruct(FEzAbilityIndex8::StaticStruct()->GetFName()))
+	{
+		FEzAbilityIndex8 OldValue;
+		FEzAbilityIndex8::StaticStruct()->SerializeItem(Slot, &OldValue, nullptr);
+
+		int32 NewValue = OldValue.AsInt32();
+		if (!IsValidIndex(NewValue))
+		{
+			NewValue = INDEX_NONE;
+		}
+		
+		*this = FEzAbilityIndex16(NewValue);
+		
+		return true;
+	}
+	
+	return false;
+}
+
+bool FEzAbilityIndex8::SerializeFromMismatchedTag(const FPropertyTag& Tag, FStructuredArchive::FSlot Slot)
+{
+	// Support loading from Index16.
+	if (Tag.GetType().IsStruct(FEzAbilityIndex16::StaticStruct()->GetFName()))
+	{
+		FEzAbilityIndex16 OldValue;
+		FEzAbilityIndex16::StaticStruct()->SerializeItem(Slot, &OldValue, nullptr);
+
+		int32 NewValue = OldValue.AsInt32();
+		if (!IsValidIndex(NewValue))
+		{
+			NewValue = INDEX_NONE;
+		}
+		
+		*this = FEzAbilityIndex8(NewValue);
+		
+		return true;
+	}
+	
+	return false;
+}

+ 84 - 0
Ability/Plugins/EzAbility/Source/EzAbility/Private/EzAbilityInstanceData.cpp

@@ -112,11 +112,95 @@ void FEzAbilityInstanceStorage::ResetTemporaryInstances()
 	TemporaryInstances.Reset();
 }
 
+FStructView FEzAbilityInstanceStorage::AddTemporaryInstance(UObject& InOwner, const FEzAbilityExecutionFrame& Frame, const FEzAbilityIndex16 OwnerNodeIndex, const FEzAbilityDataHandle DataHandle, FConstStructView NewInstanceData)
+{
+	FEzAbilityTemporaryInstanceData* TempInstance = TemporaryInstances.FindByPredicate([&Frame, &OwnerNodeIndex, &DataHandle](const FEzAbilityTemporaryInstanceData& TempInstance)
+	{
+		return TempInstance.Ability == Frame.Ability
+				&& TempInstance.RootState == Frame.RootState
+				&& TempInstance.OwnerNodeIndex == OwnerNodeIndex
+				&& TempInstance.DataHandle == DataHandle;
+	});
+	
+	if (TempInstance)
+	{
+		if (TempInstance->Instance.GetScriptStruct() != NewInstanceData.GetScriptStruct())
+		{
+			TempInstance->Instance = NewInstanceData;
+		}
+	}
+	else
+	{
+		TempInstance = &TemporaryInstances.AddDefaulted_GetRef();
+		check(TempInstance);
+		TempInstance->Ability = Frame.Ability;
+		TempInstance->RootState = Frame.RootState;
+		TempInstance->OwnerNodeIndex = OwnerNodeIndex;
+		TempInstance->DataHandle = DataHandle;
+		TempInstance->Instance = NewInstanceData;
+	}
+
+	if (FEzAbilityInstanceObjectWrapper* Wrapper = TempInstance->Instance.GetMutablePtr<FEzAbilityInstanceObjectWrapper>())
+	{
+		if (Wrapper->InstanceObject)
+		{
+			Wrapper->InstanceObject = EzAbility::DuplicateNodeInstance(*Wrapper->InstanceObject, InOwner);
+		}
+	}
+
+	return TempInstance->Instance;
+}
+
+FStructView FEzAbilityInstanceStorage::GetMutableTemporaryStruct(const FEzAbilityExecutionFrame& Frame, const FEzAbilityDataHandle DataHandle)
+{
+	FEzAbilityTemporaryInstanceData* ExistingInstance = TemporaryInstances.FindByPredicate([&Frame, &DataHandle](const FEzAbilityTemporaryInstanceData& TempInstance)
+	{
+		return TempInstance.Ability == Frame.Ability
+				&& TempInstance.RootState == Frame.RootState
+				&& TempInstance.DataHandle == DataHandle;
+	});
+	return ExistingInstance ? FStructView(ExistingInstance->Instance) : FStructView();
+}
+
+UObject* FEzAbilityInstanceStorage::GetMutableTemporaryObject(const FEzAbilityExecutionFrame& Frame, const FEzAbilityDataHandle DataHandle)
+{
+	FEzAbilityTemporaryInstanceData* ExistingInstance = TemporaryInstances.FindByPredicate([&Frame, &DataHandle](const FEzAbilityTemporaryInstanceData& TempInstance)
+	{
+		return TempInstance.Ability == Frame.Ability
+				&& TempInstance.RootState == Frame.RootState
+				&& TempInstance.DataHandle == DataHandle;
+	});
+	if (ExistingInstance)
+	{
+		const FEzAbilityInstanceObjectWrapper& Wrapper = ExistingInstance->Instance.Get<FEzAbilityInstanceObjectWrapper>();
+		return Wrapper.InstanceObject;
+	}
+	return nullptr;
+}
+
 void FEzAbilityInstanceStorage::SetGlobalParameters(const FInstancedPropertyBag& Parameters)
 {
 	GlobalParameters = Parameters;
 }
 
+void FEzAbilityInstanceStorage::ResetTransitionRequests()
+{
+	TransitionRequests.Reset();
+}
+
+void FEzAbilityInstanceStorage::AddTransitionRequest(const UObject* Owner, const FEzAbilityTransitionRequest& Request)
+{
+	constexpr int32 MaxPendingTransitionRequests = 32;
+	
+	if (TransitionRequests.Num() >= MaxPendingTransitionRequests)
+	{
+		UE_VLOG_UELOG(Owner, LogEzAbility, Error, TEXT("%s: Too many transition requests sent to '%s' (%d pending). Dropping request."), ANSI_TO_TCHAR(__FUNCTION__), *GetNameSafe(Owner), TransitionRequests.Num());
+		return;
+	}
+
+	TransitionRequests.Add(Request);
+}
+
 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 ///InstanceData
 

+ 7 - 4
Ability/Plugins/EzAbility/Source/EzAbility/Private/EzAbilityTypes.cpp

@@ -3,12 +3,19 @@
 
 #include "EzAbilityTypes.h"
 
+#include "EzAbilityIndexTypes.h"
+
 const FEzAbilityStateHandle FEzAbilityStateHandle::Invalid		= FEzAbilityStateHandle();
 const FEzAbilityStateHandle FEzAbilityStateHandle::Succeeded	= FEzAbilityStateHandle(SucceededIndex);
 const FEzAbilityStateHandle FEzAbilityStateHandle::Failed		= FEzAbilityStateHandle(FailedIndex);
 const FEzAbilityStateHandle FEzAbilityStateHandle::Stopped		= FEzAbilityStateHandle(StoppedIndex);
 const FEzAbilityStateHandle FEzAbilityStateHandle::Root			= FEzAbilityStateHandle(0);
 
+const FEzAbilityDataHandle FEzAbilityDataHandle::Invalid = FEzAbilityDataHandle();
+
+const FEzAbilityIndex16 FEzAbilityIndex16::Invalid	= FEzAbilityIndex16();
+const FEzAbilityIndex8	FEzAbilityIndex8::Invalid	= FEzAbilityIndex8();
+
 EAbilityRunStatus FEzAbilityStateHandle::ToCompletionStatus() const
 {
 	if (Index == SucceededIndex)
@@ -49,7 +56,3 @@ FCompactEzAbilityState::FCompactEzAbilityState()
 FCompactEzAbilityTransition::FCompactEzAbilityTransition()
 {
 }
-
-void FEzAbilityExecutionState::Reset()
-{
-}

+ 41 - 1
Ability/Plugins/EzAbility/Source/EzAbility/Private/EzAbility_Replicate.cpp

@@ -3,4 +3,44 @@
 bool UEzAbility::ShouldReplicate(const FEzAbilityInstance& Instance) const
 {
 	return true;
-}
+}
+
+TSharedPtr<FEzAbilityInstanceData> UEzAbility::GetSharedInstanceData() const
+{
+	// Create a unique index for each thread.
+	static std::atomic_int ThreadIndexCounter {0};
+	static thread_local int32 ThreadIndex = INDEX_NONE; // Cannot init directly on WinRT
+	if (ThreadIndex == INDEX_NONE)
+	{
+		ThreadIndex = ThreadIndexCounter.fetch_add(1);
+	}
+
+	// If shared instance data for this thread exists, return it.
+	{
+		FReadScopeLock ReadLock(PerThreadSharedInstanceDataLock);
+		if (ThreadIndex < PerThreadSharedInstanceData.Num())
+		{
+			return PerThreadSharedInstanceData[ThreadIndex];
+		}
+	}
+
+	// Not initialized yet, create new instances up to the index.
+	FWriteScopeLock WriteLock(PerThreadSharedInstanceDataLock);
+
+	// It is possible that multiple threads are waiting for the write lock,
+	// which means that execution may get here so that 'ThreadIndex' is already in valid range.
+	// The loop below is organized to handle that too.
+	
+	const int32 NewNum = ThreadIndex + 1;
+	PerThreadSharedInstanceData.Reserve(NewNum);
+	UEzAbility* NonConstThis = const_cast<UEzAbility*>(this); 
+	
+	for (int32 Index = PerThreadSharedInstanceData.Num(); Index < NewNum; Index++)
+	{
+		TSharedPtr<FEzAbilityInstanceData> SharedData = MakeShared<FEzAbilityInstanceData>();
+		SharedData->CopyFrom(*NonConstThis, SharedInstanceData);
+		PerThreadSharedInstanceData.Add(SharedData);
+	}
+
+	return PerThreadSharedInstanceData[ThreadIndex];
+}

+ 101 - 0
Ability/Plugins/EzAbility/Source/EzAbility/Public/Debugger/EzAbilityTrace.h

@@ -0,0 +1,101 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#pragma once
+
+#if WITH_EZABILITY_DEBUGGER
+
+#include "Trace/Trace.h"
+#include "Containers/ContainersFwd.h"
+
+UE_TRACE_CHANNEL_EXTERN(EzAbilityDebugChannel, EZABILITY_API)
+
+class UEzAbility;
+struct FEzAbilityIndex16;
+struct FEzAbilityExecutionFrame;
+struct FEzAbilityStateHandle;
+struct FEzAbilityStateHandle;
+struct FEzAbilityDataView;
+struct FEzAbilityTransitionSource;
+struct FEzAbilityInstanceDebugId;
+enum class EAbilityRunStatus : uint8;
+enum class EEzAbilityUpdatePhase : uint8;
+enum class EEzAbilityTraceEventType : uint8;
+
+namespace UE::EzAbilityTrace
+{
+	void RegisterGlobalDelegates();
+	void UnregisterGlobalDelegates();
+	FEzAbilityIndex16 FindOrAddDebugIdForAsset(const UEzAbility* Ability);
+	void OutputPhaseScopeEvent(FEzAbilityInstanceDebugId InstanceId, EEzAbilityUpdatePhase Phase, EEzAbilityTraceEventType EventType, FEzAbilityStateHandle StateHandle);
+	void OutputAssetDebugIdEvent(const UEzAbility* Ability, FEzAbilityIndex16 AssetDebugId);
+	void OutputInstanceLifetimeEvent(FEzAbilityInstanceDebugId InstanceId, const UEzAbility* Ability, const TCHAR* InstanceName, EEzAbilityTraceEventType EventType);
+	void OutputInstanceFrameEvent(FEzAbilityInstanceDebugId InstanceId, const FEzAbilityExecutionFrame* Frame);
+	void OutputLogEventTrace(FEzAbilityInstanceDebugId InstanceId, const TCHAR* Fmt, ...);
+	void OutputStateEventTrace(FEzAbilityInstanceDebugId InstanceId, FEzAbilityStateHandle StateHandle, EEzAbilityTraceEventType EventType);
+	void OutputTaskEventTrace(FEzAbilityInstanceDebugId InstanceId, FEzAbilityIndex16 TaskIdx, FEzAbilityDataView DataView, EEzAbilityTraceEventType EventType, EAbilityRunStatus Status);
+	void OutputEvaluatorEventTrace(FEzAbilityInstanceDebugId InstanceId, FEzAbilityIndex16 EvaluatorIdx, FEzAbilityDataView DataView, EEzAbilityTraceEventType EventType);
+	void OutputConditionEventTrace(FEzAbilityInstanceDebugId InstanceId, FEzAbilityIndex16 ConditionIdx, FEzAbilityDataView DataView, EEzAbilityTraceEventType EventType);
+	void OutputTransitionEventTrace(FEzAbilityInstanceDebugId InstanceId, FEzAbilityTransitionSource TransitionSource, EEzAbilityTraceEventType EventType);
+	void OutputActiveStatesEventTrace(FEzAbilityInstanceDebugId InstanceId, TConstArrayView<FEzAbilityExecutionFrame> ActiveFrames);
+}
+
+#define TRACE_EZ_ABILITY_INSTANCE_EVENT(InstanceID, Ability, InstanceName, EventType) \
+	UE::EzAbilityTrace::OutputInstanceLifetimeEvent(InstanceID, Ability, InstanceName, EventType);
+
+#define TRACE_EZ_ABILITY_INSTANCE_FRAME_EVENT(InstanceID, Frame) \
+	UE::EzAbilityTrace::OutputInstanceFrameEvent(InstanceID, Frame);
+
+#define TRACE_EZ_ABILITY_PHASE_EVENT(InstanceID, Phase, EventType, StateHandle) \
+	UE::EzAbilityTrace::OutputPhaseScopeEvent(InstanceID, Phase, EventType, StateHandle); \
+
+#define TRACE_EZ_ABILITY_LOG_EVENT(InstanceId, Format, ...) \
+	if (UE_TRACE_CHANNELEXPR_IS_ENABLED(EzAbilityDebugChannel)) \
+	{ \
+		UE::EzAbilityTrace::OutputLogEventTrace(InstanceId, Format, ##__VA_ARGS__); \
+	}
+
+#define TRACE_EZ_ABILITY_STATE_EVENT(InstanceId, StateHandle, EventType) \
+	if (UE_TRACE_CHANNELEXPR_IS_ENABLED(EzAbilityDebugChannel)) \
+	{ \
+		UE::EzAbilityTrace::OutputStateEventTrace(InstanceId, StateHandle, EventType); \
+	}
+
+#define TRACE_EZ_ABILITY_TASK_EVENT(InstanceId, TaskIdx, DataView, EventType, Status) \
+	if (UE_TRACE_CHANNELEXPR_IS_ENABLED(EzAbilityDebugChannel)) \
+	{ \
+		UE::EzAbilityTrace::OutputTaskEventTrace(InstanceId, TaskIdx, DataView, EventType, Status); \
+	}
+
+#define TRACE_EZ_ABILITY_EVALUATOR_EVENT(InstanceId, EvaluatorIdx, DataView, EventType) \
+	if (UE_TRACE_CHANNELEXPR_IS_ENABLED(EzAbilityDebugChannel)) \
+	{ \
+		UE::EzAbilityTrace::OutputEvaluatorEventTrace(InstanceId, EvaluatorIdx, DataView, EventType); \
+	}
+
+#define TRACE_EZ_ABILITY_CONDITION_EVENT(InstanceId, ConditionIdx, DataView, EventType) \
+	if (UE_TRACE_CHANNELEXPR_IS_ENABLED(EzAbilityDebugChannel)) \
+	{ \
+		UE::EzAbilityTrace::OutputConditionEventTrace(InstanceId, ConditionIdx, DataView, EventType); \
+	}
+
+#define TRACE_EZ_ABILITY_TRANSITION_EVENT(InstanceId, TransitionIdx, EventType) \
+	if (UE_TRACE_CHANNELEXPR_IS_ENABLED(EzAbilityDebugChannel)) \
+	{ \
+		UE::EzAbilityTrace::OutputTransitionEventTrace(InstanceId, TransitionIdx, EventType); \
+	}
+
+#define TRACE_EZ_ABILITY_ACTIVE_STATES_EVENT(InstanceId, ActivateFrames) \
+		UE::EzAbilityTrace::OutputActiveStatesEventTrace(InstanceId, ActivateFrames);
+
+#else
+#define TRACE_EZ_ABILITY_INSTANCE_EVENT(InstanceID, Ability, InstanceName, EventType)
+#define TRACE_EZ_ABILITY_INSTANCE_FRAME_EVENT(InstanceID, Frame)
+#define TRACE_EZ_ABILITY_PHASE_EVENT(InstanceID, Phase, EventType, StateHandle)
+#define TRACE_EZ_ABILITY_LOG_EVENT(InstanceId, Format, ...)
+#define TRACE_EZ_ABILITY_STATE_EVENT(InstanceId, StateHandle, EventType)
+#define TRACE_EZ_ABILITY_TASK_EVENT(InstanceId, TaskIdx, DataView, EventType, Status)
+#define TRACE_EZ_ABILITY_EVALUATOR_EVENT(InstanceId, EvaluatorIdx, DataView, EventType)
+#define TRACE_EZ_ABILITY_CONDITION_EVENT(InstanceId, ConditionIdx, DataView, EventType)
+#define TRACE_EZ_ABILITY_TRANSITION_EVENT(InstanceId, TransitionIdx, EventType)
+#define TRACE_EZ_ABILITY_ACTIVE_STATES_EVENT(InstanceId, ActiveFrames)
+#endif 

+ 4 - 0
Ability/Plugins/EzAbility/Source/EzAbility/Public/Evaluator/EzAbilityEvaluator.h

@@ -7,6 +7,8 @@
 #include "UObject/Object.h"
 #include "EzAbilityEvaluator.generated.h"
 
+struct FEzAbilityContext;
+
 /**
  * 
  */
@@ -14,4 +16,6 @@ USTRUCT()
 struct EZABILITY_API FEzAbilityEvaluator : public FEzAbilityNodeBase
 {
 	GENERATED_BODY()
+
+	virtual void Stop(FEzAbilityContext& Context) const {}
 };

+ 27 - 3
Ability/Plugins/EzAbility/Source/EzAbility/Public/EzAbility.h

@@ -6,7 +6,7 @@
 #include "EzAbilityInstanceData.h"
 #include "InstancedStructContainer.h"
 #include "EzAbilityTypes.h"
-#include "StructUtils/PropertyBag.h"
+#include "PropertyBag.h"
 #include "EzAbility.generated.h"
 
 struct FEzAbilityContext;
@@ -24,7 +24,7 @@ public:
 	virtual bool ShouldReplicate(const FEzAbilityInstance& Instance) const;
 
 	const FInstancedPropertyBag& GetDefaultParameters() const { return Parameters; }
-	
+	TSharedPtr<FEzAbilityInstanceData> GetSharedInstanceData() const;
 protected:
 	UFUNCTION(BlueprintNativeEvent)
 	bool K2_ActivateAbility(FEzAbilityContext& Context) const;
@@ -36,7 +36,7 @@ public:
 
 	//Slices
 	UPROPERTY()
-	TArray<FCompactEzAbilityState>		Slices;
+	TArray<FCompactEzAbilityState>		States;
 	
 	//Transitions
 	UPROPERTY()
@@ -57,4 +57,28 @@ public:
 
 	UPROPERTY()
 	uint16 NumGlobalInstanceData = 0;
+
+	UPROPERTY()
+	uint16 EvaluatorsBegin = 0;
+	
+	UPROPERTY()
+	uint16 EvaluatorsNum = 0;
+
+	UPROPERTY()
+	uint16 GlobalTasksBegin = 0;
+	
+	UPROPERTY()
+	uint16 GlobalTasksNum = 0;
+
+	UPROPERTY()
+	FEzAbilityIndex16		ParameterTemplateIndex	= FEzAbilityIndex16::Invalid;
+	
+	UPROPERTY()
+	FEzAbilityDataHandle	ParameterDataHandle		= FEzAbilityDataHandle::Invalid;
+
+	mutable FRWLock PerThreadSharedInstanceDataLock;
+	mutable TArray<TSharedPtr<FEzAbilityInstanceData>> PerThreadSharedInstanceData;
+
+	UPROPERTY()
+	uint32 LastCompiledEditorDataHash = 0;
 };

+ 60 - 3
Ability/Plugins/EzAbility/Source/EzAbility/Public/EzAbilityContext.h

@@ -51,6 +51,9 @@ public:
 	bool IsValid() const;
 	bool SetGlobalParameters(const FInstancedPropertyBag& Parameters) const;
 	
+	UObject*	GetOwner() const { return OwnerActor; }
+	UWorld*		GetWorld() const { return OwnerActor->GetWorld(); };
+	
 	const	FEzAbilityInstanceData* GetInstanceData()			const	{ return &InstanceData; }
 			FEzAbilityInstanceData* GetMutableInstanceData()			{ return &InstanceData; }
 
@@ -62,9 +65,9 @@ public:
 
 protected:
 	void Reset();
-
-	void UpdateInstanceData(TConstArrayView<FEzAbilityFrame> CurrentActiveFrames, TArrayView<FEzAbilityFrame> NextActiveFrames);
-	
+	FString GetInstanceDescription() const;
+	void UpdateInstanceData(TConstArrayView<FEzAbilityExecutionFrame> CurrentActiveFrames, TArrayView<FEzAbilityExecutionFrame> NextActiveFrames);
+	void StopTemporaryEvaluatorsAndGlobalTasks(TArrayView<FEzAbilityTemporaryInstanceData> TempInstances);
 protected:
 	UPROPERTY(Transient)
 	FEzAbilityInstanceData			InstanceData;
@@ -88,4 +91,58 @@ protected:
 
 	UPROPERTY(Transient)
 	FEzAbilityTarget				Target;
+
+public:
+	/** Helper struct to track currently processed frame. */
+	struct FCurrentlyProcessedFrameScope
+	{
+		FCurrentlyProcessedFrameScope(FEzAbilityContext& InContext, const FEzAbilityExecutionFrame* CurrentParentFrame, const FEzAbilityExecutionFrame& CurrentFrame);
+
+		~FCurrentlyProcessedFrameScope();
+
+	private:
+		FEzAbilityContext& Context;
+		int32 SavedFrameIndex = 0;
+		FEzAbilityInstanceStorage* SavedSharedInstanceDataStorage = nullptr;
+		const FEzAbilityExecutionFrame* SavedFrame = nullptr;
+		const FEzAbilityExecutionFrame* SavedParentFrame = nullptr;
+	};
+
+	const FEzAbilityExecutionFrame* CurrentlyProcessedParentFrame = nullptr; 
+	const FEzAbilityExecutionFrame* CurrentlyProcessedFrame = nullptr;
+	FEzAbilityInstanceStorage* CurrentlyProcessedSharedInstanceStorage = nullptr;
+
+
+	
+	/** Helper struct to set current node data. */
+	struct FNodeInstanceDataScope
+	{
+		FNodeInstanceDataScope(FEzAbilityContext& InContext, const FEzAbilityDataHandle InNodeDataHandle, const FEzAbilityDataView InNodeInstanceData)
+			: Context(InContext)
+		{
+			SavedNodeDataHandle = Context.CurrentNodeDataHandle;
+			SavedNodeInstanceData = Context.CurrentNodeInstanceData;
+			Context.CurrentNodeDataHandle = InNodeDataHandle;
+			Context.CurrentNodeInstanceData = InNodeInstanceData;
+		}
+
+		~FNodeInstanceDataScope()
+		{
+			Context.CurrentNodeDataHandle = SavedNodeDataHandle;
+			Context.CurrentNodeInstanceData = SavedNodeInstanceData;
+		}
+
+	private:
+		FEzAbilityContext& Context;
+		FEzAbilityDataHandle SavedNodeDataHandle;
+		FEzAbilityDataView SavedNodeInstanceData;
+	};
+	
+	FEzAbilityDataHandle CurrentNodeDataHandle;
+	FEzAbilityDataView CurrentNodeInstanceData;
+
+protected:
+#if WITH_EZABILITY_DEBUGGER
+	FEzAbilityInstanceDebugId GetInstanceDebugId() const;
+#endif // WITH_STATETREE_DEBUGGER
 };

+ 59 - 0
Ability/Plugins/EzAbility/Source/EzAbility/Public/EzAbilityEvents.h

@@ -0,0 +1,59 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#pragma once
+
+#include "CoreMinimal.h"
+#include "GameplayTagContainer.h"
+#include "StructView.h"
+#include "UObject/Object.h"
+#include "EzAbilityEvents.generated.h"
+
+USTRUCT(BlueprintType)
+struct EZABILITY_API FEzAbilityEvent
+{
+	GENERATED_BODY()
+
+	FEzAbilityEvent() = default;
+
+	explicit FEzAbilityEvent(const FGameplayTag InTag)
+		: Tag(InTag)
+	{
+	}
+	
+	explicit FEzAbilityEvent(const FGameplayTag InTag, const FConstStructView InPayload, const FName InOrigin)
+		: Tag(InTag)
+		, Payload(InPayload)
+		, Origin(InOrigin)
+	{
+	}
+	
+	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Default", meta=(Categories="Ability Event"))
+	FGameplayTag Tag;
+	
+	UPROPERTY(EditAnywhere, Category = "Default")
+	FInstancedStruct Payload;
+
+	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Default")
+	FName Origin;
+};
+
+
+USTRUCT()
+struct EZABILITY_API FEzAbilityEventQueue
+{
+	GENERATED_BODY()
+
+	static constexpr int32 MaxActiveEvents = 64;
+
+	TConstArrayView<FEzAbilityEvent> GetEvents() const { return Events; }
+	void Reset() { Events.Reset(); }
+	void SendEvent(const UObject* Owner, const FGameplayTag& Tag, const FConstStructView Payload = FConstStructView(), const FName Origin = FName());
+
+protected:
+	TArray<FEzAbilityEvent>& GetEventsArray() { return Events; };
+
+	UPROPERTY()
+	TArray<FEzAbilityEvent> Events;
+
+	friend struct FEzAbilityInstanceData;
+};

+ 504 - 0
Ability/Plugins/EzAbility/Source/EzAbility/Public/EzAbilityExecutionTypes.h

@@ -0,0 +1,504 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#pragma once
+
+#include "CoreMinimal.h"
+#include "EzAbilityIndexTypes.h"
+#include "EzAbilityTypes.h"
+#include "UObject/Object.h"
+#include "EzAbilityExecutionTypes.generated.h"
+
+UENUM()
+enum class EEzAbilityUpdatePhase : uint8
+{
+	Unset					= 0,
+	Start					UMETA(DisplayName = "Start"),
+	Stop					UMETA(DisplayName = "Stop"),
+	StartGlobalTasks		UMETA(DisplayName = "Start Global Tasks & Evaluators"),
+	StopGlobalTasks			UMETA(DisplayName = "Stop Global Tasks & Evaluators"),
+	Tick					UMETA(DisplayName = "Tick"),
+	ApplyTransitions		UMETA(DisplayName = "Transition"),
+	TriggerTransitions		UMETA(DisplayName = "Trigger Transitions"),
+	TickingGlobalTasks		UMETA(DisplayName = "Tick Global Tasks & Evaluators"),
+	TickingTasks			UMETA(DisplayName = "Tick Tasks"),
+	TransitionConditions	UMETA(DisplayName = "Transition conditions"),
+	StateSelection			UMETA(DisplayName = "Try Enter"),
+	TrySelectBehavior		UMETA(DisplayName = "Try Select Behavior"),
+	EnterConditions			UMETA(DisplayName = "Enter conditions"),
+	EnterStates				UMETA(DisplayName = "Enter States"),
+	ExitStates				UMETA(DisplayName = "Exit States"),
+	StateCompleted			UMETA(DisplayName = "State(s) Completed")
+};
+
+UENUM()
+enum class EEzAbilityStateChangeType : uint8
+{
+	/** Not an activation */
+	None,
+	
+	/** The state became activated or deactivated. */
+	Changed,
+	
+	/** The state is parent of new active state and sustained previous active state. */
+	Sustained,
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+///Debug
+#if WITH_EZABILITY_DEBUGGER
+struct EZABILITY_API FEzAbilityInstanceDebugId
+{
+	FEzAbilityInstanceDebugId() = default;
+	FEzAbilityInstanceDebugId(const uint32 InstanceId, const uint32 SerialNumber)
+		: Id(InstanceId), SerialNumber(SerialNumber)
+	{
+	}
+	
+	bool IsValid() const { return Id != INDEX_NONE && SerialNumber != INDEX_NONE; }
+	bool IsInvalid() const { return !IsValid(); }
+	void Reset() { *this = Invalid; }
+
+	bool operator==(const FEzAbilityInstanceDebugId& Other) const
+	{
+		return Id == Other.Id && SerialNumber == Other.SerialNumber;
+	}
+
+	bool operator!=(const FEzAbilityInstanceDebugId& Other) const
+	{
+		return !(*this == Other);
+	}
+
+	friend uint32 GetTypeHash(const FEzAbilityInstanceDebugId InstanceDebugId)
+	{
+		return HashCombine(InstanceDebugId.Id, InstanceDebugId.SerialNumber);
+	}
+
+	friend FString LexToString(const FEzAbilityInstanceDebugId InstanceDebugId)
+	{
+		return FString::Printf(TEXT("0x%x | %d"), InstanceDebugId.Id, InstanceDebugId.SerialNumber);
+	}
+
+	static const FEzAbilityInstanceDebugId Invalid;
+	
+	uint32 Id = INDEX_NONE;
+	uint32 SerialNumber = INDEX_NONE;
+};
+#endif
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+///TransitionRequest
+USTRUCT(BlueprintType)
+struct EZABILITY_API FEzAbilityTransitionRequest
+{
+	GENERATED_BODY()
+
+	FEzAbilityTransitionRequest() = default;
+
+	explicit FEzAbilityTransitionRequest(const FEzAbilityStateHandle InTargetState, const EEzAbilityTransitionPriority InPriority = EEzAbilityTransitionPriority::Normal)
+		: TargetState(InTargetState)
+		, Priority(InPriority)
+	{
+	}
+	
+	UPROPERTY()
+	FEzAbilityStateHandle SourceState;
+	
+	UPROPERTY()
+	TObjectPtr<const class UEzAbility> SourceAbility = nullptr;
+	
+	UPROPERTY()
+	FEzAbilityStateHandle SourceRootState = FEzAbilityStateHandle::Invalid;
+	
+	UPROPERTY(EditDefaultsOnly, Category = "Default")
+	FEzAbilityStateHandle TargetState;
+
+	UPROPERTY(EditDefaultsOnly, Category = "Default")
+	EEzAbilityTransitionPriority Priority = EEzAbilityTransitionPriority::Normal;
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+///ActiveStates
+USTRUCT(BlueprintType)
+struct EZABILITY_API FEzAbilityActiveStates
+{
+	GENERATED_BODY()
+
+	static constexpr uint8 MaxStates = 8;	// Max number of active states
+
+	FEzAbilityActiveStates() = default;
+	
+	explicit FEzAbilityActiveStates(const FEzAbilityStateHandle StateHandle)
+	{
+		Push(StateHandle);
+	}
+	
+	/** Resets the active state array to empty. */
+	void Reset()
+	{
+		NumStates = 0;
+	}
+
+	/** Pushes new state at the back of the array and returns true if there was enough space. */
+	bool Push(const FEzAbilityStateHandle StateHandle)
+	{
+		if ((NumStates + 1) > MaxStates)
+		{
+			return false;
+		}
+		
+		States[NumStates++] = StateHandle;
+		
+		return true;
+	}
+	
+	bool PushFront(const FEzAbilityStateHandle StateHandle)
+	{
+		if ((NumStates + 1) > MaxStates)
+		{
+			return false;
+		}
+
+		NumStates++;
+		for (int32 Index = (int32)NumStates - 1; Index > 0; Index--)
+		{
+			States[Index] = States[Index - 1];
+		}
+		States[0] = StateHandle;
+		
+		return true;
+	}
+
+	/** Pops a state from the back of the array and returns the popped value, or invalid handle if the array was empty. */
+	FEzAbilityStateHandle Pop()
+	{
+		if (NumStates == 0)
+		{
+			return FEzAbilityStateHandle::Invalid;			
+		}
+
+		const FEzAbilityStateHandle Ret = States[NumStates - 1];
+		NumStates--;
+		return Ret;
+	}
+	
+	void SetNum(const int32 NewNum)
+	{
+		check(NewNum >= 0 && NewNum <= MaxStates);
+		if (NewNum > (int32)NumStates)
+		{
+			for (int32 Index = NumStates; Index < NewNum; Index++)
+			{
+				States[Index] = FEzAbilityStateHandle::Invalid;
+			}
+		}
+		NumStates = static_cast<uint8>(NewNum);
+	}
+	
+	bool Contains(const FEzAbilityStateHandle StateHandle) const
+	{
+		for (const FEzAbilityStateHandle& Handle : *this)
+		{
+			if (Handle == StateHandle)
+			{
+				return true;
+			}
+		}
+		return false;
+	}
+	
+	bool Contains(const FEzAbilityStateHandle StateHandle, const uint8 MaxNumStatesToCheck) const
+	{
+		const int32 Num = (int32)FMath::Min(NumStates, MaxNumStatesToCheck);
+		for (int32 Index = 0; Index < Num; Index++)
+		{
+			if (States[Index] == StateHandle)
+			{
+				return true;
+			}
+		}
+		return false;
+	}
+	
+	int32 IndexOfReverse(const FEzAbilityStateHandle StateHandle) const
+	{
+		for (int32 Index = (int32)NumStates - 1; Index >= 0; Index--)
+		{
+			if (States[Index] == StateHandle)
+				return Index;
+		}
+		return INDEX_NONE;
+	}
+	
+	FEzAbilityStateHandle Last() const { return NumStates > 0 ? States[NumStates - 1] : FEzAbilityStateHandle::Invalid; }
+	
+	int32	Num()							const { return NumStates; }
+	bool	IsValidIndex(const int32 Index) const { return Index >= 0 && Index < (int32)NumStates; }
+	bool	IsEmpty()						const { return NumStates == 0; } 
+
+	FORCEINLINE FEzAbilityStateHandle operator[](const int32 Index) const
+	{
+		check(Index >= 0 && Index < (int32)NumStates);
+		return States[Index];
+	}
+
+	FORCEINLINE FEzAbilityStateHandle& operator[](const int32 Index)
+	{
+		check(Index >= 0 && Index < (int32)NumStates);
+		return States[Index];
+	}
+	
+	FEzAbilityStateHandle GetStateSafe(const int32 Index) const
+	{
+		return (Index >= 0 && Index < (int32)NumStates) ? States[Index] : FEzAbilityStateHandle::Invalid;
+	}
+	
+	FORCEINLINE FEzAbilityStateHandle* begin() { return &States[0]; }
+	FORCEINLINE FEzAbilityStateHandle* end  () { return &States[0] + Num(); }
+	FORCEINLINE const FEzAbilityStateHandle* begin() const { return &States[0]; }
+	FORCEINLINE const FEzAbilityStateHandle* end  () const { return &States[0] + Num(); }
+
+
+	UPROPERTY(EditDefaultsOnly, Category = Default)
+	FEzAbilityStateHandle States[MaxStates];
+
+	UPROPERTY(EditDefaultsOnly, Category = Default)
+	uint8 NumStates = 0;
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+///TransitionSource
+UENUM()
+enum class EEzAbilityTransitionSourceType : uint8
+{
+	Unset,
+	Asset,
+	ExternalRequest,
+	Internal
+};
+
+USTRUCT()
+struct EZABILITY_API FEzAbilityTransitionSource
+{
+	GENERATED_BODY()
+
+	FEzAbilityTransitionSource() = default;
+
+	explicit FEzAbilityTransitionSource(const EEzAbilityTransitionSourceType SourceType, const FEzAbilityIndex16 TransitionIndex, const FEzAbilityStateHandle TargetState, const EEzAbilityTransitionPriority Priority)
+		: SourceType(SourceType)
+		, TransitionIndex(TransitionIndex)
+		, TargetState(TargetState)
+		, Priority(Priority)
+	{
+	}
+
+	explicit FEzAbilityTransitionSource(const FEzAbilityIndex16 TransitionIndex, const FEzAbilityStateHandle TargetState, const EEzAbilityTransitionPriority Priority)
+		: FEzAbilityTransitionSource(EEzAbilityTransitionSourceType::Asset, TransitionIndex, TargetState, Priority)
+	{
+	}
+
+	explicit FEzAbilityTransitionSource(const EEzAbilityTransitionSourceType SourceType, const FEzAbilityStateHandle TargetState, const EEzAbilityTransitionPriority Priority)
+		: FEzAbilityTransitionSource(SourceType, FEzAbilityIndex16::Invalid, TargetState, Priority)
+	{
+	}
+
+	void Reset()
+	{
+		*this = {};
+	}
+	
+	EEzAbilityTransitionSourceType	SourceType		= EEzAbilityTransitionSourceType::Unset;
+	FEzAbilityIndex16				TransitionIndex;
+	FEzAbilityStateHandle			TargetState		= FEzAbilityStateHandle::Invalid;
+	EEzAbilityTransitionPriority	Priority		= EEzAbilityTransitionPriority::None;
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+///TransitionDelayedState
+USTRUCT()
+struct EZABILITY_API FEzAbilityTransitionDelayedState
+{
+	GENERATED_BODY()
+
+	UPROPERTY()
+	TObjectPtr<const UEzAbility> Ability = nullptr;
+
+	UPROPERTY()
+	FEzAbilityIndex16 TransitionIndex = FEzAbilityIndex16::Invalid;
+
+	UPROPERTY()
+	float TimeLeft = 0.0f;
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+///Frame
+USTRUCT(BlueprintType)
+struct EZABILITY_API FEzAbilityExecutionFrame
+{
+	GENERATED_BODY()
+
+	bool IsSameFrame(const FEzAbilityExecutionFrame& OtherFrame) const
+	{
+		return Ability == OtherFrame.Ability && RootState == OtherFrame.RootState;
+	}
+	
+	UPROPERTY()
+	TObjectPtr<class UEzAbility> Ability = nullptr;
+
+	UPROPERTY()
+	FEzAbilityActiveStates ActiveStates;
+
+	UPROPERTY()
+	FEzAbilityIndex16 ExternalDataBaseIndex = FEzAbilityIndex16::Invalid;
+	
+	UPROPERTY()
+	FEzAbilityIndex16 GlobalInstanceIndexBase = FEzAbilityIndex16::Invalid;
+	
+	UPROPERTY()
+	FEzAbilityIndex16 ActiveInstanceIndexBase = FEzAbilityIndex16::Invalid;
+	
+	UPROPERTY()
+	FEzAbilityDataHandle StateParameterDataHandle = FEzAbilityDataHandle::Invalid; 
+
+	UPROPERTY()
+	FEzAbilityDataHandle GlobalParameterDataHandle = FEzAbilityDataHandle::Invalid; 
+
+	/** Number of states in ActiveStates which have instance data. Used during state selection to decide which active state data is safe to access. */
+	uint8 NumCurrentlyActiveStates = 0;
+	
+	UPROPERTY()
+	uint8 bIsGlobalFrame : 1 = false;
+
+	UPROPERTY()
+	FEzAbilityStateHandle RootState = FEzAbilityStateHandle::Root; 
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+///Exe
+USTRUCT()
+struct EZABILITY_API FEzAbilityExecutionState
+{
+	GENERATED_BODY()
+
+	PRAGMA_DISABLE_DEPRECATION_WARNINGS
+	FEzAbilityExecutionState() = default;
+	FEzAbilityExecutionState(const FEzAbilityExecutionState&) = default;
+	FEzAbilityExecutionState(FEzAbilityExecutionState&&) = default;
+	FEzAbilityExecutionState& operator=(const FEzAbilityExecutionState&) = default;
+	FEzAbilityExecutionState& operator=(FEzAbilityExecutionState&&) = default;
+	PRAGMA_ENABLE_DEPRECATION_WARNINGS
+	
+	void Reset()
+	{
+		ActiveFrames.Reset();
+		EnterStateFailedTaskIndex = FEzAbilityIndex16::Invalid;
+		LastTickStatus = EAbilityRunStatus::Failed;
+		TreeRunStatus = EAbilityRunStatus::Unset;
+		RequestedStop = EAbilityRunStatus::Unset;
+		CurrentPhase = EEzAbilityUpdatePhase::Unset;
+		CompletedStateHandle = FEzAbilityStateHandle::Invalid;
+		StateChangeCount = 0;
+	}
+
+	FEzAbilityTransitionDelayedState* FindDelayedTransition(const UEzAbility* Ability, const FEzAbilityIndex16 TransitionIndex)
+	{
+		return DelayedTransitions.FindByPredicate([Ability, TransitionIndex](const FEzAbilityTransitionDelayedState& TransitionState)
+		{
+			return TransitionState.Ability == Ability && TransitionState.TransitionIndex == TransitionIndex;
+		});
+	}
+	
+	UPROPERTY()
+	TArray<FEzAbilityExecutionFrame> ActiveFrames;
+	
+	UPROPERTY()
+	TArray<FEzAbilityTransitionDelayedState> DelayedTransitions;
+	
+	UPROPERTY()
+	FEzAbilityIndex16 EnterStateFailedFrameIndex = FEzAbilityIndex16::Invalid;
+	
+	UPROPERTY()
+	FEzAbilityIndex16 EnterStateFailedTaskIndex = FEzAbilityIndex16::Invalid;
+	
+	UPROPERTY()
+	EAbilityRunStatus LastTickStatus = EAbilityRunStatus::Failed;
+	
+	UPROPERTY()
+	EAbilityRunStatus TreeRunStatus = EAbilityRunStatus::Unset;
+	
+	UPROPERTY()
+	EAbilityRunStatus RequestedStop = EAbilityRunStatus::Unset;
+	
+	UPROPERTY()
+	EEzAbilityUpdatePhase CurrentPhase = EEzAbilityUpdatePhase::Unset;
+	
+	UPROPERTY()
+	FEzAbilityIndex16 CompletedFrameIndex = FEzAbilityIndex16::Invalid; 
+	
+	UPROPERTY()
+	FEzAbilityStateHandle CompletedStateHandle = FEzAbilityStateHandle::Invalid;
+
+	UPROPERTY()
+	uint16 StateChangeCount = 0;
+
+
+#if WITH_EZABILITY_DEBUGGER
+	/** Id for the active instance used for debugging. */
+	mutable FEzAbilityInstanceDebugId InstanceDebugId;
+#endif
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+///TransitionResult
+USTRUCT(BlueprintType)
+struct EZABILITY_API FEzAbilityTransitionResult
+{
+	GENERATED_BODY()
+
+PRAGMA_DISABLE_DEPRECATION_WARNINGS
+	FEzAbilityTransitionResult() = default;
+	FEzAbilityTransitionResult(const FEzAbilityTransitionResult&) = default;
+	FEzAbilityTransitionResult(FEzAbilityTransitionResult&&) = default;
+	FEzAbilityTransitionResult& operator=(const FEzAbilityTransitionResult&) = default;
+	FEzAbilityTransitionResult& operator=(FEzAbilityTransitionResult&&) = default;
+PRAGMA_ENABLE_DEPRECATION_WARNINGS
+	
+	void Reset()
+	{
+		NextActiveFrames.Reset();
+		CurrentRunStatus = EAbilityRunStatus::Unset;
+		TargetState = FEzAbilityStateHandle::Invalid;
+		CurrentState = FEzAbilityStateHandle::Invalid;
+		ChangeType = EEzAbilityStateChangeType::Changed;
+		Priority = EEzAbilityTransitionPriority::None;
+	}
+	
+	UPROPERTY(EditDefaultsOnly, Category = "Default", BlueprintReadOnly)
+	TArray<FEzAbilityExecutionFrame> NextActiveFrames;
+	
+	UPROPERTY(EditDefaultsOnly, Category = "Default", BlueprintReadOnly)
+	EAbilityRunStatus CurrentRunStatus = EAbilityRunStatus::Unset;
+	
+	UPROPERTY(EditDefaultsOnly, Category = "Default", BlueprintReadOnly)
+	FEzAbilityStateHandle SourceState = FEzAbilityStateHandle::Invalid;
+	
+	UPROPERTY(EditDefaultsOnly, Category = "Default", BlueprintReadOnly)
+	FEzAbilityStateHandle TargetState = FEzAbilityStateHandle::Invalid;
+	
+	UPROPERTY(EditDefaultsOnly, Category = "Default", BlueprintReadOnly)
+	FEzAbilityStateHandle CurrentState = FEzAbilityStateHandle::Invalid;
+	
+	UPROPERTY(EditDefaultsOnly, Category = "Default", BlueprintReadOnly)
+	EEzAbilityStateChangeType ChangeType = EEzAbilityStateChangeType::Changed; 
+
+	/** Priority of the transition that caused the state change. */
+	UPROPERTY(EditDefaultsOnly, Category = "Default", BlueprintReadOnly)
+	EEzAbilityTransitionPriority Priority = EEzAbilityTransitionPriority::None;
+
+	UPROPERTY(EditDefaultsOnly, Category = "Default", BlueprintReadOnly)
+	TObjectPtr<const class UEzAbility> SourceAbility = nullptr;
+
+	UPROPERTY(EditDefaultsOnly, Category = "Default", BlueprintReadOnly)
+	FEzAbilityStateHandle SourceRootState = FEzAbilityStateHandle::Invalid;
+	
+};
+

+ 104 - 0
Ability/Plugins/EzAbility/Source/EzAbility/Public/EzAbilityIndexTypes.h

@@ -0,0 +1,104 @@
+// Fill out your copyright notice in the Description page of Project Settings.
+
+#pragma once
+
+#include "CoreMinimal.h"
+#include "UObject/Object.h"
+#include "EzAbilityIndexTypes.generated.h"
+
+USTRUCT(BlueprintType)
+struct EZABILITY_API FEzAbilityIndex16
+{
+	GENERATED_BODY()
+
+	static constexpr uint16 InvalidValue = MAX_uint16;
+	static const FEzAbilityIndex16 Invalid;
+
+	friend FORCEINLINE uint32 GetTypeHash(const FEzAbilityIndex16 Index)
+	{
+		return GetTypeHash(Index.Value);
+	}
+	
+	static bool IsValidIndex(const int32 Index)
+	{
+		return Index >= 0 && Index < (int32)MAX_uint16;
+	}
+
+	FEzAbilityIndex16() = default;
+	
+	explicit FEzAbilityIndex16(const uint16 InIndex) : Value(InIndex)
+	{
+	}
+	
+	explicit FEzAbilityIndex16(const int32 InIndex)
+	{
+		check(InIndex == INDEX_NONE || IsValidIndex(InIndex));
+		Value = InIndex == INDEX_NONE ? InvalidValue : (uint16)InIndex;
+	}
+	
+	uint16 Get() const { return Value; }
+	int32 AsInt32() const { return Value == InvalidValue ? INDEX_NONE : Value; }
+	bool IsValid() const { return Value != InvalidValue; }
+
+	bool operator==(const FEzAbilityIndex16& RHS) const { return Value == RHS.Value; }
+	bool operator!=(const FEzAbilityIndex16& RHS) const { return Value != RHS.Value; }
+
+	bool SerializeFromMismatchedTag(const FPropertyTag& Tag, FStructuredArchive::FSlot Slot);
+
+protected:
+	UPROPERTY()
+	uint16 Value = InvalidValue;
+};
+
+template<>
+struct TStructOpsTypeTraits<FEzAbilityIndex16> : public TStructOpsTypeTraitsBase2<FEzAbilityIndex16>
+{
+	enum
+	{
+		WithStructuredSerializeFromMismatchedTag = true,
+	};
+};
+
+USTRUCT(BlueprintType)
+struct EZABILITY_API FEzAbilityIndex8
+{
+	GENERATED_BODY()
+
+	static constexpr uint8 InvalidValue = MAX_uint8;
+	static const FEzAbilityIndex8 Invalid;
+	
+	static bool IsValidIndex(const int32 Index)
+	{
+		return Index >= 0 && Index < (int32)MAX_uint8;
+	}
+	
+	FEzAbilityIndex8() = default;
+	
+	explicit FEzAbilityIndex8(const int32 InIndex)
+	{
+		check(InIndex == INDEX_NONE || IsValidIndex(InIndex));
+		Value = InIndex == INDEX_NONE ? InvalidValue : (uint8)InIndex;
+	}
+	
+	uint8 Get() const { return Value; }
+	int32 AsInt32() const { return Value == InvalidValue ? INDEX_NONE : Value; }
+	bool IsValid() const { return Value != InvalidValue; }
+
+	bool operator==(const FEzAbilityIndex8& RHS) const { return Value == RHS.Value; }
+	bool operator!=(const FEzAbilityIndex8& RHS) const { return Value != RHS.Value; }
+
+	bool SerializeFromMismatchedTag(const FPropertyTag& Tag, FStructuredArchive::FSlot Slot);
+	
+protected:
+	UPROPERTY()
+	uint8 Value = InvalidValue;
+};
+
+template<>
+struct TStructOpsTypeTraits<FEzAbilityIndex8> : public TStructOpsTypeTraitsBase2<FEzAbilityIndex8>
+{
+	enum
+	{
+		WithStructuredSerializeFromMismatchedTag = true,
+	};
+};

+ 50 - 20
Ability/Plugins/EzAbility/Source/EzAbility/Public/EzAbilityInstanceData.h

@@ -3,9 +3,12 @@
 #pragma once
 
 #include "CoreMinimal.h"
+#include "EzAbilityIndexTypes.h"
 #include "EzAbilityTypes.h"
+#include "EzAbilityEvents.h"
+#include "EzAbilityExecutionTypes.h"
 #include "InstancedStructContainer.h"
-#include "StructUtils/PropertyBag.h"
+#include "PropertyBag.h"
 #include "UObject/Object.h"
 #include "EzAbilityInstanceData.generated.h"
 
@@ -27,16 +30,16 @@ struct EZABILITY_API FEzAbilityTemporaryInstanceData
 	GENERATED_BODY()
 
 	UPROPERTY()
-	TObjectPtr<const UEzAbility> Ability = nullptr;
+	TObjectPtr<class UEzAbility> Ability = nullptr;
 
-	// UPROPERTY()
-	// FStateTreeStateHandle RootState = FStateTreeStateHandle::Invalid;
-	//
-	// UPROPERTY()
-	// FStateTreeDataHandle DataHandle = FStateTreeDataHandle::Invalid;
-	//
-	// UPROPERTY()
-	// FStateTreeIndex16 OwnerNodeIndex = FStateTreeIndex16::Invalid; 
+	UPROPERTY()
+	FEzAbilityStateHandle RootState = FEzAbilityStateHandle::Invalid;
+	
+	UPROPERTY()
+	FEzAbilityDataHandle DataHandle = FEzAbilityDataHandle::Invalid;
+	
+	UPROPERTY()
+	FEzAbilityIndex16 OwnerNodeIndex = FEzAbilityIndex16::Invalid; 
 	
 	UPROPERTY()
 	FInstancedStruct Instance;
@@ -51,9 +54,11 @@ struct EZABILITY_API FEzAbilityInstanceStorage
 public:
 
 	void Reset();
-	
-	const	FEzAbilityExecutionState& GetExecutionState()			const	{ return ExecutionState; }
-			FEzAbilityExecutionState& GetMutableExecutionState()			{ return ExecutionState; }
+
+	const	FEzAbilityEventQueue&		GetEventQueue()				const	{ return EventQueue; }
+			FEzAbilityEventQueue&		GetMutableEventQueue()				{ return EventQueue; }
+	const	FEzAbilityExecutionState&	GetExecutionState()			const	{ return ExecutionState; }
+			FEzAbilityExecutionState&	GetMutableExecutionState()			{ return ExecutionState; }
 	
 	FConstStructView	GetStruct		(const int32 Index) const	{ return InstanceStructs[Index]; }
 	FStructView			GetMutableStruct(const int32 Index)			{ return InstanceStructs[Index]; }
@@ -66,12 +71,20 @@ public:
 
 	bool AreAllInstancesValid() const;
 	void ResetTemporaryInstances();
+	FStructView AddTemporaryInstance(UObject& InOwner, const FEzAbilityExecutionFrame& Frame, const FEzAbilityIndex16 OwnerNodeIndex, const FEzAbilityDataHandle DataHandle, FConstStructView NewInstanceData);
+	FStructView GetMutableTemporaryStruct(const FEzAbilityExecutionFrame& Frame, const FEzAbilityDataHandle DataHandle);
+	UObject*	GetMutableTemporaryObject(const FEzAbilityExecutionFrame& Frame, const FEzAbilityDataHandle DataHandle);
 	
 	TArrayView<FEzAbilityTemporaryInstanceData> GetMutableTemporaryInstances() { return TemporaryInstances; }
 	
 	void				SetGlobalParameters(const FInstancedPropertyBag& Parameters);
 	FConstStructView	GetGlobalParameters()			const	{ return GlobalParameters.GetValue(); }
 	FStructView			GetMutableGlobalParameters()			{ return GlobalParameters.GetMutableValue(); }
+
+	void ResetTransitionRequests();
+	void AddTransitionRequest(const UObject* Owner, const FEzAbilityTransitionRequest& Request);
+	TConstArrayView<FEzAbilityTransitionRequest> GetTransitionRequests() const { return TransitionRequests; }
+	
 protected:
 	UPROPERTY()
 	FEzAbilityExecutionState				ExecutionState;
@@ -85,9 +98,11 @@ protected:
 	UPROPERTY(Transient)
 	FInstancedPropertyBag					GlobalParameters;
 	
-	//event
-	//transitions
-
+	UPROPERTY()
+	FEzAbilityEventQueue					EventQueue;
+	
+	UPROPERTY()
+	TArray<FEzAbilityTransitionRequest>		TransitionRequests;
 };
 
 /**
@@ -137,12 +152,27 @@ public:
 			UObject*	GetMutableObject	(const int32 Index) const { return GetMutableStorage().GetMutableObject(Index); }
 	const	UObject*	GetObject			(const int32 Index) const { return GetStorage().GetObject(Index); }
 	
-	const	FEzAbilityExecutionState* GetExecutionState()			const { return &GetStorage().GetExecutionState(); }
-			FEzAbilityExecutionState* GetMutableExecutionState()	const { return &GetMutableStorage().GetMutableExecutionState(); }
-
+	const	FEzAbilityExecutionState*	GetExecutionState()			const { return &GetStorage().GetExecutionState(); }
+			FEzAbilityExecutionState*	GetMutableExecutionState()	const { return &GetMutableStorage().GetMutableExecutionState(); }
+	const	FEzAbilityEventQueue&		GetEventQueue()				const { return GetStorage().EventQueue; }
+			FEzAbilityEventQueue&		GetMutableEventQueue()		const { return GetMutableStorage().EventQueue; }
+	
 	void ResetTemporaryInstances() const { return GetMutableStorage().ResetTemporaryInstances(); }
 
-	
+	FStructView AddTemporaryInstance(UObject& InOwner, const FEzAbilityExecutionFrame& Frame, const FEzAbilityIndex16 OwnerNodeIndex, const FEzAbilityDataHandle DataHandle, FConstStructView NewInstanceData) const
+	{
+		return GetMutableStorage().AddTemporaryInstance(InOwner, Frame, OwnerNodeIndex, DataHandle, NewInstanceData);
+	}
+
+	FStructView GetMutableTemporaryStruct(const FEzAbilityExecutionFrame& Frame, const FEzAbilityDataHandle DataHandle) const
+	{
+		return GetMutableStorage().GetMutableTemporaryStruct(Frame, DataHandle);
+	}
+
+	UObject* GetMutableTemporaryObject(const FEzAbilityExecutionFrame& Frame, const FEzAbilityDataHandle DataHandle) const
+	{
+		return GetMutableStorage().GetMutableTemporaryObject(Frame, DataHandle);
+	}
 protected:
 	TSharedRef<FEzAbilityInstanceStorage> InstanceStorage = MakeShared<FEzAbilityInstanceStorage>();
 };

+ 3 - 0
Ability/Plugins/EzAbility/Source/EzAbility/Public/EzAbilityLog.h

@@ -5,3 +5,6 @@
 #include "CoreMinimal.h"
 
 EZABILITY_API DECLARE_LOG_CATEGORY_EXTERN(LogEzAbility, Log, All);
+
+#define EZ_ABILITY_LOG(Verbosity, Format, ...) UE_VLOG(GetOwner(), LogEzAbility, Verbosity, Format, ##__VA_ARGS__)
+#define EZ_ABILITY_CLOG(Condition, Verbosity, Format, ...) UE_CVLOG((Condition), GetOwner(), LogEzAbility, Verbosity, Format, ##__VA_ARGS__)

+ 15 - 0
Ability/Plugins/EzAbility/Source/EzAbility/Public/EzAbilityNodeBase.h

@@ -3,6 +3,8 @@
 #pragma once
 
 #include "CoreMinimal.h"
+#include "EzAbilityIndexTypes.h"
+#include "EzAbilityTypes.h"
 #include "UObject/Object.h"
 #include "EzAbilityNodeBase.generated.h"
 
@@ -26,5 +28,18 @@ PRAGMA_ENABLE_DEPRECATION_WARNINGS
 
 	virtual UStruct* GetInstanceDataType() const { return nullptr; }
 	virtual bool Link() { return false; }
+
+
+	UPROPERTY(EditDefaultsOnly, Category = "", meta=(EditCondition = "false", EditConditionHides))
+	FName Name;
+	
+	UPROPERTY()
+	FEzAbilityIndex16		BindingsBatch			= FEzAbilityIndex16::Invalid;
 	
+	UPROPERTY()
+	FEzAbilityIndex16		InstanceTemplateIndex	= FEzAbilityIndex16::Invalid;
+	
+	UPROPERTY()
+	FEzAbilityDataHandle	InstanceDataHandle		= FEzAbilityDataHandle::Invalid;
 };
+

+ 413 - 24
Ability/Plugins/EzAbility/Source/EzAbility/Public/EzAbilityTypes.h

@@ -3,9 +3,56 @@
 #pragma once
 
 #include "CoreMinimal.h"
+#include "EzAbilityIndexTypes.h"
+#include "PropertyBag.h"
 #include "UObject/Object.h"
 #include "EzAbilityTypes.generated.h"
 
+UENUM()
+enum class EEzAbilityTraceEventType : uint8
+{
+	Unset,
+	OnEntering				UMETA(DisplayName = "Entering"),
+	OnEntered				UMETA(DisplayName = "Entered"),
+	OnExiting				UMETA(DisplayName = "Exiting"),
+	OnExited				UMETA(DisplayName = "Exited"),
+	Push					UMETA(DisplayName = "Push"),
+	Pop						UMETA(DisplayName = "Pop"),
+	OnStateSelected			UMETA(DisplayName = "Selected"),
+	OnStateCompleted		UMETA(DisplayName = "Completed"),
+	OnTicking				UMETA(DisplayName = "Tick"),
+	OnTaskCompleted			UMETA(DisplayName = "Completed"),
+	OnTicked				UMETA(DisplayName = "Ticked"),
+	Passed					UMETA(DisplayName = "Passed"),
+	Failed					UMETA(DisplayName = "Failed"),
+	ForcedSuccess			UMETA(DisplayName = "Forced Success"),
+	ForcedFailure			UMETA(DisplayName = "Forced Failure"),
+	InternalForcedFailure	UMETA(DisplayName = "Internal Forced Failure"),
+	OnEvaluating			UMETA(DisplayName = "Evaluating"),
+	OnTransition			UMETA(DisplayName = "Transition"),
+	OnAbilityStarted		UMETA(DisplayName = "Ability Started"),
+	OnAbilityStopped		UMETA(DisplayName = "Ability Stopped")
+
+};
+
+UENUM(BlueprintType)
+enum class EEzAbilityTransitionPriority : uint8
+{
+	None UMETA(Hidden),
+	
+	/** Normal priority. */
+	Normal,
+	
+	/** Medium priority. */
+	Medium,
+	
+	/** High priority. */
+	High,
+	
+	/** Critical priority. */
+	Critical,
+};
+
 UENUM(BlueprintType)
 enum class EAbilityRunStatus : uint8
 {
@@ -17,7 +64,7 @@ enum class EAbilityRunStatus : uint8
 };
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-///Slice
+///State
 UENUM()
 enum class ESliceType
 {
@@ -78,6 +125,168 @@ struct EZABILITY_API FEzAbilityStateHandle
 	uint16 Index = InvalidIndex;
 };
 
+UENUM()
+enum class EEzAbilityStateType : uint8
+{
+	State,
+	Group,
+	Linked,
+	LinkedAsset,
+	Subtree,
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+///Data Handle
+
+UENUM(BlueprintType)
+enum class EEzAbilityDataSourceType : uint8
+{
+	None UMETA(Hidden),
+	GlobalInstanceData,
+	GlobalInstanceDataObject,
+	ActiveInstanceData,
+	ActiveInstanceDataObject,
+	SharedInstanceData,
+	SharedInstanceDataObject,
+	ContextData,
+	ExternalData,
+	GlobalParameterData,
+	SubtreeParameterData,
+	StateParameterData,
+};
+
+USTRUCT(BlueprintType)
+struct EZABILITY_API FEzAbilityDataHandle
+{
+	GENERATED_BODY()
+
+	static const FEzAbilityDataHandle Invalid;
+	static constexpr uint16 InvalidIndex = MAX_uint16;
+
+	/** @return true if the given index can be represented by the type. */
+	static bool IsValidIndex(const int32 Index)
+	{
+		return Index >= 0 && Index < (int32)InvalidIndex;
+	}
+
+	friend FORCEINLINE uint32 GetTypeHash(const FEzAbilityDataHandle& Handle)
+	{
+		uint32 Hash = GetTypeHash(Handle.Source);
+		Hash = HashCombineFast(Hash, GetTypeHash(Handle.Source));
+		Hash = HashCombineFast(Hash, GetTypeHash(Handle.StateHandle));
+		return Hash;
+	}
+	
+	FEzAbilityDataHandle() = default;
+	
+	explicit FEzAbilityDataHandle(const EEzAbilityDataSourceType InSource, const uint16 InIndex, const FEzAbilityStateHandle InStateHandle = FEzAbilityStateHandle::Invalid)
+		: Source(InSource)
+		, Index(InIndex)
+		, StateHandle(InStateHandle)
+	{
+		// Require valid state for active instance data
+		check(Source != EEzAbilityDataSourceType::ActiveInstanceData || (Source == EEzAbilityDataSourceType::ActiveInstanceData && StateHandle.IsValid()));
+		check(Source != EEzAbilityDataSourceType::ActiveInstanceDataObject || (Source == EEzAbilityDataSourceType::ActiveInstanceDataObject && StateHandle.IsValid()));
+		check(Source == EEzAbilityDataSourceType::GlobalParameterData || InIndex != InvalidIndex);
+	}
+
+	explicit FEzAbilityDataHandle(const EEzAbilityDataSourceType InSource, const int32 InIndex, const FEzAbilityStateHandle InStateHandle = FEzAbilityStateHandle::Invalid)
+		: Source(InSource)
+		, StateHandle(InStateHandle)
+	{
+		// Require valid state for active instance data
+		check(Source != EEzAbilityDataSourceType::ActiveInstanceData || (Source == EEzAbilityDataSourceType::ActiveInstanceData && StateHandle.IsValid()));
+		check(Source != EEzAbilityDataSourceType::ActiveInstanceDataObject || (Source == EEzAbilityDataSourceType::ActiveInstanceDataObject && StateHandle.IsValid()));
+		check(Source == EEzAbilityDataSourceType::GlobalParameterData || IsValidIndex(InIndex));
+		Index = static_cast<uint16>(InIndex);
+	}
+
+	explicit FEzAbilityDataHandle(const EEzAbilityDataSourceType InSource)
+		: FEzAbilityDataHandle(InSource, FEzAbilityDataHandle::InvalidIndex)
+	{}
+	
+	void Reset()
+	{
+		Source = EEzAbilityDataSourceType::None;
+		Index = InvalidIndex;
+		StateHandle = FEzAbilityStateHandle::Invalid;
+	}
+
+	bool operator==(const FEzAbilityDataHandle& RHS) const { return Source == RHS.Source && Index == RHS.Index && StateHandle == RHS.StateHandle; }
+	bool operator!=(const FEzAbilityDataHandle& RHS) const { return !(*this == RHS); }
+
+	bool						IsValid()	const { return Source != EEzAbilityDataSourceType::None; }
+	EEzAbilityDataSourceType	GetSource() const { return Source; }
+	int32						GetIndex()	const { return Index; }
+	FEzAbilityStateHandle		GetState()	const { return StateHandle; }
+
+	bool IsObjectSource() const
+	{
+		return Source == EEzAbilityDataSourceType::GlobalInstanceDataObject
+			|| Source == EEzAbilityDataSourceType::ActiveInstanceDataObject
+			|| Source == EEzAbilityDataSourceType::SharedInstanceDataObject;
+	}
+	
+	FEzAbilityDataHandle ToObjectSource() const
+	{
+		switch (Source)
+		{
+		case EEzAbilityDataSourceType::GlobalInstanceData:
+			return FEzAbilityDataHandle(EEzAbilityDataSourceType::GlobalInstanceDataObject, Index, StateHandle);
+		case EEzAbilityDataSourceType::ActiveInstanceData:
+			return FEzAbilityDataHandle(EEzAbilityDataSourceType::ActiveInstanceDataObject, Index, StateHandle);
+		case EEzAbilityDataSourceType::SharedInstanceData:
+			return FEzAbilityDataHandle(EEzAbilityDataSourceType::SharedInstanceDataObject, Index, StateHandle);
+		default:
+			return *this;
+		}
+	}
+	
+	FString Describe() const
+	{
+
+		switch (Source)
+		{
+		case EEzAbilityDataSourceType::None:
+			return TEXT("None");
+		case EEzAbilityDataSourceType::GlobalInstanceData:
+			return FString::Printf(TEXT("Global[%d]"), Index);
+		case EEzAbilityDataSourceType::GlobalInstanceDataObject:
+			return FString::Printf(TEXT("GlobalO[%d]"), Index);
+		case EEzAbilityDataSourceType::ActiveInstanceData:
+			return FString::Printf(TEXT("Active[%d]"), Index);
+		case EEzAbilityDataSourceType::ActiveInstanceDataObject:
+			return FString::Printf(TEXT("ActiveO[%d]"), Index);
+		case EEzAbilityDataSourceType::SharedInstanceData:
+			return FString::Printf(TEXT("Shared[%d]"), Index);
+		case EEzAbilityDataSourceType::SharedInstanceDataObject:
+			return FString::Printf(TEXT("SharedO[%d]"), Index);
+		case EEzAbilityDataSourceType::ContextData:
+			return FString::Printf(TEXT("Context[%d]"), Index);
+		case EEzAbilityDataSourceType::GlobalParameterData:
+			return FString::Printf(TEXT("GlobalParam"));
+		case EEzAbilityDataSourceType::SubtreeParameterData:
+			return FString::Printf(TEXT("SubtreeParam[%d]"), Index);
+		case EEzAbilityDataSourceType::StateParameterData:
+			return FString::Printf(TEXT("LinkedParam[%d]"), Index);
+		default:
+			return TEXT("---");
+		}
+	}
+	
+private:
+	UPROPERTY()
+	EEzAbilityDataSourceType Source = EEzAbilityDataSourceType::None;
+
+	UPROPERTY()
+	uint16 Index = InvalidIndex;
+
+	UPROPERTY()
+	FEzAbilityStateHandle StateHandle = FEzAbilityStateHandle::Invalid; 
+};
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+///State
 USTRUCT()
 struct EZABILITY_API FCompactEzAbilityState
 {
@@ -90,9 +299,14 @@ struct EZABILITY_API FCompactEzAbilityState
 
 	UPROPERTY()
 	uint8 bEnabled : 1;
-	
+
+	UPROPERTY()
+	uint8 bHasTransitionTasks : 1;
+
 	UPROPERTY()
-	ESliceType Type = ESliceType::Main;
+	EEzAbilityStateType Type = EEzAbilityStateType::State;
+	// UPROPERTY()
+	// ESliceType Type = ESliceType::Main;
 	
 	UPROPERTY()
 	FName Name;
@@ -111,47 +325,222 @@ struct EZABILITY_API FCompactEzAbilityState
 	
 	UPROPERTY()
 	uint16 ChildrenEnd = 0;
-};
 
-////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-///Transition
-USTRUCT()
-struct EZABILITY_API FCompactEzAbilityTransition
-{
-	GENERATED_BODY()
+	UPROPERTY()
+	uint8 EnterConditionsNum = 0;
+	
+	UPROPERTY()
+	uint8 TransitionsNum = 0;
 
-	FCompactEzAbilityTransition();
+	UPROPERTY()
+	uint16 TasksBegin = 0;
 	
 	UPROPERTY()
-	FEzAbilityStateHandle State = FEzAbilityStateHandle::Invalid;
+	uint8 TasksNum = 0;
+	
+	UPROPERTY()
+	uint8 InstanceDataNum = 0;
+
+	UPROPERTY()
+	FEzAbilityDataHandle ParameterDataHandle = FEzAbilityDataHandle::Invalid;
+
+	UPROPERTY()
+	FEzAbilityIndex16 ParameterTemplateIndex = FEzAbilityIndex16::Invalid;
 };
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-///Frame
+///State Parameters
 USTRUCT()
-struct EZABILITY_API FEzAbilityFrame
+struct EZABILITY_API FCompactEzAbilityParameters
 {
 	GENERATED_BODY()
 
-	UPROPERTY()
-	TObjectPtr<class UEzAbility> Ability = nullptr;
-
-	UPROPERTY()
-	uint8 bIsGlobalFrame : 1 = false;
+	FCompactEzAbilityParameters() = default;
 
+	FCompactEzAbilityParameters(const FInstancedPropertyBag& InParameters)
+		: Parameters(InParameters)
+	{
+	}
+	
 	UPROPERTY()
-	FEzAbilityStateHandle RootState = FEzAbilityStateHandle::Root; 
+	FInstancedPropertyBag Parameters;
 };
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-///Exe
+///Transition
 USTRUCT()
-struct EZABILITY_API FEzAbilityExecutionState
+struct EZABILITY_API FCompactEzAbilityTransition
 {
 	GENERATED_BODY()
 
+	FCompactEzAbilityTransition();
+	
 	UPROPERTY()
-	TArray<FEzAbilityFrame> ActiveFrames;
+	FEzAbilityStateHandle State = FEzAbilityStateHandle::Invalid;
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+///Data View
+struct EZABILITY_API FEzAbilityDataView
+{
+	FEzAbilityDataView() = default;
+
+	// USTRUCT() constructor.
+	FEzAbilityDataView(const UStruct* InStruct, uint8* InMemory) : Struct(InStruct), Memory(InMemory)
+	{
+		// Must have type with valid pointer.
+		check(!Memory || (Memory && Struct));
+	}
+
+	// UOBJECT() constructor.
+	FEzAbilityDataView(UObject* Object) : Struct(Object ? Object->GetClass() : nullptr), Memory(reinterpret_cast<uint8*>(Object))
+	{
+		// Must have type with valid pointer.
+		check(!Memory || (Memory && Struct));
+	}
+
+	// USTRUCT() from a StructView.
+	FEzAbilityDataView(FStructView StructView) : Struct(StructView.GetScriptStruct()), Memory(StructView.GetMemory())
+	{
+		// Must have type with valid pointer.
+		check(!Memory || (Memory && Struct));
+	}
+
+	/**
+	 * Check is the view is valid (both pointer and type are set). On valid views it is safe to call the Get<>() methods returning a reference.
+	 * @return True if the view is valid.
+	*/
+	bool IsValid() const
+	{
+		return Memory != nullptr && Struct != nullptr;
+	}
+
+	/*
+	 * UOBJECT() getters (reference & pointer, const & mutable)
+	 */
+	template <typename T>
+    typename TEnableIf<TIsDerivedFrom<T, UObject>::IsDerived, const T&>::Type Get() const
+	{
+		check(Memory != nullptr);
+		check(Struct != nullptr);
+		check(Struct->IsChildOf(T::StaticClass()));
+		return *((T*)Memory);
+	}
+
+	template <typename T>
+	typename TEnableIf<TIsDerivedFrom<T, UObject>::IsDerived, T&>::Type GetMutable() const
+	{
+		check(Memory != nullptr);
+		check(Struct != nullptr);
+		check(Struct->IsChildOf(T::StaticClass()));
+		return *((T*)Memory);
+	}
+
+	template <typename T>
+	typename TEnableIf<TIsDerivedFrom<T, UObject>::IsDerived, const T*>::Type GetPtr() const
+	{
+		// If Memory is set, expect Struct too. Otherwise, let nulls pass through.
+		check(!Memory || (Memory && Struct));
+		check(!Struct || Struct->IsChildOf(T::StaticClass()));
+		return ((T*)Memory);
+	}
+
+	template <typename T>
+    typename TEnableIf<TIsDerivedFrom<T, UObject>::IsDerived, T*>::Type GetMutablePtr() const
+	{
+		// If Memory is set, expect Struct too. Otherwise, let nulls pass through.
+		check(!Memory || (Memory && Struct));
+		check(!Struct || Struct->IsChildOf(T::StaticClass()));
+		return ((T*)Memory);
+	}
+
+	/*
+	 * USTRUCT() getters (reference & pointer, const & mutable)
+	 */
+	template <typename T>
+	typename TEnableIf<!TIsDerivedFrom<T, UObject>::IsDerived && !TIsIInterface<T>::Value, const T&>::Type Get() const
+	{
+		check(Memory != nullptr);
+		check(Struct != nullptr);
+		check(Struct->IsChildOf(T::StaticStruct()));
+		return *((T*)Memory);
+	}
+
+	template <typename T>
+    typename TEnableIf<!TIsDerivedFrom<T, UObject>::IsDerived && !TIsIInterface<T>::Value, T&>::Type GetMutable() const
+	{
+		check(Memory != nullptr);
+		check(Struct != nullptr);
+		check(Struct->IsChildOf(T::StaticStruct()));
+		return *((T*)Memory);
+	}
+
+	template <typename T>
+    typename TEnableIf<!TIsDerivedFrom<T, UObject>::IsDerived && !TIsIInterface<T>::Value, const T*>::Type GetPtr() const
+	{
+		// If Memory is set, expect Struct too. Otherwise, let nulls pass through.
+		check(!Memory || (Memory && Struct));
+		check(!Struct || Struct->IsChildOf(T::StaticStruct()));
+		return ((T*)Memory);
+	}
+
+	template <typename T>
+    typename TEnableIf<!TIsDerivedFrom<T, UObject>::IsDerived && !TIsIInterface<T>::Value, T*>::Type GetMutablePtr() const
+	{
+		// If Memory is set, expect Struct too. Otherwise, let nulls pass through.
+		check(!Memory || (Memory && Struct));
+		check(!Struct || Struct->IsChildOf(T::StaticStruct()));
+		return ((T*)Memory);
+	}
+
+	/*
+	 * IInterface() getters (reference & pointer, const & mutable)
+	 */
+	template <typename T>
+	typename TEnableIf<TIsIInterface<T>::Value, const T&>::Type Get() const
+	{
+		check(!Memory || (Memory && Struct));
+		check(Struct->IsChildOf(UObject::StaticClass()) && ((UClass*)Struct)->ImplementsInterface(T::UClassType::StaticClass()));
+		return *(T*)((UObject*)Memory)->GetInterfaceAddress(T::UClassType::StaticClass());
+	}
+
+	template <typename T>
+    typename TEnableIf<TIsIInterface<T>::Value, T&>::Type GetMutable() const
+	{
+		check(!Memory || (Memory && Struct));
+		check(Struct->IsChildOf(UObject::StaticClass()) && ((UClass*)Struct)->ImplementsInterface(T::UClassType::StaticClass()));
+		return *(T*)((UObject*)Memory)->GetInterfaceAddress(T::UClassType::StaticClass());
+	}
+
+	template <typename T>
+    typename TEnableIf<TIsIInterface<T>::Value, const T*>::Type GetPtr() const
+	{
+		check(!Memory || (Memory && Struct));
+		check(Struct->IsChildOf(UObject::StaticClass()) && ((UClass*)Struct)->ImplementsInterface(T::UClassType::StaticClass()));
+		return (T*)((UObject*)Memory)->GetInterfaceAddress(T::UClassType::StaticClass());
+	}
+
+	template <typename T>
+    typename TEnableIf<TIsIInterface<T>::Value, T*>::Type GetMutablePtr() const
+	{
+		check(!Memory || (Memory && Struct));
+		check(Struct->IsChildOf(UObject::StaticClass()) && ((UClass*)Struct)->ImplementsInterface(T::UClassType::StaticClass()));
+		return (T*)((UObject*)Memory)->GetInterfaceAddress(T::UClassType::StaticClass());
+	}
+
+	/** @return Struct describing the data type. */
+	const UStruct* GetStruct() const { return Struct; }
+
+	/** @return Raw const pointer to the data. */
+	const uint8* GetMemory() const { return Memory; }
+
+	/** @return Raw mutable pointer to the data. */
+	uint8* GetMutableMemory() const { return Memory; }
+	
+protected:
+	/** UClass or UScriptStruct of the data. */
+	const UStruct* Struct = nullptr;
 
-	void Reset();
+	/** Memory pointing at the class or struct */
+	uint8* Memory = nullptr;
 };

+ 6 - 2
Ability/Plugins/EzAbility/Source/EzAbility/Public/Task/EzAbilityTask.h

@@ -3,15 +3,19 @@
 #pragma once
 
 #include "CoreMinimal.h"
+#include "EzAbilityNodeBase.h"
 #include "UObject/Object.h"
 #include "EzAbilityTask.generated.h"
 
+struct FEzAbilityContext;
+struct FEzAbilityTransitionResult;
 /**
  * 
  */
 USTRUCT()
-struct EZABILITY_API FEzAbilityTask
+struct EZABILITY_API FEzAbilityTask : public FEzAbilityNodeBase
 {
 	GENERATED_BODY()
-	
+
+	virtual void ExitState(FEzAbilityContext& Context, const FEzAbilityTransitionResult& Transition) const {}
 };