Add recorded inputs integration testt

main
parent cb661bd98f
commit 9275927cc7

@ -0,0 +1,4 @@
@REM for whitebox
call shadergen.bat
set compileopts=/Zi /DDEBUG /DLL /OUT:flight_dll /LD
call build_msvc.bat

@ -16,7 +16,7 @@
// #define PROFILING
// Intensive profiling means profiling a lot of little tiny stuff. Not always enabled because tanks performance
#define INTENSIVE_PROFILING
// #define INTENSIVE_PROFILING
// #define DEBUG_RENDERING
#define DEBUG_WORLD
// #define UNLOCK_ALL

Binary file not shown.

Binary file not shown.

Binary file not shown.

@ -0,0 +1,4 @@
set EXECUTABLE=x64\Debug\Flight.exe
START /b %EXECUTABLE% host=true replay_inputs_from=input_go_up.bin
%EXECUTABLE% replay_inputs_from=input_go_down.bin

108
main.c

@ -60,6 +60,7 @@ typedef struct KeyPressed
static KeyPressed keypressed[MAX_KEYDOWN] = {0};
static cpVect mouse_pos = {0};
static FILE *record_inputs_to = NULL;
static FILE *replay_inputs_from = NULL;
static bool fullscreened = false;
static bool picking_new_boxtype = false;
static double exec_time = 0.0; // cosmetic bouncing, network stats
@ -269,6 +270,16 @@ struct SquadMeta squad_meta(enum Squad squad)
return (struct SquadMeta){0};
}
static size_t serialized_inputframe_length()
{
InputFrame frame = {0};
unsigned char serialized[1024 * 5] = {0};
SerState ser = init_serializing(&gs, serialized, ARRLEN(serialized), NULL, false);
ser_inputframe(&ser, &frame);
flight_assert(ser_size(&ser) > 1);
return ser_size(&ser);
}
static enum BoxType currently_building()
{
flight_assert(cur_toolbar_slot >= 0);
@ -514,7 +525,8 @@ static void init(void)
printf(
"Usage: astris.exe [option]=data , the =stuff is required\n"
"host - hosts a server locally if exists in commandline, like `astris.exe host=yes`\n"
"record_inputs_to - records inputs to the file specified");
"record_inputs_to - records inputs to the file specified\n"
"replay_inputs_from - replays inputs from the file specified\n");
if (sargs_exists("host"))
{
server_thread_handle = (void *)_beginthread(server, 0, (void *)&server_info);
@ -523,16 +535,32 @@ static void init(void)
if (sargs_exists("record_inputs_to"))
{
const char *filename = sargs_value("record_inputs_to");
Log("Recording inputs to %s\n", filename);
if (filename == NULL)
{
quit_with_popup("Failed to record inputs, filename not specified", "Failed to record inputs");
}
fopen_s(&record_inputs_to, filename, "wb");
errno_t error = fopen_s(&record_inputs_to, filename, "wb");
Log("%d\n", error);
if (record_inputs_to == NULL)
{
quit_with_popup("Failed to open file to record inputs into", "Failed to record inputs");
}
}
if (sargs_exists("replay_inputs_from"))
{
const char *filename = sargs_value("replay_inputs_from");
Log("Replaying inputs from %s\n", filename);
if (filename == NULL)
{
quit_with_popup("Failed to replay inputs, filename not specified", "Failed to replay inputs");
}
fopen_s(&replay_inputs_from, filename, "rb");
if (replay_inputs_from == NULL)
{
quit_with_popup("Failed to open file to replay inputs from", "Failed to replay inputs");
}
}
}
// audio
@ -1634,11 +1662,8 @@ static void frame(void)
if (applied_gamestate_packet)
{
uint64_t server_current_tick = tick(&gs);
int ticks_should_repredict = (int)predicted_to_tick - (int)server_current_tick;
int healthy_num_ticks_ahead = (int)ceil((((double)peer->roundTripTime + (double)peer->roundTripTimeVariance * CAUTIOUS_MULTIPLIER) / 1000.0) / TIMESTEP) + 6;
int ticks_to_repredict = ticks_should_repredict;
if (ticks_should_repredict < healthy_num_ticks_ahead - 1)
@ -1746,8 +1771,7 @@ static void frame(void)
}
// Create and send input packet, and predict a frame of gamestate
static InputFrame cur_input_frame = {
0}; // keep across frames for high refresh rate screens
static InputFrame cur_input_frame = {0}; // keep across frames for high refresh rate screens
static size_t last_input_committed_tick = 0;
{
// prepare the current input frame, such that when processed next,
@ -1814,10 +1838,36 @@ static void frame(void)
do
{
// "commit" the input. each input must be on a successive tick.
if (tick(&gs) > last_input_committed_tick)
// if (tick(&gs) > last_input_committed_tick)
while(tick(&gs) > last_input_committed_tick)
{
cur_input_frame.tick = tick(&gs);
last_input_committed_tick = tick(&gs);
if (replay_inputs_from != NULL)
{
unsigned char deserialized[2048] = {0};
flight_assert(ARRLEN(deserialized) >= serialized_inputframe_length());
size_t bytes_read = fread(deserialized, 1, serialized_inputframe_length(), replay_inputs_from);
if (bytes_read != serialized_inputframe_length())
{
// no more inputs in the saved file
flight_assert(myentity() != NULL);
Entity *should_be_medbay = get_entity(&gs, myentity()->currently_inside_of_box);
flight_assert(should_be_medbay != NULL);
flight_assert(should_be_medbay->is_box && should_be_medbay->box_type == BoxMedbay);
exit(0);
}
SerState ser = init_deserializing(&gs, deserialized, serialized_inputframe_length(), false);
SerMaybeFailure maybe_fail = ser_inputframe(&ser, &cur_input_frame);
flight_assert(!maybe_fail.failed);
flight_assert(serialized_inputframe_length() == ser_size(&ser));
}
else
{
cur_input_frame.tick = last_input_committed_tick + 1;
// cur_input_frame.tick = tick(&gs);
}
last_input_committed_tick = cur_input_frame.tick;
InputFrame *to_push_to = queue_push_element(&input_queue);
if (to_push_to == NULL)
@ -1830,8 +1880,40 @@ static void frame(void)
*to_push_to = cur_input_frame;
/*
optionally save the current input frame for debugging purposes
le informative comment
*/
if (record_inputs_to != NULL)
{
unsigned char serialized[2048] = {0};
SerState ser = init_serializing(&gs, serialized, ARRLEN(serialized), NULL, false);
SerMaybeFailure maybe_fail = ser_inputframe(&ser, &cur_input_frame);
flight_assert(!maybe_fail.failed);
flight_assert(serialized_inputframe_length() == ser_size(&ser));
size_t written = fwrite(serialized, 1, ser_size(&ser), record_inputs_to);
flight_assert(written == ser_size(&ser));
}
if (myplayer() != NULL)
{
myplayer()->input = cur_input_frame; // for the client side prediction!
}
cur_input_frame = (InputFrame){0};
cur_input_frame.take_over_squad = -1; // @Robust make this zero initialized
@ -2456,7 +2538,13 @@ static void frame(void)
void cleanup(void)
{
sargs_shutdown();
fclose(log_file);
if (record_inputs_to != NULL)
fclose(record_inputs_to);
if (replay_inputs_from != NULL)
fclose(replay_inputs_from);
sg_destroy_pipeline(hueshift_pipeline);
ma_mutex_lock(&server_info.info_mutex);

@ -225,8 +225,11 @@ void server(void *info_raw)
if (get_entity(&gs, gs.players[player_slot].entity) == NULL)
buffer_to_fill = &throwaway_buffer;
queue_clear(&player_input_queues[player_slot]);
struct ClientToServer received = {.mic_data = buffer_to_fill, .input_data = &player_input_queues[player_slot]};
Queue new_inputs = {0};
char new_inputs_data[QUEUE_SIZE_FOR_ELEMENTS(sizeof(InputFrame), INPUT_QUEUE_MAX)] = {0};
queue_init(&new_inputs, sizeof(InputFrame), new_inputs_data, ARRLEN(new_inputs_data));
struct ClientToServer received = {.mic_data = buffer_to_fill, .input_data = &new_inputs};
unsigned char decompressed[MAX_CLIENT_TO_SERVER] = {0};
size_t decompressed_max_len = MAX_CLIENT_TO_SERVER;
flight_assert(LZO1X_MEM_DECOMPRESS == 0);
@ -241,6 +244,26 @@ void server(void *info_raw)
{
Log("Bad packet from client %d | %d %s\n", (int)player_slot, maybe_fail.line, maybe_fail.expression);
}
else
{
QUEUE_ITER(&new_inputs, InputFrame, new_input)
{
QUEUE_ITER(&player_input_queues[player_slot], InputFrame, existing_input)
{
if (existing_input->tick == new_input->tick && existing_input->been_processed)
{
new_input->been_processed = true;
}
}
}
queue_clear(&player_input_queues[player_slot]);
QUEUE_ITER(&new_inputs, InputFrame, cur)
{
InputFrame *new_elem = queue_push_element(&player_input_queues[player_slot]);
flight_assert(new_elem != NULL);
*new_elem = *cur;
}
}
}
else
{
@ -291,16 +314,21 @@ void server(void *info_raw)
{
PROFILE_SCOPE("World Processing")
{
CONNECTED_PEERS(enet_host, cur)
CONNECTED_PEERS(enet_host, cur_peer)
{
int this_player_index = (int)(int64_t)cur->data;
int this_player_index = (int)(int64_t)cur_peer->data;
QUEUE_ITER(&player_input_queues[this_player_index], InputFrame, cur)
{
if (cur->tick == tick(&gs))
{
gs.players[this_player_index].input = *cur;
cur->been_processed = true;
break;
}
if (cur->tick < tick(&gs) && !cur->been_processed)
{
Log("Did not process input from client %d %llu ticks ago!\n", this_player_index,tick(&gs) - cur->tick);
}
}
}

@ -232,6 +232,9 @@ enum ScannerPointKind
typedef struct InputFrame
{
uint64_t tick;
bool been_processed; // not serialized, used by server just to keep track of what inputs have been processed
cpVect movement;
double rotation;
@ -511,6 +514,7 @@ SerState init_serializing(GameState *gs, unsigned char *bytes, size_t max_size,
SerState init_deserializing(GameState *gs, unsigned char *bytes, size_t max_size, bool from_disk);
SerMaybeFailure ser_server_to_client(SerState *ser, ServerToClient *s);
SerMaybeFailure ser_client_to_server(SerState *ser, ClientToServer *msg);
SerMaybeFailure ser_inputframe(SerState *ser, InputFrame *i);
// entities
bool is_burning(Entity *missile);

Loading…
Cancel
Save