TCP networking

This commit is contained in:
Tenari
2026-04-02 07:37:58 -05:00
parent dfdc2e643c
commit 1f8e2f3181
9 changed files with 522 additions and 266 deletions
+4
View File
@@ -238,6 +238,10 @@
// only valid if there's an in-scope variable bool `debug_mode`
#define dbg(fmt, ...) osDebugPrint(debug_mode, fmt, ##__VA_ARGS__)
///// SYSTEM INCLUDES I always want
#include <stdio.h>
#include <unistd.h>
///// TYPES
// integer types
typedef unsigned char u8;
-2
View File
@@ -2,10 +2,8 @@
#define _POSIX_C_SOURCE 200809L
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
#include <stdarg.h>
#include <stdio.h>
#include "all.h"
global pthread_barrier_t linux_thread_barrier;
-2
View File
@@ -1,10 +1,8 @@
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
#include <stdarg.h>
#include <stdio.h>
#include "all.h"
#include "pthread_barrier.h"
-1
View File
@@ -1,6 +1,5 @@
#include "string.h"
#include <userenv.h>
#include <stdio.h>
static u64 w32_ticks_per_sec = 1;
static u32 w32_thread_context_index;
+70 -78
View File
@@ -13,9 +13,6 @@
#include "lib/network.c"
#include "lib/tui.c"
#include "string_chunk.c"
//#include "assets/asset1.h"
//#include "assets/asset2.h"
//#include "assets/asset3.h"
///// #define a bunch of client-only tunable game constants
#define SYSTEM_MESSAGES_LEN 32
@@ -167,8 +164,7 @@ typedef struct GameState {
MenuState menu;
MenuState row; // for tracking which row of a table the user has selected
MenuState modal_choice;
UDPMessage keep_alive_msg;
UDPClient client;
TCPClient client;
StringChunkList message_input;
StarSystem map[STAR_SYSTEM_COUNT];
Pos2 pos;
@@ -277,23 +273,6 @@ fn void renderSystemMessages(Pixel* buf, Dim2 screen_dimensions, Box sys_msg_box
}
}
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 void resetTabRow(Tab tab) {
if (state.menu.selected_index == TabMarket) {
state.market_tab_state = MarketTabStateTable;
@@ -339,10 +318,17 @@ fn void moveRowUpDown(bool user_pressed_up, bool user_pressed_down) {
}
}
fn void handleIncomingMessage(u8* message, u32 len, SocketAddress sender, i32 socket) {
fn void handleIncomingMessage(u8* message, i32 len) {
assert(len >= 0); // TODO actually handle this error
u64 msg_pos = 0;
Message msg_type = message[msg_pos++];
dbg("handleIncomingMessage() of len=%d, message=%s\n", len, MESSAGE_STRINGS[msg_type]);
char sbuf[SBUFLEN] = {0};
sprintf(sbuf, "handleIncomingMessage() of len=%d, message=%s\n", len, MESSAGE_STRINGS[msg_type]);
if (msg_type != MessageSystemPassengers && msg_type != MessageSystemCommodities) {
addSystemMessage((u8*)sbuf);
}
u8List bytes = {len, len, message};
ParsedServerMessage parsed = {0};
parsed.type = msg_type;
@@ -462,29 +448,24 @@ fn void handleIncomingMessage(u8* message, u32 len, SocketAddress sender, i32 so
psmThreadSafeQueuePush(network_recv_queue, &parsed);
}
fn void* receiveNetworkUpdates(void* udp) {
UDPClient client = *(UDPClient*)udp;
fn void* receiveNetworkUpdates(void* net_client) {
TCPClient client = *(TCPClient*)net_client;
dbg("receiveNetworkUpdates() sock=%d\n", client.socket);
UDPServer server = {
.ready = true,
.server_address = client.server_address,
.server_socket = client.socket
};
infiniteReadUDPServer(&server, handleIncomingMessage);
infiniteReadTCPClient(client.socket, &should_quit, handleIncomingMessage, addSystemMessage);
return NULL;
}
fn void* sendNetworkUpdates(void* udp) {
i32 socket_fd = ((UDPClient*)udp)->socket;
dbg("sendNetworkUpdates() sock=%d\n", socket_fd);
u8List bytes_list = {0};
fn void* sendNetworkUpdates(void* net_client) {
TCPClient* client = (TCPClient*)net_client;
dbg("sendNetworkUpdates() sock=%d\n", client->socket);
NetworkMessage msg = {
.use_socket = true,
.socket_fd = client->socket,
.address = client->server_address,
};
while (!should_quit) {
UDPMessage msg = {0};
outgoingMessageQueuePop(network_send_queue, &msg);
bytes_list.items = msg.bytes;
bytes_list.length = msg.bytes_len;
bytes_list.capacity = msg.bytes_len;
sendUDPu8List(socket_fd, &msg.address, &bytes_list);
sendTCPMessage(msg);
}
return NULL;
}
@@ -528,15 +509,16 @@ fn Box defaultModal(TuiState* tui) {
return modal_outline;
}
fn void sendLoginCommand(SocketAddress addr, u16 client_port) {
fn void sendLoginCommand(TCPClient* net_client) {
u32 msg_idx = 0;
// drop the login message into the network_send_queue
UDPMessage msg = { .address = addr };
NetworkMessage msg = { .use_socket = true, .socket_fd = net_client->socket, .address = net_client->server_address };
// 1. msg type/CommandType
msg.bytes[msg_idx++] = CommandLogin;
// 2. our LAN-IP to handle the case where we are on the same LAN as the guy we are trying to fight
// and our "listened" UDP port
msg_idx += writeU16ToBufferLE(msg.bytes + msg_idx, ~client_port);
//msg_idx += writeU16ToBufferLE(msg.bytes + msg_idx, ~client_port);
msg_idx += writeU16ToBufferLE(msg.bytes + msg_idx, 9999); // we aren't actually doing this right now
msg_idx += writeI32ToBufferLE(msg.bytes + msg_idx, ~osLanIPAddress());
// 2. how long is the name
msg.bytes[msg_idx++] = state.login_state.name.length;
@@ -551,7 +533,6 @@ fn void sendLoginCommand(SocketAddress addr, u16 client_port) {
fn bool updateAndRender(TuiState* tui, void* s, u8* input_buffer, u64 loop_count) {
GameState* state = (GameState*) s;
state->loop_count = loop_count;
UDPClient* udp = &state->client;
state->old_screen = state->screen;
Dim2 screen_dimensions = tui->screen_dimensions;
bool game_screen_changed = state->old_screen != state->screen; // detect new screens
@@ -594,7 +575,7 @@ fn bool updateAndRender(TuiState* tui, void* s, u8* input_buffer, u64 loop_count
// auto-re-login
if (state->me.base_cost > 0) {
addSystemMessage((u8*)"auto re logging in due to MessageNotAlive");
sendLoginCommand(udp->server_address, udp->client_port);
sendLoginCommand(&state->client);
state->screen = ScreenLogin;
state->login_state.state = LoginScreenStateLoading;
}
@@ -859,9 +840,12 @@ fn bool updateAndRender(TuiState* tui, void* s, u8* input_buffer, u64 loop_count
state->me.remaining_mortgage = template.base_cost - STARTING_DOWN_PAYMENT;
state->me.interest_rate = calcInterestRate(template.base_cost, STARTING_DOWN_PAYMENT);
// send character ship details to server
UDPMessage msg = {0};
msg.address = udp->server_address;
msg.bytes_len = 2;
NetworkMessage msg = {
.use_socket = true,
.address = state->client.server_address,
.socket_fd = state->client.socket,
.bytes_len = 2,
};
// 1. msg type/CommandType
msg.bytes[0] = CommandCreateCharacter;
// 2. chosen ShipType
@@ -1114,14 +1098,16 @@ fn bool updateAndRender(TuiState* tui, void* s, u8* input_buffer, u64 loop_count
state->destination_sys_idx = i;
// send the destination to the server
u32 msg_idx = 0;
UDPMessage msg = {0};
msg.address = udp->server_address;
NetworkMessage msg = {
.use_socket = true,
.address = state->client.server_address,
.socket_fd = state->client.socket,
};
// 1. msg type/CommandType
msg.bytes[msg_idx++] = CommandSetDestination;
// 2. buy?
msg.bytes[msg_idx++] = state->destination_sys_idx;
msg.bytes_len = msg_idx;
outgoingMessageQueuePush(network_send_queue, &msg);
}
}
@@ -1186,14 +1172,16 @@ fn bool updateAndRender(TuiState* tui, void* s, u8* input_buffer, u64 loop_count
if (user_pressed_enter && state->ship_tab_states == ShipTabStateMain) {
// send the "ready" or "not ready" message to the server
u32 msg_idx = 0;
UDPMessage msg = {0};
msg.address = udp->server_address;
NetworkMessage msg = {
.use_socket = true,
.address = state->client.server_address,
.socket_fd = state->client.socket,
};
// 1. msg type/CommandType
msg.bytes[msg_idx++] = CommandReadyStatus;
// 2. buy?
msg.bytes[msg_idx++] = !state->me.ready_to_depart;
msg.bytes_len = msg_idx;
outgoingMessageQueuePush(network_send_queue, &msg);
}
}
@@ -1276,14 +1264,16 @@ fn bool updateAndRender(TuiState* tui, void* s, u8* input_buffer, u64 loop_count
if (user_pressed_enter) {
// send the "payoff" message to server
u32 msg_idx = 0;
UDPMessage msg = {0};
msg.address = udp->server_address;
NetworkMessage msg = {
.use_socket = true,
.address = state->client.server_address,
.socket_fd = state->client.socket,
};
// 1. msg type/CommandType
msg.bytes[msg_idx++] = CommandPayMortgage;
// 2. buy?
msg_idx += writeU64ToBufferLE(msg.bytes + msg_idx, input_quantity);
msg.bytes_len = msg_idx;
outgoingMessageQueuePush(network_send_queue, &msg);
state->ship_tab_states = ShipTabStateLoading;
@@ -1344,8 +1334,11 @@ fn bool updateAndRender(TuiState* tui, void* s, u8* input_buffer, u64 loop_count
if (user_pressed_enter) {
// send the "buy this auction" message to the server
u32 msg_idx = 0;
UDPMessage msg = {0};
msg.address = udp->server_address;
NetworkMessage msg = {
.use_socket = true,
.address = state->client.server_address,
.socket_fd = state->client.socket,
};
msg.bytes[msg_idx++] = CommandBuyAuction;
msg.bytes_len = msg_idx;
outgoingMessageQueuePush(network_send_queue, &msg);
@@ -1550,8 +1543,11 @@ fn bool updateAndRender(TuiState* tui, void* s, u8* input_buffer, u64 loop_count
// send the "purchase" or "sell" message to server
// get back new "status" of things
u32 msg_idx = 0;
UDPMessage msg = {0};
msg.address = udp->server_address;
NetworkMessage msg = {
.use_socket = true,
.address = state->client.server_address,
.socket_fd = state->client.socket,
};
// 1. msg type/CommandType
msg.bytes[msg_idx++] = CommandTransact;
// 2. buy?
@@ -1561,7 +1557,6 @@ fn bool updateAndRender(TuiState* tui, void* s, u8* input_buffer, u64 loop_count
// 4. commodity
msg.bytes[msg_idx++] = selected_commodity.type;
msg.bytes_len = msg_idx;
outgoingMessageQueuePush(network_send_queue, &msg);
state->market_tab_state = MarketTabStateLoading;
@@ -1701,8 +1696,11 @@ fn bool updateAndRender(TuiState* tui, void* s, u8* input_buffer, u64 loop_count
// send the "job accept" message to server
// get back new "status" of things
u32 msg_idx = 0;
UDPMessage msg = {0};
msg.address = udp->server_address;
NetworkMessage msg = {
.use_socket = true,
.address = state->client.server_address,
.socket_fd = state->client.socket,
};
msg.bytes[msg_idx++] = CommandAcceptPassengerJob;
msg.bytes[msg_idx++] = current_row.goal_system_idx;
msg.bytes[msg_idx++] = current_row.people;
@@ -1787,7 +1785,7 @@ fn bool updateAndRender(TuiState* tui, void* s, u8* input_buffer, u64 loop_count
state->login_state.selected_field = 1;
state->login_state.field_index = 0;
} else {
sendLoginCommand(udp->server_address, udp->client_port);
sendLoginCommand(&state->client);
// show loading signal, stop accepting input
state->login_state.state = LoginScreenStateLoading;
addSystemMessage((u8*)state->login_state.name.bytes);
@@ -1867,11 +1865,6 @@ fn bool updateAndRender(TuiState* tui, void* s, u8* input_buffer, u64 loop_count
break;
}
if (loop_count % 10 == 0) {
outgoingMessageQueuePush(network_send_queue, &state->keep_alive_msg);
//outgoingMessageQueuePush(network_send_queue, &testm);
}
return should_quit;
}
@@ -1913,23 +1906,22 @@ i32 main(i32 argc, ptr argv[]) {
exit(1);
}
ptr server_address = NULL;
char input_string[16] = { 0 };
if (argc > 1) {
server_address = argv[1];
} else {
printf("Server IP Address: ");
char input_string[16] = { 0 };
scanf("%15s", input_string);
printf("%s", input_string);
if (input_string[0] && input_string[1] && input_string[2] && input_string[3]) {
server_address = input_string;
}
}
state.client = createUDPClient(7777, server_address);
// "hardcoded" keep alive message to periodically send to server
state.keep_alive_msg.address = state.client.server_address;
state.keep_alive_msg.bytes[0] = CommandKeepAlive;
state.keep_alive_msg.bytes_len = 1;
state.client = createTCPClient(SERVER_PORT, server_address);
if (!state.client.ready) {
printf("network could not connect() to server");
exit(1);
}
Thread recv_thread = spawnThread(&receiveNetworkUpdates, &state.client);
Thread send_thread = spawnThread(&sendNetworkUpdates, &state.client);
+295 -19
View File
@@ -1,4 +1,5 @@
#include "../base/all.h"
#include <poll.h>
// https://stackoverflow.com/questions/1098897/what-is-the-largest-safe-udp-packet-size-on-the-internet
#define UDP_MAX_MESSAGE_LEN 508
@@ -6,14 +7,42 @@
#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 MultiServer {
bool ready;
i32 udp_socket_fd;
i32 tcp_socket_fd;
SocketAddress address;
} MultiServer;
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 ServableUDPInfo {
UDPServer server;
void (*callback)(u8* udp_message, u32 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 +50,16 @@ typedef struct UDPClient {
i32 socket;
} UDPClient;
typedef struct UDPMessage {
typedef struct NetworkMessage {
bool use_socket; // default = false = UDP, just sendto(address)
u16 bytes_len;
SocketAddress address;
i32 socket_fd;
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;
@@ -46,7 +77,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 +91,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 +111,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,7 +130,6 @@ 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;
@@ -109,6 +139,15 @@ fn bool socketAddressEqual(SocketAddress a, SocketAddress b) {
// 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
i32 recv_exact(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;
}
UDPServer createUDPServer(u16 server_port) {
UDPServer result = {0};
// define the address we'll be listening on
@@ -166,21 +205,247 @@ void infiniteReadUDPServer(UDPServer* server, void (*handleMessage)(u8* udp_mess
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);
}
}
void* infinitelyServeUDPSocket(void* servable) {
ServableUDPInfo info = *(ServableUDPInfo*)servable;
infiniteReadUDPServer(&info.server, info.callback);
return NULL;
}
TCPClient createTCPClient(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;
}
TCPServer createTCPServer(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;
}
i32 recvMessage(i32 socket, u8* message_buffer, void (*addSystemMessage)(u8* msg)) {
i32 bytes_recieved;
u16 msg_len;
i32 first_recv_got = recv_exact(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 = recv_exact(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;
}
void infiniteReadTCPServer(
TCPServer* server,
void (*handleMessage)(u8* udp_message, u32 udp_len, SocketAddress sending_address, i32 socket),
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;
bool should_keep_serving = true;
while (should_keep_serving) {
poll_event_count = poll(pollable_fds, pollable_fd_count, -1);
if (poll_event_count == -1) {
// TODO handle error better
should_keep_serving = false;
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 = recvMessage(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 infiniteReadTCPClient(
i32 socket,
bool* should_quit,
void (*handleMessage)(u8* udp_message, i32 bytes_recieved),
void (*addSystemMessage)(u8* msg)
) {
u8 message_buffer[UDP_MAX_MESSAGE_LEN] = {0};
i32 bytes_recieved = 0;
while ((*should_quit) != true) {
bytes_recieved = recvMessage(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);
}
continue;
}
handleMessage(message_buffer, bytes_recieved);
MemoryZero(message_buffer, UDP_MAX_MESSAGE_LEN);
}
}
MultiServer createMultiServer(u16 server_port) {
MultiServer 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 UDP socket
result.udp_socket_fd = socket(PF_INET, SOCK_DGRAM, 0);
if (result.udp_socket_fd < 0) {
return result;
}
// get a FileDescriptor number from the OS to use for our TCP socket
result.tcp_socket_fd = socket(PF_INET, SOCK_STREAM, 0);
if (result.tcp_socket_fd < 0) {
return result;
}
// to let us immediately kill and restart server
i32 optval = 1;
setsockopt(result.udp_socket_fd, SOL_SOCKET, SO_REUSEADDR, (const void *)&optval, sizeof(i32));
setsockopt(result.tcp_socket_fd, SOL_SOCKET, SO_REUSEADDR, (const void *)&optval, sizeof(i32));
// bind() the UDP
result.ready = bind(result.udp_socket_fd, (struct sockaddr *)&result.address, sizeof(result.address)) >= 0;
// bind() the TCP
result.ready = result.ready && (bind(result.tcp_socket_fd, (struct sockaddr *)&result.address, sizeof(result.address)) >= 0);
if (result.ready) {
result.ready = result.ready && (listen(result.tcp_socket_fd, 10) >= 0);
}
return result;
}
void infiniteReadMultiServer(MultiServer* server, void (*handleMessage)(u8* udp_message, u32 udp_len, SocketAddress sending_address, i32 socket)) {
UDPServer udp_server = {
.ready = server->ready,
.server_address = server->address,
.server_socket = server->udp_socket_fd,
};
ServableUDPInfo udp_info = {
.server = udp_server,
.callback = handleMessage,
};
Thread udp_listener_thread = spawnThread(&infinitelyServeUDPSocket, &udp_info);
u8 message_buffer[UDP_MAX_MESSAGE_LEN] = {0};
SocketAddress client_address = {0};
i32 addrlen = sizeof(struct sockaddr);
i32 new_fd;
i32 bytes_recieved = 0;
while (true) {
// TCP
new_fd = accept(server->tcp_socket_fd, (struct sockaddr *)&client_address, (socklen_t*)&addrlen);
bytes_recieved = recv(new_fd, message_buffer, UDP_MAX_MESSAGE_LEN, 0);
if (bytes_recieved > 0) {
handleMessage(message_buffer, bytes_recieved, client_address, new_fd);
}
MemoryZero(message_buffer, UDP_MAX_MESSAGE_LEN);
}
osThreadJoin(udp_listener_thread, MAX_u64);
}
// TODO: sendall() to handle cases when the sendto() bytes return value is less than the intended bytes to send... stupid kernel fuckin wit us.
i32 sendUDPu8List(i32 using_socket, SocketAddress* to, u8List* message) {
return sendto(
@@ -196,3 +461,14 @@ i32 sendUDPu8List(i32 using_socket, SocketAddress* to, u8List* message) {
i32 sendUDPMessage(UDPServer* to, u8* message, u32 len) {
return sendto(to->server_socket, message, len, 0, (struct sockaddr *)&to->server_address, sizeof(struct sockaddr));
}
i32 sendTCPMessage(NetworkMessage msg) {
assert(msg.use_socket);
assert(msg.socket_fd >= 0);
u16 msg_len = htons(msg.bytes_len);
i32 result = send(msg.socket_fd, &msg_len, 2, 0);
if (result == -1) {
return result;
}
return send(msg.socket_fd, msg.bytes, msg.bytes_len, 0);
}
-2
View File
@@ -1,8 +1,6 @@
#include "../base/all.h"
#include "../string_chunk.h"
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#define UTF8_MAX_WIDTH 4
+146 -154
View File
@@ -6,13 +6,13 @@
* my_variable
* MY_CONSTANT
* */
#include <stdio.h>
#include <stdlib.h>
#include <sys/random.h>
#include <math.h>
#include "shared.h"
#include "base/impl.c"
#define NET_OUTGOING_MESSAGE_QUEUE_LEN 64
#define NET_OUTGOING_MESSAGE_QUEUE_LEN (64)
#define NET_SERVER_MAX_CLIENTS (16)
#include "lib/network.c"
#include "lib/tui.c"
#include "string_chunk.c"
@@ -21,9 +21,7 @@
#define LEFT_ROOM_ENTITES_LEN (KB(1))
#define ROOM_MAP_COLLISIONS_LEN MAX_ROOMS/8
#define CLIENT_COMMAND_LIST_LEN 8
#define SERVER_PORT 7777
#define SERVER_MAX_HEAP_MEMORY MB(256)
#define SERVER_MAX_CLIENTS 16
#define GAME_THREAD_CONCURRENCY 2
#define GOAL_NETWORK_SEND_LOOPS_PER_S 8
#define GOAL_NETWORK_SEND_LOOP_US 1000000/GOAL_NETWORK_SEND_LOOPS_PER_S
@@ -53,6 +51,7 @@ typedef struct ParsedClientCommand {
u16 alt_port;
u32 sender_ip;
u32 alt_ip;
i32 socket_fd;
u32 qty;
CommodityType commodity;
StringChunkList name;
@@ -80,12 +79,11 @@ typedef struct Account {
} Account;
typedef struct Client {
u16 lan_port;
i32 lan_ip;
bool active;
i32 socket_fd;
u64 account_id;
SocketAddress address;
CommandType commands[CLIENT_COMMAND_LIST_LEN];
u64 last_ping;
} Client;
typedef struct ClientList {
@@ -214,14 +212,16 @@ fn void exitWithErrorMessage(ptr msg) {
exit(1);
}
fn u32 pushClient(ClientList* clients, SocketAddress addr) {
Client new_client = {0};
new_client.last_ping = state.frame;
new_client.address = addr;
fn u32 pushClient(ClientList* clients, SocketAddress addr, i32 socket_fd) {
Client new_client = {
.active = true,
.socket_fd = socket_fd,
.address = addr,
};
// first, try to overwrite an old dc'ed client
for (u32 i = 1; i < clients->length; i++) {
if (clients->items[i].last_ping == 0) {
if (clients->items[i].active == false) {
clients->items[i] = new_client;
return i;
}
@@ -248,20 +248,10 @@ fn bool deleteClientByAccountId(ClientList* clients, u64 id) {
return succeeded;
}
fn u32 findClientHandleByAccountId(ClientList* clients, u64 id) {
fn u32 findClientHandle(ClientList* clients, SocketAddress address, i32 socket_fd) {
for (u32 i = 0; i < clients->length; i++) {
Client c = clients->items[i];
if (c.account_id == id) {
return i;
}
}
return 0;
}
fn u32 findClientHandleBySocketAddress(ClientList* clients, SocketAddress address) {
for (u32 i = 0; i < clients->length; i++) {
Client c = clients->items[i];
if (socketAddressEqual(address, c.address)) {
if (socketAddressEqual(address, c.address) && socket_fd == c.socket_fd) {
return i;
}
}
@@ -295,8 +285,8 @@ fn bool shipIsNull(PlayerShip* ship) {
return ship->id == 0 && ship->base_cost == 0;
}
fn UDPMessage makeMessageSystemPassengers(StarSystem* sys) {
UDPMessage outgoing_message = {0};
fn NetworkMessage makeMessageSystemPassengers(StarSystem* sys) {
NetworkMessage outgoing_message = { .use_socket = true };
u32 msg_i = 0;
outgoing_message.bytes[msg_i++] = (u8)MessageSystemPassengers;
outgoing_message.bytes[msg_i++] = (u8)sys->idx;
@@ -312,8 +302,8 @@ fn UDPMessage makeMessageSystemPassengers(StarSystem* sys) {
return outgoing_message;
}
fn UDPMessage makeMessageSystemCommodities(StarSystem* sys) {
UDPMessage outgoing_message = {0};
fn NetworkMessage makeMessageSystemCommodities(StarSystem* sys) {
NetworkMessage outgoing_message = { .use_socket = true };
u32 planet_count = starSystemPlanetCount(sys);
u32 msg_i = 0;
outgoing_message.bytes[msg_i++] = (u8)MessageSystemCommodities;
@@ -328,8 +318,8 @@ fn UDPMessage makeMessageSystemCommodities(StarSystem* sys) {
return outgoing_message;
}
fn void sendMessageTransactionResult(SocketAddress addr, u32 qty, bool buying, u32 credits) {
UDPMessage outgoing_message = {.address = addr};
fn void sendMessageTransactionResult(i32 socket_fd, SocketAddress addr, u32 qty, bool buying, u32 credits) {
NetworkMessage outgoing_message = { .address = addr, .use_socket = true, .socket_fd = socket_fd };
u32 msg_i = 0;
outgoing_message.bytes[msg_i++] = (u8)MessageTransactionResult;
outgoing_message.bytes[msg_i++] = buying;
@@ -340,8 +330,8 @@ fn void sendMessageTransactionResult(SocketAddress addr, u32 qty, bool buying, u
addSystemMessage((u8*)"Message TransactionResult sent");
}
fn UDPMessage makeMessagePlayerDetails(PlayerShip ship) {
UDPMessage outgoing_message = {0};
fn NetworkMessage makeMessagePlayerDetails(PlayerShip ship) {
NetworkMessage outgoing_message = {0};
u32 msg_i = 0;
outgoing_message.bytes[msg_i++] = (u8)MessagePlayerDetails;
outgoing_message.bytes[msg_i++] = ship.type;
@@ -373,9 +363,8 @@ fn UDPMessage makeMessagePlayerDetails(PlayerShip ship) {
return outgoing_message;
}
fn void sendMessageJobAcceptResult(SocketAddress addr, bool result) {
UDPMessage outgoing_message = {0};
outgoing_message.address = addr;
fn void sendMessageJobAcceptResult(i32 socket_fd, SocketAddress addr, bool result) {
NetworkMessage outgoing_message = { .address = addr, .use_socket = true, .socket_fd = socket_fd };
u32 msg_i = 0;
outgoing_message.bytes[msg_i++] = (u8)MessageJobAcceptResult;
outgoing_message.bytes[msg_i++] = result;
@@ -384,9 +373,8 @@ fn void sendMessageJobAcceptResult(SocketAddress addr, bool result) {
addSystemMessage((u8*)"MessageJobAcceptResult sent");
}
fn void sendMessagePayoffResult(SocketAddress addr) {
UDPMessage outgoing_message = {0};
outgoing_message.address = addr;
fn void sendMessagePayoffResult(i32 socket_fd, SocketAddress addr) {
NetworkMessage outgoing_message = { .address = addr, .use_socket = true, .socket_fd = socket_fd };
u32 msg_i = 0;
outgoing_message.bytes[msg_i++] = (u8)MessagePayoffResult;
outgoing_message.bytes_len = msg_i;
@@ -394,9 +382,8 @@ fn void sendMessagePayoffResult(SocketAddress addr) {
addSystemMessage((u8*)"MessagePayoffResult sent");
}
fn void sendMessageStarPositions(SocketAddress addr) {
UDPMessage outgoing_message = {0};
outgoing_message.address = addr;
fn void sendMessageStarPositions(i32 socket_fd, SocketAddress addr) {
NetworkMessage outgoing_message = { .use_socket = true, .address = addr, .socket_fd = socket_fd };
// tell the client about the map
u32 star_msg_size = 2+MAX_PLANETS;
outgoing_message.bytes_len = 1+(star_msg_size*STAR_SYSTEM_COUNT);
@@ -409,18 +396,24 @@ fn void sendMessageStarPositions(SocketAddress addr) {
}
}
outgoingMessageQueuePush(state.network_send_queue, &outgoing_message);
addSystemMessage((u8*)"MessageStarPositions sent");
char sbuf[SBUFLEN] = {0};
sprintf(sbuf, "MessageStarPositions sent socket=%d, message_len=%d", socket_fd, outgoing_message.bytes_len);
addSystemMessage((u8*)sbuf);
}
fn void sendMessagePlayerDetails(PlayerShip ship, SocketAddress addr) {
UDPMessage outgoing_message = makeMessagePlayerDetails(ship);
fn void sendMessagePlayerDetails(i32 socket_fd, PlayerShip ship, SocketAddress addr) {
NetworkMessage outgoing_message = makeMessagePlayerDetails(ship);
outgoing_message.address = addr;
outgoing_message.use_socket = true;
outgoing_message.socket_fd = socket_fd;
outgoingMessageQueuePush(state.network_send_queue, &outgoing_message);
addSystemMessage((u8*)"MessagePlayerDetails sent");
char sbuf[SBUFLEN] = {0};
sprintf(sbuf, "MessagePlayerDetails sent socket=%d, message_len=%d", socket_fd, outgoing_message.bytes_len);
addSystemMessage((u8*)sbuf);
}
fn void sendMessageAuctionBidResult(SocketAddress addr, AuctionBidResult result) {
UDPMessage outgoing_message = {.address = addr};
fn void sendMessageAuctionBidResult(i32 socket_fd, SocketAddress addr, AuctionBidResult result) {
NetworkMessage outgoing_message = { .address = addr, .use_socket = true, .socket_fd = socket_fd };
u32 msg_i = 0;
outgoing_message.bytes[msg_i++] = (u8)MessageAuctionBidResult;
outgoing_message.bytes[msg_i++] = result;
@@ -430,11 +423,13 @@ fn void sendMessageAuctionBidResult(SocketAddress addr, AuctionBidResult result)
}
fn void handleIncomingMessage(u8* message, u32 len, SocketAddress sender, i32 socket) {
dbg("%d: %s from %s:%d\n", len, command_type_strings[message[0]], inet_ntoa(sender.sin_addr), sender.sin_port);
char sbuf[SBUFLEN] = {0};
sprintf(sbuf, "%d: %s from %s:%d\n", len, command_type_strings[message[0]], inet_ntoa(sender.sin_addr), sender.sin_port);
addSystemMessage((u8*)sbuf);
u32 msg_idx = 0;
ParsedClientCommand parsed = {
.type = (CommandType)message[msg_idx++],
.socket_fd = socket,
.sender_ip = sender.sin_addr.s_addr,
.sender_port = sender.sin_port,
};
@@ -482,7 +477,6 @@ fn void handleIncomingMessage(u8* message, u32 len, SocketAddress sender, i32 so
sprintf(sbuf, "Logging in player: %s %d %s\n", command_type_strings[parsed.type], name_len, message + 7);
addSystemMessage((u8*)sbuf);
} break;
case CommandKeepAlive: break;
case CommandPayMortgage: {
parsed.id = readU64FromBufferLE(message + msg_idx);
MemoryZero(sbuf, SBUFLEN);
@@ -521,18 +515,30 @@ fn void handleIncomingMessage(u8* message, u32 len, SocketAddress sender, i32 so
pccThreadSafeQueuePush(state.network_recv_queue, &parsed);
}
fn void* receiveNetworkUpdates(void* udp) {
UDPServer server = *(UDPServer*)udp;
dbg("receiveNetworkUpdates() sock=%d\n", server.server_socket);
infiniteReadUDPServer(&server, handleIncomingMessage);
fn void removeClientBySocketFd(i32 socket_fd) {
Client blank_client = {0};
// i=1 because first client is null-client
for (u32 i = 1; i < state.clients.length; i++) {
if (state.clients.items[i].socket_fd == socket_fd) {
state.clients.items[i] = blank_client;
}
}
}
fn void* receiveNetworkUpdates(void* tcp_server) {
TCPServer server = *(TCPServer*)tcp_server;
char sbuf[SBUFLEN] = {0};
sprintf(sbuf, "receiveNetworkUpdates() sock=%d\n", server.socket_fd);
addSystemMessage((u8*)sbuf);
infiniteReadTCPServer(&server, handleIncomingMessage, removeClientBySocketFd, addSystemMessage);
return NULL;
}
fn void* sendNetworkUpdates(void* sock) {
fn void* sendNetworkUpdates(void* tcp_server) {
TCPServer server = *(TCPServer*)tcp_server;
ThreadContext tctx = {0};
tctxInit(&tctx);
i32* socket_ptr = (i32*)sock;
i32 socket = *socket_ptr;
i32 socket = server.socket_fd;
char sbuf[SBUFLEN] = {0};
u64 send_loop = 0;
while (!should_quit) {
@@ -541,103 +547,96 @@ fn void* sendNetworkUpdates(void* sock) {
// 1. clear out our "outgoingMessage" queue
{
UDPMessage to_send = { 0 };
UDPMessage* next_to_send = outgoingMessageNonblockingQueuePop(state.network_send_queue, &to_send);
NetworkMessage to_send = { 0 };
NetworkMessage* next_to_send = outgoingMessageNonblockingQueuePop(state.network_send_queue, &to_send);
u8List bytes_list = { 0 };
while (next_to_send != NULL) {
if (to_send.use_socket) {
char sbuf[SBUFLEN] = {0};
sprintf(sbuf, "sendTCPMessage() sock=%d msg=%s len=%d\n", to_send.socket_fd, MESSAGE_STRINGS[to_send.bytes[0]], to_send.bytes_len);
addSystemMessage((u8*)sbuf);
sendTCPMessage(to_send);
} else { // UDP
bytes_list.items = to_send.bytes;
bytes_list.length = to_send.bytes_len;
bytes_list.capacity = to_send.bytes_len;
sendUDPu8List(socket, &to_send.address, &bytes_list);
}
next_to_send = outgoingMessageNonblockingQueuePop(state.network_send_queue, &to_send);
}
}
UDPMessage sys_udp_msg = { 0 };
sys_udp_msg = makeMessageSystemCommodities(&state.map[send_loop/2 % STAR_SYSTEM_COUNT]);
u8List sys_msg = {
.capacity = UDP_MAX_MESSAGE_LEN,
.items = sys_udp_msg.bytes,
.length = sys_udp_msg.bytes_len,
};
UDPMessage sys_pass_udp_msg = { 0 };
sys_pass_udp_msg = makeMessageSystemPassengers(&state.map[send_loop/2 % STAR_SYSTEM_COUNT]);
u8List sys_pass_msg = {
.capacity = UDP_MAX_MESSAGE_LEN,
.items = sys_pass_udp_msg.bytes,
.length = sys_pass_udp_msg.bytes_len,
};
NetworkMessage base_sys_msg = makeMessageSystemCommodities(&state.map[send_loop/2 % STAR_SYSTEM_COUNT]);
NetworkMessage base_pass_msg = makeMessageSystemPassengers(&state.map[send_loop/2 % STAR_SYSTEM_COUNT]);
lockMutex(&state.client_mutex); {
// WARNING the `i` starts at 1 here because state.clients.items[0] is a "null" Client
for (u32 i = 1; i < state.clients.length; i++) {
Client client = state.clients.items[i];
if (client.last_ping+CLIENT_TIMEOUT_FRAMES < state.frame) {
memset(&state.clients.items[i], 0, sizeof(Client));
if (client.active == false) {
continue;
}
if (send_loop % 2 == 0) {
// every other "send-frame" we send each connnected client the current prices for a different system
// so that the prices mostly stay up to date pretty quickly without having to track changes
sendUDPu8List(socket, &client.address, &sys_msg);
base_sys_msg.socket_fd = client.socket_fd;
sendTCPMessage(base_sys_msg);
// and the passenger offers
sendUDPu8List(socket, &client.address, &sys_pass_msg);
base_pass_msg.socket_fd = client.socket_fd;
sendTCPMessage(base_pass_msg);
// send the client the current auction details for their current system
Account* account = &state.accounts[client.account_id];
if (!accountIsEmpty(account)) {
StarSystem curr = state.map[account->ship.system_idx];
MemoryZero(sbuf, SBUFLEN);
u32 msgidx = 0;
sbuf[msgidx++] = MessageAuctionDetails;
sbuf[msgidx++] = curr.auction.type;
sbuf[msgidx++] = curr.auction.qty;
msgidx += writeU32ToBufferLE((u8*)sbuf + msgidx, curr.auction.price);
msgidx += writeU32ToBufferLE((u8*)sbuf + msgidx, curr.auction.started_at);
msgidx += writeU32ToBufferLE((u8*)sbuf + msgidx, curr.auction.finished_at);
u8List msg = {
.capacity = UDP_MAX_MESSAGE_LEN,
.items = (u8*)sbuf,
.length = msgidx,
NetworkMessage auction_msg = {
.use_socket = true,
.socket_fd = client.socket_fd,
.address = client.address,
};
sendUDPu8List(socket, &client.address, &msg);
u32 msgidx = 0;
auction_msg.bytes[msgidx++] = MessageAuctionDetails;
auction_msg.bytes[msgidx++] = curr.auction.type;
auction_msg.bytes[msgidx++] = curr.auction.qty;
msgidx += writeU32ToBufferLE((u8*)auction_msg.bytes + msgidx, curr.auction.price);
msgidx += writeU32ToBufferLE((u8*)auction_msg.bytes + msgidx, curr.auction.started_at);
msgidx += writeU32ToBufferLE((u8*)auction_msg.bytes + msgidx, curr.auction.finished_at);
auction_msg.bytes_len = msgidx;
sendTCPMessage(auction_msg);
}
}
// update all the changed systems
UDPMessage msg_data;
NetworkMessage msg_data;
for (u32 ii = 0; ii < STAR_SYSTEM_COUNT; ii++) {
if (state.map[ii].changed == true) {
// send commodities
msg_data = makeMessageSystemCommodities(&state.map[ii]);
u8List msg = {
.capacity = UDP_MAX_MESSAGE_LEN,
.items = msg_data.bytes,
.length = msg_data.bytes_len,
};
sendUDPu8List(socket, &client.address, &msg);
msg_data.address = client.address;
msg_data.socket_fd = client.socket_fd;
msg_data.use_socket = true;
sendTCPMessage(msg_data);
// send passenger jobs
MemoryZero(sbuf, SBUFLEN);
sprintf(sbuf, "sending passenger jobs for %s\n", STAR_NAMES[ii]);
addSystemMessage((u8*)sbuf);
msg_data = makeMessageSystemPassengers(&state.map[ii]);
msg.capacity = UDP_MAX_MESSAGE_LEN;
msg.items = msg_data.bytes;
msg.length = msg_data.bytes_len;
sendUDPu8List(socket, &client.address, &msg);
msg_data.address = client.address;
msg_data.socket_fd = client.socket_fd;
msg_data.use_socket = true;
sendTCPMessage(msg_data);
}
}
// update all the changed accounts
for (u32 ii = 0; ii < ACCOUNT_LEN; ii++) {
if (state.accounts[ii].changed == true) {
msg_data = makeMessagePlayerDetails(state.accounts[ii].ship);
u8List msg = {
.capacity = UDP_MAX_MESSAGE_LEN,
.items = msg_data.bytes,
.length = msg_data.bytes_len,
};
sendUDPu8List(socket, &client.address, &msg);
msg_data.address = client.address;
msg_data.socket_fd = client.socket_fd;
msg_data.use_socket = true;
sendTCPMessage(msg_data);
}
}
}
@@ -670,7 +669,7 @@ fn void* gameLoop(void* params) {
char sbuf[SBUFLEN] = {0};
sprintf(sbuf, "Lane %lld (%lld) of %lld starting.", lane_ctx->lane_idx, LaneIdx(), lane_ctx->lane_count);
addSystemMessage((u8*)sbuf);
UDPMessage outgoing_message = {0};
NetworkMessage outgoing_message = { .use_socket = true };
u64 loop_start;
u64 last_burn = 0;
u64 last_hp_regen = 0;
@@ -698,7 +697,7 @@ fn void* gameLoop(void* params) {
sender.sin_addr.s_addr = msg.sender_ip;
sender.sin_port = msg.sender_port;
// find which client it is
u32 client_handle = findClientHandleBySocketAddress(&state.clients, sender);
u32 client_handle = findClientHandle(&state.clients, sender, msg.socket_fd);
Client* client = &state.clients.items[client_handle];
switch (msg.type) {
case CommandBuyAuction: {
@@ -713,15 +712,15 @@ fn void* gameLoop(void* params) {
player_sys->auction.finished_at = state.frame;
account->ship.credits -= player_sys->auction.price;
account->ship.commodities[player_sys->auction.type] += player_sys->auction.qty;
sendMessageAuctionBidResult(sender, AuctionBidResultPurchased);
sendMessagePlayerDetails(account->ship, sender);
sendMessageAuctionBidResult(msg.socket_fd, sender, AuctionBidResultPurchased);
sendMessagePlayerDetails(msg.socket_fd, account->ship, sender);
} else if (!player_has_money_for_purchase) {
sendMessageAuctionBidResult(sender, AuctionBidResultNotEnoughMoney);
sendMessageAuctionBidResult(msg.socket_fd, sender, AuctionBidResultNotEnoughMoney);
} else {
sendMessageAuctionBidResult(sender, AuctionBidResultNotEnoughCargoSpace);
sendMessageAuctionBidResult(msg.socket_fd, sender, AuctionBidResultNotEnoughCargoSpace);
}
} else {
sendMessageAuctionBidResult(sender, AuctionBidResultAuctionAlreadyFinished);
sendMessageAuctionBidResult(msg.socket_fd, sender, AuctionBidResultAuctionAlreadyFinished);
}
} break;
case CommandAcceptPassengerJob: {
@@ -751,7 +750,7 @@ fn void* gameLoop(void* params) {
MemoryZero(&player_sys->offers[i], sizeof(PassengerJobOffer));
player_sys->changed = true;
succeeded = true;
sendMessageJobAcceptResult(sender, succeeded);
sendMessageJobAcceptResult(client->socket_fd, sender, succeeded);
// end both for loops "break break;"
ii = MAX_PASSENGER_BERTHS;
i = MAX_PASSENGER_JOB_OFFERS;
@@ -762,7 +761,7 @@ fn void* gameLoop(void* params) {
}
}
if (!succeeded) {
sendMessageJobAcceptResult(sender, succeeded);
sendMessageJobAcceptResult(client->socket_fd, sender, succeeded);
}
} break;
case CommandPayMortgage: {
@@ -778,7 +777,7 @@ fn void* gameLoop(void* params) {
account->ship.credits -= amount_to_pay;
account->ship.remaining_mortgage -= amount_to_pay;
account->changed = true;
sendMessagePayoffResult(sender);
sendMessagePayoffResult(client->socket_fd, sender);
} break;
case CommandSetDestination: {
if (client_handle == 0) break;
@@ -789,7 +788,7 @@ fn void* gameLoop(void* params) {
if (client_handle == 0) break;
Account* account = &state.accounts[client->account_id];
account->ship.ready_to_depart = msg.byte;
sendMessagePlayerDetails(account->ship, sender);
sendMessagePlayerDetails(client->socket_fd, account->ship, sender);
state.all_accounts_ready = true;
for (u32 i = 0; i < ACCOUNT_LEN; i++) {
if (!accountIsEmpty(&state.accounts[i])) {
@@ -853,33 +852,17 @@ fn void* gameLoop(void* params) {
}
}
sys->changed = true;
sendMessagePlayerDetails(account->ship, sender);
sendMessageTransactionResult(sender, qty_traded, is_buying_from_system, credit_value);
} break;
case CommandKeepAlive: {
//MemoryZero(sbuf, SBUFLEN);
//sprintf(sbuf, "KeepAlive for client_handle=%d on frame %lld", client_handle, state.frame);
//addSystemMessage((u8*)sbuf);
if (client_handle == 0) {
outgoing_message.bytes[0] = (u8)MessageNotAlive;
outgoing_message.bytes_len = 1;
outgoing_message.address = sender;
outgoingMessageQueuePush(state.network_send_queue, &outgoing_message);
} else {
state.clients.items[client_handle].last_ping = state.frame;
}
sendMessagePlayerDetails(client->socket_fd, account->ship, sender);
sendMessageTransactionResult(client->socket_fd, sender, qty_traded, is_buying_from_system, credit_value);
} break;
case CommandLogin: {
if (client_handle == 0) {
client_handle = pushClient(&state.clients, sender);
client_handle = pushClient(&state.clients, sender, msg.socket_fd);
client = &state.clients.items[client_handle];
MemoryZero(sbuf, SBUFLEN);
sprintf(sbuf, "pushed new client handle = %d\n", client_handle);
addSystemMessage((u8*)sbuf);
}
// update/set the lan_ip/port info for p2p connections
client->lan_ip = htonl(msg.alt_ip);
client->lan_port = htons(msg.alt_port);
/*
struct in_addr ipaddr;
@@ -906,12 +889,13 @@ fn void* gameLoop(void* params) {
if (pw_matches) {
addSystemMessage((u8*)" pw matched\n");
existing_account->changed = true;
sendMessageStarPositions(sender);
sendMessageStarPositions(client->socket_fd, sender);
} else {
// tell the client they did a bad pw
outgoing_message.bytes[0] = (u8)MessageBadPw;
outgoing_message.bytes_len = 1;
outgoing_message.address = sender;
outgoing_message.socket_fd = client->socket_fd;
outgoingMessageQueuePush(state.network_send_queue, &outgoing_message);
addSystemMessage((u8*)"MessageBadPw sent\n");
break;
@@ -937,6 +921,7 @@ fn void* gameLoop(void* params) {
writeU64ToBufferLE(outgoing_message.bytes + 1, existing_account->ship.id);
outgoing_message.bytes_len = 9;
outgoing_message.address = sender;
outgoing_message.socket_fd = client->socket_fd;
outgoingMessageQueuePush(state.network_send_queue, &outgoing_message);
addSystemMessage((u8*)"MessageCharacterId sent\n");
} else {
@@ -944,6 +929,7 @@ fn void* gameLoop(void* params) {
outgoing_message.bytes[0] = (u8)MessageNewAccountCreated;
outgoing_message.bytes_len = 1;
outgoing_message.address = sender;
outgoing_message.socket_fd = client->socket_fd;
outgoingMessageQueuePush(state.network_send_queue, &outgoing_message);
addSystemMessage((u8*)"MessageNewAccountCreated sent\n");
}
@@ -971,7 +957,7 @@ fn void* gameLoop(void* params) {
.base_cost = template.base_cost,
.remaining_mortgage = template.base_cost - STARTING_DOWN_PAYMENT,
.interest_rate = calcInterestRate(template.base_cost, STARTING_DOWN_PAYMENT),
.credits = 20000.0,
.credits = 2000.0,
.cu_m_fuel = template.cu_m_fuel,
.cu_m_o2 = template.cu_m_o2,
.id = account->id,
@@ -990,18 +976,20 @@ fn void* gameLoop(void* params) {
// tell the client their account id
outgoing_message.address = sender;
outgoing_message.socket_fd = client->socket_fd;
outgoing_message.bytes_len = 9;
outgoing_message.bytes[0] = (u8)MessageCharacterId;
writeU64ToBufferLE(outgoing_message.bytes + 1, account->ship.id);
outgoingMessageQueuePush(state.network_send_queue, &outgoing_message);
addSystemMessage((u8*)"MessageCharacterId sent\n");
sendMessagePlayerDetails(account->ship, sender);
sendMessagePlayerDetails(client->socket_fd, account->ship, sender);
sendMessageStarPositions(sender);
sendMessageStarPositions(client->socket_fd, sender);
outgoing_message = makeMessageSystemCommodities(&starting_system);
outgoing_message.address = sender;
outgoing_message.socket_fd = client->socket_fd;
outgoingMessageQueuePush(state.network_send_queue, &outgoing_message);
MemoryZero(sbuf, SBUFLEN);
sprintf(sbuf, "%s sent\n", MESSAGE_STRINGS[outgoing_message.bytes[0]]);
@@ -1020,9 +1008,10 @@ fn void* gameLoop(void* params) {
}
if (state.all_accounts_ready) {
for (u32 i = 1; i < SERVER_MAX_CLIENTS; i++) {
if (state.clients.items[i].last_ping != 0) {
for (u32 i = 1; i < NET_SERVER_MAX_CLIENTS; i++) {
if (state.clients.items[i].active) {
outgoing_message.address = state.clients.items[i].address;
outgoing_message.socket_fd = state.clients.items[i].socket_fd;
outgoing_message.bytes_len = 1;
outgoing_message.bytes[0] = (u8)MessageTurnTick;
outgoingMessageQueuePush(state.network_send_queue, &outgoing_message);
@@ -1031,9 +1020,10 @@ fn void* gameLoop(void* params) {
}
if (state.someone_won) {
for (u32 i = 1; i < SERVER_MAX_CLIENTS; i++) {
if (state.clients.items[i].last_ping != 0) {
for (u32 i = 1; i < NET_SERVER_MAX_CLIENTS; i++) {
if (state.clients.items[i].active) {
outgoing_message.address = state.clients.items[i].address;
outgoing_message.socket_fd = state.clients.items[i].socket_fd;
outgoing_message.bytes_len = 2;
outgoing_message.bytes[0] = (u8)MessageGameOver;
outgoing_message.bytes[1] = state.winner_id;
@@ -1074,7 +1064,7 @@ fn void* gameLoop(void* params) {
f32 t_sec = ((f32)state.frame - (f32)grace_period_ends_at) / (f32)GOAL_GAME_LOOPS_PER_S;
// t is in minutes
f32 t = t_sec / 60;
f32 decay = -0.60 * t;
f32 decay = -0.65 * t;
f32 floor_price = COMMODITIES[sys->auction.type].price * 0.9;
sys->auction.price = Max((initial_price * pow(EULERS_E, decay)), floor_price);
}
@@ -1179,8 +1169,9 @@ fn void* gameLoop(void* params) {
addSystemMessage((u8*)sbuf);
// send a job completion message?
for (u32 i = 1; i < state.clients.length; i++) {
if (state.clients.items[i].last_ping != 0 && state.clients.items[i].account_id == acct->id) {
if (state.clients.items[i].active && state.clients.items[i].account_id == acct->id) {
outgoing_message.address = state.clients.items[i].address;
outgoing_message.socket_fd = state.clients.items[i].socket_fd;
outgoing_message.bytes_len = 2;
outgoing_message.bytes[0] = (u8)MessageJobComplete;
outgoing_message.bytes[1] = true;
@@ -1201,8 +1192,9 @@ fn void* gameLoop(void* params) {
addSystemMessage((u8*)sbuf);
// send a job failed message?
for (u32 i = 1; i < state.clients.length; i++) {
if (state.clients.items[i].last_ping != 0 && state.clients.items[i].account_id == acct->id) {
if (state.clients.items[i].active && state.clients.items[i].account_id == acct->id) {
outgoing_message.address = state.clients.items[i].address;
outgoing_message.socket_fd = state.clients.items[i].socket_fd;
outgoing_message.bytes_len = 2;
outgoing_message.bytes[0] = (u8)MessageJobComplete;
outgoing_message.bytes[1] = false;
@@ -1456,9 +1448,9 @@ i32 main(i32 argc, ptr argv[]) {
state.network_send_queue = newOutgoingMessageQueue(&permanent_arena);
// alloc the global hashmap of rooms
// init + alloc clients
state.clients.capacity = SERVER_MAX_CLIENTS;
state.clients.capacity = NET_SERVER_MAX_CLIENTS;
state.clients.length = 1; // making entry 0 to be a "null" client
state.clients.items = (Client*)arenaAllocArray(&permanent_arena, Client, SERVER_MAX_CLIENTS);
state.clients.items = (Client*)arenaAllocArray(&permanent_arena, Client, NET_SERVER_MAX_CLIENTS);
for (i32 i = 0; i < SYSTEM_MESSAGES_LEN; i++) {
system_messages[i].capacity = MAX_SYSTEM_MESSAGE_LEN;
system_messages[i].length = 0;
@@ -1657,13 +1649,13 @@ i32 main(i32 argc, ptr argv[]) {
}
// 2. spin off sendNetworkUpdates() infinite loop thread
UDPServer listener = createUDPServer(SERVER_PORT);
if (!listener.ready) {
exitWithErrorMessage("Couldn't start the udp server");
TCPServer server = createTCPServer(SERVER_PORT);
if (!server.ready) {
exitWithErrorMessage("Couldn't start the server");
}
Thread send_thread = spawnThread(&sendNetworkUpdates, &listener.server_socket);
Thread send_thread = spawnThread(&sendNetworkUpdates, &server);
// 3. infinitely wait for incoming UDP messages and process them (usually by just dropping user-commands into the relevant block of shared memory)
Thread recv_thread = spawnThread(&receiveNetworkUpdates, &listener);
Thread recv_thread = spawnThread(&receiveNetworkUpdates, &server);
u64 lane_broadcast_val = 0;
Barrier barrier = osBarrierAlloc(GAME_THREAD_CONCURRENCY);
+1 -2
View File
@@ -18,6 +18,7 @@
#define AUCTION_COMMODITY_CUTOFF (7)
#define MAX_SCREEN_HEIGHT 300
#define MAX_SCREEN_WIDTH 800
#define SERVER_PORT 7777
#define AUCTION_PRICE_START_MULTIPLE (12)
@@ -366,7 +367,6 @@ typedef enum Direction {
typedef enum CommandType {
CommandInvalid,
CommandKeepAlive,
CommandLogin,
CommandCreateCharacter,
CommandTransact,
@@ -380,7 +380,6 @@ typedef enum CommandType {
static const char* command_type_strings[CommandType_Count] = {
"Invalid",
"KeepAlive",
"Login",
"CreateCharacter",
"ReadyStatus",