Hello,
I’ve been experiencing crashes when the state tree transitions from one hierarchy to another, the source code tries to clean up unused state instance data and destroy the structs but it seems the memory is invalid for some reason. The common pattern for these crashes is that it is always on the CompactStateTreeParameters instance data, which I’ve found out is what is by default applied to every state on state tree compile. I also found that this happened a lot on states that did not have any tasks, and adding dummy tasks that do nothing on the state seemed to help alleviate the issue. However, now I’ve added dummy tasks on every empty state, which was working for awhile, but upon adding some new states and tasks I am getting the crash again despite having tasks on every state.
In my screenshot of the debugger you can see that the instance data index 5 is causing the issue. Any ideas on what else I can do to alleviate this issue?
I’ve also found in the source code a comment about these CompactStateTreeParameters (StateTreeExecutionContext.cpp:3767): “// @todo: Empty params is valid and common case, we should not require to create empty parameters data (this needs to be handle in compiler and UpdateInstanceData too).”. Any idea when this todo would be incoming? I feel like it would probably help fix my issue.
An important note: I am ticking the state tree myself via my own processor, not going through mass or a state tree component. Here is some of the code I use for that, mostly copied from the mass state tree processor.
auto CollectDataCallback = FOnCollectStateTreeExternalData::CreateStatic(
CollectExternalData, Entity, EntitySubsystem);
FEntitySystemStateContext StateTreeExecContext = FEntitySystemStateContext(
*this,
*EntityStateTreeData->StateTree,
EntityStateTreeData->StateTreeInstanceData,
Entity,
CollectDataCallback);
if (!StateTreeExecContext.IsValid()
|| StateTreeExecContext.GetExecutionState().CurrentPhase == EStateTreeUpdatePhase::TickStateTree)
continue;
bool bHasTickScheduled = EntityStateTreeData->UpdateWithinSeconds != -1;
double TimePassedSinceLastTick = TimeInSeconds - EntityStateTreeData->LastUpdateTimeInSeconds;
if (bHasTickScheduled && TimePassedSinceLastTick >= EntityStateTreeData->UpdateWithinSeconds)
{
const float AdjustedDeltaTime = TimeInSeconds - EntityStateTreeData->LastUpdateTimeInSeconds;
EntityStateTreeData->ResetUpdateTimer();
EntityStateTreeData->LastUpdateTimeInSeconds = TimeInSeconds;
StateTreeExecContext.Tick(AdjustedDeltaTime);
// When last tick status is different than "Running", the state tree needs to be updated again
// For performance reason, tick again to see if we could find a new state right away instead of
// waiting for the next frame.
if (StateTreeExecContext.GetLastTickStatus() != EStateTreeRunStatus::Running)
{
StateTreeExecContext.Tick(0.0f);
// Could not find new state yet, try again next frame
if (StateTreeExecContext.GetLastTickStatus() != EStateTreeRunStatus::Running)
EntityStateTreeData->UpdateNextFrame();
}
}
else
{
StateTreeExecContext.TickTriggerTransitions();
}