Untested circle buffer implementation

This commit is contained in:
Kevin Poretti 2023-06-26 21:34:58 -04:00
parent ec6daa06fb
commit 2c5569b6d5
7 changed files with 168 additions and 45 deletions

View File

@ -7,16 +7,18 @@ UFFInputBufferComponent::UFFInputBufferComponent()
PrimaryComponentTick.bCanEverTick = false; PrimaryComponentTick.bCanEverTick = false;
} }
void UFFInputBufferComponent::Initialize(int32 BufferSize)
{
Buffer.Reserve(BufferSize);
}
void UFFInputBufferComponent::AddInput(const FFFInputState& InputState) void UFFInputBufferComponent::AddInput(const FFFInputState& InputState)
{ {
InputBuffer.ForcePush(InputState);
} }
bool UFFInputBufferComponent::CheckInputSequence(const FFFInputSequence& InputSequence) bool UFFInputBufferComponent::CheckInputSequence(const FFFInputSequence& InputSequence)
{ {
for(int InpIdx = 0; InpIdx < InputBuffer.Num(); InpIdx++)
{
// read input sequence
FFFInputState CurrInput = InputBuffer[InpIdx];
}
return true; return true;
} }

View File

@ -2,14 +2,27 @@
#pragma once #pragma once
// FF includes
#include "Utils/TCircleBuffer.h"
// UE includes // UE includes
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "Components/ActorComponent.h" #include "Components/ActorComponent.h"
#include "Containers/RingBuffer.h"
#include "FFInputBufferComponent.generated.h" #include "FFInputBufferComponent.generated.h"
struct FFFInputState; /**
* Struct representing the state of a player's inputs for one frame
*/
USTRUCT()
struct FFFInputState
{
GENERATED_BODY()
FVector2D MoveAxes;
FVector2D LookAxes;
int32 Buttons;
};
USTRUCT() USTRUCT()
struct FFFInputSequence struct FFFInputSequence
@ -26,13 +39,11 @@ class UNREALFIGHTINGFRAMEWORK_API UFFInputBufferComponent : public UActorCompone
public: public:
UFFInputBufferComponent(); UFFInputBufferComponent();
void Initialize(int32 BufferSize = 120);
void AddInput(const FFFInputState& InputState); void AddInput(const FFFInputState& InputState);
bool CheckInputSequence(const FFFInputSequence& InputSequence); bool CheckInputSequence(const FFFInputSequence& InputSequence);
protected: protected:
/** The underlying buffer data structure for holding past input states */ /** The underlying buffer data structure for holding past input states */
TRingBuffer<FFFInputState> Buffer; TCircleBuffer<FFFInputState, 120> InputBuffer;
}; };

View File

@ -21,7 +21,7 @@ void AFFPlayerController::SendInputsToRemote() const
void AFFPlayerController::FixedTick(float OneFrame) void AFFPlayerController::FixedTick(float OneFrame)
{ {
UnacknowledgedInputs.Add(CurrInput); //UnacknowledgedInputs.Push(CurrInput);
InputBuffer->AddInput(CurrInput); InputBuffer->AddInput(CurrInput);
SendInputsToRemote(); SendInputsToRemote();

View File

@ -4,29 +4,16 @@
// FF includes // FF includes
#include "FFInputBufferComponent.h" #include "FFInputBufferComponent.h"
#include "UnrealFightingFramework/IFFSystemInterface.h" #include "IFFSystemInterface.h"
#include "Utils/TCircleBuffer.h"
// UE includes // UE includes
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "GameFramework/PlayerController.h" #include "GameFramework/PlayerController.h"
#include "InputMappingContext.h" #include "InputMappingContext.h"
#include "Containers/RingBuffer.h"
#include "FFPlayerController.generated.h" #include "FFPlayerController.generated.h"
/**
* Struct representing the state of a player's inputs for one frame
*/
USTRUCT()
struct FFFInputState
{
GENERATED_BODY()
FVector2D MoveAxes;
FVector2D LookAxes;
int32 Buttons;
};
/** /**
* A class that collects player inputs, stores them in an input buffer, and sends a rolling window of * A class that collects player inputs, stores them in an input buffer, and sends a rolling window of
* unacknowledged inputs to a remote client or server for processing. * unacknowledged inputs to a remote client or server for processing.
@ -72,5 +59,5 @@ protected:
* you want the remote machine to re-simulate, where X is the oldest input you want to * you want the remote machine to re-simulate, where X is the oldest input you want to
* allow to be re-simulated. * allow to be re-simulated.
*/ */
TRingBuffer<FFFInputState> UnacknowledgedInputs; TCircleBuffer<FFFInputState, 20> UnacknowledgedInputs;
}; };

View File

@ -1,5 +1,5 @@
// Unreal Fighting Framework by Kevin Poretti // Unreal Fighting Framework by Kevin Poretti
using System.IO;
using UnrealBuildTool; using UnrealBuildTool;
public class UnrealFightingFramework : ModuleRules public class UnrealFightingFramework : ModuleRules
@ -10,14 +10,13 @@ public class UnrealFightingFramework : ModuleRules
PublicIncludePaths.AddRange( PublicIncludePaths.AddRange(
new string[] { new string[] {
"UnrealFightingFramework" Path.Combine(PluginDirectory,"Source/UnrealFightingFramework"),
} }
); );
PrivateIncludePaths.AddRange( PrivateIncludePaths.AddRange(
new string[] { new string[] {
"UnrealFightingFramework"
} }
); );

View File

@ -1,11 +0,0 @@
// Unreal Fighting Framework by Kevin Poretti
#include "Utils/TCircleBuffer.h"
TCircleBuffer::TCircleBuffer()
{
}
TCircleBuffer::~TCircleBuffer()
{
}

View File

@ -5,11 +5,146 @@
#include "CoreMinimal.h" #include "CoreMinimal.h"
/** /**
* CircleBuffer is a templated array data structure allowing for FIFO, O(1) insertion and removal,
* and wraparound when iterating/accessing elements of the buffer.
* *
* The underlying container type is a statically allocated array. You can use the second
* template parameter to specify the static size of the buffer. Defaults to 64.
*/ */
template<typename T, SIZE_T L = 64>
class UNREALFIGHTINGFRAMEWORK_API TCircleBuffer class UNREALFIGHTINGFRAMEWORK_API TCircleBuffer
{ {
public: public:
TCircleBuffer(); TCircleBuffer()
~TCircleBuffer(); : WriteIdx(0)
, ReadIdx(0)
{
}
/**
* @brief Adds a new element to the buffer only if there is room.
* @param Element to add to the buffer
* @return true if there was room in the buffer and the element was added, false otherwise
*/
bool Push(const T& Element)
{
if(_Num == L)
{
// buffer full so can't add element
return false;
}
Buffer[WriteIdx] = Element;
WriteIdx = (WriteIdx + 1) % L;
_Num++;
return true;
}
/**
* @brief Adds a new element to the buffer.
* This function always adds an element. If the buffer is full the oldest element will be overwritten.
* @param Element to add to the buffer
*/
void ForcePush(const T& Element)
{
if(_Num == L)
{
// buffer is full so we need to overwrite the oldest element
// and make the read index point to the next oldest element
ReadIdx = (ReadIdx + 1) % L;
}
Buffer[WriteIdx] = Element;
WriteIdx = (WriteIdx + 1) % L;
_Num = FMath::Min(_Num + 1, L);
}
/**
* @brief Returns the oldest element in the buffer and removes that element from the buffer.
* @param Result out parameter to be filled in with the popped element
* @return true if there was an element in the buffer to pop, false otherwise
*/
bool Pop(T* Result)
{
if(IsEmpty())
{
return false;
}
*Result = Buffer[ReadIdx];
ReadIdx = (ReadIdx + 1) % L;
_Num--;
return true;
}
/**
* @brief Returns the newest element that was added to the buffer
* @param Result out parameter to be filled in with the most recently added element
* @return true if there was an element in the buffer to peak, false otherwise
*/
bool PeakLast(T* Result)
{
if(IsEmpty())
{
return false;
}
*Result = Buffer[((SSIZE_T)WriteIdx - 1) <= 0 ? L - 1 : (WriteIdx - 1)];
return true;
}
/**
* @brief Returns the oldest element that was added to the buffer
* @param Result out parameter to be filled in with the oldest element
* @return true if there was an element in the buffer to peak, false otherwise
*/
bool PeakFirst(T* Result)
{
if(IsEmpty())
{
return false;
}
*Result = Buffer[ReadIdx];
return true;
}
/**
* @brief Returns the element at an index supplied to the function.
* This function will account for write index offset and wraparound of the supplied index.
* i.e. Index 0 will be the oldest added element while index Num - 1 will be the newest added element
* @param Index of the element to return
* @return the element at the index supplied
*/
T& operator[](SIZE_T Index)
{
return Buffer[((SSIZE_T)WriteIdx - (SSIZE_T)Index) < 0 ? L - (Index - WriteIdx) : WriteIdx - Index];
}
FORCEINLINE bool IsEmpty() const
{
return _Num == 0;
}
/**
* @brief Returns the max size of the underlying buffer
* @return Max size of the underlying buffer
*/
FORCEINLINE SIZE_T Max() const
{
return L;
}
/**
* @brief Returns the current number of elements of the underlying buffer
* @return Max size of the underlying buffer
*/
FORCEINLINE SIZE_T Num() const
{
return _Num;
}
private:
T Buffer[L];
SIZE_T WriteIdx;
SIZE_T ReadIdx;
SIZE_T _Num;
}; };