initial Commit

This commit is contained in:
2026-07-02 17:29:08 +02:00
commit 4082d1960b
42 changed files with 7867 additions and 0 deletions

View File

@@ -0,0 +1,28 @@
{
"FileVersion": 3,
"Version": 1,
"VersionName": "1.0",
"FriendlyName": "ReplaySystem",
"Description": "Exposes the Unreal replay system to blueprints",
"Category": "Replay",
"CreatedBy": "TareE",
"CreatedByURL": "https://www.oyintare.dev/",
"DocsURL": "https://replay.oyintare.dev/",
"MarketplaceURL": "com.epicgames.launcher://ue/marketplace/product/1b0cbe31111d482baaf531d187651e7f",
"SupportURL": "",
"CanContainContent": true,
"Installed": true,
"Modules": [
{
"Name": "ReplaySystem",
"Type": "Runtime",
"LoadingPhase": "EarliestPossible",
"PlatformAllowList": [
"Win64",
"Mac",
"Linux",
"Android"
]
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

@@ -0,0 +1,24 @@
// Copyright 2020-Present Oyintare Ebelo. All Rights Reserved.
#include "DeleteReplayObject.h"
#include "NetworkReplayStreaming.h"
void UDeleteReplayObject::DeleteReplay(const FString& ReplayName)
{
EnumerateStreamsPtr = FNetworkReplayStreaming::Get().GetFactory().CreateReplayStreamer();
OnDeleteFinishedStreamCompleteDel = FDeleteFinishedStreamCallback::CreateUObject(this, &UDeleteReplayObject::OnDeleteFinishedStreamComplete);
if (EnumerateStreamsPtr.Get())
{
EnumerateStreamsPtr.Get()->DeleteFinishedStream(ReplayName, OnDeleteFinishedStreamCompleteDel);
}
}
void UDeleteReplayObject::OnDeleteFinishedStreamComplete(const FDeleteFinishedStreamResult& Result)
{
const bool WasSuccessful = Result.WasSuccessful();
OnDeleteComplete.Broadcast(WasSuccessful);
}

View File

@@ -0,0 +1,45 @@
// Copyright 2020-Present Oyintare Ebelo. All Rights Reserved.
#include "GetSavedReplaysObject.h"
#include "ReplayStructs.h"
#include "ReplayObject.h"
#include "NetworkReplayStreaming.h"
void UGetSavedReplaysObject::GetSavedReplays()
{
EnumerateStreamsPtr = FNetworkReplayStreaming::Get().GetFactory().CreateReplayStreamer();
EnumerateStreamsCallbackDel = FEnumerateStreamsCallback::CreateUObject(this, &UGetSavedReplaysObject::OnEnumerateStreamsComplete);
if (EnumerateStreamsPtr.Get())
{
EnumerateStreamsPtr.Get()->EnumerateStreams(FNetworkReplayVersion(), INDEX_NONE, FString(), TArray<FString>(), EnumerateStreamsCallbackDel);
}
}
void UGetSavedReplaysObject::OnEnumerateStreamsComplete(const FEnumerateStreamsResult& Result)
{
TArray<UReplayObject* > Replays;
TArray<FNetworkReplayStreamInfo> StreamInfos = Result.FoundStreams;
for (FNetworkReplayStreamInfo StreamInfo : StreamInfos)
{
FReplayInfo ReplayInfoTemp;
ReplayInfoTemp.FriendlyName = StreamInfo.FriendlyName;
ReplayInfoTemp.ActualName = StreamInfo.Name;
ReplayInfoTemp.RecordDate = StreamInfo.Timestamp;
ReplayInfoTemp.LengthInMS = StreamInfo.LengthInMS;
const float SizeInKb = StreamInfo.SizeInBytes / 1024.0f;
ReplayInfoTemp.SizeInMb = SizeInKb / 1024.0f;
UReplayObject* ReplayObj = NewObject<UReplayObject>();
ReplayObj->ReplayInfo = ReplayInfoTemp;
Replays.Add(ReplayObj);
}
OnGetReplaysComplete.Broadcast(Replays);
}

View File

@@ -0,0 +1,118 @@
// Copyright 2020-Present Oyintare Ebelo. All Rights Reserved.
#include "GoToTimeObject.h"
#include "Engine.h"
#include "ReplayPlayerController.h"
#include "ReplaySystemBPLibrary.h"
#include "Engine/DemoNetDriver.h"
#include "Kismet/GameplayStatics.h"
#include "Kismet/KismetMathLibrary.h"
// Sets default values
UGoToTimeObject::UGoToTimeObject()
{
}
void UGoToTimeObject::GoToTime(UObject* WorldContextObject, float TimeToGoTo,bool bRetainPauseState)
{
if (const UWorld* World = GEngine->GetWorldFromContextObjectChecked(WorldContextObject))
{
if (const AWorldSettings* WorldSettings = World->GetWorldSettings())
{
// clamp time just in case
const float ClampedTime = UKismetMathLibrary::FClamp(TimeToGoTo,0.0f,UReplaySystemBPLibrary::GetReplayLength(WorldContextObject));
if (UDemoNetDriver* DemoDriver = UReplaySystemBPLibrary::GetDemoDriver(World))
{
if (DemoDriver && DemoDriver->ServerConnection && DemoDriver->ServerConnection->PlayerController && DemoDriver->ServerConnection->PlayerController->PlayerState)
{
if(const AReplayPlayerController * ReplayPC = Cast<AReplayPlayerController>(DemoDriver->ServerConnection->PlayerController))
{
if(ReplayPC->bIsSpectating)
{
if(const AActor* ActorBeingSpectated = Cast<AActor>(ReplayPC->GetViewTarget()))
{
const FNetworkGUID ActorBeingSpectatedGUID = DemoDriver->GetGUIDForActor(ActorBeingSpectated);
DemoDriver->AddNonQueuedGUIDForScrubbing(ActorBeingSpectatedGUID);
}
}
if(const AActor* ActorPossessed = Cast<AActor>(ReplayPC->GetPawn()))
{
const FNetworkGUID SpectatorPawnGUID = DemoDriver->GetGUIDForActor(ActorPossessed);
DemoDriver->AddNonQueuedGUIDForScrubbing(SpectatorPawnGUID);
}
}
else
{
if(const APlayerController * PC = Cast<APlayerController>(DemoDriver->ServerConnection->PlayerController))
{
if(const AActor* ActorPossessed = Cast<AActor>(PC->GetPawn()))
{
const FNetworkGUID SpectatorPawnGUID = DemoDriver->GetGUIDForActor(ActorPossessed);
DemoDriver->AddNonQueuedGUIDForScrubbing(SpectatorPawnGUID);
}
}
}
for ( FActorIterator It( World->GetWorld()); It; ++It )
{
if ( It->bAlwaysRelevant )
{
const FNetworkGUID ActorGUID = DemoDriver->GetGUIDForActor(*It);
DemoDriver->AddNonQueuedGUIDForScrubbing(ActorGUID);
}
}
bRetainPauseStateBeforeMove = bRetainPauseState;
WCO = WorldContextObject;
if(WorldSettings->GetPauserPlayerState())
{
bPauseStateBeforeMove = true;
}
OnGoToTimeDelegate = FOnGotoTimeDelegate::CreateUObject(this,&UGoToTimeObject::OnGoToTimeProcessed);
DemoDriver->GotoTimeInSeconds(ClampedTime,OnGoToTimeDelegate);
}
}
}
}
}
void UGoToTimeObject::OnGoToTimeProcessed(bool bWasSuccessful)
{
const UObject* WorldContext = WCO;
if (const UWorld* World = GEngine->GetWorldFromContextObjectChecked(WorldContext))
{
if (AWorldSettings* WorldSettings = World->GetWorldSettings())
{
if (UDemoNetDriver* DemoDriver = UReplaySystemBPLibrary::GetDemoDriver(World))
{
if(OnGotoTimeComplete.IsBound())
{
OnGotoTimeComplete.Broadcast(bWasSuccessful);
}
if(bRetainPauseStateBeforeMove && !bPauseStateBeforeMove == false)
{
UReplaySystemBPLibrary::PausePlayback(WCO);
}
if( AReplayPlayerController* ReplayPC = Cast<AReplayPlayerController>(UGameplayStatics::GetPlayerController(World,0)))
{
ReplayPC->OnGoToTime(UReplaySystemBPLibrary::GetCurrentReplayTime(WCO));
ReplayPC->OnStopSpectateActor();
}
}
}
}
}

View File

@@ -0,0 +1,32 @@
// Copyright 2020-Present Oyintare Ebelo. All Rights Reserved.
#include "ModifyReplayObject.h"
#include "NetworkReplayStreaming.h"
void UModifyReplayObject::RenameReplay(const FString& ReplayName, const FString& NewName, const int32 UserIndex,bool bIsNormalName)
{
EnumerateStreamsPtr = FNetworkReplayStreaming::Get().GetFactory().CreateReplayStreamer();
OnRenameReplayCompleteDel = FRenameReplayCallback::CreateUObject(this, &UModifyReplayObject::OnRenameReplayComplete);
if (EnumerateStreamsPtr.Get())
{
if (bIsNormalName)
{
EnumerateStreamsPtr.Get()->RenameReplay(ReplayName,NewName,UserIndex, OnRenameReplayCompleteDel);
}
else
{
EnumerateStreamsPtr.Get()->RenameReplayFriendlyName(ReplayName, NewName,UserIndex, OnRenameReplayCompleteDel);
}
}
}
void UModifyReplayObject::OnRenameReplayComplete(const FRenameReplayResult& Result)
{
bool WasSuccessful = Result.WasSuccessful();
//OnRenameComplete.Broadcast(WasSuccessful);
}

View File

@@ -0,0 +1,32 @@
// Copyright 2020-Present Oyintare Ebelo. All Rights Reserved.
#include "RenameReplayObject.h"
#include "NetworkReplayStreaming.h"
void URenameReplayObject::RenameReplay(const FString& ReplayName, const FString& NewName, const int32 UserIndex,bool bIsNormalName)
{
EnumerateStreamsPtr = FNetworkReplayStreaming::Get().GetFactory().CreateReplayStreamer();
OnRenameReplayCompleteDel = FRenameReplayCallback::CreateUObject(this, &URenameReplayObject::OnRenameReplayComplete);
if (EnumerateStreamsPtr.Get())
{
if (bIsNormalName)
{
EnumerateStreamsPtr.Get()->RenameReplay(ReplayName,NewName,UserIndex, OnRenameReplayCompleteDel);
}
else
{
EnumerateStreamsPtr.Get()->RenameReplayFriendlyName(ReplayName, NewName,UserIndex, OnRenameReplayCompleteDel);
}
}
}
void URenameReplayObject::OnRenameReplayComplete(const FRenameReplayResult& Result)
{
bool WasSuccessful = Result.WasSuccessful();
OnRenameComplete.Broadcast(WasSuccessful);
}

View File

@@ -0,0 +1,984 @@
// Copyright 2020-Present Oyintare Ebelo. All Rights Reserved.
#include "ReplayDataObject.h"
#include "Kismet/KismetStringLibrary.h"
#include "HAL/FileManagerGeneric.h"
#include "ReplayStructs.h"
#include "Kismet/KismetTextLibrary.h"
void UReplayDataObject::AddBooleanData(FString Name,bool Data)
{
FReplayBoolData StructVar;
StructVar.Name = Name;
StructVar.Value = Data;
if(const int Index = DoesBooleanDataExist(Name) != -1)
{
boolData[Index] = StructVar;
}
else
{
boolData.Add(StructVar);
}
}
void UReplayDataObject::AddByteData(FString Name,TArray<uint8> Data)
{
FReplayByteData StructVar;
StructVar.Name = Name;
StructVar.Value = Data;
if(const int Index = DoesByteDataExist(Name) != -1)
{
byteData[Index] = StructVar;
}
else
{
byteData.Add(StructVar);
}
}
void UReplayDataObject::AddIntegerData(FString Name,int Data)
{
FReplayIntData StructVar;
StructVar.Name = Name;
StructVar.Value = Data;
if(const int Index = DoesIntegerDataExist(Name) != -1)
{
intData[Index] = StructVar;
}
else
{
intData.Add(StructVar);
}
}
void UReplayDataObject::AddInteger64Data(FString Name,int64 Data)
{
FReplayInt64Data StructVar;
StructVar.Name = Name;
StructVar.Value = Data;
if(const int Index = DoesInteger64DataExist(Name) != -1)
{
int64Data[Index] = StructVar;
}
else
{
int64Data.Add(StructVar);
}
}
void UReplayDataObject::AddFloatData(FString Name,float Data)
{
FReplayFloatData StructVar;
StructVar.Name = Name;
StructVar.Value = Data;
if(const int Index = DoesFloatDataExist(Name) != -1)
{
floatData[Index] = StructVar;
}
else
{
floatData.Add(StructVar);
}
}
void UReplayDataObject::AddNameData(FString Name,FName Data)
{
FReplayNameData StructVar;
StructVar.Name = Name;
StructVar.Value = Data;
if(const int Index = DoesNameDataExist(Name) != -1)
{
nameData[Index] = StructVar;
}
else
{
nameData.Add(StructVar);
}
}
void UReplayDataObject::AddStringData(FString Name,FString Data)
{
FReplayStringData StructVar;
StructVar.Name = Name;
StructVar.Value = Data;
if(const int Index = DoesStringDataExist(Name) != -1)
{
stringData[Index] = StructVar;
}
else
{
stringData.Add(StructVar);
}
}
void UReplayDataObject::AddTextData(FString Name,FText Data)
{
FReplayTextData StructVar;
StructVar.Name = Name;
StructVar.Value = Data;
if(const int Index = DoesTextDataExist(Name) != -1)
{
textData[Index] = StructVar;
}
else
{
textData.Add(StructVar);
}
}
void UReplayDataObject::AddVectorData(FString Name,FVector Data)
{
FReplayVectorData StructVar;
StructVar.Name = Name;
StructVar.Value = Data;
if(const int Index = DoesVectorDataExist(Name) != -1)
{
vectorData[Index] = StructVar;
}
else
{
vectorData.Add(StructVar);
}
}
void UReplayDataObject::AddRotatorData(FString Name,FRotator Data)
{
FReplayRotatorData StructVar;
StructVar.Name = Name;
StructVar.Value = Data;
if(const int Index = DoesRotatorDataExist(Name) != -1)
{
rotatorData[Index] = StructVar;
}
else
{
rotatorData.Add(StructVar);
}
}
void UReplayDataObject::AddTransformData(FString Name,FTransform Data)
{
FReplayTransformData StructVar;
StructVar.Name = Name;
StructVar.Value = Data;
if(const int Index = DoesTransformDataExist(Name) != -1)
{
transformData[Index] = StructVar;
}
else
{
transformData.Add(StructVar);
}
}
int UReplayDataObject::DoesBooleanDataExist (FString Name)
{
for (int i = 0; i < boolData.Num(); ++i)
{
if (boolData[i].Name == Name)
{
return i;
}
}
return -1;
}
int UReplayDataObject::DoesByteDataExist (FString Name)
{
for (int i = 0; i < byteData.Num(); ++i)
{
if (byteData[i].Name == Name)
{
return i;
}
}
return -1;
}
int UReplayDataObject::DoesIntegerDataExist (FString Name)
{
for (int i = 0; i < intData.Num(); ++i)
{
if (intData[i].Name == Name)
{
return i;
}
}
return -1;
}
int UReplayDataObject::DoesInteger64DataExist (FString Name)
{
for (int i = 0; i < int64Data.Num(); ++i)
{
if (int64Data[i].Name == Name)
{
return i;
}
}
return -1;
}
int UReplayDataObject::DoesFloatDataExist (FString Name)
{
for (int i = 0; i < floatData.Num(); ++i)
{
if (floatData[i].Name == Name)
{
return i;
}
}
return -1;
}
int UReplayDataObject::DoesNameDataExist (FString Name)
{
for (int i = 0; i < nameData.Num(); ++i)
{
if (nameData[i].Name == Name)
{
return i;
}
}
return -1;
}
int UReplayDataObject::DoesStringDataExist (FString Name)
{
for (int i = 0; i < stringData.Num(); ++i)
{
if (stringData[i].Name == Name)
{
return i;
}
}
return -1;
}
int UReplayDataObject::DoesTextDataExist (FString Name)
{
for (int i = 0; i < textData.Num(); ++i)
{
if (textData[i].Name == Name)
{
return i;
}
}
return -1;
}
int UReplayDataObject::DoesVectorDataExist (FString Name)
{
for (int i = 0; i < vectorData.Num(); ++i)
{
if (vectorData[i].Name == Name)
{
return i;
}
}
return -1;
}
int UReplayDataObject::DoesRotatorDataExist (FString Name)
{
for (int i = 0; i < stringData.Num(); ++i)
{
if (stringData[i].Name == Name)
{
return i;
}
}
return -1;
}
int UReplayDataObject::DoesTransformDataExist (FString Name)
{
for (int i = 0; i < transformData.Num(); ++i)
{
if (transformData[i].Name == Name)
{
return i;
}
}
return -1;
}
void UReplayDataObject::RemoveBooleanData(FString Name)
{
if (const int Index = DoesBooleanDataExist(Name) != -1)
{
boolData.RemoveAt(Index);
}
}
void UReplayDataObject::RemoveByteData(FString Name)
{
if (const int Index = DoesByteDataExist(Name) != -1)
{
byteData.RemoveAt(Index);
}
}
void UReplayDataObject::RemoveIntegerData(FString Name)
{
if (const int Index = DoesIntegerDataExist(Name) != -1)
{
intData.RemoveAt(Index);
}
}
void UReplayDataObject::RemoveInteger64Data(FString Name)
{
if (const int Index = DoesInteger64DataExist(Name) != -1)
{
int64Data.RemoveAt(Index);
}
}
void UReplayDataObject::RemoveFloatData(FString Name)
{
if (const int Index = DoesFloatDataExist(Name) != -1)
{
floatData.RemoveAt(Index);
}
}
void UReplayDataObject::RemoveNameData(FString Name)
{
if (const int Index = DoesNameDataExist(Name) != -1)
{
nameData.RemoveAt(Index);
}
}
void UReplayDataObject::RemoveStringData(FString Name)
{
if (const int Index = DoesStringDataExist(Name) != -1)
{
stringData.RemoveAt(Index);
}
}
void UReplayDataObject::RemoveTextData(FString Name)
{
if (const int Index = DoesTextDataExist(Name) != -1)
{
textData.RemoveAt(Index);
}
}
void UReplayDataObject::RemoveVectorData(FString Name)
{
if (const int Index = DoesVectorDataExist(Name) != -1)
{
vectorData.RemoveAt(Index);
}
}
void UReplayDataObject::RemoveRotatorData(FString Name)
{
if (const int Index = DoesRotatorDataExist(Name) != -1)
{
rotatorData.RemoveAt(Index);
}
}
void UReplayDataObject::RemoveTransformData(FString Name)
{
if (const int Index = DoesTransformDataExist(Name) != -1)
{
transformData.RemoveAt(Index);
}
}
bool UReplayDataObject::GetBooleanData(FString Name)
{
bool bReturnValue = false;
for (FReplayBoolData Data : boolData)
{
if (Data.Name == Name)
{
return Data.Value;
}
}
return bReturnValue;
}
TArray<uint8> UReplayDataObject::GetByteData(FString Name)
{
TArray<uint8> ReturnValue;
for (FReplayByteData Data : byteData)
{
if (Data.Name == Name)
{
return Data.Value;
}
}
return ReturnValue;
}
int UReplayDataObject::GetIntegerData(FString Name)
{
int ReturnValue = 0;
for (FReplayIntData Data : intData)
{
if (Data.Name == Name)
{
return Data.Value;
}
}
return ReturnValue;
}
int64 UReplayDataObject::GetInteger64Data(FString Name)
{
int64 ReturnValue = 0;
for (FReplayInt64Data Data : int64Data)
{
if (Data.Name == Name)
{
return Data.Value;
}
}
return ReturnValue;
}
float UReplayDataObject::GetFloatData(FString Name)
{
float ReturnValue = 0.0;
for (FReplayFloatData Data : floatData)
{
if (Data.Name == Name)
{
return Data.Value;
}
}
return ReturnValue;
}
FName UReplayDataObject::GetNameData(FString Name)
{
FName ReturnValue = "";
for (FReplayNameData Data : nameData)
{
if (Data.Name == Name)
{
return Data.Value;
}
}
return ReturnValue;
}
FString UReplayDataObject::GetStringData(FString Name)
{
FString ReturnValue = "";
for (FReplayStringData Data : stringData)
{
if (Data.Name == Name)
{
return Data.Value;
}
}
return ReturnValue;
}
FText UReplayDataObject::GetTextData(FString Name)
{
FText ReturnValue;
for (FReplayTextData Data : textData)
{
if (Data.Name == Name)
{
return Data.Value;
}
}
return ReturnValue;
}
FVector UReplayDataObject::GetVectorData(FString Name)
{
FVector ReturnValue = FVector();
for (FReplayVectorData Data : vectorData)
{
if (Data.Name == Name)
{
return Data.Value;
}
}
return ReturnValue;
}
FRotator UReplayDataObject::GetRotatorData(FString Name)
{
FRotator ReturnValue = FRotator();
for (FReplayRotatorData Data : rotatorData)
{
if (Data.Name == Name)
{
return Data.Value;
}
}
return ReturnValue;
}
FTransform UReplayDataObject::GetTransformData(FString Name)
{
FTransform ReturnValue = FTransform();
for (FReplayTransformData Data : transformData)
{
if (Data.Name == Name)
{
return Data.Value;
}
}
return ReturnValue;
}
FString UReplayDataObject::SaveReplayMetaDataToString()
{
//First Write all structs into strings
TArray<FString> BoolArray;
for (FReplayBoolData Data : boolData)
{
FString DataName = Data.Name;
FString DataValue = UKismetStringLibrary::Conv_BoolToString(Data.Value);
BoolArray.Add(DataName + DataValuesSeperator + DataValue);
}
TArray<FString> ByteArray;
for (FReplayByteData Data : byteData)
{
FString DataName = Data.Name;
FString DataValue;
FUTF8ToTCHAR Src = FUTF8ToTCHAR((const ANSICHAR*) (Data.Value.GetData() + 0), Data.Value.Num());
DataValue.AppendChars(Src.Get(), Src.Length());
ByteArray.Add(DataName + DataValuesSeperator + DataValue);
}
TArray<FString> IntArray;
for (FReplayIntData Data : intData)
{
FString DataName = Data.Name;
FString DataValue = UKismetStringLibrary::Conv_IntToString(Data.Value);
IntArray.Add(DataName + DataValuesSeperator + DataValue);
}
TArray<FString> Int64Array;
for (FReplayInt64Data Data : int64Data)
{
FString DataName = Data.Name;
FString DataValue = UKismetStringLibrary::Conv_IntToString(Data.Value);
Int64Array.Add(DataName + DataValuesSeperator + DataValue);
}
TArray<FString> FloatArray;
for (FReplayFloatData Data : floatData)
{
FString DataName = Data.Name;
#if ENGINE_MAJOR_VERSION <= 4
FString DataValue = UKismetStringLibrary::Conv_FloatToString(Data.Value);
#else
FString DataValue = FString::SanitizeFloat(Data.Value);
#endif
FloatArray.Add(DataName + DataValuesSeperator + DataValue);
}
TArray<FString> NameArray;
for (FReplayNameData Data : nameData)
{
FString DataName = Data.Name;
FString DataValue = UKismetStringLibrary::Conv_NameToString(Data.Value);
NameArray.Add(DataName + DataValuesSeperator + DataValue);
}
TArray<FString> StringArray;
for (FReplayStringData Data : stringData)
{
FString DataName = Data.Name;
FString DataValue = Data.Value;
StringArray.Add(DataName + DataValuesSeperator + DataValue);
}
TArray<FString> TextArray;
for (FReplayTextData Data : textData)
{
FString DataName = Data.Name;
FString DataValue = UKismetTextLibrary::Conv_TextToString(Data.Value);
TextArray.Add(DataName + DataValuesSeperator + DataValue);
}
TArray<FString> VectorArray;
for (FReplayVectorData Data : vectorData)
{
FString DataName = Data.Name;
FString DataValue = UKismetStringLibrary::Conv_VectorToString(Data.Value);
VectorArray.Add(DataName + DataValuesSeperator + DataValue);
}
TArray<FString> RotatorArray;
for (FReplayRotatorData Data : rotatorData)
{
FString DataName = Data.Name;
FString DataValue = UKismetStringLibrary::Conv_RotatorToString(Data.Value);
RotatorArray.Add(DataName + DataValuesSeperator + DataValue);
}
TArray<FString> TransformArray;
for (FReplayTransformData Data : transformData)
{
FString DataName = Data.Name;
FString DataVectorPart = UKismetStringLibrary::Conv_VectorToString(Data.Value.GetLocation());
FString DataRotatorPart = UKismetStringLibrary::Conv_RotatorToString(Data.Value.GetRotation().Rotator());
FString DataScalePart = UKismetStringLibrary::Conv_VectorToString(Data.Value.GetScale3D());
TArray<FString> DataValues;
DataValues.Add(DataVectorPart);
DataValues.Add(DataRotatorPart);
DataValues.Add(DataScalePart);
FString DataValue = UKismetStringLibrary::JoinStringArray(DataValues,transformDataSeperator);
TransformArray.Add(DataName + DataValuesSeperator + DataValue);
}
FString BoolArrayAsSingleString = UKismetStringLibrary::JoinStringArray(BoolArray, DataArraySeperator);
FString ByteArrayAsSingleString = UKismetStringLibrary::JoinStringArray(ByteArray, DataArraySeperator);
FString IntArrayAsSingleString = UKismetStringLibrary::JoinStringArray(IntArray, DataArraySeperator);
FString Int64ArrayAsSingleString = UKismetStringLibrary::JoinStringArray(Int64Array, DataArraySeperator);
FString FloatArrayAsSingleString = UKismetStringLibrary::JoinStringArray(FloatArray, DataArraySeperator);
FString NameArrayAsSingleString = UKismetStringLibrary::JoinStringArray(NameArray, DataArraySeperator);
FString StringArrayAsSingleString = UKismetStringLibrary::JoinStringArray(StringArray, DataArraySeperator);
FString TextArrayAsSingleString = UKismetStringLibrary::JoinStringArray(TextArray, DataArraySeperator);
FString VectorArrayAsSingleString = UKismetStringLibrary::JoinStringArray(VectorArray, DataArraySeperator);
FString RotatorArrayAsSingleString = UKismetStringLibrary::JoinStringArray(RotatorArray, DataArraySeperator);
FString TransformArrayAsSingleString = UKismetStringLibrary::JoinStringArray(TransformArray, DataArraySeperator);
TArray<FString> FinalStringArray;
FinalStringArray.Add(boolDataIdentifier + DataIdentifierSeperator + BoolArrayAsSingleString);
FinalStringArray.Add(byteDataIdentifier + DataIdentifierSeperator + ByteArrayAsSingleString);
FinalStringArray.Add(intDataIdentifier + DataIdentifierSeperator + IntArrayAsSingleString);
FinalStringArray.Add(int64DataIdentifier + DataIdentifierSeperator + Int64ArrayAsSingleString);
FinalStringArray.Add(floatDataIdentifier + DataIdentifierSeperator + FloatArrayAsSingleString);
FinalStringArray.Add(nameDataIdentifier + DataIdentifierSeperator + NameArrayAsSingleString);
FinalStringArray.Add(stringDataIdentifier + DataIdentifierSeperator + StringArrayAsSingleString);
FinalStringArray.Add(textDataIdentifier + DataIdentifierSeperator + TextArrayAsSingleString);
FinalStringArray.Add(vectorDataIdentifier + DataIdentifierSeperator + VectorArrayAsSingleString);
FinalStringArray.Add(rotatorDataIdentifier + DataIdentifierSeperator + RotatorArrayAsSingleString);
FinalStringArray.Add(transformDataIdentifier + DataIdentifierSeperator + TransformArrayAsSingleString);
FString FinalString = UKismetStringLibrary::JoinStringArray(FinalStringArray, FinalArraySeperator);
return FinalString;
}
bool UReplayDataObject::LoadReplayMetaDataFromString(FString StringDataToParse)
{
boolData.Empty();
byteData.Empty();
intData.Empty();
int64Data.Empty();
floatData.Empty();
nameData.Empty();
stringData.Empty();
textData.Empty();
vectorData.Empty();
rotatorData.Empty();
transformData.Empty();
TArray<FString> LoadedStringArray = UKismetStringLibrary::ParseIntoArray(StringDataToParse, FinalArraySeperator);
for (FString Field : LoadedStringArray)
{
FString DataIdentifier;
FString DataPart;
UKismetStringLibrary::Split(Field,DataIdentifierSeperator,DataIdentifier,DataPart);
TArray<FString> DataArray = UKismetStringLibrary::ParseIntoArray(DataPart,DataArraySeperator);
for (FString DataItem : DataArray)
{
if (DataIdentifier == boolDataIdentifier)
{
FReplayBoolData LoadedData;
FString Name;
FString Value;
UKismetStringLibrary::Split(DataItem,DataValuesSeperator,Name, Value);
LoadedData.Name = Name;
if(Value == "true")
{
LoadedData.Value = true;
}
AddBooleanData(LoadedData.Name,LoadedData.Value);
}
if (DataIdentifier == byteDataIdentifier)
{
FReplayByteData LoadedData;
FString Name;
FString Value;
UKismetStringLibrary::Split(DataItem,DataValuesSeperator,Name, Value);
LoadedData.Name = Name;
TArray<uint8> ByteArray;
if(!(Value.Len() <= 0))
{
FTCHARToUTF8 Src = FTCHARToUTF8(Value.GetCharArray().GetData());
ByteArray.Append((uint8*) Src.Get(), Src.Length());
}
LoadedData.Value = ByteArray;
AddByteData(LoadedData.Name,LoadedData.Value);
}
if (DataIdentifier == intDataIdentifier)
{
FReplayIntData LoadedData;
FString Name;
FString Value;
UKismetStringLibrary::Split(DataItem,DataValuesSeperator,Name, Value);
LoadedData.Name = Name;
LoadedData.Value = UKismetStringLibrary::Conv_StringToInt(Value);
AddIntegerData(LoadedData.Name,LoadedData.Value);
}
if (DataIdentifier == int64DataIdentifier)
{
FReplayInt64Data LoadedData;
FString Name;
FString Value;
UKismetStringLibrary::Split(DataItem,DataValuesSeperator,Name, Value);
LoadedData.Name = Name;
LoadedData.Value = UKismetStringLibrary::Conv_StringToInt(Value);
AddInteger64Data(LoadedData.Name,LoadedData.Value);
}
if (DataIdentifier == floatDataIdentifier)
{
FReplayFloatData LoadedData;
FString Name;
FString Value;
UKismetStringLibrary::Split(DataItem,DataValuesSeperator,Name, Value);
LoadedData.Name = Name;
#if ENGINE_MAJOR_VERSION <= 4
LoadedData.Value = UKismetStringLibrary::Conv_StringToFloat(Value);
#else
LexFromString(LoadedData.Value,*Value);
#endif
AddFloatData(LoadedData.Name,LoadedData.Value);
}
if (DataIdentifier == nameDataIdentifier)
{
FReplayNameData LoadedData;
FString Name;
FString Value;
UKismetStringLibrary::Split(DataItem,DataValuesSeperator,Name, Value);
LoadedData.Name = Name;
LoadedData.Value = UKismetStringLibrary::Conv_StringToName(Value);
AddNameData(LoadedData.Name,LoadedData.Value);
}
if (DataIdentifier == stringDataIdentifier)
{
FReplayStringData LoadedData;
FString Name;
FString Value;
UKismetStringLibrary::Split(DataItem,DataValuesSeperator,Name, Value);
LoadedData.Name = Name;
LoadedData.Value = Value;
AddStringData(LoadedData.Name,LoadedData.Value);
}
if (DataIdentifier == textDataIdentifier)
{
FReplayTextData LoadedData;
FString Name;
FString Value;
UKismetStringLibrary::Split(DataItem,DataValuesSeperator,Name, Value);
LoadedData.Name = Name;
LoadedData.Value = UKismetTextLibrary::Conv_StringToText(Value);
AddTextData(LoadedData.Name,LoadedData.Value);
}
if (DataIdentifier == vectorDataIdentifier)
{
FReplayVectorData LoadedData;
FString Name;
FString Value;
UKismetStringLibrary::Split(DataItem,DataValuesSeperator,Name, Value);
LoadedData.Name = Name;
FVector ConvertedValue;
bool bWasSuccessful;
UKismetStringLibrary::Conv_StringToVector(Value,ConvertedValue,bWasSuccessful);
LoadedData.Value = ConvertedValue;
AddVectorData(LoadedData.Name,LoadedData.Value);
}
if (DataIdentifier == rotatorDataIdentifier)
{
FReplayRotatorData LoadedData;
FString Name;
FString Value;
UKismetStringLibrary::Split(DataItem,DataValuesSeperator,Name, Value);
LoadedData.Name = Name;
FRotator ConvertedValue;
bool bWasSuccessful;
UKismetStringLibrary::Conv_StringToRotator(Value,ConvertedValue,bWasSuccessful);
LoadedData.Value = ConvertedValue;
AddRotatorData(LoadedData.Name,LoadedData.Value);
}
if (DataIdentifier == transformDataIdentifier)
{
FReplayTransformData LoadedData;
FString Name;
FString Value;
UKismetStringLibrary::Split(DataItem,DataValuesSeperator,Name, Value);
LoadedData.Name = Name;
TArray<FString> DataComponents = UKismetStringLibrary::ParseIntoArray(Value,transformDataSeperator);
FVector ConvertedVectorValue;
bool bWasSuccessful;
UKismetStringLibrary::Conv_StringToVector(DataComponents[0],ConvertedVectorValue,bWasSuccessful);
FRotator ConvertedRotatorValue;
UKismetStringLibrary::Conv_StringToRotator(DataComponents[1],ConvertedRotatorValue,bWasSuccessful);
FVector ConvertedScaleValue;
UKismetStringLibrary::Conv_StringToVector(DataComponents[2],ConvertedScaleValue,bWasSuccessful);
LoadedData.Value = FTransform(ConvertedRotatorValue,ConvertedVectorValue,ConvertedScaleValue);
AddTransformData(LoadedData.Name,LoadedData.Value);
}
}
}
return true;
}

View File

@@ -0,0 +1,48 @@
// Copyright 2020-Present Oyintare Ebelo. All Rights Reserved.
#include "ReplayObject.h"
#include "ReplayDataObject.h"
#include "NetworkReplayStreaming.h"
#include "HAL/FileManagerGeneric.h"
#include "Engine/DemoNetDriver.h"
void UReplayObject::RequestEvents(FString Group,int UserIndex)
{
const TSharedPtr<INetworkReplayStreamer> EnumerateStreamsPtr = FNetworkReplayStreaming::Get().GetFactory().CreateReplayStreamer();
EnumerateEventsDel = FEnumerateEventsCallback::CreateUObject(this, &UReplayObject::OnEnumerateEventsComplete);
if (EnumerateStreamsPtr.Get())
{
EnumerateStreamsPtr.Get()->EnumerateEvents(ReplayInfo.ActualName,Group,UserIndex, EnumerateEventsDel);
}
}
void UReplayObject::OnEnumerateEventsComplete(const FEnumerateEventsResult& Results)
{
TArray<FReplayEvent> ReplayEvents;
if(Results.WasSuccessful())
{
for(FReplayEventListItem EventItem : Results.ReplayEventList.ReplayEvents)
{
FReplayEvent ReplayEventTemp;
ReplayEventTemp.EventID = EventItem.ID;
ReplayEventTemp.Group = EventItem.Group;
ReplayEventTemp.TimeInMs = (int32)EventItem.Time1;
UReplayDataObject* DataObject = NewObject<UReplayDataObject>();
DataObject->LoadReplayMetaDataFromString(EventItem.Metadata);
ReplayEventTemp.Data = DataObject;
ReplayEvents.Add(ReplayEventTemp);
}
}
OnRequestEventsComplete.Broadcast(ReplayEvents);
}

View File

@@ -0,0 +1,10 @@
// Copyright 2020-Present Oyintare Ebelo. All Rights Reserved.
#include "ReplayPlayerController.h"
AReplayPlayerController::AReplayPlayerController()
{
bShouldPerformFullTickWhenPaused = true;
SetTickableWhenPaused(true);
}

View File

@@ -0,0 +1,24 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#include "ReplaySystem.h"
DEFINE_LOG_CATEGORY(LogReplaySystem);
#define LOCTEXT_NAMESPACE "FReplaySystemModule"
void FReplaySystemModule::StartupModule()
{
// This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module
}
void FReplaySystemModule::ShutdownModule()
{
// This function may be called during shutdown to clean up your module. For modules that support dynamic reloading,
// we call this function before unloading the module.
}
#undef LOCTEXT_NAMESPACE
IMPLEMENT_MODULE(FReplaySystemModule, ReplaySystem)

View File

@@ -0,0 +1,490 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#include "ReplaySystemBPLibrary.h"
#include "Engine/World.h"
#include "GameFramework/WorldSettings.h"
#include "Engine/DemoNetDriver.h"
#include "NetworkReplayStreaming.h"
#include "Engine/GameInstance.h"
#include "Engine/Engine.h"
#include "DeleteReplayObject.h"
#include "GetSavedReplaysObject.h"
#include "GoToTimeObject.h"
#include "RenameReplayObject.h"
#include "ReplayDataObject.h"
#include "ReplayPlayerController.h"
#include "Containers/UnrealString.h"
#include "RequestEventsObject.h"
#include "Kismet/GameplayStatics.h"
#include "Kismet/KismetMathLibrary.h"
UReplaySystemBPLibrary::UReplaySystemBPLibrary(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
}
void UReplaySystemBPLibrary::SetReplaySavePath(UObject* WorldContextObject, const FString& Path)
{
if (UWorld* World = GEngine->GetWorldFromContextObjectChecked(WorldContextObject))
{
if (const TSharedPtr<INetworkReplayStreamer> EnumerateStreamsPtr = FNetworkReplayStreaming::Get().GetFactory().
CreateReplayStreamer())
{
EnumerateStreamsPtr->SetDemoPath(Path);
}
}
}
FString UReplaySystemBPLibrary::GetReplaySavePath(UObject* WorldContextObject)
{
FString Path = "";
if (UWorld* World = GEngine->GetWorldFromContextObjectChecked(WorldContextObject))
{
if (const TSharedPtr<INetworkReplayStreamer> EnumerateStreamsPtr = FNetworkReplayStreaming::Get().GetFactory().
CreateReplayStreamer())
{
EnumerateStreamsPtr->GetDemoPath(Path);
}
}
return Path;
}
void UReplaySystemBPLibrary::RecordReplay(UObject* WorldContextObject, const FString& ReplayName,
const FString& ReplayFriendlyName)
{
if (const UWorld* World = GEngine->GetWorldFromContextObjectChecked(WorldContextObject))
{
if (UGameInstance* GI = Cast<UGameInstance>(World->GetGameInstance()))
{
const TArray<FString> Options;
GI->StartRecordingReplay(ReplayName, ReplayFriendlyName, Options);
}
}
}
void UReplaySystemBPLibrary::StopRecordingReplay(UObject* WorldContextObject)
{
if (const UWorld* World = GEngine->GetWorldFromContextObjectChecked(WorldContextObject))
{
if (IsRecordingReplay(WorldContextObject))
{
if (UGameInstance* GI = Cast<UGameInstance>(World->GetGameInstance()))
{
GI->StopRecordingReplay();
}
}
}
}
bool UReplaySystemBPLibrary::IsRecordingReplay(UObject* WorldContextObject)
{
if (UWorld* World = GEngine->GetWorldFromContextObjectChecked(WorldContextObject))
{
if (World != nullptr && GetDemoDriver(World) != nullptr)
{
return GetDemoDriver(World)->IsRecording();
}
}
return false;
}
UDeleteReplayObject* UReplaySystemBPLibrary::DeleteReplay(UObject* WorldContextObject, const FString& ReplayName)
{
if (UWorld* World = GEngine->GetWorldFromContextObjectChecked(WorldContextObject))
{
if (UDeleteReplayObject* ReplayDeleteObj = NewObject<UDeleteReplayObject>())
{
ReplayDeleteObj->DeleteReplay(ReplayName);
return ReplayDeleteObj;
}
}
return nullptr;
}
URenameReplayObject* UReplaySystemBPLibrary::RenameReplay(UObject* WorldContextObject, const FString& ReplayName,
const FString& NewReplayName, const int32 UserIndex)
{
if (UWorld* World = GEngine->GetWorldFromContextObjectChecked(WorldContextObject))
{
if (URenameReplayObject* ReplayRenameObj = NewObject<URenameReplayObject>())
{
ReplayRenameObj->RenameReplay(ReplayName, NewReplayName, UserIndex, true);
return ReplayRenameObj;
}
}
return nullptr;
}
URenameReplayObject* UReplaySystemBPLibrary::RenameReplayFriendly(UObject* WorldContextObject,
const FString& ReplayName,
const FString& NewFriendlyReplayName,
const int32 UserIndex)
{
if (UWorld* World = GEngine->GetWorldFromContextObjectChecked(WorldContextObject))
{
if (URenameReplayObject* ReplayRenameObj = NewObject<URenameReplayObject>())
{
ReplayRenameObj->RenameReplay(ReplayName, NewFriendlyReplayName, UserIndex, false);
return ReplayRenameObj;
}
}
return nullptr;
}
UGetSavedReplaysObject* UReplaySystemBPLibrary::GetSavedReplays(UObject* WorldContextObject)
{
if (UWorld* World = GEngine->GetWorldFromContextObjectChecked(WorldContextObject))
{
if (UGetSavedReplaysObject* SavedReplaysObj = NewObject<UGetSavedReplaysObject>())
{
SavedReplaysObj->GetSavedReplays();
return SavedReplaysObj;
}
}
return nullptr;
}
bool UReplaySystemBPLibrary::PlayRecordedReplay(UObject* WorldContextObject, const FString& ReplayName)
{
if (const UWorld* World = GEngine->GetWorldFromContextObjectChecked(WorldContextObject))
{
if (UGameInstance* GI = Cast<UGameInstance>(World->GetGameInstance()))
{
const TArray<FString> Options;
return GI->PlayReplay(ReplayName, nullptr, Options);
}
}
return false;
}
UGoToTimeObject* UReplaySystemBPLibrary::RestartReplayPlayback(UObject* WorldContextObject)
{
UGoToTimeObject* GoToTimeObject = NewObject<UGoToTimeObject>();
if (!GoToTimeObject)
{
return nullptr;
}
GoToTimeObject->GoToTime(WorldContextObject, 0.0f, false);
return GoToTimeObject;
}
UGoToTimeObject* UReplaySystemBPLibrary::GoToSpecificTime(UObject* WorldContextObject, float TimeToGoTo,
bool bRetainCurrentPauseState)
{
UGoToTimeObject* GoToTimeObject = NewObject<UGoToTimeObject>();
if (!GoToTimeObject)
{
return nullptr;
}
GoToTimeObject->GoToTime(WorldContextObject, TimeToGoTo, bRetainCurrentPauseState);
return GoToTimeObject;
}
void UReplaySystemBPLibrary::PausePlayback(UObject* WorldContextObject)
{
if (UWorld* World = GEngine->GetWorldFromContextObjectChecked(WorldContextObject))
{
if (AWorldSettings* WorldSettings = World->GetWorldSettings())
{
if (const UDemoNetDriver* DemoDriver = GetDemoDriver(World))
{
if (DemoDriver && DemoDriver->ServerConnection && DemoDriver->ServerConnection->PlayerController && DemoDriver->ServerConnection->PlayerController->PlayerState)
{
World->bIsCameraMoveableWhenPaused = true;
WorldSettings->SetPauserPlayerState(DemoDriver->ServerConnection->PlayerController->PlayerState);
if (AReplayPlayerController* ReplayPC = Cast<AReplayPlayerController>(
UGameplayStatics::GetPlayerController(World, 0)))
{
ReplayPC->OnTogglePause(true);
}
}
}
}
}
}
void UReplaySystemBPLibrary::ResumePlayback(UObject* WorldContextObject)
{
if (const UWorld* World = GEngine->GetWorldFromContextObjectChecked(WorldContextObject))
{
if (AWorldSettings* WorldSettings = World->GetWorldSettings())
{
WorldSettings->SetPauserPlayerState(nullptr);
if (AReplayPlayerController* ReplayPC = Cast<AReplayPlayerController>(
UGameplayStatics::GetPlayerController(World, 0)))
{
ReplayPC->OnTogglePause(false);
}
}
}
}
void UReplaySystemBPLibrary::SetPlaybackSpeed(UObject* WorldContextObject, float Speed)
{
if (const UWorld* World = GEngine->GetWorldFromContextObjectChecked(WorldContextObject))
{
if (AWorldSettings* WorldSettings = World->GetWorldSettings())
{
WorldSettings->DemoPlayTimeDilation = Speed;
}
}
}
float UReplaySystemBPLibrary::GetPlaybackSpeed(UObject* WorldContextObject)
{
if (const UWorld* World = GEngine->GetWorldFromContextObjectChecked(WorldContextObject))
{
if (const AWorldSettings* WorldSettings = World->GetWorldSettings())
{
return WorldSettings->DemoPlayTimeDilation;
}
}
return 1.0f;
}
float UReplaySystemBPLibrary::GetCurrentReplayTime(UObject* WorldContextObject)
{
if (UWorld* World = GEngine->GetWorldFromContextObjectChecked(WorldContextObject))
{
if (const UDemoNetDriver* DemoDriver = GetDemoDriver(World))
{
return DemoDriver->GetDemoCurrentTime();
}
else
{
return 0.0f;
}
}
return 0.0f;
}
float UReplaySystemBPLibrary::GetReplayLength(UObject* WorldContextObject)
{
if (UWorld* World = GEngine->GetWorldFromContextObjectChecked(WorldContextObject))
{
if (const UDemoNetDriver* DemoDriver = GetDemoDriver(World))
{
if (IsRecordingReplay(WorldContextObject))
{
return DemoDriver->AccumulatedRecordTime;
}
if (IsPlayingReplay(WorldContextObject))
{
return DemoDriver->GetDemoTotalTime();
}
}
}
return 0.0f;
}
bool UReplaySystemBPLibrary::IsPlayingReplay(UObject* WorldContextObject)
{
if (UWorld* World = GEngine->GetWorldFromContextObjectChecked(WorldContextObject))
{
if (const UDemoNetDriver* DemoDriver = GetDemoDriver(World))
{
return DemoDriver->IsPlaying();
}
}
return false;
}
bool UReplaySystemBPLibrary::IsReplayPlaybackPaused(UObject* WorldContextObject)
{
if (const UWorld* World = GEngine->GetWorldFromContextObjectChecked(WorldContextObject))
{
if (IsPlayingReplay(WorldContextObject))
{
if (const AWorldSettings* WorldSettings = World->GetWorldSettings())
{
return WorldSettings->GetPauserPlayerState() != nullptr;
}
}
}
return false;
}
FString UReplaySystemBPLibrary::GetActiveReplayName(UObject* WorldContextObject)
{
if (UWorld* World = GEngine->GetWorldFromContextObjectChecked(WorldContextObject))
{
if (World != nullptr && GetDemoDriver(World) != nullptr)
{
return GetDemoDriver(World)->GetActiveReplayName();
}
}
return "None";
}
bool UReplaySystemBPLibrary::AddEventToActiveReplay(UObject* WorldContextObject, const FString& EventName,
const FString& Group, UReplayDataObject* DataObject)
{
if (UWorld* World = GEngine->GetWorldFromContextObjectChecked(WorldContextObject))
{
if (IsRecordingReplay(WorldContextObject))
{
if (World != nullptr && GetDemoDriver(World) != nullptr)
{
FString EmptyString = "";
const TArray<uint8> Data;
if (DataObject)
{
FString ReplayDataString = DataObject->SaveReplayMetaDataToString();
EmptyString = ReplayDataString;
}
GetDemoDriver(World)->AddOrUpdateEvent(EventName, Group, EmptyString, Data);
return true;
}
}
}
return false;
}
URequestEventsObject* UReplaySystemBPLibrary::RequestActiveReplayEvents(UObject* WorldContextObject, FString Group,
int UserIndex)
{
if (UWorld* World = GEngine->GetWorldFromContextObjectChecked(WorldContextObject))
{
if (!IsRecordingReplay(WorldContextObject) && IsPlayingReplay(WorldContextObject))
{
if (URequestEventsObject* RequestEventsObject = NewObject<URequestEventsObject>())
{
RequestEventsObject->RequestEvents(GetActiveReplayName(WorldContextObject), Group, UserIndex);
return RequestEventsObject;
}
}
}
return nullptr;
}
UReplayDataObject* UReplaySystemBPLibrary::CreateReplayDataObject()
{
UReplayDataObject* DataObject = NewObject<UReplayDataObject>();
return DataObject;
}
float UReplaySystemBPLibrary::MsToSeconds(const int32 MS)
{
#if ENGINE_MAJOR_VERSION <= 4
const float MsAsFloat = UKismetMathLibrary::Conv_IntToFloat(MS);
#else
const float MsAsFloat = UKismetMathLibrary::Conv_IntToDouble(MS);
#endif
const float MsAsSeconds = MsAsFloat / 1000;
return MsAsSeconds;
}
void UReplaySystemBPLibrary::SpectateActor(UObject* WorldContextObject, AActor* Actor, FBlendSettings BlendSettings)
{
if (const UWorld* World = GEngine->GetWorldFromContextObjectChecked(WorldContextObject))
{
if (!Actor)
{
return;
}
if (UGameplayStatics::GetPlayerController(World, 0))
{
if (APlayerController* PC = UGameplayStatics::GetPlayerController(World, 0))
{
if (!PC->IsLocalController())
{
return;
}
//Everything from here is happening only locally/on the client
PC->SetViewTargetWithBlend(Actor, BlendSettings.BlendTime, BlendSettings.BlendFunction,
BlendSettings.BlendExponent, BlendSettings.bLockOutgoing);
if (AReplayPlayerController* ReplayPC = Cast<AReplayPlayerController>(
UGameplayStatics::GetPlayerController(World, 0)))
{
ReplayPC->OnSpectateActor(Actor);
ReplayPC->bIsSpectating = true;
}
}
}
}
}
void UReplaySystemBPLibrary::StopSpectating(UObject* WorldContextObject, FBlendSettings BlendSettings)
{
if (const UWorld* World = GEngine->GetWorldFromContextObjectChecked(WorldContextObject))
{
if (UGameplayStatics::GetPlayerController(World, 0))
{
if (APlayerController* PC = UGameplayStatics::GetPlayerController(World, 0))
{
if (!PC->IsLocalController())
{
return;
}
//Everything from here is happening only locally/on the client
PC->SetViewTargetWithBlend(PC->GetPawn(), BlendSettings.BlendTime, BlendSettings.BlendFunction,
BlendSettings.BlendExponent, BlendSettings.bLockOutgoing);
if (AReplayPlayerController* ReplayPC = Cast<AReplayPlayerController>(
UGameplayStatics::GetPlayerController(World, 0)))
{
ReplayPC->OnStopSpectateActor();
ReplayPC->bIsSpectating = false;
}
}
}
}
}
UDemoNetDriver* UReplaySystemBPLibrary::GetDemoDriver(const UObject* WorldContextObject)
{
if (UWorld* World = GEngine->GetWorldFromContextObjectChecked(WorldContextObject))
{
#if ENGINE_MAJOR_VERSION > 4
return World->GetDemoNetDriver();
#elif ENGINE_MINOR_VERSION >= 26
return World->GetDemoNetDriver();
#elif ENGINE_MINOR_VERSION < 26
return World->DemoNetDriver;
#endif
}
return nullptr;
}
void UReplaySystemBPLibrary::SetMaxRecordHz(UObject* WorldContextObject, float Hz)
{
if (const UWorld* World = GEngine->GetWorldFromContextObjectChecked(WorldContextObject))
{
if (UGameplayStatics::GetPlayerController(World, 0))
{
#if ENGINE_MAJOR_VERSION <= 4
FString Command = "demo.recordhz " + UKismetStringLibrary::Conv_FloatToString(Hz);
#else
FString Command = "demo.recordhz " + FString::SanitizeFloat(Hz);
#endif
UGameplayStatics::GetPlayerController(World, 0)->ConsoleCommand(Command);
}
}
}
float UReplaySystemBPLibrary::GetMaxRecordHz()
{
static const auto CVar = IConsoleManager::Get().FindConsoleVariable(TEXT("demo.recordhz"));
const float value = CVar->GetFloat();
return value;
}

View File

@@ -0,0 +1,47 @@
// Copyright 2020-Present Oyintare Ebelo. All Rights Reserved.
#include "RequestEventsObject.h"
#include "ReplayStructs.h"
#include "ReplayObject.h"
#include "NetworkReplayStreaming.h"
#include "ReplayDataObject.h"
void URequestEventsObject::RequestEvents(FString ReplayName,FString Group, int UserIndex)
{
const TSharedPtr<INetworkReplayStreamer> EnumerateStreamsPtr = FNetworkReplayStreaming::Get().GetFactory().CreateReplayStreamer();
EnumerateEventsDel = FEnumerateEventsCallback::CreateUObject(this, &URequestEventsObject::OnEnumerateEventsComplete);
if (EnumerateStreamsPtr.Get())
{
EnumerateStreamsPtr.Get()->EnumerateEvents(ReplayName,Group,UserIndex, EnumerateEventsDel);
}
}
void URequestEventsObject::OnEnumerateEventsComplete(const FEnumerateEventsResult& Results)
{
TArray<FReplayEvent> ReplayEvents;
if(Results.WasSuccessful())
{
for(FReplayEventListItem EventItem : Results.ReplayEventList.ReplayEvents)
{
FReplayEvent ReplayEventTemp;
ReplayEventTemp.EventID = EventItem.ID;
ReplayEventTemp.Group = EventItem.Group;
ReplayEventTemp.TimeInMs = static_cast<int32>(EventItem.Time1);
UReplayDataObject* DataObject = NewObject<UReplayDataObject>();
DataObject->LoadReplayMetaDataFromString(EventItem.Metadata);
ReplayEventTemp.Data = DataObject;
ReplayEvents.Add(ReplayEventTemp);
}
}
OnRequestEventsComplete.Broadcast(ReplayEvents);
}

View File

@@ -0,0 +1,35 @@
// Copyright 2020-Present Oyintare Ebelo. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "NetworkReplayStreaming.h"
#include "ReplayDelegates.h"
#include "DeleteReplayObject.generated.h"
/**
*
*/
UCLASS(BlueprintType, Blueprintable)
class REPLAYSYSTEM_API UDeleteReplayObject : public UObject
{
GENERATED_BODY()
public:
UPROPERTY(BlueprintAssignable, Category = "Replay")
FOnDeleteComplete OnDeleteComplete;
void DeleteReplay(const FString& ReplayName);
TSharedPtr<INetworkReplayStreamer> EnumerateStreamsPtr;
FDeleteFinishedStreamCallback OnDeleteFinishedStreamCompleteDel;
void OnDeleteFinishedStreamComplete(const FDeleteFinishedStreamResult& Result);
};

View File

@@ -0,0 +1,37 @@
// Copyright 2020-Present Oyintare Ebelo. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "ReplayObject.h"
#include "NetworkReplayStreaming.h"
#include "GetSavedReplaysObject.generated.h"
/**
*
*/
UCLASS(BlueprintType, Blueprintable)
class REPLAYSYSTEM_API UGetSavedReplaysObject : public UObject
{
GENERATED_BODY()
public:
UPROPERTY(BlueprintAssignable, Category = "Replay")
FOnGetReplaysComplete OnGetReplaysComplete;
void GetSavedReplays();
TSharedPtr<INetworkReplayStreamer> EnumerateStreamsPtr;
FEnumerateStreamsCallback EnumerateStreamsCallbackDel;
/** Called when we get results from the replay streaming interface */
void OnEnumerateStreamsComplete(const FEnumerateStreamsResult& Result);
};

View File

@@ -0,0 +1,43 @@
// Copyright 2020-Present Oyintare Ebelo. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "ReplayDelegates.h"
#include "Engine.h"
#include "Engine/DemoNetDriver.h"
#include "GoToTimeObject.generated.h"
UCLASS()
class REPLAYSYSTEM_API UGoToTimeObject : public UObject
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
UGoToTimeObject();
protected:
public:
UPROPERTY(BlueprintAssignable)
FOnGotoTimeComplete OnGotoTimeComplete;
void GoToTime(UObject* WorldContextObject,float TimeToGoTo,bool bRetainPauseState);
UPROPERTY()
UObject* WCO;
bool bPauseStateBeforeMove = false;
bool bRetainPauseStateBeforeMove = false;
FOnGotoTimeDelegate OnGoToTimeDelegate;
void OnGoToTimeProcessed(bool bWasSuccessful);
};

View File

@@ -0,0 +1,35 @@
// Copyright 2020-Present Oyintare Ebelo. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "NetworkReplayStreaming.h"
#include "ReplayDelegates.h"
#include "ModifyReplayObject.generated.h"
/**
*
*/
UCLASS(BlueprintType, Blueprintable)
class REPLAYSYSTEM_API UModifyReplayObject : public UObject
{
GENERATED_BODY()
public:
//UPROPERTY(BlueprintAssignable, Category = "Replay")
//FOnModifyReplayComplete OnRenameComplete;
void RenameReplay(const FString& ReplayName, const FString& NewName, const int32 UserIndex, bool bIsNormalName);
TSharedPtr<INetworkReplayStreamer> EnumerateStreamsPtr;
FRenameReplayCallback OnRenameReplayCompleteDel;
void OnRenameReplayComplete(const FRenameReplayResult& Result);
};

View File

@@ -0,0 +1,35 @@
// Copyright 2020-Present Oyintare Ebelo. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "NetworkReplayStreaming.h"
#include "ReplayDelegates.h"
#include "RenameReplayObject.generated.h"
/**
*
*/
UCLASS(BlueprintType, Blueprintable)
class REPLAYSYSTEM_API URenameReplayObject : public UObject
{
GENERATED_BODY()
public:
UPROPERTY(BlueprintAssignable, Category = "Replay")
FOnRenameReplayComplete OnRenameComplete;
void RenameReplay(const FString& ReplayName, const FString& NewName, const int32 UserIndex, bool bIsNormalName);
TSharedPtr<INetworkReplayStreamer> EnumerateStreamsPtr;
FRenameReplayCallback OnRenameReplayCompleteDel;
void OnRenameReplayComplete(const FRenameReplayResult& Result);
};

View File

@@ -0,0 +1,242 @@
#pragma once
#include "CoreMinimal.h"
#include "ReplayStructs.h"
#include "ReplayDataObject.generated.h"
/**
*
*/
UCLASS(BlueprintType, Blueprintable)
class REPLAYSYSTEM_API UReplayDataObject : public UObject
{
GENERATED_BODY()
public:
FString DataPlaceHolder = "---PlaceHolderValue---";
FString DataArraySeperator = "---DataArray---";
FString DataValuesSeperator = "---DataValue---";
FString FinalArraySeperator = "---FinalArraySep---";
FString NewLineReplacer = " ---NewLineRep--- ";
FString NewLineReplacerR = " ---NEWLINE-R--- ";
FString boolDataIdentifier = "---BoolI---";
FString byteDataIdentifier = "---ByteI---";
FString intDataIdentifier = "---IntI---";
FString int64DataIdentifier = "---Int64I---";
FString floatDataIdentifier = "---FloatI---";
FString nameDataIdentifier = "---NameI---";
FString stringDataIdentifier = "---StringI---";
FString textDataIdentifier = "---TextI---";
FString vectorDataIdentifier = "---VectorI---";
FString rotatorDataIdentifier = "---RotatorI---";
FString transformDataIdentifier = "---TransformI---";
FString DataIdentifierSeperator = "---IdentifierSep---";
FString transformDataSeperator = "---TransformDataSep---";
UPROPERTY(BlueprintReadOnly, Category = "DataObject")
TArray<FReplayBoolData> boolData;
UPROPERTY(BlueprintReadOnly, Category = "DataObject")
TArray<FReplayByteData> byteData;
UPROPERTY(BlueprintReadOnly, Category = "DataObject")
TArray<FReplayIntData> intData;
UPROPERTY(BlueprintReadOnly, Category = "DataObject")
TArray<FReplayInt64Data> int64Data;
UPROPERTY(BlueprintReadOnly, Category = "DataObject")
TArray<FReplayFloatData> floatData;
UPROPERTY(BlueprintReadOnly, Category = "DataObject")
TArray<FReplayNameData> nameData;
UPROPERTY(BlueprintReadOnly, Category = "DataObject")
TArray<FReplayStringData> stringData;
UPROPERTY(BlueprintReadOnly, Category = "DataObject")
TArray<FReplayTextData> textData;
UPROPERTY(BlueprintReadOnly, Category = "DataObject")
TArray<FReplayVectorData> vectorData;
UPROPERTY(BlueprintReadOnly, Category = "DataObject")
TArray<FReplayRotatorData> rotatorData;
UPROPERTY(BlueprintReadOnly, Category = "DataObject")
TArray<FReplayTransformData> transformData;
UFUNCTION(BlueprintCallable,BlueprintPure, Category = "DataObject")
int DoesBooleanDataExist (FString Name);
UFUNCTION(BlueprintCallable,BlueprintPure, Category = "DataObject")
int DoesByteDataExist (FString Name);
UFUNCTION(BlueprintCallable,BlueprintPure, Category = "DataObject")
int DoesIntegerDataExist (FString Name);
UFUNCTION(BlueprintCallable,BlueprintPure, Category = "DataObject")
int DoesInteger64DataExist (FString Name);
UFUNCTION(BlueprintCallable,BlueprintPure, Category = "DataObject")
int DoesFloatDataExist (FString Name);
UFUNCTION(BlueprintCallable,BlueprintPure, Category = "DataObject")
int DoesNameDataExist (FString Name);
UFUNCTION(BlueprintCallable,BlueprintPure, Category = "DataObject")
int DoesStringDataExist (FString Name);
UFUNCTION(BlueprintCallable,BlueprintPure, Category = "DataObject")
int DoesTextDataExist (FString Name);
UFUNCTION(BlueprintCallable,BlueprintPure, Category = "DataObject")
int DoesVectorDataExist (FString Name);
UFUNCTION(BlueprintCallable,BlueprintPure, Category = "DataObject")
int DoesRotatorDataExist (FString Name);
UFUNCTION(BlueprintCallable,BlueprintPure, Category = "DataObject")
int DoesTransformDataExist (FString Name);
// Adds or updates data of the specified type (Case Sensitive)
UFUNCTION(BlueprintCallable, Category = "DataObject")
void AddBooleanData(FString Name,bool Data);
// Adds or updates data of the specified type (Case Sensitive)
UFUNCTION(BlueprintCallable, Category = "DataObject")
void AddByteData(FString Name,TArray<uint8> Data);
// Adds or updates data of the specified type (Case Sensitive)
UFUNCTION(BlueprintCallable, Category = "DataObject")
void AddIntegerData(FString Name,int Data);
// Adds or updates data of the specified type (Case Sensitive)
UFUNCTION(BlueprintCallable, Category = "DataObject")
void AddInteger64Data(FString Name,int64 Data);
// Adds or updates data of the specified type (Case Sensitive)
UFUNCTION(BlueprintCallable, Category = "DataObject")
void AddFloatData(FString Name,float Data);
// Adds or updates data of the specified type (Case Sensitive)
UFUNCTION(BlueprintCallable, Category = "DataObject")
void AddNameData(FString Name,FName Data);
// Adds or updates data of the specified type (Case Sensitive)
UFUNCTION(BlueprintCallable, Category = "DataObject")
void AddStringData(FString Name,FString Data);
// Adds or updates data of the specified type (Case Sensitive)
UFUNCTION(BlueprintCallable, Category = "DataObject")
void AddTextData(FString Name,FText Data);
// Adds or updates data of the specified type (Case Sensitive)
UFUNCTION(BlueprintCallable, Category = "DataObject")
void AddVectorData(FString Name,FVector Data);
// Adds or updates data of the specified type (Case Sensitive)
UFUNCTION(BlueprintCallable, Category = "DataObject")
void AddRotatorData(FString Name,FRotator Data);
// Adds or updates data of the specified type (Case Sensitive)
UFUNCTION(BlueprintCallable, Category = "DataObject")
void AddTransformData(FString Name,FTransform Data);
// Removes data of the specified type (Case Sensitive)
UFUNCTION(BlueprintCallable, Category = "DataObject")
void RemoveBooleanData(FString Name);
// Removes data of the specified type (Case Sensitive)
UFUNCTION(BlueprintCallable, Category = "DataObject")
void RemoveByteData(FString Name);
// Removes data of the specified type (Case Sensitive)
UFUNCTION(BlueprintCallable, Category = "DataObject")
void RemoveIntegerData(FString Name);
// Removes data of the specified type (Case Sensitive)
UFUNCTION(BlueprintCallable, Category = "DataObject")
void RemoveInteger64Data(FString Name);
// Removes data of the specified type (Case Sensitive)
UFUNCTION(BlueprintCallable, Category = "DataObject")
void RemoveFloatData(FString Name);
// Removes data of the specified type (Case Sensitive)
UFUNCTION(BlueprintCallable, Category = "DataObject")
void RemoveNameData(FString Name);
// Removes data of the specified type (Case Sensitive)
UFUNCTION(BlueprintCallable, Category = "DataObject")
void RemoveStringData(FString Name);
// Removes data of the specified type (Case Sensitive)
UFUNCTION(BlueprintCallable, Category = "DataObject")
void RemoveTextData(FString Name);
// Removes data of the specified type (Case Sensitive)
UFUNCTION(BlueprintCallable, Category = "DataObject")
void RemoveVectorData(FString Name);
// Removes data of the specified type (Case Sensitive)
UFUNCTION(BlueprintCallable, Category = "DataObject")
void RemoveRotatorData(FString Name);
// Removes data of the specified type (Case Sensitive)
UFUNCTION(BlueprintCallable, Category = "DataObject")
void RemoveTransformData(FString Name);
// Returns the stored value if it exists (Case Sensitive)
UFUNCTION(BlueprintCallable, Category = "DataObject")
bool GetBooleanData(FString Name);
// Returns the stored value if it exists (Case Sensitive)
UFUNCTION(BlueprintCallable, Category = "DataObject")
TArray<uint8> GetByteData(FString Name);
// Returns the stored value if it exists (Case Sensitive)
UFUNCTION(BlueprintCallable, Category = "DataObject")
int GetIntegerData(FString Name);
// Returns the stored value if it exists (Case Sensitive)
UFUNCTION(BlueprintCallable, Category = "DataObject")
int64 GetInteger64Data(FString Name);
// Returns the stored value if it exists (Case Sensitive)
UFUNCTION(BlueprintCallable, Category = "DataObject")
float GetFloatData(FString Name);
// Returns the stored value if it exists (Case Sensitive)
UFUNCTION(BlueprintCallable, Category = "DataObject")
FName GetNameData(FString Name);
// Returns the stored value if it exists (Case Sensitive)
UFUNCTION(BlueprintCallable, Category = "DataObject")
FString GetStringData(FString Name);
// Returns the stored value if it exists (Case Sensitive)
UFUNCTION(BlueprintCallable, Category = "DataObject")
FText GetTextData(FString Name);
// Returns the stored value if it exists (Case Sensitive)
UFUNCTION(BlueprintCallable, Category = "DataObject")
FVector GetVectorData(FString Name);
// Returns the stored value if it exists (Case Sensitive)
UFUNCTION(BlueprintCallable, Category = "DataObject")
FRotator GetRotatorData(FString Name);
// Returns the stored value if it exists (Case Sensitive)
UFUNCTION(BlueprintCallable, Category = "DataObject")
FTransform GetTransformData(FString Name);
/*Used Internaly to Create/Save meta data for a replay*/
UFUNCTION(Category = "DataObject")
FString SaveReplayMetaDataToString();
/*Used Internaly to Load a meta data for a replay*/
UFUNCTION(Category = "DataObject")
bool LoadReplayMetaDataFromString(FString StringDataToParse);
};

View File

@@ -0,0 +1,27 @@
// Copyright 2020-Present Oyintare Ebelo. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "ReplayStructs.h"
#include "ReplayDelegates.generated.h"
class UReplayObject;
class APawn;
UDELEGATE()
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnRequestEventsComplete,const TArray<FReplayEvent>&, Events);
UDELEGATE()
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnRenameReplayComplete, bool, bWasSuccessful);
UDELEGATE()
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnGetReplaysComplete,const TArray<UReplayObject *> &, Replays);
UDELEGATE()
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnDeleteComplete, bool, bWasSuccessful);
UDELEGATE()
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnGotoTimeComplete, const bool ,bWasSuccessful);
UDELEGATE()
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnReplayComplete);

View File

@@ -0,0 +1,51 @@
// Copyright 2020-Present Oyintare Ebelo. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "NetworkReplayStreaming.h"
#include "ReplayDelegates.h"
#include "ReplayStructs.h"
#include "ReplayObject.generated.h"
/**
*
*/
class UReplayDataObject;
UCLASS(BlueprintType, Blueprintable)
class REPLAYSYSTEM_API UReplayObject : public UObject
{
GENERATED_BODY()
public:
UPROPERTY()
UWorld* World = nullptr;
UPROPERTY(BlueprintReadOnly, EditAnywhere, Category = Replay)
FReplayInfo ReplayInfo;
// called once the events have been gotten
UPROPERTY(BlueprintAssignable, Category = "Replay|Events")
FOnRequestEventsComplete OnRequestEventsComplete;
/**
* Gets the events of a loaded replay
* @param Group
* @param UserIndex
*/
UFUNCTION(BlueprintCallable, Category = "Replay")
void RequestEvents(FString Group,int UserIndex);
FEnumerateEventsCallback EnumerateEventsDel;
void OnEnumerateEventsComplete(const FEnumerateEventsResult& Results);
};

View File

@@ -0,0 +1,39 @@
// Copyright 2020-Present Oyintare Ebelo. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/PlayerController.h"
#include "ReplayPlayerController.generated.h"
/**
*
*/
UCLASS()
class REPLAYSYSTEM_API AReplayPlayerController : public APlayerController
{
GENERATED_BODY()
AReplayPlayerController();
public:
UFUNCTION(BlueprintImplementableEvent,Category="Replay")
void OnGoToTime(float currentTime);
/**
* true means paused
* @param PauseState
*/
UFUNCTION(BlueprintImplementableEvent,Category="Replay")
void OnTogglePause(bool PauseState);
UFUNCTION(BlueprintImplementableEvent,Category="Replay")
void OnSpectateActor(AActor* Actor);
UFUNCTION(BlueprintImplementableEvent,Category="Replay")
void OnStopSpectateActor();
bool bIsSpectating = false;
};

View File

@@ -0,0 +1,371 @@
// Copyright 2020-Present Oyintare Ebelo. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Camera/PlayerCameraManager.h"
#include "Curves/CurveVector.h"
#include "ReplayStructs.generated.h"
class UReplayDataObject;
class UCurveVector;
class FNetworkGUID;
USTRUCT(BlueprintType)
struct FReplayInfo
{
GENERATED_USTRUCT_BODY()
public:
//The UI name of the replay
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = Replay)
FString FriendlyName;
//The actual name of the replay on disk
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = Replay)
FString ActualName;
//The date the replay was recorded
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = Replay)
FDateTime RecordDate;
//The length of the replay in milliseconds
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = Replay)
int32 LengthInMS = 0;
//The size of the replay in Mb
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = Replay)
float SizeInMb = 0.0f;
};
USTRUCT(BlueprintType)
struct FBlendSettings
{
GENERATED_USTRUCT_BODY()
public:
//The time taken to blend
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = Replay)
float BlendTime = 0.2f;
//Cubic, Linear etc functions for blending
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = Replay)
TEnumAsByte<EViewTargetBlendFunction> BlendFunction = EViewTargetBlendFunction::VTBlend_Linear;
///Exponent used by certain blend functions to control the blend
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = Replay)
float BlendExponent = 0;
//If true, lock outgoing view target to last frame's camera position for the remainder of the blend
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = Replay)
bool bLockOutgoing = false;
};
USTRUCT(BlueprintType)
struct FReplayEvent
{
GENERATED_USTRUCT_BODY()
public:
//The Event ID
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = Replay)
FString EventID;
//The group this event belongs to
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = Replay)
FString Group;
// The time the event was added at in milliseconds
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = Replay)
int32 TimeInMs = 0;
//The event data
UPROPERTY(BlueprintReadOnly, EditAnywhere, Category = Replay)
UReplayDataObject* Data=nullptr;
};
USTRUCT(BlueprintType)
struct FReplayBoolData
{
GENERATED_USTRUCT_BODY()
public:
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = Replay)
FString Name;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = Replay)
bool Value = true;
FORCEINLINE bool operator<(const FReplayBoolData& Other) const
{
return Name < Other.Name;
}
FORCEINLINE bool operator==(const FReplayBoolData& Other) const
{
return Name == Other.Name;
}
};
USTRUCT(BlueprintType)
struct FReplayByteData
{
GENERATED_USTRUCT_BODY()
public:
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = Replay)
FString Name;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = Replay)
TArray<uint8> Value;
FORCEINLINE bool operator<(const FReplayByteData& Other) const
{
return Name < Other.Name;
}
FORCEINLINE bool operator==(const FReplayByteData& Other) const
{
return Name == Other.Name;
}
};
USTRUCT(BlueprintType)
struct FReplayIntData
{
GENERATED_USTRUCT_BODY()
public:
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = Replay)
FString Name;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = Replay)
int Value=0;
FORCEINLINE bool operator<(const FReplayIntData& Other) const
{
return Name < Other.Name;
}
FORCEINLINE bool operator==(const FReplayIntData& Other) const
{
return Name == Other.Name;
}
};
USTRUCT(BlueprintType)
struct FReplayInt64Data
{
GENERATED_USTRUCT_BODY()
public:
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = Replay)
FString Name;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = Replay)
int64 Value = 0;
FORCEINLINE bool operator<(const FReplayInt64Data& Other) const
{
return Name < Other.Name;
}
FORCEINLINE bool operator==(const FReplayInt64Data& Other) const
{
return Name == Other.Name;
}
};
USTRUCT(BlueprintType)
struct FReplayFloatData
{
GENERATED_USTRUCT_BODY()
public:
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = Replay)
FString Name;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = Replay)
float Value = 0.0f;
FORCEINLINE bool operator<(const FReplayFloatData& Other) const
{
return Name < Other.Name;
}
FORCEINLINE bool operator==(const FReplayFloatData& Other) const
{
return Name == Other.Name;
}
};
USTRUCT(BlueprintType)
struct FReplayNameData
{
GENERATED_USTRUCT_BODY()
public:
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = Replay)
FString Name;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = Replay)
FName Value;
FORCEINLINE bool operator<(const FReplayNameData& Other) const
{
return Name < Other.Name;
}
FORCEINLINE bool operator==(const FReplayNameData& Other) const
{
return Name == Other.Name;
}
};
USTRUCT(BlueprintType)
struct FReplayStringData
{
GENERATED_USTRUCT_BODY()
public:
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = Replay)
FString Name;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = Replay)
FString Value;
FORCEINLINE bool operator<(const FReplayStringData& Other) const
{
return Name < Other.Name;
}
FORCEINLINE bool operator==(const FReplayStringData& Other) const
{
return Name == Other.Name;
}
};
USTRUCT(BlueprintType)
struct FReplayTextData
{
GENERATED_USTRUCT_BODY()
public:
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = Replay)
FString Name;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = Replay)
FText Value;
FORCEINLINE bool operator<(const FReplayTextData& Other) const
{
return Name < Other.Name;
}
FORCEINLINE bool operator==(const FReplayTextData& Other) const
{
return Name == Other.Name;
}
};
USTRUCT(BlueprintType)
struct FReplayVectorData
{
GENERATED_USTRUCT_BODY()
public:
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = Replay)
FString Name;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = Replay)
FVector Value = FVector(0.0f, 0.0f, 0.0f);
FORCEINLINE bool operator<(const FReplayVectorData& Other) const
{
return Name < Other.Name;
}
FORCEINLINE bool operator==(const FReplayVectorData& Other) const
{
return Name == Other.Name;
}
};
USTRUCT(BlueprintType)
struct FReplayRotatorData
{
GENERATED_USTRUCT_BODY()
public:
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = Replay)
FString Name;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = Replay)
FRotator Value = FRotator(0.0f, 0.0f, 0.0f);
FORCEINLINE bool operator<(const FReplayRotatorData& Other) const
{
return Name < Other.Name;
}
FORCEINLINE bool operator==(const FReplayRotatorData& Other) const
{
return Name == Other.Name;
}
};
USTRUCT(BlueprintType)
struct FReplayTransformData
{
GENERATED_USTRUCT_BODY()
public:
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = Replay)
FString Name;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = Replay)
FTransform Value;
FORCEINLINE bool operator<(const FReplayTransformData& Other) const
{
return Name < Other.Name;
}
FORCEINLINE bool operator==(const FReplayTransformData& Other) const
{
return Name == Other.Name;
}
};
USTRUCT(BlueprintType)
struct FReplayObjectData
{
GENERATED_USTRUCT_BODY()
public:
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = Replay)
FString Name;
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = Replay)
UObject* Value=nullptr;
FORCEINLINE bool operator<(const FReplayObjectData& Other) const
{
return Name < Other.Name;
}
FORCEINLINE bool operator==(const FReplayObjectData& Other) const
{
return Name == Other.Name;
}
};

View File

@@ -0,0 +1,15 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
DECLARE_LOG_CATEGORY_EXTERN(LogReplaySystem, Log, All);
class FReplaySystemModule : public IModuleInterface
{
public:
/** IModuleInterface implementation */
virtual void StartupModule() override;
virtual void ShutdownModule() override;
};

View File

@@ -0,0 +1,308 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "Kismet/BlueprintFunctionLibrary.h"
#include "CoreMinimal.h"
#include "NetworkReplayStreaming.h"
#include "ReplayStructs.h"
#include "ReplaySystemBPLibrary.generated.h"
/*
* Function library class.
* Each function in it is expected to be static and represents blueprint node that can be called in any blueprint.
*
* When declaring function you can define metadata for the node. Key function specifiers will be BlueprintPure and BlueprintCallable.
* BlueprintPure - means the function does not affect the owning object in any way and thus creates a node without Exec pins.
* BlueprintCallable - makes a function which can be executed in Blueprints - Thus it has Exec pins.
* DisplayName - full name of the node, shown when you mouse over the node and in the blueprint drop down menu.
* Its lets you name the node using characters not allowed in C++ function names.
* CompactNodeTitle - the word(s) that appear on the node.
* Keywords - the list of keywords that helps you to find node when you search for it using Blueprint drop-down menu.
* Good example is "Print String" node which you can find also by using keyword "log".
* Category - the category your node will be under in the Blueprint drop-down menu.
*
* For more info on custom blueprint nodes visit documentation:
* https://wiki.unrealengine.com/Custom_Blueprint_Node_Creation
*/
class UGoToTimeObject;
class UDeleteReplayObject;
class UGetSavedReplaysObject;
class URenameReplayObject;
class UReplayDataObject;
class UReplayObject;
class URequestEventsObject;
class UInstantReplayObject;
UCLASS()
class UReplaySystemBPLibrary : public UBlueprintFunctionLibrary
{
GENERATED_UCLASS_BODY()
public:
/**
* Sets the path for all replays (Does not copy existing replays over)
* @param WorldContextObject
* @param Path New path
*/
UFUNCTION(BlueprintCallable, Category = "ReplaySystem", meta = (HidePin = "WorldContextObject", DefaultToSelf = "WorldContextObject"))
static void SetReplaySavePath(UObject* WorldContextObject, const FString& Path);
/**
* Gets the current path where replays are stored
* @param WorldContextObject
* @return Path
*/
UFUNCTION(BlueprintCallable,BlueprintPure,Category = "ReplaySystem", meta = (HidePin = "WorldContextObject", DefaultToSelf = "WorldContextObject"))
static FString GetReplaySavePath(UObject* WorldContextObject);
/**
* Starts recording a replay
* @param WorldContextObject
* @param ReplayName The name to save the replay as on disk (Used internally)
* @param ReplayFriendlyName The Ui friendly name of the replay (Used by you or users)
*/
UFUNCTION(BlueprintCallable, Category = "ReplaySystem|Recording", meta = (HidePin = "WorldContextObject", DefaultToSelf = "WorldContextObject"))
static void RecordReplay(UObject* WorldContextObject, const FString& ReplayName,
const FString& ReplayFriendlyName);
/**
* Stop recording a replay
* @param WorldContextObject
* @return
*/
UFUNCTION(BlueprintCallable, Category = "ReplaySystem|Recording", meta = (HidePin = "WorldContextObject", DefaultToSelf = "WorldContextObject"))
static void StopRecordingReplay(UObject* WorldContextObject);
/**
* Finds out if a replay is being recorded
* @param WorldContextObject
* @return
*/
UFUNCTION(BlueprintCallable,BlueprintPure, Category = "ReplaySystem", meta = (HidePin = "WorldContextObject", DefaultToSelf = "WorldContextObject"))
static bool IsRecordingReplay(UObject* WorldContextObject);
/**
* Delete a replay
* @param WorldContextObject
* @param ReplayName The name the replay is saved as on disk
* @return
*/
UFUNCTION(BlueprintCallable, Category = "ReplaySystem", meta = (HidePin = "WorldContextObject", DefaultToSelf = "WorldContextObject"))
static UDeleteReplayObject* DeleteReplay(UObject* WorldContextObject, const FString& ReplayName);
/**
* Changes the name a replay is saved as on the disk
* @param WorldContextObject
* @param ReplayName The current name on disk of this replay
* @param NewReplayName New name to save as on disk
* @param UserIndex
* @return
*/
UFUNCTION(BlueprintCallable, Category = "ReplaySystem", meta = (HidePin = "WorldContextObject", DefaultToSelf = "WorldContextObject"))
static URenameReplayObject* RenameReplay(UObject* WorldContextObject, const FString& ReplayName, const FString& NewReplayName, const int32 UserIndex);
/**
* Changes the friendly name of a replay
* @param WorldContextObject
* @param ReplayName The current name on disk of this replay
* @param NewFriendlyReplayName New friendly to give it
* @param UserIndex
* @return
*/
UFUNCTION(BlueprintCallable, Category = "ReplaySystem", meta = (HidePin = "WorldContextObject", DefaultToSelf = "WorldContextObject"))
static URenameReplayObject* RenameReplayFriendly(UObject* WorldContextObject, const FString& ReplayName, const FString& NewFriendlyReplayName, const int32 UserIndex);
/**
* Get all the saved replays
* @param WorldContextObject
* @return
*/
UFUNCTION(BlueprintCallable, Category = "ReplaySystem", meta = (HidePin = "WorldContextObject", DefaultToSelf = "WorldContextObject"))
static UGetSavedReplaysObject* GetSavedReplays(UObject* WorldContextObject);
/**
* Play a recorded replay
* @param WorldContextObject
* @param ReplayName
* @return
*/
UFUNCTION(BlueprintCallable, Category = "ReplaySystem|Playback", meta = (HidePin = "WorldContextObject", DefaultToSelf = "WorldContextObject"))
static bool PlayRecordedReplay(UObject* WorldContextObject, const FString& ReplayName);
/**
* Restart the currently playing replay
* @param WorldContextObject
*/
UFUNCTION(BlueprintCallable, Category = "ReplaySystem|Playback", meta = (HidePin = "WorldContextObject", DefaultToSelf = "WorldContextObject"))
static UGoToTimeObject* RestartReplayPlayback(UObject* WorldContextObject);
/**
* Goto a specific time in the replay
* @param WorldContextObject
* @param TimeToGoTo Time in seconds to goto
* @param bRetainCurrentPauseState Use this on a need basis as it can cause some physics issues
*/
UFUNCTION(BlueprintCallable, Category = "ReplaySystem|Playback", meta = (HidePin = "WorldContextObject", DefaultToSelf = "WorldContextObject"))
static UGoToTimeObject* GoToSpecificTime(UObject* WorldContextObject, float TimeToGoTo,
bool bRetainCurrentPauseState);
/**
* Pause the replay playback
* @param WorldContextObject
*/
UFUNCTION(BlueprintCallable, Category = "ReplaySystem|Playback", meta = (HidePin = "WorldContextObject", DefaultToSelf = "WorldContextObject"))
static void PausePlayback(UObject* WorldContextObject);
/**
* Resume the replay playback
* @param WorldContextObject
*/
UFUNCTION(BlueprintCallable, Category = "ReplaySystem|Playback", meta = (HidePin = "WorldContextObject", DefaultToSelf = "WorldContextObject"))
static void ResumePlayback(UObject* WorldContextObject);
/**
* Sets the Playback speed of the replay
* @param WorldContextObject Set the playback speed of the replay
* @param Speed Like time dilation 0.1,1,10
*/
UFUNCTION(BlueprintCallable, Category = "ReplaySystem|Playback", meta = (HidePin = "WorldContextObject", DefaultToSelf = "WorldContextObject"))
static void SetPlaybackSpeed(UObject* WorldContextObject, float Speed);
/**
* Get the playback speed of the replay
* @param WorldContextObject
* @return
*/
UFUNCTION(BlueprintCallable,BlueprintPure, Category = "ReplaySystem|Playback", meta = (HidePin = "WorldContextObject", DefaultToSelf = "WorldContextObject"))
static float GetPlaybackSpeed(UObject* WorldContextObject);
/**
* Get the current time in seconds of the replay
* @param WorldContextObject
* @return
*/
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "ReplaySystem|Playback", meta = (HidePin = "WorldContextObject", DefaultToSelf = "WorldContextObject"))
static float GetCurrentReplayTime(UObject* WorldContextObject);
/**
* Get the total length in seconds of the replay currently playing or being recorded
* @param WorldContextObject
* @return
*/
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "ReplaySystem|Playback", meta = (HidePin = "WorldContextObject", DefaultToSelf = "WorldContextObject"))
static float GetReplayLength(UObject* WorldContextObject);
/**
* returns true if a replay is being played
* @param WorldContextObject
* @return
*/
UFUNCTION(BlueprintCallable,BlueprintPure, Category = "ReplaySystem", meta = (HidePin = "WorldContextObject", DefaultToSelf = "WorldContextObject"))
static bool IsPlayingReplay(UObject* WorldContextObject);
/**
* Returns true of the replay has been paused
* @param WorldContextObject
* @return
*/
UFUNCTION(BlueprintCallable,BlueprintPure, Category = "ReplaySystem", meta = (HidePin = "WorldContextObject", DefaultToSelf = "WorldContextObject"))
static bool IsReplayPlaybackPaused(UObject* WorldContextObject);
/**
* Gets the name on disk/memory of the replay currently playing or being recorded
* @param WorldContextObject
* @return
*/
UFUNCTION(BlueprintCallable,BlueprintPure, Category = "ReplaySystem", meta = (HidePin = "WorldContextObject", DefaultToSelf = "WorldContextObject"))
static FString GetActiveReplayName(UObject* WorldContextObject);
/**
* Adds or Updates said event in the replay currently being recorded
* @param WorldContextObject
* @param EventName The name of the event
* @param Group The group this event belongs to
* @param DataObject optional object containing extra data
* @return
*/
UFUNCTION(BlueprintCallable, Category = "ReplaySystem", meta = (HidePin = "WorldContextObject", DefaultToSelf = "WorldContextObject"))
static bool AddEventToActiveReplay(UObject* WorldContextObject,const FString& EventName, const FString& Group, UReplayDataObject* DataObject);
/**
* Gets the Events of the replay currently playing
* @param WorldContextObject
* @param Group The group name
* @param UserIndex
* @return
*/
UFUNCTION(BlueprintCallable, Category = "ReplaySystem", meta = (HidePin = "WorldContextObject", DefaultToSelf = "WorldContextObject"))
static URequestEventsObject* RequestActiveReplayEvents(UObject* WorldContextObject,FString Group,int UserIndex);
/**
* Creates a data object for storing event meta data for a replay
* @return
*/
UFUNCTION(BlueprintCallable, Category = "ReplaySystem")
static UReplayDataObject* CreateReplayDataObject();
static UReplayDataObject* LoadEventFromByteData(TArray<uint8> Bytes);
/**
* Helper function to convert milliseconds to seconds
* @param MS
* @return
*/
UFUNCTION(BlueprintCallable,BlueprintPure, Category = "ReplaySystem|Utilities")
static float MsToSeconds(const int32 MS);
/**
* Just sets the view target of the controller to the specified actor
* @param WorldContextObject
* @param Actor The pawn to spectate
*/
UFUNCTION(BlueprintCallable,Category= "ReplaySystem",meta = (HidePin = "WorldContextObject", DefaultToSelf = "WorldContextObject"))
static void SpectateActor(UObject* WorldContextObject, AActor* Actor, FBlendSettings BlendSettings);
/**
* Just sets the view target of the controller back to the possessed pawn
* @param WorldContextObject
* @param BlendSettings
*/
UFUNCTION(BlueprintCallable,Category= "ReplaySystem",meta = (HidePin = "WorldContextObject", DefaultToSelf = "WorldContextObject"))
static void StopSpectating(UObject* WorldContextObject,FBlendSettings BlendSettings);
UFUNCTION(Category= "ReplaySystem",meta = (HidePin = "WorldContextObject", DefaultToSelf = "WorldContextObject"))
static UDemoNetDriver* GetDemoDriver(const UObject* WorldContextObject);
/**
*Set the maximum number of frames recorded per second by the replay
*/
UFUNCTION(BlueprintCallable,Category= "ReplaySystem",meta = (HidePin = "WorldContextObject", DefaultToSelf = "WorldContextObject"))
static void SetMaxRecordHz(UObject* WorldContextObject,float Hz = 8.0f);
/**
* Get the maximum number of frames recorded per second by the replay
*/
UFUNCTION(BlueprintCallable,Category= "ReplaySystem")
static float GetMaxRecordHz();
};

View File

@@ -0,0 +1,33 @@
// Copyright 2020-Present Oyintare Ebelo. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "NetworkReplayStreaming.h"
#include "ReplayDelegates.h"
#include "RequestEventsObject.generated.h"
/**
*
*/
UCLASS(BlueprintType, Blueprintable)
class REPLAYSYSTEM_API URequestEventsObject : public UObject
{
GENERATED_BODY()
public:
UPROPERTY(BlueprintAssignable, Category = "Replay|Events")
FOnRequestEventsComplete OnRequestEventsComplete;
void RequestEvents(FString ReplayName,FString Group,int UserIndex);
FEnumerateEventsCallback EnumerateEventsDel;
void OnEnumerateEventsComplete(const FEnumerateEventsResult& Results);
};

View File

@@ -0,0 +1,54 @@
// Copyright 2020-Present Oyintare Ebelo. All Rights Reserved.
using UnrealBuildTool;
public class ReplaySystem : ModuleRules
{
public ReplaySystem(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
PublicIncludePaths.AddRange(
new string[] {
// ... add public include paths required here ...
}
);
PrivateIncludePaths.AddRange(
new string[] {
// ... add other private include paths required here ...
}
);
PublicDependencyModuleNames.AddRange(
new string[]
{
"Core",
"NetworkReplayStreaming",
"Json",
"JsonUtilities",
// ... add other public dependencies that you statically link with here ...
}
);
PrivateDependencyModuleNames.AddRange(
new string[]
{
"CoreUObject",
"Engine",
"Slate",
"SlateCore",
// ... add private dependencies that you statically link with here ...
}
);
DynamicallyLoadedModuleNames.AddRange(
new string[] {
}
);
}
}