Profiling, but also SLOWS DOWN Game???

main
Cameron Murphy Reikes 2 years ago
parent 9259536370
commit a8157f076b

2
.gitignore vendored

@ -1,3 +1,5 @@
# profiling results
*.spall
compile_commands.json
.cache/
enc_temp_folder/

@ -58,6 +58,7 @@
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
<EnableASAN>false</EnableASAN>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug No Host|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
@ -140,7 +141,7 @@
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions);DEBUG_RENDERING</PreprocessorDefinitions>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions);DEBUG_RENDERING;SERVER_ADDRESS="127.0.0.1"</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>$(ProjectDir)\thirdparty\enet\include;$(ProjectDir)\thirdparty\Chipmunk2D\include\chipmunk;$(ProjectDir)\thirdparty;$(ProjectDir)\thirdparty\Chipmunk2D\include;$(ProjectDir)\thirdparty\minilzo;$(ProjectDir)\thirdparty\opus\include</AdditionalIncludeDirectories>
<TreatWarningAsError>true</TreatWarningAsError>

File diff suppressed because it is too large Load Diff

1210
main.c

File diff suppressed because it is too large Load Diff

@ -0,0 +1,98 @@
#include "types.h"
#include <stdlib.h> // malloc the profiling buffer
#ifdef PROFILING_H
#error only include profiling.h once
#endif
#define PROFILING_H
#ifdef PROFILING
#define PROFILING_BUFFER_SIZE (1 * 1024 * 1024)
#ifdef PROFILING_IMPL
#define SPALL_IMPLEMENTATION
#pragma warning(disable : 4996) // spall uses fopen
#include "spall.h"
#define WIN32_LEAN_AND_MEAN
#define VC_EXTRALEAN
#define NOMINMAX
#include <Windows.h>
// This is slow, if you can use RDTSC and set the multiplier in SpallInit, you'll have far better timing accuracy
double get_time_in_micros()
{
static double invfreq;
if (!invfreq)
{
LARGE_INTEGER frequency;
QueryPerformanceFrequency(&frequency);
invfreq = 1000000.0 / frequency.QuadPart;
}
LARGE_INTEGER counter;
QueryPerformanceCounter(&counter);
return counter.QuadPart * invfreq;
}
SpallProfile spall_ctx;
THREADLOCAL SpallBuffer spall_buffer;
THREADLOCAL unsigned char *buffer_data = NULL;
THREADLOCAL uint32_t my_thread_id = 0;
void init_profiling(const char *filename)
{
spall_ctx = SpallInit(filename, 1);
}
void init_profiling_mythread(uint32_t id)
{
my_thread_id = id;
if (buffer_data != NULL)
{
*(int *)0 = 0;
}
buffer_data = malloc(PROFILING_BUFFER_SIZE);
spall_buffer = (SpallBuffer){
.length = PROFILING_BUFFER_SIZE,
.data = buffer_data,
};
SpallBufferInit(&spall_ctx, &spall_buffer);
}
void end_profiling_mythread()
{
SpallBufferFlush(&spall_ctx, &spall_buffer);
SpallBufferQuit(&spall_ctx, &spall_buffer);
free(buffer_data);
}
void end_profiling()
{
SpallQuit(&spall_ctx);
}
#endif // PROFILING_IMPL
#include "spall.h"
extern SpallProfile spall_ctx;
extern THREADLOCAL SpallBuffer spall_buffer;
extern THREADLOCAL uint32_t my_thread_id;
double get_time_in_micros();
void init_profiling(const char *filename);
// you can pass anything to id as long as it's different from other threads
void init_profiling_mythread(uint32_t id);
void end_profiling();
void end_profiling_mythread();
#define PROFILE_SCOPE(name) DeferLoop(SpallTraceBeginLenTidPid(&spall_ctx, &spall_buffer, name, sizeof(name) - 1, my_thread_id, 0, get_time_in_micros()), SpallTraceEndTidPid(&spall_ctx, &spall_buffer, my_thread_id, 0, get_time_in_micros()))
#else // PROFILING
void inline init_profiling(const char *filename) { (void)filename; }
// you can pass anything to id as long as it's different from other threads
void inline init_profiling_mythread(uint32_t id) { (void)id; }
void inline end_profiling() {}
void inline end_profiling_mythread() {}
#define PROFILE_SCOPE(name)
#endif

@ -1,5 +1,5 @@
#include "chipmunk.h" // initializing bodies
#include "sokol_time.h"
#include <chipmunk.h> // initializing bodies
#include "types.h"
#include <enet/enet.h>
#include <errno.h>
@ -20,40 +20,7 @@
for (ENetPeer *cur = host->peers; cur < host->peers + host->peerCount; cur++) \
if (cur->state == ENET_PEER_STATE_CONNECTED)
#ifdef PROFILING
#define SPALL_IMPLEMENTATION
#pragma warning(disable : 4996) // spall uses fopen
#include "spall.h"
#define WIN32_LEAN_AND_MEAN
#define VC_EXTRALEAN
#define NOMINMAX
#include <Windows.h>
// This is slow, if you can use RDTSC and set the multiplier in SpallInit, you'll have far better timing accuracy
double get_time_in_micros()
{
static double invfreq;
if (!invfreq)
{
LARGE_INTEGER frequency;
QueryPerformanceFrequency(&frequency);
invfreq = 1000000.0 / frequency.QuadPart;
}
LARGE_INTEGER counter;
QueryPerformanceCounter(&counter);
return counter.QuadPart * invfreq;
}
static SpallProfile spall_ctx;
static SpallBuffer spall_buffer;
#define PROFILE_SCOPE(name) DeferLoop(SpallTraceBeginLenTidPid(&spall_ctx, &spall_buffer, name, sizeof(name) - 1, 0, 0, get_time_in_micros()), SpallTraceEndTidPid(&spall_ctx, &spall_buffer, 0, 0, get_time_in_micros()))
#else // PROFILING
#define PROFILE_SCOPE(name)
#endif
#include "profiling.h"
// started in a thread from host
void server(void *info_raw)
@ -61,14 +28,6 @@ void server(void *info_raw)
ServerThreadInfo *info = (ServerThreadInfo *)info_raw;
const char *world_save_name = info->world_save;
#ifdef PROFILING
#define BUFFER_SIZE (1 * 1024 * 1024)
spall_ctx = SpallInit("server.spall", 1);
unsigned char *buffer = malloc(BUFFER_SIZE);
spall_buffer = (SpallBuffer){
.length = BUFFER_SIZE,
.data = buffer,
};
SpallBufferInit(&spall_ctx, &spall_buffer);
#endif
@ -79,6 +38,8 @@ void server(void *info_raw)
gs.server_side_computing = true;
Log("Allocated %zu bytes for entities\n", entities_size);
init_profiling_mythread(1);
create_initial_world(&gs);
// inputs
@ -95,9 +56,9 @@ void server(void *info_raw)
OpusEncoder *player_encoders[MAX_PLAYERS] = {0};
OpusDecoder *player_decoders[MAX_PLAYERS] = {0};
#ifdef DEBUG_WORLD
#ifdef DEBUG_WORLD
world_save_name = NULL;
#endif
#endif
if (world_save_name != NULL)
{
size_t read_game_data_buffer_size = entities_size;
@ -199,333 +160,336 @@ void server(void *info_raw)
double audio_time_to_send = 0.0;
double total_time = 0.0;
unsigned char *world_save_buffer = malloc(entities_size);
while (true)
PROFILE_SCOPE("Serving")
{
ma_mutex_lock(&info->info_mutex);
if (info->should_quit)
{
ma_mutex_unlock(&info->info_mutex);
break;
}
ma_mutex_unlock(&info->info_mutex);
// @Speed handle enet messages and simulate gamestate in parallel, then sync... must clone gamestate for this
while (true)
{
int ret = enet_host_service(enet_host, &event, 0);
if (ret == 0)
break;
if (ret < 0)
ma_mutex_lock(&info->info_mutex);
if (info->should_quit)
{
fprintf(stderr, "Enet host service error %d\n", ret);
ma_mutex_unlock(&info->info_mutex);
break;
}
if (ret > 0)
ma_mutex_unlock(&info->info_mutex);
// @Speed handle enet messages and simulate gamestate in parallel, then sync... must clone gamestate for this
while (true)
{
switch (event.type)
int ret = enet_host_service(enet_host, &event, 0);
if (ret == 0)
break;
if (ret < 0)
{
case ENET_EVENT_TYPE_CONNECT:
fprintf(stderr, "Enet host service error %d\n", ret);
}
if (ret > 0)
{
Log("A new client connected from %x:%u.\n",
event.peer->address.host,
event.peer->address.port);
int64_t player_slot = -1;
for (int i = 0; i < MAX_PLAYERS; i++)
switch (event.type)
{
if (!gs.players[i].connected)
case ENET_EVENT_TYPE_CONNECT:
{
Log("A new client connected from %x:%u.\n",
event.peer->address.host,
event.peer->address.port);
int64_t player_slot = -1;
for (int i = 0; i < MAX_PLAYERS; i++)
{
player_slot = i;
break;
if (!gs.players[i].connected)
{
player_slot = i;
break;
}
}
}
if (player_slot == -1)
{
enet_peer_disconnect_now(event.peer, 69);
}
else
{
event.peer->data = (void *)player_slot;
gs.players[player_slot] = (struct Player){0};
gs.players[player_slot].connected = true;
create_player(&gs.players[player_slot]);
int error;
player_encoders[player_slot] = opus_encoder_create(VOIP_SAMPLE_RATE, 1, OPUS_APPLICATION_VOIP, &error);
if (error != OPUS_OK)
Log("Failed to create encoder: %d\n", error);
player_decoders[player_slot] = opus_decoder_create(VOIP_SAMPLE_RATE, 1, &error);
if (error != OPUS_OK)
Log("Failed to create decoder: %d\n", error);
if (player_slot == -1)
{
enet_peer_disconnect_now(event.peer, 69);
}
else
{
event.peer->data = (void *)player_slot;
gs.players[player_slot] = (struct Player){0};
gs.players[player_slot].connected = true;
create_player(&gs.players[player_slot]);
int error;
player_encoders[player_slot] = opus_encoder_create(VOIP_SAMPLE_RATE, 1, OPUS_APPLICATION_VOIP, &error);
if (error != OPUS_OK)
Log("Failed to create encoder: %d\n", error);
player_decoders[player_slot] = opus_decoder_create(VOIP_SAMPLE_RATE, 1, &error);
if (error != OPUS_OK)
Log("Failed to create decoder: %d\n", error);
}
}
}
break;
break;
case ENET_EVENT_TYPE_RECEIVE:
{
// Log("A packet of length %zu was received on channel %u.\n",
// event.packet->dataLength,
// event.channelID);
if (event.packet->dataLength == 0)
case ENET_EVENT_TYPE_RECEIVE:
{
Log("Wtf an empty packet from enet?\n");
}
else
{
int64_t player_slot = (int64_t)event.peer->data;
size_t length = event.packet->dataLength;
// Log("A packet of length %zu was received on channel %u.\n",
// event.packet->dataLength,
// event.channelID);
if (event.packet->dataLength == 0)
{
Log("Wtf an empty packet from enet?\n");
}
else
{
int64_t player_slot = (int64_t)event.peer->data;
size_t length = event.packet->dataLength;
#define VOIP_QUEUE_DECL(queue_name, queue_data_name) \
Queue queue_name = {0}; \
char queue_data_name[QUEUE_SIZE_FOR_ELEMENTS(sizeof(OpusPacket), VOIP_PACKET_BUFFER_SIZE)] = {0}; \
queue_init(&queue_name, sizeof(OpusPacket), queue_data_name, QUEUE_SIZE_FOR_ELEMENTS(sizeof(OpusPacket), VOIP_PACKET_BUFFER_SIZE))
VOIP_QUEUE_DECL(throwaway_buffer, throwaway_buffer_data);
Queue *buffer_to_fill = &player_voip_buffers[player_slot];
if (get_entity(&gs, gs.players[player_slot].entity) == NULL)
buffer_to_fill = &throwaway_buffer;
VOIP_QUEUE_DECL(throwaway_buffer, throwaway_buffer_data);
Queue *buffer_to_fill = &player_voip_buffers[player_slot];
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]};
unsigned char decompressed[MAX_CLIENT_TO_SERVER] = {0};
size_t decompressed_max_len = MAX_CLIENT_TO_SERVER;
assert(LZO1X_MEM_DECOMPRESS == 0);
queue_clear(&player_input_queues[player_slot]);
struct ClientToServer received = {.mic_data = buffer_to_fill, .input_data = &player_input_queues[player_slot]};
unsigned char decompressed[MAX_CLIENT_TO_SERVER] = {0};
size_t decompressed_max_len = MAX_CLIENT_TO_SERVER;
assert(LZO1X_MEM_DECOMPRESS == 0);
int return_value = lzo1x_decompress_safe(event.packet->data, event.packet->dataLength, decompressed, &decompressed_max_len, NULL);
int return_value = lzo1x_decompress_safe(event.packet->data, event.packet->dataLength, decompressed, &decompressed_max_len, NULL);
if (return_value == LZO_E_OK)
{
if (!client_to_server_deserialize(&gs, &received, decompressed, decompressed_max_len))
if (return_value == LZO_E_OK)
{
if (!client_to_server_deserialize(&gs, &received, decompressed, decompressed_max_len))
{
Log("Bad packet from client %d\n", (int)player_slot);
}
}
else
{
Log("Bad packet from client %d\n", (int)player_slot);
Log("Couldn't decompress player packet, error code %d from lzo\n", return_value);
}
}
else
/* Clean up the packet now that we're done using it. */
enet_packet_destroy(event.packet);
}
break;
case ENET_EVENT_TYPE_DISCONNECT:
{
int player_index = (int)(int64_t)event.peer->data;
Log("%" PRId64 " disconnected player index %d.\n", (int64_t)event.peer->data, player_index);
Entity *player_body = get_entity(&gs, gs.players[player_index].entity);
if (player_body != NULL)
{
Log("Couldn't decompress player packet, error code %d from lzo\n", return_value);
entity_destroy(&gs, player_body);
}
opus_encoder_destroy(player_encoders[player_index]);
player_encoders[player_index] = NULL;
opus_decoder_destroy(player_decoders[player_index]);
player_decoders[player_index] = NULL;
gs.players[player_index].connected = false;
queue_clear(&player_voip_buffers[player_index]);
event.peer->data = NULL;
}
/* Clean up the packet now that we're done using it. */
enet_packet_destroy(event.packet);
}
break;
break;
case ENET_EVENT_TYPE_DISCONNECT:
{
int player_index = (int)(int64_t)event.peer->data;
Log("%" PRId64 " disconnected player index %d.\n", (int64_t)event.peer->data, player_index);
Entity *player_body = get_entity(&gs, gs.players[player_index].entity);
if (player_body != NULL)
case ENET_EVENT_TYPE_NONE:
{
entity_destroy(&gs, player_body);
}
opus_encoder_destroy(player_encoders[player_index]);
player_encoders[player_index] = NULL;
opus_decoder_destroy(player_decoders[player_index]);
player_decoders[player_index] = NULL;
gs.players[player_index].connected = false;
queue_clear(&player_voip_buffers[player_index]);
event.peer->data = NULL;
}
break;
case ENET_EVENT_TYPE_NONE:
{
}
break;
break;
}
}
}
}
total_time += (float)stm_sec(stm_diff(stm_now(), last_processed_time));
last_processed_time = stm_now();
// @Robost @BeforeShip if can't process quick enough will be stuck being lagged behind, think of a solution for this...
const double max_time = 5.0 * TIMESTEP;
if (total_time > max_time)
{
Log("Abnormally large total time %f, clamping\n", total_time);
total_time = max_time;
}
total_time += (float)stm_sec(stm_diff(stm_now(), last_processed_time));
last_processed_time = stm_now();
// @Robost @BeforeShip if can't process quick enough will be stuck being lagged behind, think of a solution for this...
const double max_time = 5.0 * TIMESTEP;
if (total_time > max_time)
{
Log("Abnormally large total time %f, clamping\n", total_time);
total_time = max_time;
}
PROFILE_SCOPE("World Processing")
{
while (total_time > TIMESTEP)
{
CONNECTED_PEERS(enet_host, cur)
PROFILE_SCOPE("World Processing")
{
int this_player_index = (int)(int64_t)cur->data;
QUEUE_ITER(&player_input_queues[this_player_index], cur_header)
CONNECTED_PEERS(enet_host, cur)
{
InputFrame *cur = (InputFrame *)cur_header->data;
if (cur->tick == tick(&gs))
int this_player_index = (int)(int64_t)cur->data;
QUEUE_ITER(&player_input_queues[this_player_index], cur_header)
{
gs.players[this_player_index].input = *cur;
break;
InputFrame *cur = (InputFrame *)cur_header->data;
if (cur->tick == tick(&gs))
{
gs.players[this_player_index].input = *cur;
break;
}
}
}
}
process(&gs, TIMESTEP);
total_time -= TIMESTEP;
process(&gs, TIMESTEP, false);
total_time -= TIMESTEP;
}
}
}
if (world_save_name != NULL && (stm_sec(stm_diff(stm_now(), last_saved_world_time))) > TIME_BETWEEN_WORLD_SAVE)
{
PROFILE_SCOPE("Save World")
if (world_save_name != NULL && (stm_sec(stm_diff(stm_now(), last_saved_world_time))) > TIME_BETWEEN_WORLD_SAVE)
{
last_saved_world_time = stm_now();
ServerToClient msg = (ServerToClient){
.cur_gs = &gs,
};
size_t out_len = 0;
if (server_to_client_serialize(&msg, world_save_buffer, &out_len, entities_size, NULL, true))
PROFILE_SCOPE("Save World")
{
FILE *save_file = NULL;
fopen_s(&save_file, (const char *)world_save_name, "wb");
if (save_file == NULL)
{
Log("Could not open save file: errno %d\n", errno);
}
else
last_saved_world_time = stm_now();
ServerToClient msg = (ServerToClient){
.cur_gs = &gs,
};
size_t out_len = 0;
if (server_to_client_serialize(&msg, world_save_buffer, &out_len, entities_size, NULL, true))
{
size_t data_written = fwrite(world_save_buffer, sizeof(*world_save_buffer), out_len, save_file);
if (data_written != out_len)
FILE *save_file = NULL;
fopen_s(&save_file, (const char *)world_save_name, "wb");
if (save_file == NULL)
{
Log("Failed to save world data, wanted to write %zu but could only write %zu\n", out_len, data_written);
Log("Could not open save file: errno %d\n", errno);
}
else
{
Log("Saved game world to %s\n", (const char *)world_save_name);
size_t data_written = fwrite(world_save_buffer, sizeof(*world_save_buffer), out_len, save_file);
if (data_written != out_len)
{
Log("Failed to save world data, wanted to write %zu but could only write %zu\n", out_len, data_written);
}
else
{
Log("Saved game world to %s\n", (const char *)world_save_name);
}
fclose(save_file);
}
fclose(save_file);
}
}
else
{
Log("URGENT: FAILED TO SAVE WORLD FILE!\n");
else
{
Log("URGENT: FAILED TO SAVE WORLD FILE!\n");
}
}
}
}
if (stm_sec(stm_diff(stm_now(), last_sent_gamestate_time)) > TIME_BETWEEN_SEND_GAMESTATE)
{
last_sent_gamestate_time = stm_now();
PROFILE_SCOPE("send_data")
if (stm_sec(stm_diff(stm_now(), last_sent_gamestate_time)) > TIME_BETWEEN_SEND_GAMESTATE)
{
static char lzo_working_mem[LZO1X_1_MEM_COMPRESS] = {0};
last_sent_gamestate_time = stm_now();
PROFILE_SCOPE("send_data")
{
static char lzo_working_mem[LZO1X_1_MEM_COMPRESS] = {0};
audio_time_to_send += (float)stm_sec(stm_diff(stm_now(), last_sent_audio_time));
last_sent_audio_time = stm_now();
int num_audio_packets = (int)floor(1.0 / (VOIP_TIME_PER_PACKET / audio_time_to_send));
audio_time_to_send += (float)stm_sec(stm_diff(stm_now(), last_sent_audio_time));
last_sent_audio_time = stm_now();
int num_audio_packets = (int)floor(1.0 / (VOIP_TIME_PER_PACKET / audio_time_to_send));
#define MAX_AUDIO_PACKETS_TO_SEND 12
if (num_audio_packets > MAX_AUDIO_PACKETS_TO_SEND)
{
Log("Wants %d, this is too many packets. Greater than the maximum %d\n", num_audio_packets, MAX_AUDIO_PACKETS_TO_SEND);
num_audio_packets = MAX_AUDIO_PACKETS_TO_SEND;
}
if (num_audio_packets > MAX_AUDIO_PACKETS_TO_SEND)
{
Log("Wants %d, this is too many packets. Greater than the maximum %d\n", num_audio_packets, MAX_AUDIO_PACKETS_TO_SEND);
num_audio_packets = MAX_AUDIO_PACKETS_TO_SEND;
}
opus_int16 decoded_audio_packets[MAX_PLAYERS][MAX_AUDIO_PACKETS_TO_SEND][VOIP_EXPECTED_FRAME_COUNT] = {0};
opus_int16 decoded_audio_packets[MAX_PLAYERS][MAX_AUDIO_PACKETS_TO_SEND][VOIP_EXPECTED_FRAME_COUNT] = {0};
audio_time_to_send -= num_audio_packets * VOIP_TIME_PER_PACKET;
audio_time_to_send -= num_audio_packets * VOIP_TIME_PER_PACKET;
// decode what everybody said
CONNECTED_PEERS(enet_host, cur)
{
int this_player_index = (int)(int64_t)cur->data;
for (int packet_i = 0; packet_i < num_audio_packets; packet_i++)
// decode what everybody said
CONNECTED_PEERS(enet_host, cur)
{
opus_int16 *to_dump_to = decoded_audio_packets[this_player_index][packet_i];
OpusPacket *cur_packet = (OpusPacket *)queue_pop_element(&player_voip_buffers[this_player_index]);
if (cur_packet == NULL)
opus_decode(player_decoders[this_player_index], NULL, 0, to_dump_to, VOIP_EXPECTED_FRAME_COUNT, 0);
else
opus_decode(player_decoders[this_player_index], cur_packet->data, cur_packet->length, to_dump_to, VOIP_EXPECTED_FRAME_COUNT, 0);
int this_player_index = (int)(int64_t)cur->data;
for (int packet_i = 0; packet_i < num_audio_packets; packet_i++)
{
opus_int16 *to_dump_to = decoded_audio_packets[this_player_index][packet_i];
OpusPacket *cur_packet = (OpusPacket *)queue_pop_element(&player_voip_buffers[this_player_index]);
if (cur_packet == NULL)
opus_decode(player_decoders[this_player_index], NULL, 0, to_dump_to, VOIP_EXPECTED_FRAME_COUNT, 0);
else
opus_decode(player_decoders[this_player_index], cur_packet->data, cur_packet->length, to_dump_to, VOIP_EXPECTED_FRAME_COUNT, 0);
}
}
}
// send gamestate to each player
CONNECTED_PEERS(enet_host, cur)
{
int this_player_index = (int)(int64_t)cur->data;
Entity *this_player_entity = get_entity(&gs, gs.players[this_player_index].entity);
if (this_player_entity == NULL)
continue;
// @Speed don't recreate the packet for every peer, gets expensive copying gamestate over and over again
unsigned char *bytes_buffer = malloc(sizeof *bytes_buffer * MAX_SERVER_TO_CLIENT);
unsigned char *compressed_buffer = malloc(sizeof *compressed_buffer * MAX_SERVER_TO_CLIENT);
// mix audio to be sent
VOIP_QUEUE_DECL(buffer_to_play, buffer_to_play_data);
// send gamestate to each player
CONNECTED_PEERS(enet_host, cur)
{
for (int packet_i = 0; packet_i < num_audio_packets; packet_i++)
int this_player_index = (int)(int64_t)cur->data;
Entity *this_player_entity = get_entity(&gs, gs.players[this_player_index].entity);
if (this_player_entity == NULL)
continue;
// @Speed don't recreate the packet for every peer, gets expensive copying gamestate over and over again
unsigned char *bytes_buffer = malloc(sizeof *bytes_buffer * MAX_SERVER_TO_CLIENT);
unsigned char *compressed_buffer = malloc(sizeof *compressed_buffer * MAX_SERVER_TO_CLIENT);
// mix audio to be sent
VOIP_QUEUE_DECL(buffer_to_play, buffer_to_play_data);
{
opus_int16 to_send_to_cur[VOIP_EXPECTED_FRAME_COUNT] = {0}; // mix what other players said into this buffer
CONNECTED_PEERS(enet_host, other_player)
for (int packet_i = 0; packet_i < num_audio_packets; packet_i++)
{
if (other_player != cur)
opus_int16 to_send_to_cur[VOIP_EXPECTED_FRAME_COUNT] = {0}; // mix what other players said into this buffer
CONNECTED_PEERS(enet_host, other_player)
{
int other_player_index = (int)(int64_t)other_player->data;
Entity *other_player_entity = get_entity(&gs, gs.players[other_player_index].entity);
if (other_player_entity != NULL)
if (other_player != cur)
{
double dist = cpvdist(entity_pos(this_player_entity), entity_pos(other_player_entity));
double volume = lerp(1.0, 0.0, clamp01(dist / VOIP_DISTANCE_WHEN_CANT_HEAR));
if (volume > 0.01)
int other_player_index = (int)(int64_t)other_player->data;
Entity *other_player_entity = get_entity(&gs, gs.players[other_player_index].entity);
if (other_player_entity != NULL)
{
for (int frame_i = 0; frame_i < VOIP_EXPECTED_FRAME_COUNT; frame_i++)
double dist = cpvdist(entity_pos(this_player_entity), entity_pos(other_player_entity));
double volume = lerp(1.0, 0.0, clamp01(dist / VOIP_DISTANCE_WHEN_CANT_HEAR));
if (volume > 0.01)
{
to_send_to_cur[frame_i] += (opus_int16)((float)decoded_audio_packets[other_player_index][packet_i][frame_i] * volume);
for (int frame_i = 0; frame_i < VOIP_EXPECTED_FRAME_COUNT; frame_i++)
{
to_send_to_cur[frame_i] += (opus_int16)((float)decoded_audio_packets[other_player_index][packet_i][frame_i] * volume);
}
}
}
}
}
}
OpusPacket *this_packet = (OpusPacket *)queue_push_element(&buffer_to_play);
opus_int32 ret = opus_encode(player_encoders[this_player_index], to_send_to_cur, VOIP_EXPECTED_FRAME_COUNT, this_packet->data, VOIP_PACKET_MAX_SIZE);
if (ret < 0)
{
Log("Failed to encode audio packet for player %d: opus error code %d\n", this_player_index, ret);
}
else
{
this_packet->length = ret;
OpusPacket *this_packet = (OpusPacket *)queue_push_element(&buffer_to_play);
opus_int32 ret = opus_encode(player_encoders[this_player_index], to_send_to_cur, VOIP_EXPECTED_FRAME_COUNT, this_packet->data, VOIP_PACKET_MAX_SIZE);
if (ret < 0)
{
Log("Failed to encode audio packet for player %d: opus error code %d\n", this_player_index, ret);
}
else
{
this_packet->length = ret;
}
}
}
}
ServerToClient to_send = (ServerToClient){
.cur_gs = &gs,
.your_player = this_player_index,
.audio_playback_buffer = &buffer_to_play,
};
size_t len = 0;
if (server_to_client_serialize(&to_send, bytes_buffer, &len, MAX_SERVER_TO_CLIENT, this_player_entity, false))
{
if (len > MAX_SERVER_TO_CLIENT - 8)
ServerToClient to_send = (ServerToClient){
.cur_gs = &gs,
.your_player = this_player_index,
.audio_playback_buffer = &buffer_to_play,
};
size_t len = 0;
if (server_to_client_serialize(&to_send, bytes_buffer, &len, MAX_SERVER_TO_CLIENT, this_player_entity, false))
{
Log("Too much data quitting!\n");
exit(-1);
}
if (len > MAX_SERVER_TO_CLIENT - 8)
{
Log("Too much data quitting!\n");
exit(-1);
}
size_t compressed_len = 0;
lzo1x_1_compress(bytes_buffer, len, compressed_buffer, &compressed_len, (void *)lzo_working_mem);
size_t compressed_len = 0;
lzo1x_1_compress(bytes_buffer, len, compressed_buffer, &compressed_len, (void *)lzo_working_mem);
#ifdef LOG_GAMESTATE_SIZE
Log("Size of gamestate packet before comrpession: %zu | After: %zu\n", len, compressed_len);
Log("Size of gamestate packet before comrpession: %zu | After: %zu\n", len, compressed_len);
#endif
ENetPacket *gamestate_packet = enet_packet_create((void *)compressed_buffer, compressed_len, ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT);
int err = enet_peer_send(cur, 0, gamestate_packet);
if (err < 0)
ENetPacket *gamestate_packet = enet_packet_create((void *)compressed_buffer, compressed_len, ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT);
int err = enet_peer_send(cur, 0, gamestate_packet);
if (err < 0)
{
Log("Enet failed to send packet error %d\n", err);
enet_packet_destroy(gamestate_packet);
}
}
else
{
Log("Enet failed to send packet error %d\n", err);
enet_packet_destroy(gamestate_packet);
Log("Failed to serialize data for client %d\n", this_player_index);
}
free(bytes_buffer);
free(compressed_buffer);
}
else
{
Log("Failed to serialize data for client %d\n", this_player_index);
}
free(bytes_buffer);
free(compressed_buffer);
}
}
}
@ -547,10 +511,9 @@ void server(void *info_raw)
enet_host_destroy(enet_host);
enet_deinitialize();
end_profiling_mythread();
printf("Cleanup\n");
#ifdef PROFILING
SpallBufferQuit(&spall_ctx, &spall_buffer);
SpallQuit(&spall_ctx);
#endif
}

Binary file not shown.

@ -337,7 +337,9 @@ typedef struct GameState
// @Robust for the integer tick, also store a double for how much time has been processed.
// Like a whole timestep then a double for subtimestep
double time; // @Robust separate tick integer not prone to precision issues. Could be very large as is saved to disk!
uint64_t tick;
double subframe_time;
cpVect goldpos;
@ -421,9 +423,12 @@ void create_initial_world(GameState *gs);
void initialize(struct GameState *gs, void *entity_arena, size_t entity_arena_size);
void destroy(struct GameState *gs);
void process_fixed_timestep(GameState *gs);
void process(struct GameState *gs, double dt); // does in place
// if is subframe, doesn't always increment the tick. When enough
// subframe time has been processed, increments the tick
void process(struct GameState *gs, double dt, bool is_subframe); // does in place
Entity *closest_box_to_point_in_radius(struct GameState *gs, cpVect point, double radius, bool (*filter_func)(Entity *));
uint64_t tick(struct GameState *gs);
double time(GameState *gs);
double sun_dist_no_gravity(Entity *sun);
// all of these return if successful or not

Loading…
Cancel
Save