feat(network): Server reconciliation and fix UDP messaging.
Initial implementation of server-side reconciliation for player movement. Clients respect the server as the authoritative source for game state. In 'process_game_state', the client compares its locally predicted pos with the state received from the server and snaps to the server's position if they diverge. Had to add a defensive check to the server-side Player constructor to fix a bug where the first connecting client was incorrectly assigned an ID of 0. This prevented it from sending or recieving any game data.
This commit is contained in:
parent
a2a8b052af
commit
9b4aa84a30
@ -10,6 +10,7 @@
|
|||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
#include <future>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "bettola.h"
|
#include "bettola.h"
|
||||||
@ -180,7 +181,7 @@ void Bettola::process_udp_messages(void) {
|
|||||||
if(payload_size >= sizeof(BettolaLib::Network::GameStateMessage)) {
|
if(payload_size >= sizeof(BettolaLib::Network::GameStateMessage)) {
|
||||||
BettolaLib::Network::GameStateMessage msg;
|
BettolaLib::Network::GameStateMessage msg;
|
||||||
memcpy(&msg, payload, sizeof(msg));
|
memcpy(&msg, payload, sizeof(msg));
|
||||||
update_remote_players(msg);
|
process_game_state(msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -228,6 +229,11 @@ void Bettola::update(double dt) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Bettola::render(void) {
|
void Bettola::render(void) {
|
||||||
|
static int frame_count =0 ;
|
||||||
|
if(frame_count++ % 300 == 0) {
|
||||||
|
/* Print every 5 seconds or something... */
|
||||||
|
printf("DEBUG: Rendering %zu remote players.\n", _remote_players.size());
|
||||||
|
}
|
||||||
glClearColor(0.1f, 0.1f, 0.3f, 1.0f);
|
glClearColor(0.1f, 0.1f, 0.3f, 1.0f);
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
|
||||||
@ -271,25 +277,43 @@ void Bettola::render(void) {
|
|||||||
SDL_GL_SwapWindow(_window);
|
SDL_GL_SwapWindow(_window);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Bettola::update_remote_players(const BettolaLib::Network::GameStateMessage& msg) {
|
void Bettola::process_game_state(const BettolaLib::Network::GameStateMessage& msg) {
|
||||||
|
printf("DEBUG: Receiving game state withj %u players.\n", msg.num_players);
|
||||||
|
/* First, let's handle our own player's state for reconciliation. */
|
||||||
|
bool found_our_player = false;
|
||||||
for(unsigned int i = 0; i < msg.num_players; ++i) {
|
for(unsigned int i = 0; i < msg.num_players; ++i) {
|
||||||
const auto& player_state = msg.players[i];
|
const auto& player_state = msg.players[i];
|
||||||
|
|
||||||
auto it = std::find_if(_remote_players.begin(), _remote_players.end(),
|
if(player_state.player_id == _our_player_id) {
|
||||||
[player_state](const RemotePlayer& remote_player) {
|
/*
|
||||||
return remote_player.get_id() == player_state.player_id;
|
* This is our player. Reconcile predicted state with the
|
||||||
});
|
* server's authoritative state.
|
||||||
|
*/
|
||||||
|
printf(" -> Processing remote player %u at (%.2f, %.2f)\n", player_state.player_id,
|
||||||
|
player_state.x, player_state.y);
|
||||||
|
found_our_player = true;
|
||||||
|
float dx = _player.get_x() - player_state.x;
|
||||||
|
float dy = _player.get_y() - player_state.y;
|
||||||
|
if((dx*dx+dy*dy) > 0.0001f) { /* If distance is not negligibale. */
|
||||||
|
/* Simple correction, snap to server position. */
|
||||||
|
_player.set_position(player_state.x, player_state.y);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(it != _remote_players.end()) {
|
/*
|
||||||
it->set_position(player_state.x, player_state.y);
|
* Update all remote players now.
|
||||||
} else {
|
* Might need some work later. Definately will need some work late..
|
||||||
/* We don't want to add ourselves as a remote player! */
|
*/
|
||||||
|
std::vector<RemotePlayer> updated_remote_players;
|
||||||
|
for(unsigned int i = 0; i < msg.num_players; ++i) {
|
||||||
|
const auto& player_state = msg.players[i];
|
||||||
if(player_state.player_id != _our_player_id) {
|
if(player_state.player_id != _our_player_id) {
|
||||||
_remote_players.emplace_back(player_state.player_id, player_state.x,
|
updated_remote_players.emplace_back(player_state.player_id, player_state.x, player_state.y);
|
||||||
player_state.y);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_remote_players = updated_remote_players;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Bettola::init_sdl(void) {
|
bool Bettola::init_sdl(void) {
|
||||||
|
|||||||
@ -25,7 +25,7 @@ private:
|
|||||||
void process_udp_messages(void);
|
void process_udp_messages(void);
|
||||||
void update(double dt);
|
void update(double dt);
|
||||||
void render(void);
|
void render(void);
|
||||||
void update_remote_players(const BettolaLib::Network::GameStateMessage& msg);
|
void process_game_state(const BettolaLib::Network::GameStateMessage& msg);
|
||||||
|
|
||||||
bool init_sdl(void);
|
bool init_sdl(void);
|
||||||
bool init_glew(void);
|
bool init_glew(void);
|
||||||
|
|||||||
@ -10,6 +10,10 @@ Player::Player(void) :
|
|||||||
_vy(0.0f),
|
_vy(0.0f),
|
||||||
_speed(200.0f) {}
|
_speed(200.0f) {}
|
||||||
|
|
||||||
|
void Player::set_position(float x, float y) {
|
||||||
|
_x = x; _y = y;
|
||||||
|
}
|
||||||
|
|
||||||
void Player::update(double dt) {
|
void Player::update(double dt) {
|
||||||
_x += _vx * dt;
|
_x += _vx * dt;
|
||||||
_y += _vy * dt;
|
_y += _vy * dt;
|
||||||
|
|||||||
@ -5,6 +5,7 @@ public:
|
|||||||
Player(void);
|
Player(void);
|
||||||
|
|
||||||
void update(double dt);
|
void update(double dt);
|
||||||
|
void set_position(float x, float y);
|
||||||
|
|
||||||
void set_velocity_direction(float dir_x, float dir_y);
|
void set_velocity_direction(float dir_x, float dir_y);
|
||||||
|
|
||||||
|
|||||||
@ -62,8 +62,10 @@ void Game::broadcast_game_state(BettolaLib::Network::UDPSocket& udp_socket) {
|
|||||||
|
|
||||||
for(const auto& player : _players) {
|
for(const auto& player : _players) {
|
||||||
if(player->has_udp_addr()) {
|
if(player->has_udp_addr()) {
|
||||||
udp_socket.send_to(&header, sizeof(header), player->get_udp_addr());
|
char buffer[sizeof(header) + sizeof(msg)];
|
||||||
udp_socket.send_to(&msg, sizeof(msg), player->get_udp_addr());
|
memcpy(buffer, &header, sizeof(header));
|
||||||
|
memcpy(buffer + sizeof(header), &msg, sizeof(msg));
|
||||||
|
udp_socket.send_to(buffer, sizeof(buffer), player->get_udp_addr());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,7 +6,15 @@
|
|||||||
unsigned int Player::_next_player_id = 0;
|
unsigned int Player::_next_player_id = 0;
|
||||||
|
|
||||||
Player::Player(BettolaLib::Network::TCPSocket* socket) :
|
Player::Player(BettolaLib::Network::TCPSocket* socket) :
|
||||||
_id(_next_player_id++), _x(0.0f), _y(0.0f), _socket(socket), _has_udp_addr(false) {
|
_socket(socket),
|
||||||
|
_has_udp_addr(false) {
|
||||||
|
|
||||||
|
/* Try and set a player id to 0 now you audacious pr.ck! */
|
||||||
|
if(_next_player_id == 0) {
|
||||||
|
_next_player_id = 1;
|
||||||
|
}
|
||||||
|
_id = _next_player_id++;
|
||||||
|
_x = 0.0f; _y = 0.0f;
|
||||||
memset(&_udp_addr, 0, sizeof(_udp_addr));
|
memset(&_udp_addr, 0, sizeof(_udp_addr));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user