network stats + tweaks
parent
970334aa7a
commit
63db7893b4
|
@ -42,11 +42,18 @@ static uint8_t is_handle_ctrl_held;
|
||||||
static float debug_xpos = DBG_START_XPOS;
|
static float debug_xpos = DBG_START_XPOS;
|
||||||
static float debug_ypos = DBG_START_YPOS;
|
static float debug_ypos = DBG_START_YPOS;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
L_NONE = 0,
|
||||||
|
L_SP,
|
||||||
|
L_MP,
|
||||||
|
} limit_kind;
|
||||||
|
|
||||||
typedef struct debug_item {
|
typedef struct debug_item {
|
||||||
debug_kind kind;
|
debug_kind kind;
|
||||||
char const *name;
|
char const *name;
|
||||||
float name_width;
|
float name_width;
|
||||||
uint8_t skip;
|
uint8_t skip;
|
||||||
|
limit_kind limit_to;
|
||||||
|
|
||||||
union {
|
union {
|
||||||
union {
|
union {
|
||||||
|
@ -57,7 +64,6 @@ typedef struct debug_item {
|
||||||
struct {
|
struct {
|
||||||
struct debug_item *items;
|
struct debug_item *items;
|
||||||
uint8_t is_collapsed;
|
uint8_t is_collapsed;
|
||||||
uint8_t is_sp_only;
|
|
||||||
} list;
|
} list;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
|
@ -114,8 +120,8 @@ static debug_item items[] = {
|
||||||
|
|
||||||
{ .kind = DITEM_END },
|
{ .kind = DITEM_END },
|
||||||
},
|
},
|
||||||
.is_sp_only = true,
|
},
|
||||||
}
|
.limit_to = L_SP,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.kind = DITEM_LIST,
|
.kind = DITEM_LIST,
|
||||||
|
@ -142,8 +148,27 @@ static debug_item items[] = {
|
||||||
},
|
},
|
||||||
{ .kind = DITEM_END },
|
{ .kind = DITEM_END },
|
||||||
},
|
},
|
||||||
.is_sp_only = true,
|
},
|
||||||
}
|
.limit_to = L_SP,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.kind = DITEM_LIST,
|
||||||
|
.name = "conn metrics",
|
||||||
|
.list = {
|
||||||
|
.items = (debug_item[]) {
|
||||||
|
{ .kind = DITEM_COND, .on_success = CondClientDisconnected },
|
||||||
|
{ .kind = DITEM_TEXT, .name = "status", .proc = DrawLiteral, .text = "disconnected" },
|
||||||
|
|
||||||
|
{ .kind = DITEM_COND, .on_success = CondClientConnected },
|
||||||
|
{ .kind = DITEM_TEXT, .name = "status", .proc = DrawLiteral, .text = "connected" },
|
||||||
|
|
||||||
|
{ .kind = DITEM_COND, .on_success = CondClientConnected },
|
||||||
|
{ .kind = DITEM_TEXT, .proc = DrawNetworkStats },
|
||||||
|
|
||||||
|
{ .kind = DITEM_END },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.limit_to = L_MP,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.kind = DITEM_LIST,
|
.kind = DITEM_LIST,
|
||||||
|
@ -178,9 +203,9 @@ static debug_item items[] = {
|
||||||
|
|
||||||
{ .kind = DITEM_END },
|
{ .kind = DITEM_END },
|
||||||
},
|
},
|
||||||
.is_sp_only = true,
|
|
||||||
.is_collapsed = true,
|
.is_collapsed = true,
|
||||||
}
|
},
|
||||||
|
.limit_to = L_SP,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.kind = DITEM_LIST,
|
.kind = DITEM_LIST,
|
||||||
|
@ -210,6 +235,8 @@ static debug_item items[] = {
|
||||||
debug_draw_result debug_draw_list(debug_item *list, float xpos, float ypos, bool is_shadow) {
|
debug_draw_result debug_draw_list(debug_item *list, float xpos, float ypos, bool is_shadow) {
|
||||||
is_shadow_rendered = is_shadow;
|
is_shadow_rendered = is_shadow;
|
||||||
for (debug_item *it = list; it->kind != DITEM_END; it += 1) {
|
for (debug_item *it = list; it->kind != DITEM_END; it += 1) {
|
||||||
|
if (it->limit_to == L_SP && game_get_kind() != GAMEKIND_SINGLE) continue;
|
||||||
|
if (it->limit_to == L_MP && game_get_kind() == GAMEKIND_SINGLE) continue;
|
||||||
switch (it->kind) {
|
switch (it->kind) {
|
||||||
case DITEM_GAP: {
|
case DITEM_GAP: {
|
||||||
ypos += DBG_GAP_HEIGHT;
|
ypos += DBG_GAP_HEIGHT;
|
||||||
|
@ -234,18 +261,20 @@ debug_draw_result debug_draw_list(debug_item *list, float xpos, float ypos, bool
|
||||||
UIDrawText(it->name, xpos, ypos, DBG_FONT_SIZE, color);
|
UIDrawText(it->name, xpos, ypos, DBG_FONT_SIZE, color);
|
||||||
ypos += DBG_FONT_SPACING;
|
ypos += DBG_FONT_SPACING;
|
||||||
if (it->list.is_collapsed) break;
|
if (it->list.is_collapsed) break;
|
||||||
if (it->list.is_sp_only && game_get_kind() != GAMEKIND_SINGLE) break;
|
|
||||||
debug_draw_result res = debug_draw_list(it->list.items, xpos+DBG_LIST_XPOS_OFFSET, ypos, is_shadow);
|
debug_draw_result res = debug_draw_list(it->list.items, xpos+DBG_LIST_XPOS_OFFSET, ypos, is_shadow);
|
||||||
ypos = res.y;
|
ypos = res.y;
|
||||||
}break;
|
}break;
|
||||||
|
|
||||||
case DITEM_TEXT: {
|
case DITEM_TEXT: {
|
||||||
char const *text = TextFormat("%s: ", it->name);
|
if (it->name) {
|
||||||
if (it->name_width == 0) {
|
char const *text = TextFormat("%s: ", it->name);
|
||||||
it->name_width = (float)UIMeasureText(text, DBG_FONT_SIZE);
|
if (it->name_width == 0) {
|
||||||
|
it->name_width = (float)UIMeasureText(text, DBG_FONT_SIZE);
|
||||||
|
}
|
||||||
|
UIDrawText(text, xpos, ypos, DBG_FONT_SIZE, RAYWHITE);
|
||||||
|
ZPL_ASSERT(it->proc);
|
||||||
}
|
}
|
||||||
UIDrawText(text, xpos, ypos, DBG_FONT_SIZE, RAYWHITE);
|
|
||||||
ZPL_ASSERT(it->proc);
|
|
||||||
|
|
||||||
debug_draw_result res = it->proc(it, xpos + it->name_width, ypos);
|
debug_draw_result res = it->proc(it, xpos + it->name_width, ypos);
|
||||||
ypos = res.y;
|
ypos = res.y;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#include "debug_ui.h"
|
#include "debug_ui.h"
|
||||||
#include "world/blocks.h"
|
#include "world/blocks.h"
|
||||||
#include "items.h"
|
#include "items.h"
|
||||||
|
#include "network.h"
|
||||||
|
|
||||||
void
|
void
|
||||||
ActExitGame(void) {
|
ActExitGame(void) {
|
||||||
|
@ -269,3 +270,15 @@ uint8_t
|
||||||
CondIsWorldRunning(void) {
|
CondIsWorldRunning(void) {
|
||||||
return !world_is_paused();
|
return !world_is_paused();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NOTE(zaklaus): connection metrics
|
||||||
|
|
||||||
|
uint8_t
|
||||||
|
CondClientConnected(void) {
|
||||||
|
return network_client_is_connected();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t
|
||||||
|
CondClientDisconnected(void) {
|
||||||
|
return !network_client_is_connected();
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#include "debug_ui.h"
|
#include "debug_ui.h"
|
||||||
#include "raylib.h"
|
#include "raylib.h"
|
||||||
#include "platform.h"
|
#include "platform.h"
|
||||||
|
#include "network.h"
|
||||||
#include "profiler.h"
|
#include "profiler.h"
|
||||||
|
|
||||||
//~ NOTE(zaklaus): helpers
|
//~ NOTE(zaklaus): helpers
|
||||||
|
@ -16,7 +17,16 @@ static inline debug_draw_result
|
||||||
DrawColoredText(float xpos, float ypos, char const *text, Color color) {
|
DrawColoredText(float xpos, float ypos, char const *text, Color color) {
|
||||||
ZPL_ASSERT(text);
|
ZPL_ASSERT(text);
|
||||||
UIDrawText(text, xpos, ypos, DBG_FONT_SIZE, color);
|
UIDrawText(text, xpos, ypos, DBG_FONT_SIZE, color);
|
||||||
return (debug_draw_result){.x = xpos + UIMeasureText(text, DBG_FONT_SIZE), .y = ypos + DBG_FONT_SPACING};
|
|
||||||
|
char const *p = text;
|
||||||
|
uint8_t newlines = 1;
|
||||||
|
|
||||||
|
do {
|
||||||
|
if (*p == '\n')
|
||||||
|
++newlines;
|
||||||
|
} while (*p++ != 0);
|
||||||
|
|
||||||
|
return (debug_draw_result){.x = xpos + UIMeasureText(text, DBG_FONT_SIZE), .y = ypos + DBG_FONT_SPACING*newlines};
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline debug_draw_result
|
static inline debug_draw_result
|
||||||
|
@ -99,3 +109,31 @@ DrawWorldStepSize(debug_item *it, float xpos, float ypos) {
|
||||||
(void)it;
|
(void)it;
|
||||||
return DrawFormattedText(xpos, ypos, TextFormat("%d ms", (int16_t)(sim_step_size*1000.f)));
|
return DrawFormattedText(xpos, ypos, TextFormat("%d ms", (int16_t)(sim_step_size*1000.f)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NOTE(zaklaus): network stats
|
||||||
|
static inline debug_draw_result
|
||||||
|
DrawNetworkStats(debug_item *it, float xpos, float ypos) {
|
||||||
|
(void)it;
|
||||||
|
|
||||||
|
network_client_stats s = network_client_fetch_stats();
|
||||||
|
|
||||||
|
#define _kb(x) ((x) / 1024)
|
||||||
|
|
||||||
|
debug_draw_result r;
|
||||||
|
r = DrawFormattedText(xpos, ypos, TextFormat("dn total: %d kb", _kb(s.incoming_total)));
|
||||||
|
r = DrawFormattedText(xpos, r.y, TextFormat("recv total: %lld kb", _kb(s.total_received)));
|
||||||
|
|
||||||
|
r = DrawFormattedText(xpos, r.y, TextFormat("up total: %lld kb", _kb(s.outgoing_total)));
|
||||||
|
r = DrawFormattedText(xpos, r.y, TextFormat("sent total: %lld kb", _kb(s.total_sent)));
|
||||||
|
|
||||||
|
r = DrawFormattedText(xpos, r.y, TextFormat("dn rate: %.02f kb/sec (%.02f kbit/sec)", _kb(s.incoming_bandwidth), _kb(s.incoming_bandwidth * 8.0f)));
|
||||||
|
r = DrawFormattedText(xpos, r.y, TextFormat("up rate: %.02f kb/sec (%.02f kbit/sec)", _kb(s.outgoing_bandwidth), _kb(s.outgoing_bandwidth * 8.0f)));
|
||||||
|
|
||||||
|
r = DrawFormattedText(xpos, r.y, TextFormat("packets sent: %lld", s.packets_sent));
|
||||||
|
r = DrawFormattedText(xpos, r.y, TextFormat("packets lost: %d (%.02f%%)", s.packets_lost, s.packet_loss));
|
||||||
|
|
||||||
|
r = DrawFormattedText(xpos, r.y, TextFormat("ping: %d ms", s.ping));
|
||||||
|
|
||||||
|
#undef _kb
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
|
@ -152,7 +152,7 @@ void game_init(game_kind play_mode, uint32_t num_viewers, int32_t seed, uint16_t
|
||||||
|
|
||||||
if (game_mode == GAMEKIND_HEADLESS) {
|
if (game_mode == GAMEKIND_HEADLESS) {
|
||||||
network_server_start(0, 27000);
|
network_server_start(0, 27000);
|
||||||
ecs_set_target_fps(world_ecs(), 60);
|
//ecs_set_target_fps(world_ecs(), 60);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -98,6 +98,65 @@ int32_t network_client_tick() {
|
||||||
bool network_client_is_connected() {
|
bool network_client_is_connected() {
|
||||||
return peer ? enet_peer_get_state(peer) == ENET_PEER_STATE_CONNECTED : false;
|
return peer ? enet_peer_get_state(peer) == ENET_PEER_STATE_CONNECTED : false;
|
||||||
}
|
}
|
||||||
|
network_client_stats
|
||||||
|
network_client_fetch_stats(void) {
|
||||||
|
if (!network_client_is_connected())
|
||||||
|
return (network_client_stats){0};
|
||||||
|
|
||||||
|
network_client_stats stats = {0};
|
||||||
|
|
||||||
|
stats.incoming_total = peer->incomingDataTotal;
|
||||||
|
stats.total_received = peer->totalDataReceived;
|
||||||
|
stats.outgoing_total = peer->outgoingDataTotal;
|
||||||
|
stats.total_sent = peer->totalDataSent;
|
||||||
|
|
||||||
|
static double next_measure = 0.0;
|
||||||
|
static float incoming_bandwidth = 0.0f;
|
||||||
|
static float outgoing_bandwidth = 0.0f;
|
||||||
|
|
||||||
|
if (next_measure < zpl_time_rel()) {
|
||||||
|
#define MAX_RATE_SAMPLES 8
|
||||||
|
static uint64_t last_total_sent = 0;
|
||||||
|
static uint64_t last_total_recv = 0;
|
||||||
|
static uint64_t rolling_counter = 0;
|
||||||
|
static uint64_t sent_buffer[MAX_RATE_SAMPLES] = {0};
|
||||||
|
static uint64_t recv_buffer[MAX_RATE_SAMPLES] = {0};
|
||||||
|
|
||||||
|
uint64_t sent_delta = stats.total_sent - last_total_sent;
|
||||||
|
uint64_t recv_delta = stats.total_received - last_total_recv;
|
||||||
|
last_total_sent = stats.total_sent;
|
||||||
|
last_total_recv = stats.total_received;
|
||||||
|
|
||||||
|
sent_buffer[rolling_counter % MAX_RATE_SAMPLES] = sent_delta;
|
||||||
|
recv_buffer[rolling_counter % MAX_RATE_SAMPLES] = recv_delta;
|
||||||
|
++rolling_counter;
|
||||||
|
|
||||||
|
for (int i = 0; i < MAX_RATE_SAMPLES; i++) {
|
||||||
|
stats.incoming_bandwidth += recv_buffer[i];
|
||||||
|
stats.outgoing_bandwidth += sent_buffer[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
incoming_bandwidth = stats.incoming_bandwidth /= MAX_RATE_SAMPLES;
|
||||||
|
outgoing_bandwidth = stats.outgoing_bandwidth /= MAX_RATE_SAMPLES;
|
||||||
|
|
||||||
|
next_measure = zpl_time_rel() + 1.0;
|
||||||
|
} else {
|
||||||
|
stats.incoming_bandwidth = incoming_bandwidth;
|
||||||
|
stats.outgoing_bandwidth = outgoing_bandwidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
stats.packets_sent = peer->totalPacketsSent;
|
||||||
|
stats.packets_lost = peer->totalPacketsLost;
|
||||||
|
|
||||||
|
if (stats.packets_sent > 0) {
|
||||||
|
stats.packet_loss = stats.packets_lost / (float)stats.packets_sent;
|
||||||
|
}
|
||||||
|
|
||||||
|
stats.ping = peer->roundTripTime;
|
||||||
|
stats.low_ping = peer->lowestRoundTripTime;
|
||||||
|
|
||||||
|
return stats;
|
||||||
|
}
|
||||||
|
|
||||||
//~ NOTE(zaklaus): server
|
//~ NOTE(zaklaus): server
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,30 @@ int32_t network_client_tick(void);
|
||||||
void network_client_update(void *data);
|
void network_client_update(void *data);
|
||||||
bool network_client_is_connected();
|
bool network_client_is_connected();
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
// NOTE(zaklaus): persistent stats bytes
|
||||||
|
uint32_t incoming_total;
|
||||||
|
uint64_t total_received;
|
||||||
|
uint32_t outgoing_total;
|
||||||
|
uint64_t total_sent;
|
||||||
|
|
||||||
|
// NOTE(zaklaus): bandwidth (bytes/sec)
|
||||||
|
float incoming_bandwidth;
|
||||||
|
float outgoing_bandwidth;
|
||||||
|
|
||||||
|
// NOTE(zaklaus): packet integrity
|
||||||
|
uint64_t packets_sent;
|
||||||
|
uint32_t packets_lost;
|
||||||
|
float packet_loss;
|
||||||
|
|
||||||
|
// NOTE(zaklaus): ping
|
||||||
|
uint32_t ping;
|
||||||
|
uint32_t low_ping;
|
||||||
|
} network_client_stats;
|
||||||
|
|
||||||
|
network_client_stats
|
||||||
|
network_client_fetch_stats(void);
|
||||||
|
|
||||||
// NOTE(zaklaus): server
|
// NOTE(zaklaus): server
|
||||||
int32_t network_server_start(const char *host, uint16_t port);
|
int32_t network_server_start(const char *host, uint16_t port);
|
||||||
int32_t network_server_stop(void);
|
int32_t network_server_stop(void);
|
||||||
|
|
|
@ -311,9 +311,9 @@ int32_t world_update() {
|
||||||
slow_ms = WORLD_TRACKER_UPDATE_MP_SLOW_MS;
|
slow_ms = WORLD_TRACKER_UPDATE_MP_SLOW_MS;
|
||||||
}
|
}
|
||||||
|
|
||||||
world_tracker_update(0, fast_ms, 2);
|
world_tracker_update(0, fast_ms, 1);
|
||||||
world_tracker_update(1, normal_ms, 4);
|
world_tracker_update(1, normal_ms, 2);
|
||||||
world_tracker_update(2, slow_ms, 6);
|
world_tracker_update(2, slow_ms, 3);
|
||||||
|
|
||||||
debug_replay_update();
|
debug_replay_update();
|
||||||
return 0;
|
return 0;
|
||||||
|
|
Loading…
Reference in New Issue