Skip to content

Commit

Permalink
Refactor ping code logic
Browse files Browse the repository at this point in the history
hopefully less bugs
close #188
close #177
  • Loading branch information
xtreme8000 committed Jan 9, 2024
1 parent 7fce8da commit 13ccea1
Show file tree
Hide file tree
Showing 3 changed files with 184 additions and 113 deletions.
9 changes: 6 additions & 3 deletions src/hud.c
Expand Up @@ -2383,12 +2383,13 @@ static void hud_serverlist_render(mu_Context* ctx, float scalex, float scaley) {
JSON_Array* servers = json_value_get_array(js);
server_count = json_array_get_count(servers);

HashTable* pings = malloc(sizeof(struct HashTable));
ht_setup(pings, sizeof(uint64_t), sizeof(struct ping_entry), 64);

pthread_mutex_lock(&serverlist_lock);
serverlist = realloc(serverlist, server_count * sizeof(struct serverlist_entry));
CHECK_ALLOCATION_ERROR(serverlist)

ping_start(hud_serverlist_pingupdate);

player_count = 0;
for(int k = 0; k < server_count; k++) {
JSON_Object* s = json_array_get_object(servers, k);
Expand All @@ -2410,14 +2411,16 @@ static void hud_serverlist_render(mu_Context* ctx, float scalex, float scaley) {
int port;
char ip[32];
if(network_identifier_split(serverlist[k].identifier, ip, &port))
ping_check(ip, port, serverlist[k].identifier);
ping_check(pings, ip, port, serverlist[k].identifier);

player_count += serverlist[k].current;
}

qsort(serverlist, server_count, sizeof(struct serverlist_entry), hud_serverlist_sort);
pthread_mutex_unlock(&serverlist_lock);

ping_start(pings, hud_serverlist_pingupdate);

http_release(request_serverlist);
json_value_free(js);
request_serverlist = NULL;
Expand Down
274 changes: 171 additions & 103 deletions src/ping.c
Expand Up @@ -30,30 +30,101 @@
#include "list.h"
#include "hud.h"
#include "channel.h"
#include "hashtable.h"
#include "utils.h"

#define IP_KEY(addr) (((uint64_t)addr.host << 16) | (addr.port))

struct ping_task {
HashTable* servers;
void (*ping_result)(void*, float time_delta, char* aos);
bool quit;
};

struct channel ping_queue;
struct channel ping_lan_queue;
ENetSocket sock, lan;
pthread_t ping_thread;
void (*ping_result)(void*, float time_delta, char* aos);
pthread_t ping_lan_thread;

void ping_init() {
channel_create(&ping_queue, sizeof(struct ping_entry), 64);
static bool pings_retry(void* key, void* value, void* user) {
struct ping_entry* entry = (struct ping_entry*)value;
int* retry = (int*)user;

sock = enet_socket_create(ENET_SOCKET_TYPE_DATAGRAM);
enet_socket_set_option(sock, ENET_SOCKOPT_NONBLOCK, 1);
enet_socket_send(sock, &entry->addr, &(ENetBuffer) {.data = "HELLO", .dataLength = 5}, 1);
entry->time_start = window_time();

lan = enet_socket_create(ENET_SOCKET_TYPE_DATAGRAM);
enet_socket_set_option(lan, ENET_SOCKOPT_NONBLOCK, 1);
enet_socket_set_option(lan, ENET_SOCKOPT_BROADCAST, 1);
if(*retry > 0)
log_warn("Ping timeout on %s, retrying (%i)", entry->aos, *retry);

pthread_create(&ping_thread, NULL, ping_update, NULL);
return false;
}

void ping_deinit() {
void* ping_update(void* data) {
pthread_detach(pthread_self());

ENetAddress from;
uint8_t tmp[512];

ENetBuffer buf = {
.data = tmp,
.dataLength = sizeof(tmp),
};

while(1) {
struct ping_task task;
channel_await(&ping_queue, &task);

if(task.quit)
break;

ht_iterate_remove(task.servers, (int[]) {0}, pings_retry);

for(int try = 0; try < 3; try++) {
float ping_start = window_time();

while(1) {
int timeout = (2.5F - (window_time() - ping_start)) * 1000;

if(timeout <= 0)
break;

enet_uint32 event = ENET_SOCKET_WAIT_RECEIVE;
enet_socket_wait(sock, &event, timeout);
size_t recv_length = enet_socket_receive(sock, &from, &buf, 1);
uint64_t ID = IP_KEY(from);

if(recv_length != 0) {
struct ping_entry* entry = ht_lookup(task.servers, &ID);

if(entry) {
if(recv_length > 0) { // received something!
if(!strncmp(buf.data, "HI", recv_length)) {
task.ping_result(NULL, window_time() - entry->time_start, entry->aos);
ht_erase(task.servers, &ID);
}
} else { // connection was closed
ht_erase(task.servers, &ID);
}
}
}
}

ht_iterate_remove(task.servers, (int[]) {try}, pings_retry);

if(ht_is_empty(task.servers))
break;
}

if(!ht_is_empty(task.servers))
log_warn("Some servers did not reply to ping requests");

ht_destroy(task.servers);
free(task.servers);
}

enet_socket_destroy(sock);
enet_socket_destroy(lan);

return NULL;
}

static void ping_lan() {
Expand All @@ -68,132 +139,129 @@ static void ping_lan() {
enet_socket_send(lan, &addr, &buffer, 1);
}

static bool pings_retry(void* key, void* value, void* user) {
struct ping_entry* entry = (struct ping_entry*)value;

if(window_time() - entry->time_start > 2.0F) { // timeout
// try up to 3 times after first failed attempt
if(entry->trycount >= 3) {
return true;
} else {
enet_socket_send(sock, &entry->addr, &(ENetBuffer) {.data = "HELLO", .dataLength = 5}, 1);
entry->time_start = window_time();
entry->trycount++;
log_warn("Ping timeout on %s, retrying", entry->aos);
}
}

return false;
}

#define IP_KEY(addr) (((uint64_t)addr.host << 16) | (addr.port));

void* ping_update(void* data) {
void* ping_lan_update(void* data) {
pthread_detach(pthread_self());

ping_lan();
float ping_start = window_time();
ENetAddress from;
uint8_t tmp[512];

HashTable pings;
ht_setup(&pings, sizeof(uint64_t), sizeof(struct ping_entry), 64);
ENetBuffer buf = {
.data = tmp,
.dataLength = sizeof(tmp),
};

while(1) {
size_t drain = channel_size(&ping_queue);
for(size_t k = 0; (k < drain) || (!pings.size && window_time() - ping_start >= 8.0F); k++) {
struct ping_entry entry;
channel_await(&ping_queue, &entry);
struct ping_task task;
channel_await(&ping_lan_queue, &task);

uint64_t ID = IP_KEY(entry.addr);
ht_insert(&pings, &ID, &entry);
}
if(task.quit)
break;

float ping_start = window_time();

char tmp[512];
ENetAddress from;
ping_lan();

ENetBuffer buf = {
.data = tmp,
.dataLength = sizeof(tmp),
};
bool found = false;

while(1) {
int recvLength = enet_socket_receive(sock, &from, &buf, 1);
uint64_t ID = IP_KEY(from);

if(recvLength != 0) {
struct ping_entry* entry = ht_lookup(&pings, &ID);

if(entry) {
if(recvLength > 0) { // received something!
if(!strncmp(buf.data, "HI", recvLength)) {
ping_result(NULL, window_time() - entry->time_start, entry->aos);
ht_erase(&pings, &ID);
} else {
entry->trycount++;
}
} else { // connection was closed
ht_erase(&pings, &ID);
}
}
} else { // would block
int timeout = (8.0F - (window_time() - ping_start)) * 1000;

if(timeout <= 0)
break;
}
}

ht_iterate_remove(&pings, NULL, pings_retry);
enet_uint32 event = ENET_SOCKET_WAIT_RECEIVE;
enet_socket_wait(sock, &event, timeout);

int length = enet_socket_receive(lan, &from, &buf, 1);

int length = enet_socket_receive(lan, &from, &buf, 1);
if(length) {
JSON_Value* js = json_parse_string(buf.data);
if(js) {
JSON_Object* root = json_value_get_object(js);
if(length) {
JSON_Value* js = json_parse_string(buf.data);

struct serverlist_entry e;
if(js) {
JSON_Object* root = json_value_get_object(js);

strcpy(e.country, "LAN");
e.ping = ceil((window_time() - ping_start) * 1000.0F);
snprintf(e.identifier, sizeof(e.identifier) - 1, "aos://%u:%u", from.host, from.port);
struct serverlist_entry e;

strncpy(e.name, json_object_get_string(root, "name"), sizeof(e.name) - 1);
e.name[sizeof(e.name) - 1] = 0;
strncpy(e.gamemode, json_object_get_string(root, "game_mode"), sizeof(e.gamemode) - 1);
e.gamemode[sizeof(e.gamemode) - 1] = 0;
strncpy(e.map, json_object_get_string(root, "map"), sizeof(e.map) - 1);
e.map[sizeof(e.map) - 1] = 0;
e.current = json_object_get_number(root, "players_current");
e.max = json_object_get_number(root, "players_max");
ping_result(&e, window_time() - ping_start, NULL);
strcpy(e.country, "LAN");
e.ping = ceil((window_time() - ping_start) * 1000.0F);
snprintf(e.identifier, sizeof(e.identifier) - 1, "aos://%u:%u", from.host, from.port);

json_value_free(js);
strncpy(e.name, json_object_get_string(root, "name"), sizeof(e.name) - 1);
e.name[sizeof(e.name) - 1] = 0;
strncpy(e.gamemode, json_object_get_string(root, "game_mode"), sizeof(e.gamemode) - 1);
e.gamemode[sizeof(e.gamemode) - 1] = 0;
strncpy(e.map, json_object_get_string(root, "map"), sizeof(e.map) - 1);
e.map[sizeof(e.map) - 1] = 0;
e.current = json_object_get_number(root, "players_current");
e.max = json_object_get_number(root, "players_max");
task.ping_result(&e, window_time() - ping_start, NULL);
found = true;

json_value_free(js);
}
}
}

usleep(1000);
if(!found)
log_info("No local servers found");
}

enet_socket_destroy(lan);

return NULL;
}

void ping_check(char* addr, int port, char* aos) {
struct ping_entry entry = {
.trycount = 0,
.addr.port = port,
.time_start = window_time(),
};
void ping_check(HashTable* pings, char* addr, int port, char* aos) {
struct ping_entry entry;

strncpy(entry.aos, aos, sizeof(entry.aos) - 1);
entry.aos[sizeof(entry.aos) - 1] = 0;

enet_address_set_host(&entry.addr, addr);
entry.addr.port = port;

channel_put(&ping_queue, &entry);

enet_socket_send(sock, &entry.addr, &(ENetBuffer) {.data = "HELLO", .dataLength = 5}, 1);
ht_insert(pings, (uint64_t[]) {IP_KEY(entry.addr)}, &entry);
}

void ping_start(void (*result)(void*, float, char*)) {
void ping_start(HashTable* pings, void (*result)(void*, float, char*)) {
ping_stop();

ping_result = result;
channel_put(&ping_queue,
&(struct ping_task) {
.servers = pings,
.ping_result = result,
.quit = false,
});

channel_put(&ping_lan_queue,
&(struct ping_task) {
.ping_result = result,
.quit = false,
});
}

void ping_stop() {
channel_clear(&ping_queue);
channel_clear(&ping_lan_queue);
}

void ping_init() {
channel_create(&ping_queue, sizeof(struct ping_task), 4);
channel_create(&ping_lan_queue, sizeof(struct ping_task), 4);

sock = enet_socket_create(ENET_SOCKET_TYPE_DATAGRAM);
enet_socket_set_option(sock, ENET_SOCKOPT_NONBLOCK, 1);

lan = enet_socket_create(ENET_SOCKET_TYPE_DATAGRAM);
enet_socket_set_option(lan, ENET_SOCKOPT_NONBLOCK, 1);
enet_socket_set_option(lan, ENET_SOCKOPT_BROADCAST, 1);

pthread_create(&ping_thread, NULL, ping_update, NULL);
pthread_create(&ping_lan_thread, NULL, ping_lan_update, NULL);
}

void ping_deinit() {
ping_stop();
channel_put(&ping_queue, &(struct ping_task) {.quit = true});
channel_put(&ping_lan_queue, &(struct ping_task) {.quit = true});
}
14 changes: 7 additions & 7 deletions src/ping.h
Expand Up @@ -22,18 +22,18 @@

#include <enet/enet.h>

#include "hashtable.h"

struct ping_entry {
ENetAddress addr;
char aos[64];
float time_start;
int trycount;
};

void ping_init();
void ping_deinit();
void ping_check(char* addr, int port, char* aos);
void* ping_update(void* data);
void ping_start(void (*result)(void*, float, char*));
void ping_stop();
void ping_init(void);
void ping_deinit(void);
void ping_check(HashTable* pings, char* addr, int port, char* aos);
void ping_start(HashTable* pings, void (*result)(void*, float, char*));
void ping_stop(void);

#endif

0 comments on commit 13ccea1

Please sign in to comment.