Resetting objects in a level

thank you very much
kamera sistemleri

Hello,

I’m trying to implement functionality to restart the round. This includes restoring all of the objects in the current level to their original state. I would like to accomplish this without having to call ServerTravel, or anything that requires the engine to reload the entire map. That, in my opinion, is a bad solution.

I’m using version 4.7.6.

Here’s what I have so far:

-In my GameMode header, I keep an array of AActors:

UPROPERTY()
TArray<AActor *> currentRoundActors;

These are the actors that are present at the beginning of the round.

-When the BeginPlay is called, I populate the array:

void ANimModGameMode::BeginPlay()
{
	Super::BeginPlay();

	for (FActorIterator It(GetWorld()); It; ++It)
	{
		AActor* A = *It;
		if (A && A != this && !A->IsA<AController>() && ShouldReset(A))
		{
			currentRoundActors.Add(A);
		}
	}
}

-I have a trigger volume that, when triggered by a certain player, will:
-freeze all of the players in the game.
-start a timer

-When the timer expires, it will call “RestartRound” on the current GameMode:

#define ISSERVER (GEngine->GetNetMode(GetWorld()) < NM_Client)

void ANimModGameMode::RestartRound()
{
	if (!ISSERVER)
		return;
	//Players should already be frozen
	//FreezePlayers();

	// Reset all actors (except controllers, the GameMode, and any other actors specified by ShouldReset())
	//TODO: This doesn't handle actors that are in currentRoundActors that have been destroyed before this point.
	//We need to find a way to respawn those, as well.
	TArray<AActor *> destroyActors;
	TArray<AActor *> respawnActors;
	for (FActorIterator It(GetWorld()); It; ++It)
	{
		AActor* A = *It;
		if (A && A != this && !A->IsA<AController>() && ShouldReset(A))
		{
			if (currentRoundActors.Contains(A))
			{
				respawnActors.Add(A);
				currentRoundActors.Remove(A);
			}
			else
				destroyActors.Add(A);
		}
	}

	//Whatever is left is something that is pending being deleted, or is deleted.
	//Respawn what we can.
	for (AActor *A : currentRoundActors)
	{
		if (A == nullptr)
		{
			destroyActors.Add(A);
			continue;
		}

		if (ShouldReset(A))
		{
			respawnActors.Add(A);
		}
	}

	//Respawn the ones that we do want.
	while (respawnActors.Num() > 0)
	{
		AActor *A = respawnActors[0];
		if (A == nullptr)
                {
                        respawnActors.RemoveAt(0);
			continue;
                }

		//Add a new actor as a copy of the 'reloaded' one, since the reloaded one is disappearing.
		FActorSpawnParameters spawnParams;
		spawnParams.Template = A;
		/*spawnParams.bNoFail = true;
		spawnParams.bNoCollisionFail = true;
		spawnParams.Name = NAME_None;*/
		//A->SetReplicates(true);
		FVector orig = A->GetActorLocation();
		FRotator rot = A->GetActorRotation();
		AActor *newActor = GetWorld()->SpawnActor
		(
			A->GetClass(),
			&orig,
			&rot,
			spawnParams
		);
		if (newActor == nullptr)
			continue;

		//newActor->SetOwner(A->GetOwner());

		//Add the new one
		currentRoundActors.Add(newActor);
		if (currentRoundActors.Contains(A))
			currentRoundActors.Remove(A);
		//Remove the old one
		destroyActors.Add(A);

		respawnActors.RemoveAt(0);
	}

	//Destroy all of the actors we don't want.
	while (destroyActors.Num() > 0)
	{
		AActor *actor = destroyActors[0];
		destroyActors.RemoveAt(0);
		//This can (theoretically happen) if we added something that has already been deleted.
		if (actor == nullptr)
			continue;

		actor->Destroy(true, true);
		actor = nullptr;
	}

	//GetWorld()->ForceGarbageCollection(true); //...Maybe?

	// reset the GameMode...Which does ****-all.
	//Reset();

	// Notify the level script that the level has been reset
	ALevelScriptActor* LevelScript = GetWorld()->GetLevelScriptActor();
	if (LevelScript)
	{
		LevelScript->LevelReset();
	}

	//'Respawn' the players
	for (FConstControllerIterator Iterator = GetWorld()->GetControllerIterator(); Iterator; ++Iterator)
	{
		AController* Controller = *Iterator;
		ANimModPlayerController* PlayerController = Cast<ANimModPlayerController>(Controller);
		if (PlayerController)
		{
			RestartPlayer(PlayerController);
			//PlayerController->ClientRestartRound();
			//PlayerController->ServerRestartPlayer();
		}
		else
			Controller->Reset();
	}

	//Unfreeze the players.
	UnfreezePlayers();
}

Just in case someone wants to see what my “ShouldReset” looks like:

bool ANimModGameMode::ShouldReset(AActor* ActorToReset)
{
	UClass *actorClass = ActorToReset->GetActorClass();
	AGameNetworkManager *networkManager = Cast<AGameNetworkManager>(ActorToReset);
	//AParticleEventManager *particleEventManager = Cast<AParticleEventManager>(ActorToReset);
	if
	(
		actorClass->IsChildOf(UWorld::StaticClass()) ||
		actorClass->IsChildOf(APlayerController::StaticClass()) ||
		actorClass->IsChildOf(ACharacter::StaticClass()) ||
		actorClass->IsChildOf(AGameMode::StaticClass()) ||
		actorClass->IsChildOf(APlayerCameraManager::StaticClass()) ||
		actorClass->IsChildOf(APlayerStart::StaticClass()) ||
		actorClass->IsChildOf(AHUD::StaticClass()) ||
		actorClass->IsChildOf(UGameViewportClient::StaticClass()) ||
		actorClass->IsChildOf(AGameSession::StaticClass()) ||
		/*actorClass->IsChildOf(AGameNetworkManager::StaticClass()) ||*/
		actorClass->IsChildOf(APlayerState::StaticClass()) ||
		actorClass->IsChildOf(AWorldSettings::StaticClass()) ||
		actorClass->IsChildOf(AGameState::StaticClass()) ||
		actorClass->IsChildOf(AVIPTrigger::StaticClass()) ||
		actorClass->IsChildOf(ULevel::StaticClass()) ||
		actorClass->IsChildOf(ALight::StaticClass()) ||
		actorClass->IsChildOf(ALevelScriptActor::StaticClass()) ||
		actorClass->IsChildOf(ALightmassImportanceVolume::StaticClass()) ||
		actorClass->IsChildOf(APostProcessVolume::StaticClass()) ||
		actorClass->IsChildOf(ANavigationData::StaticClass()) ||
		actorClass->IsChildOf(AAtmosphericFog::StaticClass()) ||
		actorClass->IsChildOf(ASpectatorPawn::StaticClass()) ||
/*#ifdef DEBUG*/
		actorClass->IsChildOf(AGameplayDebuggingReplicator::StaticClass()) ||
/*#endif*/
		/*particleEventManager != nullptr ||*/
		(AActor *)GetWorld()->MyParticleEventManager == ActorToReset ||
		networkManager != nullptr
	)
		return false;

	if (currentRoundActors.Contains(ActorToReset) && ActorToReset->IsPendingKill())
		return true;

	if (ActorToReset->IsRootComponentStatic())
		return false; //I...think...
	
	return true;
}

Now, this code works(ish) when I’m testing in with 1 player in a non-dedicated server. If I test with two players in a dedicated server, the objects stay the way they are. Sometimes I can get them to disappear from the clients, altogether, but it’s clear that they respawned on the server, because I “collide” with empty air where the objects are supposed to be.

I also tried using “FReloadObjectArc”. Instead of storing a pointer to the Actors at when BeginPlay was called, I was serializing them in to their own FReloadObjectArc, then trying to overwrite them in “RestartRound”. That didn’t do anything.

I’m close (I think). I just need to know why the objects aren’t respawning on the clients. And why static mesh actors aren’t respawning at all.

Thoughts?

…wat