From 5e7bff96d06774e63f8a15b58f670bbc17f5158e Mon Sep 17 00:00:00 2001 From: Barry Kane Date: Thu, 3 Aug 2023 00:15:44 +0100 Subject: [PATCH] Added basic server reconcilliation --- src/client/cspt-client.c | 80 ++++++++++++++++++++++++++++++++++++++-- src/cspt-state.c | 3 ++ src/cspt-state.h | 7 ++++ 3 files changed, 86 insertions(+), 4 deletions(-) diff --git a/src/client/cspt-client.c b/src/client/cspt-client.c index d02ffc4..a0bf3fb 100644 --- a/src/client/cspt-client.c +++ b/src/client/cspt-client.c @@ -18,6 +18,7 @@ #include "../cspt-state.h" #include "../cspt-message.h" #define ENABLE_CLIENT_SIDE_PREDICTION +#define ENABLE_SERVER_RECONCILLIATION uint8_t colours[16][3] = { @@ -43,6 +44,7 @@ struct threadParameters bool * keepRunning; struct gameState * state; struct clientInput * message; + struct inputHistory * inputBuffer; }; void DrawCircle(SDL_Renderer * renderer, int32_t centreX, int32_t centreY, int32_t radius) @@ -110,18 +112,73 @@ void * networkHandler(void * parameters) while (true) { // Send our input, recieve the state: - sendto(udpSocket, arguments->message, sizeof(struct clientInput), 0, (struct sockaddr *)&serverAddress, sizeof(struct sockaddr_in)); + sendto(udpSocket, arguments->message, sizeof(struct clientInput), 0, + (struct sockaddr *)&serverAddress, sizeof(struct sockaddr_in)); recvfrom(udpSocket, updatedState, sizeof(struct gameState), 0, NULL, NULL); // Only update the state if the given state is more recent than the current state: if(updatedState->timestamp.tv_sec > arguments->state->timestamp.tv_sec) { memcpy(arguments->state, updatedState, sizeof(struct gameState)); + #ifdef ENABLE_SERVER_RECONCILLIATION + // Throw away any already acknowledged inputs: + while (arguments->inputBuffer->start != -1 && + arguments->inputBuffer->inputs[arguments->inputBuffer->start].tickNumber < arguments->state->tickNumber) + { + arguments->inputBuffer->start = (arguments->inputBuffer->start + 1) % 256; + if(arguments->inputBuffer->start == arguments->inputBuffer->end) + { + arguments->inputBuffer->start = -1; + } + } + + uint8_t currentMessage = arguments->inputBuffer->start; + uint64_t lastTickNumber = arguments->inputBuffer->inputs[arguments->inputBuffer->start].tickNumber; + + // Re-apply the currently unused messages: + while (currentMessage != 1 && + currentMessage != arguments->inputBuffer->end) + { + updateInput(arguments->state, &arguments->inputBuffer->inputs[currentMessage]); + currentMessage = (currentMessage + 1) % 256; + if (arguments->inputBuffer->inputs[currentMessage].tickNumber != lastTickNumber) + { + doGameTick(arguments->state); + } + } + #endif } else if(updatedState->timestamp.tv_sec == arguments->state->timestamp.tv_sec && updatedState->timestamp.tv_usec > arguments->state->timestamp.tv_usec) { memcpy(arguments->state, updatedState, sizeof(struct gameState)); + #ifdef ENABLE_SERVER_RECONCILLIATION + // Throw away any already acknowledged inputs: + while (arguments->inputBuffer->start != -1 && + arguments->inputBuffer->inputs[arguments->inputBuffer->start].tickNumber < arguments->state->tickNumber) + { + arguments->inputBuffer->start = (arguments->inputBuffer->start + 1) % 256; + if(arguments->inputBuffer->start == arguments->inputBuffer->end) + { + arguments->inputBuffer->start = -1; + } + } + + uint8_t currentMessage = arguments->inputBuffer->start; + uint64_t lastTickNumber = arguments->inputBuffer->inputs[arguments->inputBuffer->start].tickNumber; + + // Re-apply the currently unused messages: + while (currentMessage != 1 && + currentMessage != arguments->inputBuffer->end) + { + updateInput(arguments->state, &arguments->inputBuffer->inputs[currentMessage]); + currentMessage = (currentMessage + 1) % 256; + if (arguments->inputBuffer->inputs[currentMessage].tickNumber != lastTickNumber) + { + doGameTick(arguments->state); + } + } + #endif } } } @@ -134,11 +191,23 @@ void * gameThreadHandler(void * parameters) while (true) { updateInput(arguments->state, arguments->message); + #ifdef ENABLE_SERVER_RECONCILLIATION + if(arguments->inputBuffer->start = -1) + { + memcpy(&arguments->inputBuffer->inputs[0], arguments->message, sizeof(struct clientInput)); + arguments->inputBuffer->start = 0; + arguments->inputBuffer->end = 1; + } + else + { + memcpy(&arguments->inputBuffer->inputs[arguments->inputBuffer->end], arguments->message, sizeof(struct clientInput)); + arguments->inputBuffer->end = (arguments->inputBuffer->end + 1) % 256; + } + #endif doGameTick(arguments->state); usleep(15625); } - #endif - + #endif } void * graphicsThreadHandler(void * parameters) @@ -329,7 +398,10 @@ int main(int argc, char ** argv) parameters.message = clientInput; parameters.ipAddress = ipAddress; parameters.keepRunning = &keepRunning; - + parameters.inputBuffer = calloc(1, sizeof(struct inputHistory)); + parameters.inputBuffer->start = -1; + parameters.inputBuffer->end = -1; + // Create all of our threads: pthread_create(&gameThread, NULL, gameThreadHandler, ¶meters); pthread_create(&networkThread, NULL, networkHandler, ¶meters); diff --git a/src/cspt-state.c b/src/cspt-state.c index 14ad76a..6fd282f 100644 --- a/src/cspt-state.c +++ b/src/cspt-state.c @@ -1,5 +1,7 @@ #include +#include #include "cspt-state.h" + void updateInput(struct gameState * state, struct clientInput * message) { if(message->clientNumber < 16 && message->clientNumber >= 0) @@ -49,6 +51,7 @@ void doGameTick(struct gameState * state) { state->clients[index].yVelocity *= 0.9; } + // Do movement: state->clients[index].xPosition += state->clients[index].xVelocity; state->clients[index].yPosition += state->clients[index].yVelocity; diff --git a/src/cspt-state.h b/src/cspt-state.h index a6e63b7..f897892 100644 --- a/src/cspt-state.h +++ b/src/cspt-state.h @@ -4,6 +4,7 @@ #include #include #include + struct clientMovement { double xPosition, yPosition, xVelocity, yVelocity; @@ -30,6 +31,12 @@ struct networkThreadArguments struct gameState * state; }; +struct inputHistory +{ + uint8_t start, end; + struct clientInput inputs[256]; +}; + void updateInput(struct gameState * state, struct clientInput * message); void doGameTick(struct gameState * state);