Multiple inputs per input packet

main
Cameron Murphy Reikes 2 years ago
parent 67f5542441
commit 3ec1103ec9

@ -268,6 +268,16 @@ void des_int(char **in, int *i)
memread(in, i); memread(in, i);
} }
void ser_uint64(char **out, uint64_t i)
{
memwrite(out, i);
}
void des_uint64(char **in, uint64_t *i)
{
memread(in, i);
}
void ser_bool(char **out, bool b) void ser_bool(char **out, bool b)
{ {
**out = (char)b; **out = (char)b;
@ -403,6 +413,8 @@ void into_bytes(struct ServerToClient *msg, char *bytes, int *out_len, int max_l
ser_int(&bytes, msg->your_player); ser_int(&bytes, msg->your_player);
LEN_CHECK(); LEN_CHECK();
ser_uint64(&bytes, gs->tick);
ser_double(&bytes, gs->time); ser_double(&bytes, gs->time);
LEN_CHECK(); LEN_CHECK();
@ -444,6 +456,9 @@ void from_bytes(struct ServerToClient *msg, char *bytes, int max_len)
des_int(&bytes, &msg->your_player); des_int(&bytes, &msg->your_player);
LEN_CHECK(); LEN_CHECK();
des_uint64(&bytes, &gs->tick);
LEN_CHECK();
des_double(&bytes, &gs->time); des_double(&bytes, &gs->time);
LEN_CHECK(); LEN_CHECK();
@ -523,6 +538,7 @@ void process(struct GameState *gs, float dt)
{ {
assert(gs->space != NULL); assert(gs->space != NULL);
gs->tick += 1;
gs->time += dt; gs->time += dt;
// process input // process input
@ -569,16 +585,15 @@ void process(struct GameState *gs, float dt)
} }
// don't allow inhabiting a grid that's already inhabited // don't allow inhabiting a grid that's already inhabited
for(int ii = 0; ii < MAX_PLAYERS; ii++) for (int ii = 0; ii < MAX_PLAYERS; ii++)
{ {
if(gs->players[ii].currently_inhabiting_index == ship_to_inhabit) if (gs->players[ii].currently_inhabiting_index == ship_to_inhabit)
{ {
Log("Attempted to inhabit already taken ship\n"); Log("Attempted to inhabit already taken ship\n");
ship_to_inhabit = -1; ship_to_inhabit = -1;
} }
} }
if (ship_to_inhabit == -1) if (ship_to_inhabit == -1)
{ {
Log("Couldn't find ship to inhabit even though point collision returned something\n"); Log("Couldn't find ship to inhabit even though point collision returned something\n");

@ -29,8 +29,9 @@ static KeyPressed keypressed[SAPP_KEYCODE_MENU] = {0};
static V2 mouse_pos = {0}; static V2 mouse_pos = {0};
static bool mouse_pressed = false; static bool mouse_pressed = false;
static uint64_t mouse_pressed_frame = 0; static uint64_t mouse_pressed_frame = 0;
static bool mouse_frozen = false; // @BeforeShip make this debug only thing static bool mouse_frozen = false; // @BeforeShip make this debug only thing
static float funval = 0.0f; // easy to play with value controlled by left mouse button when held down @BeforeShip remove on release builds static float funval = 0.0f; // easy to play with value controlled by left mouse button when held down @BeforeShip remove on release builds
static struct ClientToServer client_to_server = {0}; // buffer of inputs
static ENetHost *client; static ENetHost *client;
static ENetPeer *peer; static ENetPeer *peer;
@ -151,7 +152,7 @@ static void frame(void)
{ {
int width = sapp_width(), height = sapp_height(); int width = sapp_width(), height = sapp_height();
float ratio = width / (float)height; float ratio = width / (float)height;
float time = sapp_frame_count() * sapp_frame_duration(); double time = sapp_frame_count() * sapp_frame_duration();
float dt = sapp_frame_duration(); float dt = sapp_frame_duration();
// pressed input management // pressed input management
@ -287,32 +288,54 @@ static void frame(void)
// Create and send input packet // Create and send input packet
{ {
// @Robust accumulate total time and send input at rate like 20 hz, not every frame // @Robust accumulate total time and send input at rate like 20 hz, not every frame
struct ClientToServer curmsg = {0};
struct InputFrame cur_input_frame = {0};
V2 input = (V2){ V2 input = (V2){
.x = (float)keydown[SAPP_KEYCODE_D] - (float)keydown[SAPP_KEYCODE_A], .x = (float)keydown[SAPP_KEYCODE_D] - (float)keydown[SAPP_KEYCODE_A],
.y = (float)keydown[SAPP_KEYCODE_W] - (float)keydown[SAPP_KEYCODE_S], .y = (float)keydown[SAPP_KEYCODE_W] - (float)keydown[SAPP_KEYCODE_S],
}; };
curmsg.movement = input; cur_input_frame.movement = input;
curmsg.inhabit = keypressed[SAPP_KEYCODE_G].pressed; cur_input_frame.inhabit = keypressed[SAPP_KEYCODE_G].pressed;
curmsg.dobuild = mouse_pressed; cur_input_frame.dobuild = mouse_pressed;
curmsg.grid_index = grid_index; cur_input_frame.grid_index = grid_index;
if (curmsg.dobuild) if (cur_input_frame.dobuild)
{ {
if (grid_index != -1) if (grid_index != -1)
{ {
curmsg.build = grid_world_to_local(&gs.grids[curmsg.grid_index], build_preview.pos); cur_input_frame.build = grid_world_to_local(&gs.grids[cur_input_frame.grid_index], build_preview.pos);
V2 untransformed = grid_local_to_world(&gs.grids[curmsg.grid_index], curmsg.build); V2 untransformed = grid_local_to_world(&gs.grids[cur_input_frame.grid_index], cur_input_frame.build);
untransformed.x += 5.0f; untransformed.x += 5.0f;
} }
else else
{ {
curmsg.build = build_preview.pos; cur_input_frame.build = build_preview.pos;
}
}
struct InputFrame latest = client_to_server.inputs[0];
// if they're not the same
if (
!V2cmp(cur_input_frame.movement, latest.movement, 0.01f) ||
cur_input_frame.inhabit != latest.inhabit ||
cur_input_frame.dobuild != latest.dobuild ||
cur_input_frame.grid_index != latest.grid_index ||
!V2cmp(cur_input_frame.build, latest.build, 0.01f))
{
for (int i = 0; i < INPUT_BUFFER - 1; i++)
{
client_to_server.inputs[i + 1] = client_to_server.inputs[i];
} }
cur_input_frame.tick = gs.tick;
client_to_server.inputs[0] = cur_input_frame;
} }
// @BeforeShip figure out why tf the possess ship key is so unreliable static double last_input_sent_time = 0.0;
ENetPacket *packet = enet_packet_create((void *)&curmsg, sizeof(curmsg), ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT); if (fabs(last_input_sent_time - time) > TIME_BETWEEN_INPUT_PACKETS)
enet_peer_send(peer, 0, packet); {
ENetPacket *packet = enet_packet_create((void *)&client_to_server, sizeof(client_to_server), ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT);
enet_peer_send(peer, 0, packet);
last_input_sent_time = time;
}
} }
// @BeforeShip client side prediction and rollback to previous server authoritative state, then replay inputs // @BeforeShip client side prediction and rollback to previous server authoritative state, then replay inputs
@ -434,9 +457,6 @@ static void frame(void)
} }
} }
set_color(RED);
sgp_draw_filled_rect(1.0f, 0.5f, 0.3f, 0.3f);
// gold target // gold target
set_color(GOLD); set_color(GOLD);
sgp_draw_filled_rect(gs.goldpos.x, gs.goldpos.y, 0.1f, 0.1f); sgp_draw_filled_rect(gs.goldpos.x, gs.goldpos.y, 0.1f, 0.1f);
@ -466,21 +486,28 @@ void event(const sapp_event *e)
switch (e->type) switch (e->type)
{ {
case SAPP_EVENTTYPE_KEY_DOWN: case SAPP_EVENTTYPE_KEY_DOWN:
keydown[e->key_code] = true;
if (e->key_code == SAPP_KEYCODE_T) if (e->key_code == SAPP_KEYCODE_T)
{ {
mouse_frozen = !mouse_frozen; mouse_frozen = !mouse_frozen;
} }
if (keypressed[e->key_code].frame == 0) if (!mouse_frozen)
{ {
keypressed[e->key_code].pressed = true; keydown[e->key_code] = true;
keypressed[e->key_code].frame = e->frame_count; if (keypressed[e->key_code].frame == 0)
{
keypressed[e->key_code].pressed = true;
keypressed[e->key_code].frame = e->frame_count;
}
} }
break; break;
case SAPP_EVENTTYPE_KEY_UP: case SAPP_EVENTTYPE_KEY_UP:
keydown[e->key_code] = false; if (!mouse_frozen)
keypressed[e->key_code].pressed = false; {
keypressed[e->key_code].frame = 0; keydown[e->key_code] = false;
keypressed[e->key_code].pressed = false;
keypressed[e->key_code].frame = 0;
}
break; break;
case SAPP_EVENTTYPE_MOUSE_DOWN: case SAPP_EVENTTYPE_MOUSE_DOWN:
if (e->mouse_button == SAPP_MOUSEBUTTON_LEFT) if (e->mouse_button == SAPP_MOUSEBUTTON_LEFT)

@ -56,7 +56,7 @@ void server(void *data)
ENetAddress address; ENetAddress address;
ENetHost *server; ENetHost *server;
int sethost = enet_address_set_host_ip(&address, LOCAL_SERVER_ADDRESS); int sethost = enet_address_set_host_ip(&address, LOCAL_SERVER_ADDRESS);
if(sethost != 0) if (sethost != 0)
{ {
Log("Fishy return value from set host: %d\n", sethost); Log("Fishy return value from set host: %d\n", sethost);
} }
@ -78,6 +78,7 @@ void server(void *data)
ENetEvent event; ENetEvent event;
uint64_t last_processed_time = stm_now(); uint64_t last_processed_time = stm_now();
float total_time = 0.0f; float total_time = 0.0f;
uint64_t player_to_latest_tick_processed[MAX_PLAYERS] = {0};
while (true) while (true)
{ {
// @Speed handle enet messages and simulate gamestate in parallel, then sync... must clone gamestate for this // @Speed handle enet messages and simulate gamestate in parallel, then sync... must clone gamestate for this
@ -140,34 +141,34 @@ void server(void *data)
struct ClientToServer received = {0}; struct ClientToServer received = {0};
memcpy(&received, event.packet->data, length); memcpy(&received, event.packet->data, length);
int64_t player_slot = (int64_t)event.peer->data; int64_t player_slot = (int64_t)event.peer->data;
uint64_t latest_tick = player_to_latest_tick_processed[player_slot];
// dobuild logging if (received.inputs[0].tick > latest_tick)
if (false)
{ {
if (received.dobuild) for (int i = INPUT_BUFFER - 1; i >= 0; i--)
{ {
Log("Received build command\n"); if (received.inputs[i].tick == 0) // empty input
continue;
if(received.inputs[i].tick <= latest_tick)
continue; // don't reprocess inputs already processed
struct InputFrame cur_input = received.inputs[i];
gs.players[player_slot].movement = cur_input.movement;
gs.players[player_slot].grid_index = cur_input.grid_index;
// for these "event" inputs, only modify the game state if the event is true.
// while processing the gamestate, will mark it as false once processed. This
// prevents setting the event input to false before it's been processed.
if (cur_input.inhabit)
{
gs.players[player_slot].inhabit = cur_input.inhabit;
}
if (cur_input.dobuild)
{
gs.players[player_slot].build = cur_input.build;
gs.players[player_slot].dobuild = cur_input.dobuild;
}
} }
if (gs.players[player_slot].dobuild && !received.dobuild) player_to_latest_tick_processed[player_slot] = received.inputs[0].tick;
{
Log("Received end of build command\n");
}
}
gs.players[player_slot].movement = received.movement;
gs.players[player_slot].grid_index = received.grid_index;
// for these "event" inputs, only modify the game state if the event is true.
// while processing the gamestate, will mark it as false once processed. This
// prevents setting the event input to false before it's been processed.
if (received.inhabit)
{
gs.players[player_slot].inhabit = received.inhabit;
}
if (received.dobuild)
{
gs.players[player_slot].build = received.build;
gs.players[player_slot].dobuild = received.dobuild;
} }
} }

@ -8,7 +8,9 @@
#define MAX_BOXES_PER_GRID 32 #define MAX_BOXES_PER_GRID 32
#define BOX_MASS 1.0f #define BOX_MASS 1.0f
#define TIMESTEP (1.0f / 60.0f) // not required to simulate at this, but this defines what tick the game is on #define TIMESTEP (1.0f / 60.0f) // not required to simulate at this, but this defines what tick the game is on
#define TIME_BETWEEN_INPUT_PACKETS (1.0f / 20.0f)
#define SERVER_PORT 2551 #define SERVER_PORT 2551
#define INPUT_BUFFER 4
// must make this header and set the target address, just #define SERVER_ADDRESS "127.0.0.1" // must make this header and set the target address, just #define SERVER_ADDRESS "127.0.0.1"
#include "ipsettings.h" // don't leak IP! #include "ipsettings.h" // don't leak IP!
@ -61,6 +63,7 @@ struct GameState
{ {
cpSpace *space; cpSpace *space;
uint64_t tick;
double time; double time;
V2 goldpos; V2 goldpos;
@ -108,13 +111,17 @@ struct ServerToClient
struct ClientToServer struct ClientToServer
{ {
V2 movement; struct InputFrame
bool inhabit; {
uint64_t tick;
V2 movement;
bool inhabit;
// if grid_index != -1, this is in local coordinates to the grid // if grid_index != -1, this is in local coordinates to the grid
V2 build; V2 build;
bool dobuild; bool dobuild;
int grid_index; int grid_index;
} inputs[INPUT_BUFFER];
}; };
// server // server
@ -224,6 +231,11 @@ static V2 V2sub(V2 a, V2 b)
}; };
} }
static bool V2cmp(V2 a, V2 b, float eps)
{
return V2length(V2sub(a, b)) < eps;
}
static inline float clamp01(float f) static inline float clamp01(float f)
{ {
return fmax(0.0f, fmin(f, 1.0f)); return fmax(0.0f, fmin(f, 1.0f));

Loading…
Cancel
Save