Skip to content

Recommended Engine Changes

This document lists sets of publicly available recommended engine changes, along with the reasons for the changes and their impact. Some engine changes for specific platforms may require private requests due to NDA restrictions.

Note that we tag our engine changes internally with surrounding //$$ BEGIN and //$$ END blocks. These are preserved here to ensure that our internal tests match the code presented here.

General Engine Changes

There are no generic engine changes recommended or required at this time

JSON Serialization

FJsonValue::Duplicate does not properly handle Null json values

The Engine’s Duplicate functionality does not properly handle null values. Instead of creating a value of the FJsonValueNull type, and returning a pointer to it, it instead returns a pointer to null.

This causes a duplicated Json Value to become invalid, as other functions assume all elements are valid and have a type.

To fix this, add the following case to the switch statement in FJsonValue::Duplicate:

case EJson::Null:
{
return MakeShareable(new FJsonValueNull());
}

Steam

The Engine’s OnlineSubsystemSteam is missing some key features, and is not set up to be extensible so that those features can be easily added. As such, while our OnlineSubsystemSteamV2 does add some missing features (such as Steam Inventory), some features are more easily added to the base engine directly.

FindSessionById Implementation

Reason: the OnlineSubsystemSteam base implementation of this function trivially triggers its delegate with a failure.

Impact: the lack of implemtation in this function prevents looking up Steam Sessions by their ID, preventing some flows for joining or rejoining a Steam Session from functioning.

Notes: this change embeds a helper object, to make it easier to communicate and drop into the engine. The helper can be moved out to match other similar implementations if you wish.

Replace the following in OnlineSessionInterfaceSteam.cpp:

bool FOnlineSessionSteam::FindSessionById(const FUniqueNetId& SearchingUserId, const FUniqueNetId& SessionId, const FUniqueNetId& FriendId, const FOnSingleSessionResultCompleteDelegate& CompletionDelegates)
{
FOnlineSessionSearchResult EmptyResult;
CompletionDelegates.ExecuteIfBound(0, false, EmptyResult);
return true;
}

With the following code (the original implementation is left commented out for reference):

bool FOnlineSessionSteam::FindSessionById(const FUniqueNetId& SearchingUserId, const FUniqueNetId& SessionId, const FUniqueNetId& FriendId, const FOnSingleSessionResultCompleteDelegate& CompletionDelegates)
{
//$$ BEGIN
DECLARE_DELEGATE_TwoParams(FOnlineAsyncTaskSteamFindLobbyDelegate, const bool, const class FOnlineSessionSearchResult&);
class FOnlineAsyncTaskSteamFindLobby : public FOnlineAsyncTaskSteamFindLobbiesBase
{
public:
/** Constructor */
FOnlineAsyncTaskSteamFindLobby(class FOnlineSubsystemSteam* InSubsystem, const FUniqueNetIdSteam& InLobbyId, const TSharedPtr<FOnlineSessionSearch>& InSearchSettings, const FOnlineAsyncTaskSteamFindLobbyDelegate& InFindLobbyCompleteDelegate) :
FOnlineAsyncTaskSteamFindLobbiesBase(InSubsystem, InSearchSettings),
OnFindLobbyCompleteDelegate(InFindLobbyCompleteDelegate)
{
LobbyIDs.Add(CSteamID(*(uint64*)InLobbyId.GetBytes()));
FindLobbiesState = FOnlineAsyncTaskSteamFindLobbiesBase::EFindLobbiesState::RequestLobbyData;
}
/**
* Get a human readable description of task
*/
FString ToString() const override
{
return FString::Printf(TEXT("FOnlineAsyncTaskSteamFindLobby bWasSuccessful: %d Lobby ID: %llu"), WasSuccessful(), LobbyIDs[0].ConvertToUint64());
}
/**
* Async task is given a chance to trigger it's delegates
*/
void TriggerDelegates() override
{
if (bWasSuccessful && SearchSettings->SearchResults.Num() > 0)
{
OnFindLobbyCompleteDelegate.ExecuteIfBound(bWasSuccessful, SearchSettings->SearchResults[0]);
}
else
{
FOnlineSessionSearchResult EmptyResult;
OnFindLobbyCompleteDelegate.ExecuteIfBound(bWasSuccessful, EmptyResult);
}
}
private:
FOnlineAsyncTaskSteamFindLobbyDelegate OnFindLobbyCompleteDelegate;
};
// Create a search settings object
this->CurrentSessionSearch = MakeShareable(new FOnlineSessionSearch());
this->CurrentSessionSearch->SearchState = EOnlineAsyncTaskState::InProgress;
FOnlineAsyncTaskSteamFindLobbyDelegate completeDelegate = FOnlineAsyncTaskSteamFindLobbyDelegate::CreateLambda([CompletionDelegates](const bool success, const class FOnlineSessionSearchResult& result) {
CompletionDelegates.ExecuteIfBound(0, success, result);
});
const FUniqueNetIdSteam& SteamSessionId = (const FUniqueNetIdSteam&)SessionId;
FOnlineAsyncTaskSteamFindLobby* NewTask = new FOnlineAsyncTaskSteamFindLobby(SteamSubsystem, SteamSessionId, this->CurrentSessionSearch, completeDelegate);
SteamSubsystem->QueueAsyncTask(NewTask);
/* Original Engine Implementation
FOnlineSessionSearchResult EmptyResult;
CompletionDelegates.ExecuteIfBound(0, false, EmptyResult);
*/
return true;
//$$ END
}