Handling Player Spawning for a save system.

Hiya Guys, I am finishing up my save game system but I have come into a bug that I cannot seem to fix.

My character loads perfectly fine in the editor however when I package the game it has a completely different behaviour. My character will always spawn at the Player Start no matter what.

I have attempted manually setting the actor transform in my save game however I cannot get it working.

Everything else works perfectly fine, I just can’t for the life of me figure out how to properly load player state.

Any help would be appreciated!

// Copyright Epic Games, Inc. All Rights Reserved.

#include "ClimbingInCPPGameMode.h"
#include "ClimbingInCPPCharacter.h"
#include "EngineUtils.h"
#include "Kismet/BlueprintTypeConversions.h"
#include "Kismet/GameplayStatics.h"
#include "Serialization/ObjectAndNameAsStringProxyArchive.h"
#include "Serilize/CustomGameInstance.h"
#include "Serilize/CustomSaveGame.h"
#include "Serilize/SaveInterface.h"
#include "UObject/ConstructorHelpers.h"

AClimbingInCPPGameMode::AClimbingInCPPGameMode()
{
}

void AClimbingInCPPGameMode::StartPlay()
{
	Super::StartPlay();
}

void AClimbingInCPPGameMode::SetSlotName(FString NewSlotName)
{
	// Ignore empty name
	if (NewSlotName.Len() == 0)
	{
		return;
	}

	CurrentSlotName = NewSlotName;
}

void AClimbingInCPPGameMode::WriteSaveGame()
{
	//Debug::Print("Running write save game");
	//Debug::Print(CurrentSlotName);
	auto GameInstance = Cast<UCustomGameInstance>(UGameplayStatics::GetGameInstance(GetWorld()));
	if (GameInstance == nullptr)
	{
		// Warn about failure to save?
		//Debug::Print("FAILURE TO SAVE : GAME STATE == NULL");
		return;
	}
	if (UGameplayStatics::DoesSaveGameExist(CurrentSlotName, 0))
	{
		if (CurrentSaveGame)
		{
			CurrentSaveGame->SavedActors.Empty();
		}
	}
	// Iterate the entire world of actors
	for (FActorIterator It(GetWorld()); It; ++It)
	{
		AActor* Actor = *It;
		if (Actor->Implements<USaveInterface>())
		{
			FActorSaveData ActorData;
			ActorData.ActorName = Actor->GetFName();
			ActorData.Transform = Actor->GetActorTransform();
			ActorData.ActorClass = Actor->GetClass();
			FString LevelName;
			LevelName = GetWorld()->GetMapName();
			// Pass the array to fill with data from Actor
			FMemoryWriter MemWriter(ActorData.ByteData);

			FObjectAndNameAsStringProxyArchive Ar(MemWriter, true);
			// Find only variables with UPROPERTY(SaveGame)
			Ar.ArIsSaveGame = true;
			// Converts Actors SaveGame UPROPERTIES into binary array
			Actor->Serialize(Ar);
			//Debug::Print("Object added to save data: " + ActorData.ActorName.ToString());
			CurrentSaveGame->SavedActors.Add(ActorData);
			CurrentSaveGame->LevelName = LevelName;
		}
	}
	if (UGameplayStatics::DoesSaveGameExist(CurrentSlotName, 0))
	{
		UGameplayStatics::SaveGameToSlot(CurrentSaveGame, CurrentSlotName, 0);
		UpdateCurrentSaveGameData();
		//Debug::Print("Game saved");
	}
}

void AClimbingInCPPGameMode::LoadSaveGame()
{
	FString LevelName;
	LevelName = GetWorld()->GetMapName();
	auto GameInstance = Cast<UCustomGameInstance>(UGameplayStatics::GetGameInstance(GetWorld()));
	// Update slot name first if specified, otherwise keeps default name
	FString ProjectPath = FPlatformMisc::ProjectDir();
	SavePath = ProjectPath + "Saved/SaveGames/" +GameInstance->CurrentlyLoadedSaveState;
	CurrentSlotName = SavePath + LevelName;
	//
	//Debug::Print(SavePath + LevelName + ": This is the current save slot", 10,10);
	if (UGameplayStatics::DoesSaveGameExist(CurrentSlotName, 0))
	{
		CurrentSlotName.RemoveFromStart("Current", ESearchCase::CaseSensitive);
		auto LoadedSave = UGameplayStatics::LoadGameFromSlot(CurrentSlotName, 0);
		CurrentSaveGame = Cast<UCustomSaveGame>(LoadedSave);
		if (!CurrentSaveGame)
		{
			//Debug::Print("Failed to load SaveGame Data.");
			return;
		}
		TArray<FName> allActors;
		for (FActorIterator It(GetWorld()); It; ++It)
		{
			AActor* Actor = *It;
			FName ActorName = Actor->GetFName();
			allActors.Add(ActorName);
			allWorldActors.Add(Actor);
		}

		for (FActorSaveData& ActorData : CurrentSaveGame->SavedActors)
		{
			if (!allActors.Contains(ActorData.ActorName))
			{
				if (!ActorData.ActorName.ToString().Contains("ClimbCharacter"))
				{
					AActor* newlySpawned = GetWorld()->SpawnActor(ActorData.ActorClass);
					ActorData.ActorName = FName(*newlySpawned->GetName());
					allActors.Add(FName(*newlySpawned->GetName()));
					ActorData.RuntimeActor = true;
					allWorldActors.Add(newlySpawned);
					//Debug::Print( "Name of Actor" +newlySpawned->GetName() + "Location" + newlySpawned->GetActorLocation().ToString(),FMath::RandRange(0, 100000000),10);
				}
			}
		}
		// Iterate the entire world of actors
		for (FActorIterator It(GetWorld()); It; ++It)
		{
			AActor* Actor = *It;
			// Only interested in our 'gameplay actors'
			if (Actor->Implements<USaveInterface>())
			{
				for (FActorSaveData ActorData : CurrentSaveGame->SavedActors)
				{
					if (ActorData.ActorName == Actor->GetFName())
					{

						Actor->SetActorTransform(ActorData.Transform);
						FMemoryReader MemReader(ActorData.ByteData);
						FObjectAndNameAsStringProxyArchive Ar(MemReader, true);
						Ar.ArIsSaveGame = true;
						// Convert binary array back into actors variables
						Actor->Serialize(Ar);
						
						ISaveInterface::Execute_OnActorLoaded(Actor);
					}
				}
			}
		}

		//OnSaveGameLoaded.Broadcast(CurrentSaveGame);
	}
	else
	{
		NewSaveGameData(CurrentSlotName, GetWorld()->GetMapName(), GameInstance->CurrentlyLoadedSaveState);
	}
}

void AClimbingInCPPGameMode::NewSaveGameData(FString SlotName, FString MapName, FString DataSlot)
{
	// Create the directory
	CurrentSaveGame = Cast<UCustomSaveGame>(UGameplayStatics::CreateSaveGameObject(UCustomSaveGame::StaticClass()));
	if (DataSlot.IsEmpty())
	{
		CurrentSlotName = SlotName + "/" + "Current";
		CurrentSaveGame->LevelName = nullptr;
	}
	else
	{
		CurrentSlotName = SlotName;
		CurrentSaveGame->LevelName = MapName;
	}
	UGameplayStatics::SaveGameToSlot(CurrentSaveGame, CurrentSlotName, 0);
	//Debug::Print("No save game found : Created new save game data");
}
int FindUniqueId(TArray<int> Array) 
{
	int UniqueID = FMath::RandRange(0, 1000000000);
	
	while (Array.Contains(UniqueID))
	{
		UniqueID = FMath::RandRange(0, 1000000000);
	}
	
	return UniqueID;
}


void AClimbingInCPPGameMode::InitGame(const FString& MapName, const FString& Options, FString& ErrorMessage)
{
	Super::InitGame(MapName, Options, ErrorMessage);
	FString SelectedSaveSlot = UGameplayStatics::ParseOption(Options, "SaveGame");
	CurrentSlotName = SelectedSaveSlot;

}

void AClimbingInCPPGameMode::MakeUniqueID()
{
	for (FActorIterator It(GetWorld()); It; ++It)
	{
		AActor* Actor = *It;
		if (Cast<AInteractionComponent>(Actor))
		{
			if (Cast<AInteractionComponent>(Actor)->UniqueActorID == 0)
			{
				int tempID = FindUniqueId(UsedUniqueIDs);
				Cast<AInteractionComponent>(Actor)->UniqueActorID = tempID;
				//Debug::Print(FString::SanitizeFloat(Cast<AInteractionComponent>(Actor)->UniqueActorID),FMath::RandRange(1000, 100000000),5);
			}
		}
	}
}

AActor* AClimbingInCPPGameMode::FindUniqueID(int IDToFind)
{
	for (FActorIterator It(GetWorld()); It; ++It)
	{
		AActor* Actor = *It;
		if (Cast<AInteractionComponent>(Actor))
		{
			if (Cast<AInteractionComponent>(Actor)->UniqueActorID == IDToFind)
			{
				return Actor;
			}
		}
	}
	Debug::Print("No actor with that unique ID found");
	return nullptr;
}

void AClimbingInCPPGameMode::PostInitializeComponents()
{
	Super::PostInitializeComponents();
}

void AClimbingInCPPGameMode::BeginPlay()
{
	Super::BeginPlay();
	if (ShouldRun)
	{
		LoadSaveGame();
		UpdateCurrentSaveGameData();
		MakeUniqueID();
		//Debug::Print("Init game data", 2, 1);
	}
}

void AClimbingInCPPGameMode::UpdateCurrentSaveGameData()
{
	auto GameInstance = Cast<UCustomGameInstance>(UGameplayStatics::GetGameInstance(GetWorld()));
	auto tempSlot = GameInstance->CurrentlyLoadedSaveState;
	CurrentSlotName = SavePath + "Current";
	//Debug::Print(CurrentSlotName);
	FString LevelName = GetWorld()->GetMapName();
	if (UGameplayStatics::DoesSaveGameExist(CurrentSlotName, 0))
	{
		CurrentSaveGame->LevelName = LevelName;
		UGameplayStatics::SaveGameToSlot(CurrentSaveGame, SavePath+LevelName, 0);
		UGameplayStatics::SaveGameToSlot(CurrentSaveGame, CurrentSlotName, 0);
		//Debug::Print("Game saved");
		//Debug::Print(SavePath+LevelName, 0, 5);
	}
}