I’m attempting to keep track of some data in my game per player in a networked instance.
I have a lobby of sorts where a player can select a character, and I store that data with this struct:
USTRUCT()
struct FPlayerSelections
{
GENERATED_USTRUCT_BODY()
//UPROPERTY()
int32 PlayerID;
//UPROPERTY()
FString PlayerName;
//UPROPERTY()
EShipItCharacters SelectedCharacter;
//Constructor
FPlayerSelections()
{
PlayerID = -1;
PlayerName = "";
SelectedCharacter = EShipItCharacters::Unknown;
}
};
My game instance class is where I’m holding an array of this struct:
UCLASS()
class SHIPIT_API UShipItGameInstance : public UGameInstance
{
GENERATED_BODY()
private:
TArray<FPlayerSelections> CharacterSelections;
};
My crash comes in where I’m trying to add an element to the array:
bool UShipItGameInstance::AddNewPlayerSelectionElement(FPlayerSelections _NewElement)
{
CharacterSelections.Push(_NewElement);
return true;
}
In the debugger, I’m seeing the selections array as “Invalid.” Am I missing a proper initialization? I’ve tried CharacterSelections.Init() with and without data and I still get the same crash.
Here’s the relevant portion of the callstack
UE4Editor-Core.dll!scalable_msize(void * ptr) Line 2615 C++
> UE4Editor-Core.dll!FMallocTBB::Realloc(void * Ptr, unsigned __int64 NewSize, unsigned int Alignment) Line 70 C++
UE4Editor-Core.dll!FHeapAllocator::ForAnyElementType::ResizeAllocation(int PreviousNumElements, int NumElements, unsigned __int64 NumBytesPerElement) Line 344 C++
UE4Editor-ShipIt.dll!TArray<FPlayerSelections,FDefaultAllocator>::Emplace<FPlayerSelections const & __ptr64>(const FPlayerSelections & <Args_0>) Line 1735 C++
UE4Editor-ShipIt.dll!UShipItGameInstance::CreateOrUpdatePlayerSelection(FPlayerSelections _NewElement) Line 60 C++
UE4Editor-ShipIt.dll!AShipItGameMode::VerifyCharacterChange(TSubclassOf<AShipItCharacter> _CharacterClass, int _PlayerID, AActor * _SpawnTransform) Line 61 C++
UE4Editor-ShipIt.dll!AShipItPlayerController::ClientRequestCharacterChange_Implementation(TSubclassOf<AShipItCharacter> _CharacterClass, int _PlayerID, AActor * _SpawnTransform) Line 34 C++
Update: 12/30/15
I think I’m getting closer. (I only get maybe an hour a day to work on this, so going is slow heh) I’ve simplified the code a lot to isolate the problem.
There’s a series of events involved, and I’m beginning to realize that may be the root of my problem, so here’s the entire flow:
I have the following objects involved:
ShipItGameInstance
ShipItGameMode
ShipItPlayerController
ShipItGameInstance
bool UShipItGameInstance::UpdatePlayerSelection(FPlayerSelections _UpdatedElement)
{
for (int i = 0; i < CharacterSelections.Num(); i++)
{
if (CharacterSelections[i].PlayerID == _UpdatedElement.PlayerID)
{
CharacterSelections[i] = _UpdatedElement;
return true;
}
}
return false;
}
void UShipItGameInstance::CreateOrUpdatePlayerSelection(FPlayerSelections _NewElement)
{
if (!UpdatePlayerSelection(_NewElement))
{
CharacterSelections.Add(_NewElement);
}
}
ShipItPlayerController
In ShipItPlayerController, I have this function which sets things off in motion. In our game, players can change their character on the fly so this event sends off a request to the server to change characters.
void AShipItPlayerController::ClientRequestCharacterChange_Implementation(TSubclassOf<AShipItCharacter> _CharacterClass, int32 _PlayerID, AActor* _SpawnTransform)
{
//Check if params are actually filled
if (_CharacterClass == NULL || _PlayerID == NULL || _SpawnTransform == NULL)
{
UE_LOG(LogTemp, Error, TEXT("ClientRequestCharacterChange_Implementation param is null. Character request failed. Character Class %s, Player ID %d, SpawnTransform %s"), *_CharacterClass, _PlayerID, _SpawnTransform);
return;
}
//Check if we can use the ship it game mode
AShipItGameMode* gameModeRef = (AShipItGameMode*)GetWorld()->GetAuthGameMode();
if (gameModeRef == NULL)
{
UE_LOG(LogTemp, Error, TEXT("Game ref couldn't cast to ship it game mode. What game mode are you using?"));
}
else
{
//Send character change request over to game mode
gameModeRef->VerifyCharacterChange(_CharacterClass, _PlayerID, _SpawnTransform);
}
}
ShipItGameMode
Game mode then tries to fulfill the character change request. This may be the wrong place to put the code, but it’s what came to mind first I guess.
void AShipItGameMode::VerifyCharacterChange(TSubclassOf<AShipItCharacter> _CharacterClass, int32 _PlayerID, AActor* _SpawnTransform)
{
UE_LOG(LogTemp, Log, TEXT("VerifyCharacterChange running on server"));
//Save to the gameinstance the character that this player has chosen
UShipItGameInstance* ShipitGameInstance = (UShipItGameInstance*)GetGameInstance();
if (ShipitGameInstance != NULL)
{
FPlayerSelections newPlayerSelection;
newPlayerSelection.PlayerID = 100;
newPlayerSelection.PlayerName = "TESTPLAYERNAME";
newPlayerSelection.SelectedCharacter = EShipItCharacters::Swordsman;
ShipitGameInstance->CreateOrUpdatePlayerSelection(newPlayerSelection); //crashes when called here
}
}
The Crash
The crash only happens when CreateOrUpdatePlayerSelection() is called from game mode. When running the debugger, I get a break when I first run the editor, and I can see the TArray is valid and an element is added. When I hit a breakpoint as it’s being called from game mode, it’s listed as “invalid.”
My questions thusfar
When I run a debug instance, I get a breakpoint in the constructor for gameinstance immediately. This is expected from the documentation. I can add an element to the array just fine at this point. I’m NOT getting a breakpoint when I run a new PIE instance - I am expecting to get one based on the documentation here: UGameInstance | Unreal Engine Documentation
Seeing as the gameinstance object code is running, and crashing, I know I have a reference to AN instance somewhere. What could be making this gameinstance’s member variables invalid?