Untested circle buffer implementation
This commit is contained in:
		
							parent
							
								
									ec6daa06fb
								
							
						
					
					
						commit
						2c5569b6d5
					
				@ -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;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -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;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
@ -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();
 | 
				
			||||||
 | 
				
			|||||||
@ -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;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
@ -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"
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			);
 | 
								);
 | 
				
			||||||
			
 | 
								
 | 
				
			||||||
 | 
				
			|||||||
@ -1,11 +0,0 @@
 | 
				
			|||||||
// Unreal Fighting Framework by Kevin Poretti
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "Utils/TCircleBuffer.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
TCircleBuffer::TCircleBuffer()
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
TCircleBuffer::~TCircleBuffer()
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -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;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user