better base + networking layer + remove some garbage + update client to not crash on tcp disconnect
This commit is contained in:
+477
-32
@@ -6,14 +6,40 @@
|
||||
#define NET_OUTGOING_MESSAGE_QUEUE_LEN 16
|
||||
#endif
|
||||
|
||||
#ifndef NET_SERVER_MAX_CLIENTS
|
||||
#define NET_SERVER_MAX_CLIENTS (16)
|
||||
#endif
|
||||
|
||||
typedef struct sockaddr_in SocketAddress;
|
||||
|
||||
typedef struct TCPServer {
|
||||
bool ready;
|
||||
i32 socket_fd;
|
||||
SocketAddress address;
|
||||
} TCPServer;
|
||||
|
||||
typedef struct UDPServer {
|
||||
bool ready;
|
||||
SocketAddress server_address;
|
||||
i32 server_socket;
|
||||
} UDPServer;
|
||||
|
||||
typedef struct MultiServer {
|
||||
TCPServer tcp_server;
|
||||
UDPServer udp_server;
|
||||
} MultiServer;
|
||||
|
||||
typedef struct ServableUDPInfo {
|
||||
UDPServer server;
|
||||
void (*callback)(u8* udp_message, i32 udp_len, SocketAddress sending_address, i32 socket);
|
||||
} ServableUDPInfo;
|
||||
|
||||
typedef struct TCPClient {
|
||||
bool ready;
|
||||
SocketAddress server_address;
|
||||
i32 socket;
|
||||
} TCPClient;
|
||||
|
||||
typedef struct UDPClient {
|
||||
bool ready;
|
||||
SocketAddress server_address;
|
||||
@@ -21,14 +47,22 @@ typedef struct UDPClient {
|
||||
i32 socket;
|
||||
} UDPClient;
|
||||
|
||||
typedef struct UDPMessage {
|
||||
typedef struct MultiClient {
|
||||
TCPClient tcp_client;
|
||||
UDPClient udp_client;
|
||||
} MultiClient;
|
||||
|
||||
typedef struct NetworkMessage {
|
||||
bool tcp; // default = false = UDP, just sendto(address)
|
||||
u16 bytes_len;
|
||||
i32 socket_fd;
|
||||
char* long_bytes; // used when message is longer than UDP max, memory must be managed by caller
|
||||
SocketAddress address;
|
||||
u8 bytes[UDP_MAX_MESSAGE_LEN];
|
||||
} UDPMessage;
|
||||
} NetworkMessage;
|
||||
|
||||
typedef struct OutgoingMessageQueue {
|
||||
UDPMessage items[NET_OUTGOING_MESSAGE_QUEUE_LEN];
|
||||
NetworkMessage items[NET_OUTGOING_MESSAGE_QUEUE_LEN];
|
||||
u32 head;
|
||||
u32 tail;
|
||||
u32 count;
|
||||
@@ -37,6 +71,7 @@ typedef struct OutgoingMessageQueue {
|
||||
Cond not_full;
|
||||
} OutgoingMessageQueue;
|
||||
|
||||
typedef void (*HandleMessageCb)(u8* udp_message, i32 bytes_recieved, SocketAddress sending_address, i32 socket);
|
||||
fn OutgoingMessageQueue* newOutgoingMessageQueue(Arena* a) {
|
||||
OutgoingMessageQueue* result = arenaAlloc(a, sizeof(OutgoingMessageQueue));
|
||||
MemoryZero(result, (sizeof *result));
|
||||
@@ -46,7 +81,7 @@ fn OutgoingMessageQueue* newOutgoingMessageQueue(Arena* a) {
|
||||
return result;
|
||||
}
|
||||
|
||||
fn void outgoingMessageQueuePush(OutgoingMessageQueue* queue, UDPMessage* msg) {
|
||||
fn void outgoingMessageQueuePush(OutgoingMessageQueue* queue, NetworkMessage* msg) {
|
||||
lockMutex(&queue->mutex); {
|
||||
while (queue->count == NET_OUTGOING_MESSAGE_QUEUE_LEN) {
|
||||
waitForCondSignal(&queue->not_full, &queue->mutex);
|
||||
@@ -60,11 +95,11 @@ fn void outgoingMessageQueuePush(OutgoingMessageQueue* queue, UDPMessage* msg) {
|
||||
} unlockMutex(&queue->mutex);
|
||||
}
|
||||
|
||||
fn UDPMessage* outgoingMessageNonblockingQueuePop(OutgoingMessageQueue* q, UDPMessage* copy_target) {
|
||||
fn NetworkMessage* outgoingMessageNonblockingQueuePop(OutgoingMessageQueue* q, NetworkMessage* copy_target) {
|
||||
// immediately returns NULL if there's nothing in the ThreadQueue
|
||||
// copies the ParsedClientCommand into `copy_target` if there is something in the queue
|
||||
// and marks it as popped from the queue
|
||||
UDPMessage* result = NULL;
|
||||
NetworkMessage* result = NULL;
|
||||
|
||||
lockMutex(&q->mutex); {
|
||||
if (q->count > 0) {
|
||||
@@ -80,8 +115,8 @@ fn UDPMessage* outgoingMessageNonblockingQueuePop(OutgoingMessageQueue* q, UDPMe
|
||||
return result;
|
||||
}
|
||||
|
||||
fn UDPMessage* outgoingMessageQueuePop(OutgoingMessageQueue* q, UDPMessage* copy_target) {
|
||||
UDPMessage* result = NULL;
|
||||
fn NetworkMessage* outgoingMessageQueuePop(OutgoingMessageQueue* q, NetworkMessage* copy_target) {
|
||||
NetworkMessage* result = NULL;
|
||||
|
||||
lockMutex(&q->mutex); {
|
||||
while (q->count == 0) {
|
||||
@@ -99,17 +134,43 @@ fn UDPMessage* outgoingMessageQueuePop(OutgoingMessageQueue* q, UDPMessage* copy
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
fn bool socketAddressEqual(SocketAddress a, SocketAddress b) {
|
||||
return a.sin_addr.s_addr == b.sin_addr.s_addr
|
||||
&& a.sin_port == b.sin_port;
|
||||
}
|
||||
|
||||
i32 netRecvExact(i32 socket, void* buf, u16 bytes_to_recv) {
|
||||
i32 got, got_this_iter;
|
||||
for (got = 0; got < bytes_to_recv; got += got_this_iter) {
|
||||
got_this_iter = recv(socket, (char*)buf + got, bytes_to_recv - got, 0);
|
||||
if (got_this_iter <= 0) return got_this_iter;
|
||||
}
|
||||
return got;
|
||||
}
|
||||
|
||||
i32 netRecvMessage(i32 socket, u8* message_buffer, void (*addSystemMessage)(u8* msg)) {
|
||||
i32 bytes_recieved;
|
||||
u16 msg_len;
|
||||
i32 first_recv_got = netRecvExact(socket, &msg_len, 2);
|
||||
if (first_recv_got <= 0) {
|
||||
return first_recv_got;
|
||||
}
|
||||
msg_len = ntohs(msg_len); // parse it to our correct byte order
|
||||
|
||||
bytes_recieved = netRecvExact(socket, message_buffer, msg_len);
|
||||
if (addSystemMessage != NULL) {
|
||||
char sbuf[128] = {0};
|
||||
sprintf(sbuf, "bytes_recieved=%d\n", bytes_recieved);
|
||||
addSystemMessage((u8*)sbuf);
|
||||
}
|
||||
return bytes_recieved;
|
||||
}
|
||||
|
||||
// ONLY WORKS ON POSIX. taken from https://gist.github.com/miekg/a61d55a8ec6560ad6c4a2747b21e6128
|
||||
|
||||
// the only real difference between a udp "server" and a "client" is the bind() syscall
|
||||
// that the server makes in order to specify a port/address that it's listening on
|
||||
UDPServer createUDPServer(u16 server_port) {
|
||||
UDPServer netCreateUDPServer(u16 server_port) {
|
||||
UDPServer result = {0};
|
||||
// define the address we'll be listening on
|
||||
result.server_address.sin_family = AF_INET;
|
||||
@@ -130,7 +191,7 @@ UDPServer createUDPServer(u16 server_port) {
|
||||
return result;
|
||||
}
|
||||
|
||||
UDPClient createUDPClient(u16 server_port, str addr) {
|
||||
UDPClient netCreateUDPClient(u16 server_port, str addr) {
|
||||
UDPClient result = {0};
|
||||
// define the address we'll be listening on
|
||||
result.server_address.sin_family = AF_INET;
|
||||
@@ -159,40 +220,424 @@ UDPClient createUDPClient(u16 server_port, str addr) {
|
||||
return result;
|
||||
}
|
||||
|
||||
void infiniteReadUDPServer(UDPServer* server, void (*handleMessage)(u8* udp_message, u32 udp_len, SocketAddress sending_address, i32 socket)) {
|
||||
void netInfiniteReadUDPClient(UDPClient* client, HandleMessageCb handleMessage) {
|
||||
u8 message_buffer[UDP_MAX_MESSAGE_LEN] = {0};
|
||||
i32 bytes_recieved = 0;
|
||||
SocketAddress client_address = {0};
|
||||
i32 addrlen = sizeof(struct sockaddr);
|
||||
while (true) {
|
||||
bytes_recieved = recvfrom(client->socket, message_buffer, UDP_MAX_MESSAGE_LEN, 0, (struct sockaddr *)&client_address, (socklen_t*)&addrlen);
|
||||
handleMessage(message_buffer, bytes_recieved, client_address, client->socket);
|
||||
MemoryZero(message_buffer, UDP_MAX_MESSAGE_LEN);
|
||||
}
|
||||
}
|
||||
|
||||
void netInfiniteReadUDPServer(UDPServer* server, HandleMessageCb handleMessage) {
|
||||
u8 message_buffer[UDP_MAX_MESSAGE_LEN] = {0};
|
||||
i32 bytes_recieved = 0;
|
||||
SocketAddress client_address = {0};
|
||||
i32 addrlen = sizeof(struct sockaddr);
|
||||
while (true) {
|
||||
bytes_recieved = recvfrom(server->server_socket, message_buffer, UDP_MAX_MESSAGE_LEN, 0, (struct sockaddr *)&client_address, (socklen_t*)&addrlen);
|
||||
|
||||
//gethostbyaddr: determine who sent the datagram
|
||||
//struct hostent* hostp = gethostbyaddr(
|
||||
// (const char *)&client_address.sin_addr.s_addr,
|
||||
// sizeof(client_address.sin_addr.s_addr),
|
||||
// AF_INET
|
||||
//);
|
||||
|
||||
//ptr printable_host_IP_address_string = inet_ntoa(client_address.sin_addr);
|
||||
|
||||
handleMessage(message_buffer, bytes_recieved, client_address, server->server_socket);
|
||||
MemoryZero(message_buffer, UDP_MAX_MESSAGE_LEN);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: sendall() to handle cases when the sendto() bytes return value is less than the intended bytes to send... stupid kernel fuckin wit us.
|
||||
TCPClient netCreateTCPClient(u16 server_port, str addr) {
|
||||
TCPClient result = {0};
|
||||
// define the address we'll be listening on
|
||||
result.server_address.sin_family = AF_INET;
|
||||
if (addr == 0) {
|
||||
result.server_address.sin_addr.s_addr = inet_addr("127.0.0.1");
|
||||
} else {
|
||||
result.server_address.sin_addr.s_addr = inet_addr(addr);
|
||||
}
|
||||
result.server_address.sin_port = htons(server_port);
|
||||
|
||||
// get a FileDescriptor number from the OS to use for our socket
|
||||
result.socket = socket(PF_INET, SOCK_STREAM, 0);
|
||||
if (result.socket < 0) {
|
||||
return result;
|
||||
}
|
||||
socklen_t addr_len = sizeof(struct sockaddr_in);
|
||||
i32 connect_result = connect(result.socket, (struct sockaddr *)&result.server_address, addr_len);
|
||||
result.ready = connect_result != -1;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool netReconnectTCPClient(TCPClient* client) {
|
||||
socklen_t addr_len = sizeof(struct sockaddr_in);
|
||||
client->socket = socket(PF_INET, SOCK_STREAM, 0);
|
||||
if (client->socket < 0) {
|
||||
return false;
|
||||
}
|
||||
i32 connect_result = connect(client->socket, (struct sockaddr *)&client->server_address, addr_len);
|
||||
client->ready = connect_result != -1;
|
||||
return client->ready;
|
||||
}
|
||||
|
||||
TCPServer netCreateTCPServer(u16 server_port) {
|
||||
TCPServer result = {0};
|
||||
// define the address we'll be listening on
|
||||
result.address.sin_family = AF_INET;
|
||||
result.address.sin_addr.s_addr = inet_addr("0.0.0.0");//htonl(INADDR_ANY);
|
||||
result.address.sin_port = htons(server_port);
|
||||
|
||||
// get a FileDescriptor number from the OS to use for our TCP socket
|
||||
result.socket_fd = socket(PF_INET, SOCK_STREAM, 0);
|
||||
if (result.socket_fd < 0) {
|
||||
return result;
|
||||
}
|
||||
// to let us immediately kill and restart server
|
||||
i32 optval = 1;
|
||||
setsockopt(result.socket_fd, SOL_SOCKET, SO_REUSEADDR, (const void *)&optval, sizeof(i32));
|
||||
|
||||
// bind() the TCP
|
||||
result.ready = bind(result.socket_fd, (struct sockaddr *)&result.address, sizeof(result.address)) >= 0;
|
||||
if (result.ready) {
|
||||
result.ready = result.ready && (listen(result.socket_fd, 10) >= 0);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void netInfiniteReadTCPServer(
|
||||
TCPServer* server,
|
||||
bool* should_quit,
|
||||
HandleMessageCb handleMessage,
|
||||
void (*closeConnection)(i32 socket_fd),
|
||||
void (*addSystemMessage)(u8* msg)
|
||||
) {
|
||||
u8 message_buffer[UDP_MAX_MESSAGE_LEN] = {0};
|
||||
i32 bytes_recieved = 0;
|
||||
SocketAddress client_address = {0};
|
||||
i32 addrlen = sizeof(struct sockaddr);
|
||||
i32 new_fd;
|
||||
struct pollfd pollable_fds[NET_SERVER_MAX_CLIENTS+1] = {0}; // +1 for the listener socket
|
||||
// poll the `listen()`ed socket_fd
|
||||
pollable_fds[0].fd = server->socket_fd;
|
||||
pollable_fds[0].events = POLLIN;
|
||||
u32 pollable_fd_count = 1;
|
||||
|
||||
i32 poll_event_count;
|
||||
while (*should_quit == false) {
|
||||
poll_event_count = poll(pollable_fds, pollable_fd_count, 3000); // times out after 3 seconds so that quitting the server will actually quit the server process in relatively short order.
|
||||
if (poll_event_count == -1) {
|
||||
// TODO handle error better
|
||||
*should_quit = true;
|
||||
continue;
|
||||
}
|
||||
for(u32 i = 0; i < pollable_fd_count; i++) {
|
||||
bool is_fd_readable = pollable_fds[i].revents & (POLLIN | POLLHUP);
|
||||
if (is_fd_readable) {
|
||||
bool is_fd_main_server_listener = pollable_fds[i].fd == server->socket_fd;
|
||||
if (is_fd_main_server_listener) { // it's a new connection
|
||||
new_fd = accept(server->socket_fd, (struct sockaddr *)&client_address, (socklen_t*)&addrlen);
|
||||
if (new_fd == -1) {
|
||||
if (addSystemMessage != NULL) {
|
||||
addSystemMessage((u8*)"TODO: handle this accept() error for real, bitch");
|
||||
}
|
||||
} else {
|
||||
if (pollable_fd_count < NET_SERVER_MAX_CLIENTS+1) {
|
||||
pollable_fds[pollable_fd_count].fd = new_fd;
|
||||
pollable_fds[pollable_fd_count].events = POLLIN | POLLHUP;
|
||||
pollable_fds[pollable_fd_count].revents = 0;
|
||||
pollable_fd_count++;
|
||||
if (addSystemMessage != NULL) {
|
||||
char sbuf[128] = {0};
|
||||
sprintf(sbuf, "new connection on socket=%d, total=%d\n", new_fd, pollable_fd_count);
|
||||
addSystemMessage((u8*)sbuf);
|
||||
}
|
||||
} else {
|
||||
send(new_fd, "server full", 11, 0); // go away sir, we are out of space to keep track of this socket
|
||||
close(new_fd);
|
||||
}
|
||||
}
|
||||
} else {// Otherwise we're just a regular client
|
||||
bytes_recieved = netRecvMessage(pollable_fds[i].fd, message_buffer, addSystemMessage);
|
||||
if (bytes_recieved <= 0) { // error condition
|
||||
bool client_hung_up = bytes_recieved == 0;
|
||||
// TODO do something with the error case
|
||||
closeConnection(pollable_fds[i].fd);
|
||||
close(pollable_fds[i].fd);
|
||||
if (addSystemMessage != NULL) {
|
||||
char sbuf[256] = {0};
|
||||
sprintf(sbuf, "closed connection on socket=%d, client_hung_up? %s\n", pollable_fds[i].fd, client_hung_up ? "yes" : "no");
|
||||
addSystemMessage((u8*)sbuf);
|
||||
}
|
||||
// copy the last one over the current one and "forget" the last one by decrementing the count
|
||||
pollable_fds[i] = pollable_fds[--pollable_fd_count];
|
||||
} else { // we got an actual message from this guy
|
||||
handleMessage(message_buffer, bytes_recieved, client_address, pollable_fds[i].fd);
|
||||
}
|
||||
MemoryZero(message_buffer, UDP_MAX_MESSAGE_LEN);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void netInfiniteReadTCPClient(
|
||||
TCPClient *client,
|
||||
bool* should_quit,
|
||||
HandleMessageCb handleMessage,
|
||||
void (*addSystemMessage)(u8* msg)
|
||||
) {
|
||||
u8 message_buffer[UDP_MAX_MESSAGE_LEN] = {0};
|
||||
i32 bytes_recieved = 0;
|
||||
bool got_connection_error = false;
|
||||
while ((*should_quit) == false && got_connection_error == false) {
|
||||
bytes_recieved = netRecvMessage(client->socket, message_buffer, addSystemMessage);
|
||||
if (bytes_recieved == -1) {
|
||||
if (addSystemMessage != NULL) {
|
||||
char sbuf[128] = {0};
|
||||
sprintf(sbuf, "TODO handle this error, bytes_recieved=%d\n", bytes_recieved);
|
||||
addSystemMessage((u8*)sbuf);
|
||||
}
|
||||
got_connection_error = true;
|
||||
client->ready = false;
|
||||
close(client->socket);
|
||||
continue;
|
||||
}
|
||||
|
||||
handleMessage(message_buffer, bytes_recieved, client->server_address, client->socket);
|
||||
MemoryZero(message_buffer, UDP_MAX_MESSAGE_LEN);
|
||||
}
|
||||
}
|
||||
|
||||
MultiServer netCreateMultiServer(u16 server_port) {
|
||||
MultiServer result = {0};
|
||||
result.tcp_server = netCreateTCPServer(server_port);
|
||||
result.udp_server = netCreateUDPServer(server_port);
|
||||
return result;
|
||||
}
|
||||
|
||||
MultiClient netCreateMultiClient(u16 server_port, str addr) {
|
||||
MultiClient result = {0};
|
||||
result.tcp_client = netCreateTCPClient(server_port, addr);
|
||||
result.udp_client = netCreateUDPClient(server_port, addr);
|
||||
return result;
|
||||
}
|
||||
|
||||
// creates a new thread for listening for UDP and infinite loop on this thread for the TCP server
|
||||
void netInfiniteReadMultiServer(
|
||||
MultiServer* server,
|
||||
bool* should_quit,
|
||||
HandleMessageCb handleMessage,
|
||||
void (*closeConnection)(i32 socket_fd),
|
||||
void (*addSystemMessage)(u8* msg)
|
||||
) {
|
||||
u8 message_buffer[UDP_MAX_MESSAGE_LEN] = {0};
|
||||
i32 bytes_recieved = 0;
|
||||
SocketAddress client_address = {0};
|
||||
i32 addrlen = sizeof(struct sockaddr);
|
||||
i32 new_fd;
|
||||
struct pollfd pollable_fds[NET_SERVER_MAX_CLIENTS+1] = {0}; // +1 for the listener socket
|
||||
// poll the `listen()`ed socket_fd
|
||||
pollable_fds[0].fd = server->tcp_server.socket_fd;
|
||||
pollable_fds[0].events = POLLIN;
|
||||
// poll the udp socket_fd also
|
||||
pollable_fds[1].fd = server->udp_server.server_socket;
|
||||
pollable_fds[1].events = POLLIN;
|
||||
u32 pollable_fd_count = 2;
|
||||
|
||||
i32 poll_event_count;
|
||||
while (*should_quit == false) {
|
||||
poll_event_count = poll(pollable_fds, pollable_fd_count, 3000); // times out after 3 seconds so that quitting the server will actually quit the server process in relatively short order.
|
||||
if (poll_event_count == -1) {
|
||||
// TODO handle error better
|
||||
*should_quit = true;
|
||||
continue;
|
||||
}
|
||||
for(u32 i = 0; i < pollable_fd_count; i++) {
|
||||
bool is_fd_readable = pollable_fds[i].revents & (POLLIN | POLLHUP);
|
||||
if (is_fd_readable) {
|
||||
printf("readable fd=%d\n", pollable_fds[i].fd);
|
||||
bool is_fd_tcp_server_listener = pollable_fds[i].fd == server->tcp_server.socket_fd;
|
||||
bool is_fd_udp_server_listener = pollable_fds[i].fd == server->udp_server.server_socket;
|
||||
if (is_fd_tcp_server_listener) { // it's a new connection
|
||||
printf("reading a tcp connection\n");
|
||||
new_fd = accept(server->tcp_server.socket_fd, (struct sockaddr *)&client_address, (socklen_t*)&addrlen);
|
||||
// TODO do something with client_address
|
||||
if (new_fd == -1) {
|
||||
if (addSystemMessage != NULL) {
|
||||
addSystemMessage((u8*)"TODO: handle this accept() error for real, bitch");
|
||||
}
|
||||
} else {
|
||||
if (pollable_fd_count < NET_SERVER_MAX_CLIENTS+1) {
|
||||
pollable_fds[pollable_fd_count].fd = new_fd;
|
||||
pollable_fds[pollable_fd_count].events = POLLIN | POLLHUP;
|
||||
pollable_fds[pollable_fd_count].revents = 0;
|
||||
pollable_fd_count++;
|
||||
if (addSystemMessage != NULL) {
|
||||
char sbuf[128] = {0};
|
||||
sprintf(sbuf, "new connection on socket=%d, total=%d\n", new_fd, pollable_fd_count);
|
||||
addSystemMessage((u8*)sbuf);
|
||||
}
|
||||
} else {
|
||||
send(new_fd, "server full", 11, 0); // go away sir, we are out of space to keep track of this socket
|
||||
close(new_fd);
|
||||
}
|
||||
}
|
||||
} else if (is_fd_udp_server_listener) {
|
||||
printf("reading a udp message\n");
|
||||
bytes_recieved = recvfrom(
|
||||
server->udp_server.server_socket,
|
||||
message_buffer,
|
||||
UDP_MAX_MESSAGE_LEN,
|
||||
0,
|
||||
(struct sockaddr *)&client_address,
|
||||
(socklen_t*)&addrlen
|
||||
);
|
||||
handleMessage(message_buffer, bytes_recieved, client_address, server->udp_server.server_socket);
|
||||
MemoryZero(message_buffer, UDP_MAX_MESSAGE_LEN);
|
||||
} else {// Otherwise we're just a regular client
|
||||
printf("reading a tcp message\n");
|
||||
bytes_recieved = netRecvMessage(pollable_fds[i].fd, message_buffer, addSystemMessage);
|
||||
if (bytes_recieved <= 0) { // error condition
|
||||
bool client_hung_up = bytes_recieved == 0;
|
||||
// TODO do something with the error case
|
||||
closeConnection(pollable_fds[i].fd);
|
||||
close(pollable_fds[i].fd);
|
||||
if (addSystemMessage != NULL) {
|
||||
char sbuf[256] = {0};
|
||||
sprintf(sbuf, "closed connection on socket=%d, client_hung_up? %s\n", pollable_fds[i].fd, client_hung_up ? "yes" : "no");
|
||||
addSystemMessage((u8*)sbuf);
|
||||
}
|
||||
// copy the last one over the current one and "forget" the last one by decrementing the count
|
||||
pollable_fds[i] = pollable_fds[--pollable_fd_count];
|
||||
} else { // we got an actual message from this guy
|
||||
handleMessage(message_buffer, bytes_recieved, client_address, pollable_fds[i].fd);
|
||||
}
|
||||
MemoryZero(message_buffer, UDP_MAX_MESSAGE_LEN);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// will return only when either should_quit is true or we got a connection error on the tcp client
|
||||
void netInfiniteReadMultiClient(
|
||||
MultiClient* client,
|
||||
bool* should_quit,
|
||||
HandleMessageCb handleMessage,
|
||||
void (*addSystemMessage)(u8* msg)
|
||||
) {
|
||||
fd_set master; // master file descriptor list
|
||||
fd_set read_fds; // temp file descriptor list for select()
|
||||
i32 fdmax; // maximum file descriptor number
|
||||
FD_ZERO(&master); // clear the master and temp sets
|
||||
FD_ZERO(&read_fds);
|
||||
// add both client sockets to the master set
|
||||
FD_SET(client->udp_client.socket, &master);
|
||||
FD_SET(client->tcp_client.socket, &master);
|
||||
// keep track of the biggest file descriptor
|
||||
fdmax = Max(client->udp_client.socket, client->tcp_client.socket); // so far, it's this one
|
||||
|
||||
u8 message_buffer[UDP_MAX_MESSAGE_LEN] = {0};
|
||||
i32 bytes_recieved = 0;
|
||||
SocketAddress client_address = {0};
|
||||
i32 addrlen = sizeof(struct sockaddr);
|
||||
bool got_connection_error = false;
|
||||
while (*should_quit == false && got_connection_error == false) {
|
||||
read_fds = master; // copy it
|
||||
if (select(fdmax+1, &read_fds, NULL, NULL, NULL) == -1) {
|
||||
perror("select");
|
||||
got_connection_error = true;
|
||||
client->tcp_client.ready = false;
|
||||
close(client->tcp_client.socket);
|
||||
return;
|
||||
}
|
||||
|
||||
for(i32 i = 0; i <= fdmax; i++) {
|
||||
if (FD_ISSET(i, &read_fds)) {
|
||||
bool is_tcp_socket = i == client->tcp_client.socket;
|
||||
if (is_tcp_socket) {
|
||||
bytes_recieved = netRecvMessage(client->tcp_client.socket, message_buffer, addSystemMessage);
|
||||
if (bytes_recieved == -1) {
|
||||
if (addSystemMessage != NULL) {
|
||||
char sbuf[128] = {0};
|
||||
sprintf(sbuf, "TODO handle this error, bytes_recieved=%d\n", bytes_recieved);
|
||||
addSystemMessage((u8*)sbuf);
|
||||
}
|
||||
got_connection_error = true;
|
||||
client->tcp_client.ready = false;
|
||||
close(client->tcp_client.socket);
|
||||
continue;
|
||||
} else if (bytes_recieved == 0) { // the server hung up
|
||||
if (addSystemMessage != NULL) {
|
||||
char sbuf[128] = {0};
|
||||
sprintf(sbuf, "the server seems to have hung up on us, bytes_recieved=%d\n", bytes_recieved);
|
||||
addSystemMessage((u8*)sbuf);
|
||||
}
|
||||
got_connection_error = true;
|
||||
client->tcp_client.ready = false;
|
||||
close(client->tcp_client.socket);
|
||||
continue;
|
||||
}
|
||||
handleMessage(message_buffer, bytes_recieved, client->tcp_client.server_address, client->tcp_client.socket);
|
||||
MemoryZero(message_buffer, UDP_MAX_MESSAGE_LEN);
|
||||
} else { // UDP
|
||||
bytes_recieved = recvfrom(client->udp_client.socket, message_buffer, UDP_MAX_MESSAGE_LEN, 0, (struct sockaddr *)&client_address, (socklen_t*)&addrlen);
|
||||
handleMessage(message_buffer, bytes_recieved, client_address, client->udp_client.socket);
|
||||
MemoryZero(message_buffer, UDP_MAX_MESSAGE_LEN);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
i32 sendallto(i32 socket, void* buf, i32 len, SocketAddress* to) {
|
||||
u32 total_sent = 0;
|
||||
i32 left_to_send = len;
|
||||
i32 sent_this_round;
|
||||
while(total_sent < len) {
|
||||
sent_this_round = sendto(
|
||||
socket,
|
||||
buf+total_sent,
|
||||
left_to_send,
|
||||
0,
|
||||
(const struct sockaddr *)to,
|
||||
sizeof(struct sockaddr)
|
||||
);
|
||||
if (sent_this_round == -1) { return -1; }
|
||||
total_sent += sent_this_round;
|
||||
left_to_send -= sent_this_round;
|
||||
}
|
||||
return total_sent;
|
||||
}
|
||||
|
||||
i32 sendUDPu8List(i32 using_socket, SocketAddress* to, u8List* message) {
|
||||
return sendto(
|
||||
using_socket,
|
||||
message->items,
|
||||
message->length,
|
||||
0,
|
||||
(const struct sockaddr *)to,
|
||||
sizeof(struct sockaddr)
|
||||
);
|
||||
return sendallto(using_socket, message->items, message->length, to);
|
||||
}
|
||||
|
||||
i32 sendUDPMessage(UDPServer* to, u8* message, u32 len) {
|
||||
return sendto(to->server_socket, message, len, 0, (struct sockaddr *)&to->server_address, sizeof(struct sockaddr));
|
||||
return sendallto(to->server_socket, message, len, &to->server_address);
|
||||
}
|
||||
|
||||
i32 sendall(i32 socket, void* buf, i32 len) {
|
||||
u32 total_sent = 0;
|
||||
i32 left_to_send = len;
|
||||
i32 sent_this_round;
|
||||
while(total_sent < len) {
|
||||
sent_this_round = send(socket, buf+total_sent, left_to_send, 0);
|
||||
if (sent_this_round == -1) { return -1; }
|
||||
total_sent += sent_this_round;
|
||||
left_to_send -= sent_this_round;
|
||||
}
|
||||
return total_sent;
|
||||
}
|
||||
|
||||
i32 sendTCPMessage(NetworkMessage msg) {
|
||||
assert(msg.tcp);
|
||||
assert(msg.socket_fd >= 0);
|
||||
u16 msg_len = htons(msg.bytes_len);
|
||||
i32 result = sendall(msg.socket_fd, (void*)&msg_len, 2);
|
||||
if (result == -1) {
|
||||
return result;
|
||||
}
|
||||
return sendall(msg.socket_fd, msg.bytes, msg.bytes_len);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,109 +0,0 @@
|
||||
#include "thread.h"
|
||||
|
||||
typedef struct ThreadQueue {
|
||||
void* items;
|
||||
u32 type_size;
|
||||
u32 max;
|
||||
u32 head;
|
||||
u32 tail;
|
||||
u32 count;
|
||||
Mutex mutex;
|
||||
Cond not_empty;
|
||||
Cond not_full;
|
||||
} ThreadQueue;
|
||||
|
||||
Thread spawnThread(void * (*threadFn)(void *), void* thread_arg) {
|
||||
pthread_t thread;
|
||||
pthread_create(&thread, NULL, threadFn, thread_arg);
|
||||
Thread result = { thread };
|
||||
return result;
|
||||
}
|
||||
|
||||
Mutex newMutex() {
|
||||
Mutex result = { PTHREAD_MUTEX_INITIALIZER };
|
||||
return result;
|
||||
}
|
||||
|
||||
Cond newCond() {
|
||||
Cond result = { 0 };
|
||||
pthread_cond_init(&result.cond, NULL);
|
||||
return result;
|
||||
}
|
||||
|
||||
void lockMutex(Mutex* m) {
|
||||
pthread_mutex_lock(&m->mutex);
|
||||
}
|
||||
|
||||
void unlockMutex(Mutex* m) {
|
||||
pthread_mutex_unlock(&m->mutex);
|
||||
}
|
||||
|
||||
void signalCond(Cond* cond) {
|
||||
pthread_cond_signal(&cond->cond);
|
||||
}
|
||||
|
||||
void waitForCondSignal(Cond* cond, Mutex* mutex) {
|
||||
pthread_cond_wait(&cond->cond, &mutex->mutex);
|
||||
}
|
||||
|
||||
fn ThreadQueue newThreadQueue(Arena* a, u32 type_size, u32 items_max) {
|
||||
ThreadQueue result = {0};
|
||||
result.type_size = type_size;
|
||||
result.max = items_max;
|
||||
result.items = arenaAllocArraySized(a, type_size, items_max);
|
||||
result.mutex = newMutex();
|
||||
result.not_full = newCond();
|
||||
result.not_empty = newCond();
|
||||
return result;
|
||||
}
|
||||
|
||||
fn void threadSafeQueuePush(ThreadQueue* queue, void* item) {
|
||||
lockMutex(&queue->mutex); {
|
||||
while (queue->count == queue->max) {
|
||||
waitForCondSignal(&queue->not_full, &queue->mutex);
|
||||
}
|
||||
|
||||
memcpy(queue->items + (queue->tail * (sizeof item)), item, queue->type_size);
|
||||
queue->tail = (queue->tail + 1) % queue->max;
|
||||
queue->count++;
|
||||
|
||||
signalCond(&queue->not_empty);
|
||||
} unlockMutex(&queue->mutex);
|
||||
}
|
||||
|
||||
fn void* threadSafeQueuePop(ThreadQueue* q) {
|
||||
void* result = NULL;
|
||||
|
||||
lockMutex(&q->mutex); {
|
||||
while (q->count == 0) {
|
||||
waitForCondSignal(&q->not_empty, &q->mutex);
|
||||
}
|
||||
|
||||
result = &q->items[q->head];
|
||||
q->head = (q->head + 1) % q->max;
|
||||
q->count--;
|
||||
|
||||
signalCond(&q->not_full);
|
||||
} unlockMutex(&q->mutex);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// immediately returns NULL if there's nothing in the ThreadQueue
|
||||
fn void* threadSafeNonblockingQueuePop(ThreadQueue* q, void* copy_target, u64 len) {
|
||||
void* result = NULL;
|
||||
|
||||
lockMutex(&q->mutex); {
|
||||
if (q->count > 0) {
|
||||
result = &q->items[q->head];
|
||||
MemoryCopy(copy_target, result, len);
|
||||
q->head = (q->head + 1) % q->max;
|
||||
q->count--;
|
||||
|
||||
signalCond(&q->not_full);
|
||||
}
|
||||
} unlockMutex(&q->mutex);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
#ifndef LIB_THREAD_H
|
||||
#define LIB_THREAD_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <pthread.h>
|
||||
#include "../base/include.h"
|
||||
|
||||
typedef struct Thread {
|
||||
pthread_t thread;
|
||||
} Thread;
|
||||
|
||||
typedef struct Mutex {
|
||||
pthread_mutex_t mutex;
|
||||
} Mutex;
|
||||
|
||||
typedef struct Cond {
|
||||
pthread_cond_t cond;
|
||||
} Cond;
|
||||
|
||||
Thread spawnThread(void * (*threadFn)(void *), void* thread_arg);
|
||||
Mutex newMutex();
|
||||
Cond newCond();
|
||||
void lockMutex(Mutex* m);
|
||||
void unlockMutex(Mutex* m);
|
||||
void signalCond(Cond* cond);
|
||||
void waitForCondSignal(Cond* cond, Mutex* mutex);
|
||||
|
||||
#endif //LIB_THREAD_H
|
||||
+112
@@ -25,6 +25,14 @@
|
||||
#define ANSI_HIGHLIGHT_GRAY (16)
|
||||
#define MAX_COMMAND_PALETTE_COMMANDS (1000)
|
||||
|
||||
#ifndef SYSTEM_MESSAGES_LEN
|
||||
# define SYSTEM_MESSAGES_LEN (32)
|
||||
#endif
|
||||
#ifndef MAX_SYSTEM_MESSAGE_LEN
|
||||
# define MAX_SYSTEM_MESSAGE_LEN (512)
|
||||
#endif
|
||||
|
||||
|
||||
///// TYPES
|
||||
typedef struct Pixel {
|
||||
u8 foreground;
|
||||
@@ -73,7 +81,53 @@ typedef struct StringSearchScore {
|
||||
u32 description_match_len;
|
||||
} StringSearchScore;
|
||||
|
||||
///// GLOBALS
|
||||
global u8List system_messages[SYSTEM_MESSAGES_LEN] = {0};
|
||||
global u8 system_message_index = 0;
|
||||
|
||||
///// Functions()
|
||||
fn void initSystemMessages(Arena* a) {
|
||||
for (i32 i = 0; i < SYSTEM_MESSAGES_LEN; i++) {
|
||||
system_messages[i].capacity = MAX_SYSTEM_MESSAGE_LEN;
|
||||
system_messages[i].length = 0;
|
||||
system_messages[i].items = arenaAllocArraySized(a, sizeof(u8), MAX_SYSTEM_MESSAGE_LEN);
|
||||
}
|
||||
}
|
||||
|
||||
fn void addSystemMessage(u8* msg) {
|
||||
// save the message to our system_messages ring buffer
|
||||
memset(system_messages[system_message_index].items, 0, SYSTEM_MESSAGES_LEN);
|
||||
sprintf((char*)system_messages[system_message_index].items, "%s", msg);
|
||||
system_messages[system_message_index].length = strlen((char*)system_messages[system_message_index].items);
|
||||
system_message_index += 1;
|
||||
if (system_message_index == SYSTEM_MESSAGES_LEN) {
|
||||
system_message_index = 0;
|
||||
}
|
||||
}
|
||||
|
||||
fn void renderSystemMessages(Pixel* buf, Dim2 screen_dimensions, Box sys_msg_box) {
|
||||
i32 printable_lines = sys_msg_box.height - 2;
|
||||
if (printable_lines > SYSTEM_MESSAGES_LEN) {
|
||||
printable_lines = SYSTEM_MESSAGES_LEN;
|
||||
}
|
||||
for (i32 i = 0; i < printable_lines; i++) {
|
||||
i32 index = (system_message_index - 1 - i);
|
||||
if (index < 0) {
|
||||
index = SYSTEM_MESSAGES_LEN + index;
|
||||
}
|
||||
u32 y = sys_msg_box.y + (sys_msg_box.height - i) - 1;
|
||||
u8List sys_msg = system_messages[index];
|
||||
for (i32 j = 0; j < MAX_SYSTEM_MESSAGE_LEN && j < sys_msg_box.width-4; j++) {
|
||||
u32 pos = (sys_msg_box.x + 2+j) + (screen_dimensions.width * y);
|
||||
if (j < sys_msg.length) {
|
||||
if (sys_msg.items[j] != '\n') {
|
||||
buf[pos].bytes[0] = sys_msg.items[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn u32 rgbToNum(RGB rgb) {
|
||||
return ((rgb.r<<16) | (rgb.g<<8) | rgb.b);
|
||||
}
|
||||
@@ -216,6 +270,64 @@ fn void renderStringChunkList(TuiState* tui, StringChunkList* list, u16 x, u16 y
|
||||
}
|
||||
}
|
||||
|
||||
fn void renderPercentBar(TuiState* tui, u16 x, u16 y, u16 width, u8 ansi_color, u64 value, u64 max) {
|
||||
Pixel* buf = tui->frame_buffer;
|
||||
Dim2 screen_dimensions = tui->screen_dimensions;
|
||||
u16 pos = x + (screen_dimensions.width * y);
|
||||
buf[pos].bytes[0] = '[';
|
||||
pos = x+width + (screen_dimensions.width * y);
|
||||
buf[pos].bytes[0] = ']';
|
||||
pos = x+1 + (screen_dimensions.width * y);
|
||||
f32 base_ratio = 0;
|
||||
if (max != 0) {
|
||||
base_ratio = ((f32)value / (f32)max);
|
||||
}
|
||||
f32 raw_ratio = base_ratio * (width-2);
|
||||
u32 full_spaces_count = (u32) raw_ratio;
|
||||
f32 remainder = raw_ratio - full_spaces_count;
|
||||
for (u32 i = 0; i < full_spaces_count; i++) {
|
||||
buf[pos+i].foreground = ansi_color;
|
||||
renderUtf8CharToBuffer(buf, x+1+i, y, "█", screen_dimensions);
|
||||
}
|
||||
buf[pos+full_spaces_count].foreground = ansi_color;
|
||||
if (value == max) {
|
||||
renderUtf8CharToBuffer(buf, x+1+full_spaces_count, y, "█", screen_dimensions);
|
||||
} else if (remainder > 0.875) {
|
||||
renderUtf8CharToBuffer(buf, x+1+full_spaces_count, y, "▉", screen_dimensions);
|
||||
} else if (remainder > 0.75) {
|
||||
renderUtf8CharToBuffer(buf, x+1+full_spaces_count, y, "▊", screen_dimensions);
|
||||
} else if (remainder > 0.625) {
|
||||
renderUtf8CharToBuffer(buf, x+1+full_spaces_count, y, "▋", screen_dimensions);
|
||||
} else if (remainder > 0.5) {
|
||||
renderUtf8CharToBuffer(buf, x+1+full_spaces_count, y, "▌", screen_dimensions);
|
||||
} else if (remainder > 0.375) {
|
||||
renderUtf8CharToBuffer(buf, x+1+full_spaces_count, y, "▍", screen_dimensions);
|
||||
} else if (remainder > 0.25) {
|
||||
renderUtf8CharToBuffer(buf, x+1+full_spaces_count, y, "▎", screen_dimensions);
|
||||
} else if (remainder > 0.125) {
|
||||
renderUtf8CharToBuffer(buf, x+1+full_spaces_count, y, "▏", screen_dimensions);
|
||||
} else {
|
||||
renderUtf8CharToBuffer(buf, x+1+full_spaces_count, y, " ", screen_dimensions);
|
||||
}
|
||||
}
|
||||
|
||||
fn void renderStaticAssetToPixelBuffer(TuiState* tui, u8* asset, u32 len, u16 x, u16 y) {
|
||||
u16 line = 0;
|
||||
u16 x_in_line = 0;
|
||||
u16 pos = x + (tui->screen_dimensions.width * y);
|
||||
for (u32 i = 0; i < len; i++, x_in_line++) {
|
||||
if (asset[i] == '\n') {
|
||||
line += 1;
|
||||
x_in_line = 0;
|
||||
pos = x + (tui->screen_dimensions.width * (y+line));
|
||||
} else if (asset[i] == ' ') {
|
||||
// do nothing, we skip spaces in our assets
|
||||
} else {
|
||||
tui->frame_buffer[pos+x_in_line].bytes[0] = asset[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn u32 sprintfAnsiMoveCursorTo(ptr output, u16 x, u16 y) {
|
||||
return sprintf(output, "\x1b[%d;%df",y,x);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user