Hello there,
I have been working on a AI plug in again, and I am now doing the multithreaded cover deciding, but I crash and I don’t know why.
It seems to crash on “if(UWorld* World = This->GetWorld()) {}”, but it is in if statements so as far as I know it should not crash…
This is the code I use to call the ThreadManagerThread::Run (via JoyInit, Init and Constructor of course):
void ACoverAI_BasicGameMode::Update()
{
//...
//... Stuff that just empties arrays, stops threads if running and resets variables
//...
{ //Extra scope to prevent misinterpretation of variables
//Restart ThreadManagerThread
UE_LOG(LogTemp, Warning, TEXT("Restart ThreadManagerThread"));
ThreadManagerThread* Thread = ThreadManagerThread::JoyInit();
Thread->This = this; //An ASomething is needed for GetWorld() for spawning, raytracing etc.
}
//Other threads come here but are commented out
}
This is the code I try to run:
//the code that actually runs on the thread
uint32 ThreadManagerThread::Run()
{
FPlatformProcess::Sleep(0.5);
UE_LOG(LogTemp, Warning, TEXT("ThreadManagerThread: Run()"));
//Wait until This is set for proper spawning, take no chances
while (!This)
{
UE_LOG(LogTemp, Warning, TEXT("ThreadManagerThread: waiting until This is set"));
FPlatformProcess::Sleep(0.01);
}
UE_LOG(LogTemp, Warning, TEXT("This should be set"));
UE_LOG(LogTemp, Warning, TEXT("ThreadManagerThread: GetWorld()"));
if (UWorld* World = This->GetWorld()) //CRASH
{
UE_LOG(LogTemp, Warning, TEXT("GetWorld() succeeded"));
UE_LOG(LogTemp, Warning, TEXT("ThreadManagerThread: Cast to ACoverAI_BasicGameMode"));
if (ACoverAI_BasicGameMode* GameMode = Cast<ACoverAI_BasicGameMode>(UGameplayStatics::GetGameMode(This)))
{
UE_LOG(LogTemp, Warning, TEXT("ThreadManagerThread: Cast to ACoverAI_BasicGameMode succeeded"));
//Never stop this thread, unless its destructor is called
while (true)
{
UE_LOG(LogTemp, Warning, TEXT("ThreadManagerThread: Entered infinte loop"));
//Calculate all Enemy threads (Dangerous for enemies, so the characters of the thread are friendly)
{ //Extra scope to prevent variables from being misinterpreted
FVector Location;
//Needed for spawning of the thread, we will only change its location
FTransform Transform;
//Set new array with all friendly characters, this array will hold actors which are not in a thread yet
TArray<AActor*> FriendliesToUse;
FriendliesToUse.Append(GameMode->Friendlies);
for (int i = 0; i < FriendliesToUse.Num(); i++)
{
TArray<ABasicCharacter*> CharactersForThread;
if (FriendliesToUse[i])
{
//Set spawn and comparison location to the current tester character's location
Location = FriendliesToUse[i]->GetActorLocation();
for (int j = 0; j < FriendliesToUse.Num(); j++)
{
if (FriendliesToUse[j])
{
//Is the distance smaller or equal to the maximum allowed distance, add to the new thread of so
if ((Location - FriendliesToUse[j]->GetActorLocation()).Size() <= GameMode->MaxDistance)
{
//Make sure cast succeeded
if (ABasicCharacter* ToAdd = Cast<ABasicCharacter>(FriendliesToUse[j]))
{
//if this character was in another thread, destroy that thread and remove it from the array of threads
for (int n = 0; n < GameMode->EnemyThreads.Num(); n++)
{
for (int u = 0; u < GameMode->EnemyThreads[n]->Characters.Num(); u++)
{
//Check if still valid
if (GameMode->EnemyThreads[n])
{
//Check if still valid
if (GameMode->EnemyThreads[n]->Characters[u])
{
//Has it this character in its characters array?
if (GameMode->EnemyThreads[n]->Characters[u] == ToAdd)
{
//If so destroy that thread and remove it from the array in the game mode
ACoverAI_ThreadEnemy* Temp = GameMode->EnemyThreads[n];
GameMode->EnemyThreads.Remove(Temp);
Temp->Destroy();
}
}
}
UE_LOG(LogTemp, Warning, TEXT("Sleep(0.01)"));
//Save resources
FPlatformProcess::Sleep(0.01);
}
UE_LOG(LogTemp, Warning, TEXT("Sleep(0.01)"));
//Save resources
FPlatformProcess::Sleep(0.01);
}
//Add the character to the array of what actors will be in the thread, and remove it from the list of actors that does not have a thread yet
CharactersForThread.Add(ToAdd);
FriendliesToUse.Remove(FriendliesToUse[j]);
}
}
}
UE_LOG(LogTemp, Warning, TEXT("Sleep(0.01)"));
//Save resources
FPlatformProcess::Sleep(0.01);
}
}
//Set last few spawn parameters
Transform.SetTranslation(Location);
FActorSpawnParameters SpawnParams;
SpawnParams.bNoFail = true;
//Spawning of actors can only be done on the game thread, request game thread to spawn ThreadActor
AsyncTask(ENamedThreads::GameThread, [=]()
{
UE_LOG(LogTemp, Warning, TEXT("Spawining ACoverAI_ThreadEnemy on gamethread: 336"));
//Spawn Actor and make sure it is spawned correctly
if (AActor* ThreadActor = World->SpawnActorAbsolute(ACoverAI_ThreadEnemy::StaticClass(), Transform, SpawnParams))
{
ACoverAI_ThreadEnemy* ThreadEnemyActor = Cast<ACoverAI_ThreadEnemy>(ThreadActor);
//Make sure cast succeeded
if (ThreadEnemyActor)
{
//In the spawned thread, set its array of characters
ThreadEnemyActor->Characters.Append(CharactersForThread);
}
}
else
{
UE_LOG(LogTemp, Error, TEXT("Failed to spawn ThreadActor!"));
}
});
//Save resources
FPlatformProcess::Sleep(0.05);
}
}
//Calculate all Friendly threads (Dangerous for enemies, so the characters of the thread are Enemy)
{ //Extra scope to prevent variables from being misinterpreted
FVector Location;
//Needed for spawning of the thread, we will only change its location
FTransform Transform;
//Set new array with all Enemy characters, this array will hold actors which are not in a thread yet
TArray<AActor*> EnemiesToUse;
EnemiesToUse.Append(GameMode->Enemies);
for (int i = 0; i < EnemiesToUse.Num(); i++)
{
TArray<ABasicCharacter*> CharactersForThread;
if (EnemiesToUse[i])
{
//Set spawn and comparison location to the current tester character's location
Location = EnemiesToUse[i]->GetActorLocation();
for (int j = 0; j < EnemiesToUse.Num(); j++)
{
if (EnemiesToUse[j])
{
//Is the distance smaller or equal to the maximum allowed distance, add to the new thread of so
if ((Location - EnemiesToUse[j]->GetActorLocation()).Size() <= GameMode->MaxDistance)
{
ABasicCharacter* ToAdd = Cast<ABasicCharacter>(EnemiesToUse[j]);
//Make sure cast succeeded
if (ToAdd)
{
//if this character was in another thread, destroy that thread and remove it from the array of threads
for (int n = 0; n < GameMode->FriendlyThreads.Num(); n++)
{
for (int u = 0; u < GameMode->FriendlyThreads[n]->Characters.Num(); u++)
{
//Check if still valid
if (GameMode->FriendlyThreads[n])
{
//Check if still valid
if (GameMode->FriendlyThreads[n]->Characters[u])
{
//Has it this character in it's characters array?
if (GameMode->FriendlyThreads[n]->Characters[u] == ToAdd)
{
//If so destroy that thread and remove it from the array in the gamemode
ACoverAI_ThreadFriendly* Temp = GameMode->FriendlyThreads[n];
GameMode->FriendlyThreads.Remove(Temp);
Temp->Destroy();
}
}
}
//Save resources
FPlatformProcess::Sleep(0.01);
}
//Save resources
FPlatformProcess::Sleep(0.01);
}
//Add the character to the array of what actors will be in the thread, and remove it from the list of actors that does not have a thread yet
CharactersForThread.Add(ToAdd);
EnemiesToUse.Remove(EnemiesToUse[j]);
}
}
}
//Save resources
FPlatformProcess::Sleep(0.01);
}
}
//Set last few spawn parameters
Transform.SetTranslation(Location);
FActorSpawnParameters SpawnParams;
SpawnParams.bNoFail = true;
//Spawning of actors can only be done on the game thread, request game thread to spawn ThreadActor
AsyncTask(ENamedThreads::GameThread, [=]()
{
UE_LOG(LogTemp, Warning, TEXT("Spawining ACoverAI_ThreadFriendly on gamethread: 428"));
AActor* ThreadActor = World->SpawnActorAbsolute(ACoverAI_ThreadFriendly::StaticClass(), Transform, SpawnParams);
//Make sure the Actor is spawned correctly
if (ThreadActor)
{
ACoverAI_ThreadFriendly* ThreadFriendlyActor = Cast<ACoverAI_ThreadFriendly>(ThreadActor);
//Make sure cast succeeded
if (ThreadFriendlyActor)
{
//In the spawned thread, set its array of characters
ThreadFriendlyActor->Characters.Append(CharactersForThread);
}
}
else
{
UE_LOG(LogTemp, Error, TEXT("Failed to spawn ThreadActor!"));
}
});
//Save resources
FPlatformProcess::Sleep(0.05);
}
}
FPlatformProcess::Sleep(0.02);
}
}
else
{
UE_LOG(LogTemp, Warning, TEXT("GetWorld() failed"));
}
}
//Needed to not get a compiler error, code never gets to here
return 0;
}
And this is the output log I get just before my crash:
[2017.04.26-18.38.18:903][642]LogTemp:Warning: Beginplay Game Mode
[2017.04.26-18.38.18:903][642]LogTemp:Warning: Emptying arrays
[2017.04.26-18.38.18:903][642]LogTemp:Warning: Restart ThreadManagerThread
[2017.04.26-18.38.18:903][642]LogTemp:Warning: ThreadMangerThread: JoyInit
[2017.04.26-18.38.18:903][642]LogTemp:Warning: ThreadManagerThread Construction
[2017.04.26-18.38.18:905][642]LogTemp:Warning: ThreadMangerThread: Init
[2017.04.26-18.38.18:905][642]LogTemp:Warning: ThreadManagerThread: Run()
[2017.04.26-18.38.18:905][642]LogTemp:Warning: This should be set
[2017.04.26-18.38.18:905][642]LogTemp:Warning: ThreadManagerThread: GetWorld()
[2017.04.26-18.38.18:925][642]PIE: Info Play in editor start time for /Game/Maps/UEDPIE_0_TEST 0.933
The only thing else I can think of is memory shortage, but that seems unlikely since I have over 1 gb left on my 8gb system when I am running everything.
Thanks in advance.