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` // only valid if there's an in-scope variable bool `debug_mode`
#define dbg(fmt, ...) osDebugPrint(debug_mode, fmt, ##__VA_ARGS__) #define dbg(fmt, ...) osDebugPrint(debug_mode, fmt, ##__VA_ARGS__)
///// SYSTEM INCLUDES I always want
#include <stdio.h>
#include <unistd.h>
///// TYPES ///// TYPES
// integer types // integer types
typedef unsigned char u8; typedef unsigned char u8;
-2
View File
@@ -2,10 +2,8 @@
#define _POSIX_C_SOURCE 200809L #define _POSIX_C_SOURCE 200809L
#include <sys/stat.h> #include <sys/stat.h>
#include <fcntl.h> #include <fcntl.h>
#include <unistd.h>
#include <time.h> #include <time.h>
#include <stdarg.h> #include <stdarg.h>
#include <stdio.h>
#include "all.h" #include "all.h"
global pthread_barrier_t linux_thread_barrier; global pthread_barrier_t linux_thread_barrier;
-2
View File
@@ -1,10 +1,8 @@
#include <sys/mman.h> #include <sys/mman.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <fcntl.h> #include <fcntl.h>
#include <unistd.h>
#include <time.h> #include <time.h>
#include <stdarg.h> #include <stdarg.h>
#include <stdio.h>
#include "all.h" #include "all.h"
#include "pthread_barrier.h" #include "pthread_barrier.h"
-1
View File
@@ -1,6 +1,5 @@
#include "string.h" #include "string.h"
#include <userenv.h> #include <userenv.h>
#include <stdio.h>
static u64 w32_ticks_per_sec = 1; static u64 w32_ticks_per_sec = 1;
static u32 w32_thread_context_index; static u32 w32_thread_context_index;
+70 -78
View File
@@ -13,9 +13,6 @@
#include "lib/network.c" #include "lib/network.c"
#include "lib/tui.c" #include "lib/tui.c"
#include "string_chunk.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 a bunch of client-only tunable game constants
#define SYSTEM_MESSAGES_LEN 32 #define SYSTEM_MESSAGES_LEN 32
@@ -167,8 +164,7 @@ typedef struct GameState {
MenuState menu; MenuState menu;
MenuState row; // for tracking which row of a table the user has selected MenuState row; // for tracking which row of a table the user has selected
MenuState modal_choice; MenuState modal_choice;
UDPMessage keep_alive_msg; TCPClient client;
UDPClient client;
StringChunkList message_input; StringChunkList message_input;
StarSystem map[STAR_SYSTEM_COUNT]; StarSystem map[STAR_SYSTEM_COUNT];
Pos2 pos; 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) { fn void resetTabRow(Tab tab) {
if (state.menu.selected_index == TabMarket) { if (state.menu.selected_index == TabMarket) {
state.market_tab_state = MarketTabStateTable; 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; u64 msg_pos = 0;
Message msg_type = message[msg_pos++]; 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}; u8List bytes = {len, len, message};
ParsedServerMessage parsed = {0}; ParsedServerMessage parsed = {0};
parsed.type = msg_type; parsed.type = msg_type;
@@ -462,29 +448,24 @@ fn void handleIncomingMessage(u8* message, u32 len, SocketAddress sender, i32 so
psmThreadSafeQueuePush(network_recv_queue, &parsed); psmThreadSafeQueuePush(network_recv_queue, &parsed);
} }
fn void* receiveNetworkUpdates(void* udp) { fn void* receiveNetworkUpdates(void* net_client) {
UDPClient client = *(UDPClient*)udp; TCPClient client = *(TCPClient*)net_client;
dbg("receiveNetworkUpdates() sock=%d\n", client.socket); dbg("receiveNetworkUpdates() sock=%d\n", client.socket);
UDPServer server = { infiniteReadTCPClient(client.socket, &should_quit, handleIncomingMessage, addSystemMessage);
.ready = true,
.server_address = client.server_address,
.server_socket = client.socket
};
infiniteReadUDPServer(&server, handleIncomingMessage);
return NULL; return NULL;
} }
fn void* sendNetworkUpdates(void* udp) { fn void* sendNetworkUpdates(void* net_client) {
i32 socket_fd = ((UDPClient*)udp)->socket; TCPClient* client = (TCPClient*)net_client;
dbg("sendNetworkUpdates() sock=%d\n", socket_fd); dbg("sendNetworkUpdates() sock=%d\n", client->socket);
u8List bytes_list = {0}; NetworkMessage msg = {
.use_socket = true,
.socket_fd = client->socket,
.address = client->server_address,
};
while (!should_quit) { while (!should_quit) {
UDPMessage msg = {0};
outgoingMessageQueuePop(network_send_queue, &msg); outgoingMessageQueuePop(network_send_queue, &msg);
bytes_list.items = msg.bytes; sendTCPMessage(msg);
bytes_list.length = msg.bytes_len;
bytes_list.capacity = msg.bytes_len;
sendUDPu8List(socket_fd, &msg.address, &bytes_list);
} }
return NULL; return NULL;
} }
@@ -528,15 +509,16 @@ fn Box defaultModal(TuiState* tui) {
return modal_outline; return modal_outline;
} }
fn void sendLoginCommand(SocketAddress addr, u16 client_port) { fn void sendLoginCommand(TCPClient* net_client) {
u32 msg_idx = 0; u32 msg_idx = 0;
// drop the login message into the network_send_queue // 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 // 1. msg type/CommandType
msg.bytes[msg_idx++] = CommandLogin; 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 // 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 // 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()); msg_idx += writeI32ToBufferLE(msg.bytes + msg_idx, ~osLanIPAddress());
// 2. how long is the name // 2. how long is the name
msg.bytes[msg_idx++] = state.login_state.name.length; 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) { fn bool updateAndRender(TuiState* tui, void* s, u8* input_buffer, u64 loop_count) {
GameState* state = (GameState*) s; GameState* state = (GameState*) s;
state->loop_count = loop_count; state->loop_count = loop_count;
UDPClient* udp = &state->client;
state->old_screen = state->screen; state->old_screen = state->screen;
Dim2 screen_dimensions = tui->screen_dimensions; Dim2 screen_dimensions = tui->screen_dimensions;
bool game_screen_changed = state->old_screen != state->screen; // detect new screens 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 // auto-re-login
if (state->me.base_cost > 0) { if (state->me.base_cost > 0) {
addSystemMessage((u8*)"auto re logging in due to MessageNotAlive"); addSystemMessage((u8*)"auto re logging in due to MessageNotAlive");
sendLoginCommand(udp->server_address, udp->client_port); sendLoginCommand(&state->client);
state->screen = ScreenLogin; state->screen = ScreenLogin;
state->login_state.state = LoginScreenStateLoading; 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.remaining_mortgage = template.base_cost - STARTING_DOWN_PAYMENT;
state->me.interest_rate = calcInterestRate(template.base_cost, STARTING_DOWN_PAYMENT); state->me.interest_rate = calcInterestRate(template.base_cost, STARTING_DOWN_PAYMENT);
// send character ship details to server // send character ship details to server
UDPMessage msg = {0}; NetworkMessage msg = {
msg.address = udp->server_address; .use_socket = true,
msg.bytes_len = 2; .address = state->client.server_address,
.socket_fd = state->client.socket,
.bytes_len = 2,
};
// 1. msg type/CommandType // 1. msg type/CommandType
msg.bytes[0] = CommandCreateCharacter; msg.bytes[0] = CommandCreateCharacter;
// 2. chosen ShipType // 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; state->destination_sys_idx = i;
// send the destination to the server // send the destination to the server
u32 msg_idx = 0; u32 msg_idx = 0;
UDPMessage msg = {0}; NetworkMessage msg = {
msg.address = udp->server_address; .use_socket = true,
.address = state->client.server_address,
.socket_fd = state->client.socket,
};
// 1. msg type/CommandType // 1. msg type/CommandType
msg.bytes[msg_idx++] = CommandSetDestination; msg.bytes[msg_idx++] = CommandSetDestination;
// 2. buy? // 2. buy?
msg.bytes[msg_idx++] = state->destination_sys_idx; msg.bytes[msg_idx++] = state->destination_sys_idx;
msg.bytes_len = msg_idx; msg.bytes_len = msg_idx;
outgoingMessageQueuePush(network_send_queue, &msg); 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) { if (user_pressed_enter && state->ship_tab_states == ShipTabStateMain) {
// send the "ready" or "not ready" message to the server // send the "ready" or "not ready" message to the server
u32 msg_idx = 0; u32 msg_idx = 0;
UDPMessage msg = {0}; NetworkMessage msg = {
msg.address = udp->server_address; .use_socket = true,
.address = state->client.server_address,
.socket_fd = state->client.socket,
};
// 1. msg type/CommandType // 1. msg type/CommandType
msg.bytes[msg_idx++] = CommandReadyStatus; msg.bytes[msg_idx++] = CommandReadyStatus;
// 2. buy? // 2. buy?
msg.bytes[msg_idx++] = !state->me.ready_to_depart; msg.bytes[msg_idx++] = !state->me.ready_to_depart;
msg.bytes_len = msg_idx; msg.bytes_len = msg_idx;
outgoingMessageQueuePush(network_send_queue, &msg); 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) { if (user_pressed_enter) {
// send the "payoff" message to server // send the "payoff" message to server
u32 msg_idx = 0; u32 msg_idx = 0;
UDPMessage msg = {0}; NetworkMessage msg = {
msg.address = udp->server_address; .use_socket = true,
.address = state->client.server_address,
.socket_fd = state->client.socket,
};
// 1. msg type/CommandType // 1. msg type/CommandType
msg.bytes[msg_idx++] = CommandPayMortgage; msg.bytes[msg_idx++] = CommandPayMortgage;
// 2. buy? // 2. buy?
msg_idx += writeU64ToBufferLE(msg.bytes + msg_idx, input_quantity); msg_idx += writeU64ToBufferLE(msg.bytes + msg_idx, input_quantity);
msg.bytes_len = msg_idx; msg.bytes_len = msg_idx;
outgoingMessageQueuePush(network_send_queue, &msg); outgoingMessageQueuePush(network_send_queue, &msg);
state->ship_tab_states = ShipTabStateLoading; 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) { if (user_pressed_enter) {
// send the "buy this auction" message to the server // send the "buy this auction" message to the server
u32 msg_idx = 0; u32 msg_idx = 0;
UDPMessage msg = {0}; NetworkMessage msg = {
msg.address = udp->server_address; .use_socket = true,
.address = state->client.server_address,
.socket_fd = state->client.socket,
};
msg.bytes[msg_idx++] = CommandBuyAuction; msg.bytes[msg_idx++] = CommandBuyAuction;
msg.bytes_len = msg_idx; msg.bytes_len = msg_idx;
outgoingMessageQueuePush(network_send_queue, &msg); 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 // send the "purchase" or "sell" message to server
// get back new "status" of things // get back new "status" of things
u32 msg_idx = 0; u32 msg_idx = 0;
UDPMessage msg = {0}; NetworkMessage msg = {
msg.address = udp->server_address; .use_socket = true,
.address = state->client.server_address,
.socket_fd = state->client.socket,
};
// 1. msg type/CommandType // 1. msg type/CommandType
msg.bytes[msg_idx++] = CommandTransact; msg.bytes[msg_idx++] = CommandTransact;
// 2. buy? // 2. buy?
@@ -1561,7 +1557,6 @@ fn bool updateAndRender(TuiState* tui, void* s, u8* input_buffer, u64 loop_count
// 4. commodity // 4. commodity
msg.bytes[msg_idx++] = selected_commodity.type; msg.bytes[msg_idx++] = selected_commodity.type;
msg.bytes_len = msg_idx; msg.bytes_len = msg_idx;
outgoingMessageQueuePush(network_send_queue, &msg); outgoingMessageQueuePush(network_send_queue, &msg);
state->market_tab_state = MarketTabStateLoading; 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 // send the "job accept" message to server
// get back new "status" of things // get back new "status" of things
u32 msg_idx = 0; u32 msg_idx = 0;
UDPMessage msg = {0}; NetworkMessage msg = {
msg.address = udp->server_address; .use_socket = true,
.address = state->client.server_address,
.socket_fd = state->client.socket,
};
msg.bytes[msg_idx++] = CommandAcceptPassengerJob; msg.bytes[msg_idx++] = CommandAcceptPassengerJob;
msg.bytes[msg_idx++] = current_row.goal_system_idx; msg.bytes[msg_idx++] = current_row.goal_system_idx;
msg.bytes[msg_idx++] = current_row.people; 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.selected_field = 1;
state->login_state.field_index = 0; state->login_state.field_index = 0;
} else { } else {
sendLoginCommand(udp->server_address, udp->client_port); sendLoginCommand(&state->client);
// show loading signal, stop accepting input // show loading signal, stop accepting input
state->login_state.state = LoginScreenStateLoading; state->login_state.state = LoginScreenStateLoading;
addSystemMessage((u8*)state->login_state.name.bytes); 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; break;
} }
if (loop_count % 10 == 0) {
outgoingMessageQueuePush(network_send_queue, &state->keep_alive_msg);
//outgoingMessageQueuePush(network_send_queue, &testm);
}
return should_quit; return should_quit;
} }
@@ -1913,23 +1906,22 @@ i32 main(i32 argc, ptr argv[]) {
exit(1); exit(1);
} }
ptr server_address = NULL; ptr server_address = NULL;
char input_string[16] = { 0 };
if (argc > 1) { if (argc > 1) {
server_address = argv[1]; server_address = argv[1];
} else { } else {
printf("Server IP Address: "); printf("Server IP Address: ");
char input_string[16] = { 0 };
scanf("%15s", input_string); scanf("%15s", input_string);
printf("%s", input_string); printf("%s", input_string);
if (input_string[0] && input_string[1] && input_string[2] && input_string[3]) { if (input_string[0] && input_string[1] && input_string[2] && input_string[3]) {
server_address = input_string; server_address = input_string;
} }
} }
state.client = createUDPClient(7777, server_address); state.client = createTCPClient(SERVER_PORT, server_address);
if (!state.client.ready) {
// "hardcoded" keep alive message to periodically send to server printf("network could not connect() to server");
state.keep_alive_msg.address = state.client.server_address; exit(1);
state.keep_alive_msg.bytes[0] = CommandKeepAlive; }
state.keep_alive_msg.bytes_len = 1;
Thread recv_thread = spawnThread(&receiveNetworkUpdates, &state.client); Thread recv_thread = spawnThread(&receiveNetworkUpdates, &state.client);
Thread send_thread = spawnThread(&sendNetworkUpdates, &state.client); Thread send_thread = spawnThread(&sendNetworkUpdates, &state.client);
+295 -19
View File
@@ -1,4 +1,5 @@
#include "../base/all.h" #include "../base/all.h"
#include <poll.h>
// https://stackoverflow.com/questions/1098897/what-is-the-largest-safe-udp-packet-size-on-the-internet // https://stackoverflow.com/questions/1098897/what-is-the-largest-safe-udp-packet-size-on-the-internet
#define UDP_MAX_MESSAGE_LEN 508 #define UDP_MAX_MESSAGE_LEN 508
@@ -6,14 +7,42 @@
#define NET_OUTGOING_MESSAGE_QUEUE_LEN 16 #define NET_OUTGOING_MESSAGE_QUEUE_LEN 16
#endif #endif
#ifndef NET_SERVER_MAX_CLIENTS
#define NET_SERVER_MAX_CLIENTS (16)
#endif
typedef struct sockaddr_in SocketAddress; 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 { typedef struct UDPServer {
bool ready; bool ready;
SocketAddress server_address; SocketAddress server_address;
i32 server_socket; i32 server_socket;
} UDPServer; } 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 { typedef struct UDPClient {
bool ready; bool ready;
SocketAddress server_address; SocketAddress server_address;
@@ -21,14 +50,16 @@ typedef struct UDPClient {
i32 socket; i32 socket;
} UDPClient; } UDPClient;
typedef struct UDPMessage { typedef struct NetworkMessage {
bool use_socket; // default = false = UDP, just sendto(address)
u16 bytes_len; u16 bytes_len;
SocketAddress address; SocketAddress address;
i32 socket_fd;
u8 bytes[UDP_MAX_MESSAGE_LEN]; u8 bytes[UDP_MAX_MESSAGE_LEN];
} UDPMessage; } NetworkMessage;
typedef struct OutgoingMessageQueue { typedef struct OutgoingMessageQueue {
UDPMessage items[NET_OUTGOING_MESSAGE_QUEUE_LEN]; NetworkMessage items[NET_OUTGOING_MESSAGE_QUEUE_LEN];
u32 head; u32 head;
u32 tail; u32 tail;
u32 count; u32 count;
@@ -46,7 +77,7 @@ fn OutgoingMessageQueue* newOutgoingMessageQueue(Arena* a) {
return result; return result;
} }
fn void outgoingMessageQueuePush(OutgoingMessageQueue* queue, UDPMessage* msg) { fn void outgoingMessageQueuePush(OutgoingMessageQueue* queue, NetworkMessage* msg) {
lockMutex(&queue->mutex); { lockMutex(&queue->mutex); {
while (queue->count == NET_OUTGOING_MESSAGE_QUEUE_LEN) { while (queue->count == NET_OUTGOING_MESSAGE_QUEUE_LEN) {
waitForCondSignal(&queue->not_full, &queue->mutex); waitForCondSignal(&queue->not_full, &queue->mutex);
@@ -60,11 +91,11 @@ fn void outgoingMessageQueuePush(OutgoingMessageQueue* queue, UDPMessage* msg) {
} unlockMutex(&queue->mutex); } 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 // immediately returns NULL if there's nothing in the ThreadQueue
// copies the ParsedClientCommand into `copy_target` if there is something in the queue // copies the ParsedClientCommand into `copy_target` if there is something in the queue
// and marks it as popped from the queue // and marks it as popped from the queue
UDPMessage* result = NULL; NetworkMessage* result = NULL;
lockMutex(&q->mutex); { lockMutex(&q->mutex); {
if (q->count > 0) { if (q->count > 0) {
@@ -80,8 +111,8 @@ fn UDPMessage* outgoingMessageNonblockingQueuePop(OutgoingMessageQueue* q, UDPMe
return result; return result;
} }
fn UDPMessage* outgoingMessageQueuePop(OutgoingMessageQueue* q, UDPMessage* copy_target) { fn NetworkMessage* outgoingMessageQueuePop(OutgoingMessageQueue* q, NetworkMessage* copy_target) {
UDPMessage* result = NULL; NetworkMessage* result = NULL;
lockMutex(&q->mutex); { lockMutex(&q->mutex); {
while (q->count == 0) { while (q->count == 0) {
@@ -99,7 +130,6 @@ fn UDPMessage* outgoingMessageQueuePop(OutgoingMessageQueue* q, UDPMessage* copy
return result; return result;
} }
fn bool socketAddressEqual(SocketAddress a, SocketAddress b) { fn bool socketAddressEqual(SocketAddress a, SocketAddress b) {
return a.sin_addr.s_addr == b.sin_addr.s_addr return a.sin_addr.s_addr == b.sin_addr.s_addr
&& a.sin_port == b.sin_port; && 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 // 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 // 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 createUDPServer(u16 server_port) {
UDPServer result = {0}; UDPServer result = {0};
// define the address we'll be listening on // 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); i32 addrlen = sizeof(struct sockaddr);
while (true) { while (true) {
bytes_recieved = recvfrom(server->server_socket, message_buffer, UDP_MAX_MESSAGE_LEN, 0, (struct sockaddr *)&client_address, (socklen_t*)&addrlen); 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); handleMessage(message_buffer, bytes_recieved, client_address, server->server_socket);
MemoryZero(message_buffer, UDP_MAX_MESSAGE_LEN); 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. // 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) { i32 sendUDPu8List(i32 using_socket, SocketAddress* to, u8List* message) {
return sendto( return sendto(
@@ -196,3 +461,14 @@ i32 sendUDPu8List(i32 using_socket, SocketAddress* to, u8List* message) {
i32 sendUDPMessage(UDPServer* to, u8* message, u32 len) { 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 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 "../base/all.h"
#include "../string_chunk.h" #include "../string_chunk.h"
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h> #include <fcntl.h>
#define UTF8_MAX_WIDTH 4 #define UTF8_MAX_WIDTH 4
+152 -160
View File
@@ -6,13 +6,13 @@
* my_variable * my_variable
* MY_CONSTANT * MY_CONSTANT
* */ * */
#include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <sys/random.h> #include <sys/random.h>
#include <math.h> #include <math.h>
#include "shared.h" #include "shared.h"
#include "base/impl.c" #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/network.c"
#include "lib/tui.c" #include "lib/tui.c"
#include "string_chunk.c" #include "string_chunk.c"
@@ -21,9 +21,7 @@
#define LEFT_ROOM_ENTITES_LEN (KB(1)) #define LEFT_ROOM_ENTITES_LEN (KB(1))
#define ROOM_MAP_COLLISIONS_LEN MAX_ROOMS/8 #define ROOM_MAP_COLLISIONS_LEN MAX_ROOMS/8
#define CLIENT_COMMAND_LIST_LEN 8 #define CLIENT_COMMAND_LIST_LEN 8
#define SERVER_PORT 7777
#define SERVER_MAX_HEAP_MEMORY MB(256) #define SERVER_MAX_HEAP_MEMORY MB(256)
#define SERVER_MAX_CLIENTS 16
#define GAME_THREAD_CONCURRENCY 2 #define GAME_THREAD_CONCURRENCY 2
#define GOAL_NETWORK_SEND_LOOPS_PER_S 8 #define GOAL_NETWORK_SEND_LOOPS_PER_S 8
#define GOAL_NETWORK_SEND_LOOP_US 1000000/GOAL_NETWORK_SEND_LOOPS_PER_S #define GOAL_NETWORK_SEND_LOOP_US 1000000/GOAL_NETWORK_SEND_LOOPS_PER_S
@@ -53,6 +51,7 @@ typedef struct ParsedClientCommand {
u16 alt_port; u16 alt_port;
u32 sender_ip; u32 sender_ip;
u32 alt_ip; u32 alt_ip;
i32 socket_fd;
u32 qty; u32 qty;
CommodityType commodity; CommodityType commodity;
StringChunkList name; StringChunkList name;
@@ -80,12 +79,11 @@ typedef struct Account {
} Account; } Account;
typedef struct Client { typedef struct Client {
u16 lan_port; bool active;
i32 lan_ip; i32 socket_fd;
u64 account_id; u64 account_id;
SocketAddress address; SocketAddress address;
CommandType commands[CLIENT_COMMAND_LIST_LEN]; CommandType commands[CLIENT_COMMAND_LIST_LEN];
u64 last_ping;
} Client; } Client;
typedef struct ClientList { typedef struct ClientList {
@@ -214,14 +212,16 @@ fn void exitWithErrorMessage(ptr msg) {
exit(1); exit(1);
} }
fn u32 pushClient(ClientList* clients, SocketAddress addr) { fn u32 pushClient(ClientList* clients, SocketAddress addr, i32 socket_fd) {
Client new_client = {0}; Client new_client = {
new_client.last_ping = state.frame; .active = true,
new_client.address = addr; .socket_fd = socket_fd,
.address = addr,
};
// first, try to overwrite an old dc'ed client // first, try to overwrite an old dc'ed client
for (u32 i = 1; i < clients->length; i++) { 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; clients->items[i] = new_client;
return i; return i;
} }
@@ -248,20 +248,10 @@ fn bool deleteClientByAccountId(ClientList* clients, u64 id) {
return succeeded; 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++) { for (u32 i = 0; i < clients->length; i++) {
Client c = clients->items[i]; Client c = clients->items[i];
if (c.account_id == id) { if (socketAddressEqual(address, c.address) && socket_fd == c.socket_fd) {
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)) {
return i; return i;
} }
} }
@@ -295,8 +285,8 @@ fn bool shipIsNull(PlayerShip* ship) {
return ship->id == 0 && ship->base_cost == 0; return ship->id == 0 && ship->base_cost == 0;
} }
fn UDPMessage makeMessageSystemPassengers(StarSystem* sys) { fn NetworkMessage makeMessageSystemPassengers(StarSystem* sys) {
UDPMessage outgoing_message = {0}; NetworkMessage outgoing_message = { .use_socket = true };
u32 msg_i = 0; u32 msg_i = 0;
outgoing_message.bytes[msg_i++] = (u8)MessageSystemPassengers; outgoing_message.bytes[msg_i++] = (u8)MessageSystemPassengers;
outgoing_message.bytes[msg_i++] = (u8)sys->idx; outgoing_message.bytes[msg_i++] = (u8)sys->idx;
@@ -312,8 +302,8 @@ fn UDPMessage makeMessageSystemPassengers(StarSystem* sys) {
return outgoing_message; return outgoing_message;
} }
fn UDPMessage makeMessageSystemCommodities(StarSystem* sys) { fn NetworkMessage makeMessageSystemCommodities(StarSystem* sys) {
UDPMessage outgoing_message = {0}; NetworkMessage outgoing_message = { .use_socket = true };
u32 planet_count = starSystemPlanetCount(sys); u32 planet_count = starSystemPlanetCount(sys);
u32 msg_i = 0; u32 msg_i = 0;
outgoing_message.bytes[msg_i++] = (u8)MessageSystemCommodities; outgoing_message.bytes[msg_i++] = (u8)MessageSystemCommodities;
@@ -328,8 +318,8 @@ fn UDPMessage makeMessageSystemCommodities(StarSystem* sys) {
return outgoing_message; return outgoing_message;
} }
fn void sendMessageTransactionResult(SocketAddress addr, u32 qty, bool buying, u32 credits) { fn void sendMessageTransactionResult(i32 socket_fd, SocketAddress addr, u32 qty, bool buying, u32 credits) {
UDPMessage outgoing_message = {.address = addr}; NetworkMessage outgoing_message = { .address = addr, .use_socket = true, .socket_fd = socket_fd };
u32 msg_i = 0; u32 msg_i = 0;
outgoing_message.bytes[msg_i++] = (u8)MessageTransactionResult; outgoing_message.bytes[msg_i++] = (u8)MessageTransactionResult;
outgoing_message.bytes[msg_i++] = buying; 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"); addSystemMessage((u8*)"Message TransactionResult sent");
} }
fn UDPMessage makeMessagePlayerDetails(PlayerShip ship) { fn NetworkMessage makeMessagePlayerDetails(PlayerShip ship) {
UDPMessage outgoing_message = {0}; NetworkMessage outgoing_message = {0};
u32 msg_i = 0; u32 msg_i = 0;
outgoing_message.bytes[msg_i++] = (u8)MessagePlayerDetails; outgoing_message.bytes[msg_i++] = (u8)MessagePlayerDetails;
outgoing_message.bytes[msg_i++] = ship.type; outgoing_message.bytes[msg_i++] = ship.type;
@@ -373,9 +363,8 @@ fn UDPMessage makeMessagePlayerDetails(PlayerShip ship) {
return outgoing_message; return outgoing_message;
} }
fn void sendMessageJobAcceptResult(SocketAddress addr, bool result) { fn void sendMessageJobAcceptResult(i32 socket_fd, SocketAddress addr, bool result) {
UDPMessage outgoing_message = {0}; NetworkMessage outgoing_message = { .address = addr, .use_socket = true, .socket_fd = socket_fd };
outgoing_message.address = addr;
u32 msg_i = 0; u32 msg_i = 0;
outgoing_message.bytes[msg_i++] = (u8)MessageJobAcceptResult; outgoing_message.bytes[msg_i++] = (u8)MessageJobAcceptResult;
outgoing_message.bytes[msg_i++] = result; outgoing_message.bytes[msg_i++] = result;
@@ -384,9 +373,8 @@ fn void sendMessageJobAcceptResult(SocketAddress addr, bool result) {
addSystemMessage((u8*)"MessageJobAcceptResult sent"); addSystemMessage((u8*)"MessageJobAcceptResult sent");
} }
fn void sendMessagePayoffResult(SocketAddress addr) { fn void sendMessagePayoffResult(i32 socket_fd, SocketAddress addr) {
UDPMessage outgoing_message = {0}; NetworkMessage outgoing_message = { .address = addr, .use_socket = true, .socket_fd = socket_fd };
outgoing_message.address = addr;
u32 msg_i = 0; u32 msg_i = 0;
outgoing_message.bytes[msg_i++] = (u8)MessagePayoffResult; outgoing_message.bytes[msg_i++] = (u8)MessagePayoffResult;
outgoing_message.bytes_len = msg_i; outgoing_message.bytes_len = msg_i;
@@ -394,9 +382,8 @@ fn void sendMessagePayoffResult(SocketAddress addr) {
addSystemMessage((u8*)"MessagePayoffResult sent"); addSystemMessage((u8*)"MessagePayoffResult sent");
} }
fn void sendMessageStarPositions(SocketAddress addr) { fn void sendMessageStarPositions(i32 socket_fd, SocketAddress addr) {
UDPMessage outgoing_message = {0}; NetworkMessage outgoing_message = { .use_socket = true, .address = addr, .socket_fd = socket_fd };
outgoing_message.address = addr;
// tell the client about the map // tell the client about the map
u32 star_msg_size = 2+MAX_PLANETS; u32 star_msg_size = 2+MAX_PLANETS;
outgoing_message.bytes_len = 1+(star_msg_size*STAR_SYSTEM_COUNT); 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); 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) { fn void sendMessagePlayerDetails(i32 socket_fd, PlayerShip ship, SocketAddress addr) {
UDPMessage outgoing_message = makeMessagePlayerDetails(ship); NetworkMessage outgoing_message = makeMessagePlayerDetails(ship);
outgoing_message.address = addr; outgoing_message.address = addr;
outgoing_message.use_socket = true;
outgoing_message.socket_fd = socket_fd;
outgoingMessageQueuePush(state.network_send_queue, &outgoing_message); 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) { fn void sendMessageAuctionBidResult(i32 socket_fd, SocketAddress addr, AuctionBidResult result) {
UDPMessage outgoing_message = {.address = addr}; NetworkMessage outgoing_message = { .address = addr, .use_socket = true, .socket_fd = socket_fd };
u32 msg_i = 0; u32 msg_i = 0;
outgoing_message.bytes[msg_i++] = (u8)MessageAuctionBidResult; outgoing_message.bytes[msg_i++] = (u8)MessageAuctionBidResult;
outgoing_message.bytes[msg_i++] = result; 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) { 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}; 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; u32 msg_idx = 0;
ParsedClientCommand parsed = { ParsedClientCommand parsed = {
.type = (CommandType)message[msg_idx++], .type = (CommandType)message[msg_idx++],
.socket_fd = socket,
.sender_ip = sender.sin_addr.s_addr, .sender_ip = sender.sin_addr.s_addr,
.sender_port = sender.sin_port, .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); sprintf(sbuf, "Logging in player: %s %d %s\n", command_type_strings[parsed.type], name_len, message + 7);
addSystemMessage((u8*)sbuf); addSystemMessage((u8*)sbuf);
} break; } break;
case CommandKeepAlive: break;
case CommandPayMortgage: { case CommandPayMortgage: {
parsed.id = readU64FromBufferLE(message + msg_idx); parsed.id = readU64FromBufferLE(message + msg_idx);
MemoryZero(sbuf, SBUFLEN); MemoryZero(sbuf, SBUFLEN);
@@ -521,18 +515,30 @@ fn void handleIncomingMessage(u8* message, u32 len, SocketAddress sender, i32 so
pccThreadSafeQueuePush(state.network_recv_queue, &parsed); pccThreadSafeQueuePush(state.network_recv_queue, &parsed);
} }
fn void* receiveNetworkUpdates(void* udp) { fn void removeClientBySocketFd(i32 socket_fd) {
UDPServer server = *(UDPServer*)udp; Client blank_client = {0};
dbg("receiveNetworkUpdates() sock=%d\n", server.server_socket); // i=1 because first client is null-client
infiniteReadUDPServer(&server, handleIncomingMessage); 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; return NULL;
} }
fn void* sendNetworkUpdates(void* sock) { fn void* sendNetworkUpdates(void* tcp_server) {
TCPServer server = *(TCPServer*)tcp_server;
ThreadContext tctx = {0}; ThreadContext tctx = {0};
tctxInit(&tctx); tctxInit(&tctx);
i32* socket_ptr = (i32*)sock; i32 socket = server.socket_fd;
i32 socket = *socket_ptr;
char sbuf[SBUFLEN] = {0}; char sbuf[SBUFLEN] = {0};
u64 send_loop = 0; u64 send_loop = 0;
while (!should_quit) { while (!should_quit) {
@@ -541,103 +547,96 @@ fn void* sendNetworkUpdates(void* sock) {
// 1. clear out our "outgoingMessage" queue // 1. clear out our "outgoingMessage" queue
{ {
UDPMessage to_send = { 0 }; NetworkMessage to_send = { 0 };
UDPMessage* next_to_send = outgoingMessageNonblockingQueuePop(state.network_send_queue, &to_send); NetworkMessage* next_to_send = outgoingMessageNonblockingQueuePop(state.network_send_queue, &to_send);
u8List bytes_list = { 0 }; u8List bytes_list = { 0 };
while (next_to_send != NULL) { while (next_to_send != NULL) {
bytes_list.items = to_send.bytes; if (to_send.use_socket) {
bytes_list.length = to_send.bytes_len; char sbuf[SBUFLEN] = {0};
bytes_list.capacity = to_send.bytes_len; sprintf(sbuf, "sendTCPMessage() sock=%d msg=%s len=%d\n", to_send.socket_fd, MESSAGE_STRINGS[to_send.bytes[0]], to_send.bytes_len);
sendUDPu8List(socket, &to_send.address, &bytes_list); 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); next_to_send = outgoingMessageNonblockingQueuePop(state.network_send_queue, &to_send);
} }
} }
UDPMessage sys_udp_msg = { 0 }; NetworkMessage base_sys_msg = makeMessageSystemCommodities(&state.map[send_loop/2 % STAR_SYSTEM_COUNT]);
sys_udp_msg = makeMessageSystemCommodities(&state.map[send_loop/2 % STAR_SYSTEM_COUNT]); NetworkMessage base_pass_msg = makeMessageSystemPassengers(&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,
};
lockMutex(&state.client_mutex); { lockMutex(&state.client_mutex); {
// WARNING the `i` starts at 1 here because state.clients.items[0] is a "null" Client // 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++) { for (u32 i = 1; i < state.clients.length; i++) {
Client client = state.clients.items[i]; Client client = state.clients.items[i];
if (client.last_ping+CLIENT_TIMEOUT_FRAMES < state.frame) { if (client.active == false) {
memset(&state.clients.items[i], 0, sizeof(Client));
continue; continue;
} }
if (send_loop % 2 == 0) { if (send_loop % 2 == 0) {
// every other "send-frame" we send each connnected client the current prices for a different system // 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 // 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 // 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 // send the client the current auction details for their current system
Account* account = &state.accounts[client.account_id]; Account* account = &state.accounts[client.account_id];
if (!accountIsEmpty(account)) { if (!accountIsEmpty(account)) {
StarSystem curr = state.map[account->ship.system_idx]; StarSystem curr = state.map[account->ship.system_idx];
MemoryZero(sbuf, SBUFLEN); NetworkMessage auction_msg = {
u32 msgidx = 0; .use_socket = true,
sbuf[msgidx++] = MessageAuctionDetails; .socket_fd = client.socket_fd,
sbuf[msgidx++] = curr.auction.type; .address = client.address,
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,
}; };
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 // update all the changed systems
UDPMessage msg_data; NetworkMessage msg_data;
for (u32 ii = 0; ii < STAR_SYSTEM_COUNT; ii++) { for (u32 ii = 0; ii < STAR_SYSTEM_COUNT; ii++) {
if (state.map[ii].changed == true) { if (state.map[ii].changed == true) {
// send commodities // send commodities
msg_data = makeMessageSystemCommodities(&state.map[ii]); msg_data = makeMessageSystemCommodities(&state.map[ii]);
u8List msg = { msg_data.address = client.address;
.capacity = UDP_MAX_MESSAGE_LEN, msg_data.socket_fd = client.socket_fd;
.items = msg_data.bytes, msg_data.use_socket = true;
.length = msg_data.bytes_len, sendTCPMessage(msg_data);
};
sendUDPu8List(socket, &client.address, &msg);
// send passenger jobs // send passenger jobs
MemoryZero(sbuf, SBUFLEN); MemoryZero(sbuf, SBUFLEN);
sprintf(sbuf, "sending passenger jobs for %s\n", STAR_NAMES[ii]); sprintf(sbuf, "sending passenger jobs for %s\n", STAR_NAMES[ii]);
addSystemMessage((u8*)sbuf); addSystemMessage((u8*)sbuf);
msg_data = makeMessageSystemPassengers(&state.map[ii]); msg_data = makeMessageSystemPassengers(&state.map[ii]);
msg.capacity = UDP_MAX_MESSAGE_LEN; msg_data.address = client.address;
msg.items = msg_data.bytes; msg_data.socket_fd = client.socket_fd;
msg.length = msg_data.bytes_len; msg_data.use_socket = true;
sendUDPu8List(socket, &client.address, &msg); sendTCPMessage(msg_data);
} }
} }
// update all the changed accounts // update all the changed accounts
for (u32 ii = 0; ii < ACCOUNT_LEN; ii++) { for (u32 ii = 0; ii < ACCOUNT_LEN; ii++) {
if (state.accounts[ii].changed == true) { if (state.accounts[ii].changed == true) {
msg_data = makeMessagePlayerDetails(state.accounts[ii].ship); msg_data = makeMessagePlayerDetails(state.accounts[ii].ship);
u8List msg = { msg_data.address = client.address;
.capacity = UDP_MAX_MESSAGE_LEN, msg_data.socket_fd = client.socket_fd;
.items = msg_data.bytes, msg_data.use_socket = true;
.length = msg_data.bytes_len, sendTCPMessage(msg_data);
};
sendUDPu8List(socket, &client.address, &msg);
} }
} }
} }
@@ -670,7 +669,7 @@ fn void* gameLoop(void* params) {
char sbuf[SBUFLEN] = {0}; char sbuf[SBUFLEN] = {0};
sprintf(sbuf, "Lane %lld (%lld) of %lld starting.", lane_ctx->lane_idx, LaneIdx(), lane_ctx->lane_count); sprintf(sbuf, "Lane %lld (%lld) of %lld starting.", lane_ctx->lane_idx, LaneIdx(), lane_ctx->lane_count);
addSystemMessage((u8*)sbuf); addSystemMessage((u8*)sbuf);
UDPMessage outgoing_message = {0}; NetworkMessage outgoing_message = { .use_socket = true };
u64 loop_start; u64 loop_start;
u64 last_burn = 0; u64 last_burn = 0;
u64 last_hp_regen = 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_addr.s_addr = msg.sender_ip;
sender.sin_port = msg.sender_port; sender.sin_port = msg.sender_port;
// find which client it is // 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]; Client* client = &state.clients.items[client_handle];
switch (msg.type) { switch (msg.type) {
case CommandBuyAuction: { case CommandBuyAuction: {
@@ -713,15 +712,15 @@ fn void* gameLoop(void* params) {
player_sys->auction.finished_at = state.frame; player_sys->auction.finished_at = state.frame;
account->ship.credits -= player_sys->auction.price; account->ship.credits -= player_sys->auction.price;
account->ship.commodities[player_sys->auction.type] += player_sys->auction.qty; account->ship.commodities[player_sys->auction.type] += player_sys->auction.qty;
sendMessageAuctionBidResult(sender, AuctionBidResultPurchased); sendMessageAuctionBidResult(msg.socket_fd, sender, AuctionBidResultPurchased);
sendMessagePlayerDetails(account->ship, sender); sendMessagePlayerDetails(msg.socket_fd, account->ship, sender);
} else if (!player_has_money_for_purchase) { } else if (!player_has_money_for_purchase) {
sendMessageAuctionBidResult(sender, AuctionBidResultNotEnoughMoney); sendMessageAuctionBidResult(msg.socket_fd, sender, AuctionBidResultNotEnoughMoney);
} else { } else {
sendMessageAuctionBidResult(sender, AuctionBidResultNotEnoughCargoSpace); sendMessageAuctionBidResult(msg.socket_fd, sender, AuctionBidResultNotEnoughCargoSpace);
} }
} else { } else {
sendMessageAuctionBidResult(sender, AuctionBidResultAuctionAlreadyFinished); sendMessageAuctionBidResult(msg.socket_fd, sender, AuctionBidResultAuctionAlreadyFinished);
} }
} break; } break;
case CommandAcceptPassengerJob: { case CommandAcceptPassengerJob: {
@@ -751,7 +750,7 @@ fn void* gameLoop(void* params) {
MemoryZero(&player_sys->offers[i], sizeof(PassengerJobOffer)); MemoryZero(&player_sys->offers[i], sizeof(PassengerJobOffer));
player_sys->changed = true; player_sys->changed = true;
succeeded = true; succeeded = true;
sendMessageJobAcceptResult(sender, succeeded); sendMessageJobAcceptResult(client->socket_fd, sender, succeeded);
// end both for loops "break break;" // end both for loops "break break;"
ii = MAX_PASSENGER_BERTHS; ii = MAX_PASSENGER_BERTHS;
i = MAX_PASSENGER_JOB_OFFERS; i = MAX_PASSENGER_JOB_OFFERS;
@@ -762,7 +761,7 @@ fn void* gameLoop(void* params) {
} }
} }
if (!succeeded) { if (!succeeded) {
sendMessageJobAcceptResult(sender, succeeded); sendMessageJobAcceptResult(client->socket_fd, sender, succeeded);
} }
} break; } break;
case CommandPayMortgage: { case CommandPayMortgage: {
@@ -778,7 +777,7 @@ fn void* gameLoop(void* params) {
account->ship.credits -= amount_to_pay; account->ship.credits -= amount_to_pay;
account->ship.remaining_mortgage -= amount_to_pay; account->ship.remaining_mortgage -= amount_to_pay;
account->changed = true; account->changed = true;
sendMessagePayoffResult(sender); sendMessagePayoffResult(client->socket_fd, sender);
} break; } break;
case CommandSetDestination: { case CommandSetDestination: {
if (client_handle == 0) break; if (client_handle == 0) break;
@@ -789,7 +788,7 @@ fn void* gameLoop(void* params) {
if (client_handle == 0) break; if (client_handle == 0) break;
Account* account = &state.accounts[client->account_id]; Account* account = &state.accounts[client->account_id];
account->ship.ready_to_depart = msg.byte; account->ship.ready_to_depart = msg.byte;
sendMessagePlayerDetails(account->ship, sender); sendMessagePlayerDetails(client->socket_fd, account->ship, sender);
state.all_accounts_ready = true; state.all_accounts_ready = true;
for (u32 i = 0; i < ACCOUNT_LEN; i++) { for (u32 i = 0; i < ACCOUNT_LEN; i++) {
if (!accountIsEmpty(&state.accounts[i])) { if (!accountIsEmpty(&state.accounts[i])) {
@@ -853,33 +852,17 @@ fn void* gameLoop(void* params) {
} }
} }
sys->changed = true; sys->changed = true;
sendMessagePlayerDetails(account->ship, sender); sendMessagePlayerDetails(client->socket_fd, account->ship, sender);
sendMessageTransactionResult(sender, qty_traded, is_buying_from_system, credit_value); sendMessageTransactionResult(client->socket_fd, 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;
}
} break; } break;
case CommandLogin: { case CommandLogin: {
if (client_handle == 0) { 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]; client = &state.clients.items[client_handle];
MemoryZero(sbuf, SBUFLEN); MemoryZero(sbuf, SBUFLEN);
sprintf(sbuf, "pushed new client handle = %d\n", client_handle); sprintf(sbuf, "pushed new client handle = %d\n", client_handle);
addSystemMessage((u8*)sbuf); 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; struct in_addr ipaddr;
@@ -906,12 +889,13 @@ fn void* gameLoop(void* params) {
if (pw_matches) { if (pw_matches) {
addSystemMessage((u8*)" pw matched\n"); addSystemMessage((u8*)" pw matched\n");
existing_account->changed = true; existing_account->changed = true;
sendMessageStarPositions(sender); sendMessageStarPositions(client->socket_fd, sender);
} else { } else {
// tell the client they did a bad pw // tell the client they did a bad pw
outgoing_message.bytes[0] = (u8)MessageBadPw; outgoing_message.bytes[0] = (u8)MessageBadPw;
outgoing_message.bytes_len = 1; outgoing_message.bytes_len = 1;
outgoing_message.address = sender; outgoing_message.address = sender;
outgoing_message.socket_fd = client->socket_fd;
outgoingMessageQueuePush(state.network_send_queue, &outgoing_message); outgoingMessageQueuePush(state.network_send_queue, &outgoing_message);
addSystemMessage((u8*)"MessageBadPw sent\n"); addSystemMessage((u8*)"MessageBadPw sent\n");
break; break;
@@ -937,6 +921,7 @@ fn void* gameLoop(void* params) {
writeU64ToBufferLE(outgoing_message.bytes + 1, existing_account->ship.id); writeU64ToBufferLE(outgoing_message.bytes + 1, existing_account->ship.id);
outgoing_message.bytes_len = 9; outgoing_message.bytes_len = 9;
outgoing_message.address = sender; outgoing_message.address = sender;
outgoing_message.socket_fd = client->socket_fd;
outgoingMessageQueuePush(state.network_send_queue, &outgoing_message); outgoingMessageQueuePush(state.network_send_queue, &outgoing_message);
addSystemMessage((u8*)"MessageCharacterId sent\n"); addSystemMessage((u8*)"MessageCharacterId sent\n");
} else { } else {
@@ -944,6 +929,7 @@ fn void* gameLoop(void* params) {
outgoing_message.bytes[0] = (u8)MessageNewAccountCreated; outgoing_message.bytes[0] = (u8)MessageNewAccountCreated;
outgoing_message.bytes_len = 1; outgoing_message.bytes_len = 1;
outgoing_message.address = sender; outgoing_message.address = sender;
outgoing_message.socket_fd = client->socket_fd;
outgoingMessageQueuePush(state.network_send_queue, &outgoing_message); outgoingMessageQueuePush(state.network_send_queue, &outgoing_message);
addSystemMessage((u8*)"MessageNewAccountCreated sent\n"); addSystemMessage((u8*)"MessageNewAccountCreated sent\n");
} }
@@ -971,7 +957,7 @@ fn void* gameLoop(void* params) {
.base_cost = template.base_cost, .base_cost = template.base_cost,
.remaining_mortgage = template.base_cost - STARTING_DOWN_PAYMENT, .remaining_mortgage = template.base_cost - STARTING_DOWN_PAYMENT,
.interest_rate = calcInterestRate(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_fuel = template.cu_m_fuel,
.cu_m_o2 = template.cu_m_o2, .cu_m_o2 = template.cu_m_o2,
.id = account->id, .id = account->id,
@@ -990,18 +976,20 @@ fn void* gameLoop(void* params) {
// tell the client their account id // tell the client their account id
outgoing_message.address = sender; outgoing_message.address = sender;
outgoing_message.socket_fd = client->socket_fd;
outgoing_message.bytes_len = 9; outgoing_message.bytes_len = 9;
outgoing_message.bytes[0] = (u8)MessageCharacterId; outgoing_message.bytes[0] = (u8)MessageCharacterId;
writeU64ToBufferLE(outgoing_message.bytes + 1, account->ship.id); writeU64ToBufferLE(outgoing_message.bytes + 1, account->ship.id);
outgoingMessageQueuePush(state.network_send_queue, &outgoing_message); outgoingMessageQueuePush(state.network_send_queue, &outgoing_message);
addSystemMessage((u8*)"MessageCharacterId sent\n"); 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 = makeMessageSystemCommodities(&starting_system);
outgoing_message.address = sender; outgoing_message.address = sender;
outgoing_message.socket_fd = client->socket_fd;
outgoingMessageQueuePush(state.network_send_queue, &outgoing_message); outgoingMessageQueuePush(state.network_send_queue, &outgoing_message);
MemoryZero(sbuf, SBUFLEN); MemoryZero(sbuf, SBUFLEN);
sprintf(sbuf, "%s sent\n", MESSAGE_STRINGS[outgoing_message.bytes[0]]); 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) { if (state.all_accounts_ready) {
for (u32 i = 1; i < SERVER_MAX_CLIENTS; i++) { for (u32 i = 1; i < NET_SERVER_MAX_CLIENTS; i++) {
if (state.clients.items[i].last_ping != 0) { if (state.clients.items[i].active) {
outgoing_message.address = state.clients.items[i].address; 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_len = 1;
outgoing_message.bytes[0] = (u8)MessageTurnTick; outgoing_message.bytes[0] = (u8)MessageTurnTick;
outgoingMessageQueuePush(state.network_send_queue, &outgoing_message); outgoingMessageQueuePush(state.network_send_queue, &outgoing_message);
@@ -1031,9 +1020,10 @@ fn void* gameLoop(void* params) {
} }
if (state.someone_won) { if (state.someone_won) {
for (u32 i = 1; i < SERVER_MAX_CLIENTS; i++) { for (u32 i = 1; i < NET_SERVER_MAX_CLIENTS; i++) {
if (state.clients.items[i].last_ping != 0) { if (state.clients.items[i].active) {
outgoing_message.address = state.clients.items[i].address; 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_len = 2;
outgoing_message.bytes[0] = (u8)MessageGameOver; outgoing_message.bytes[0] = (u8)MessageGameOver;
outgoing_message.bytes[1] = state.winner_id; 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; f32 t_sec = ((f32)state.frame - (f32)grace_period_ends_at) / (f32)GOAL_GAME_LOOPS_PER_S;
// t is in minutes // t is in minutes
f32 t = t_sec / 60; 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; f32 floor_price = COMMODITIES[sys->auction.type].price * 0.9;
sys->auction.price = Max((initial_price * pow(EULERS_E, decay)), floor_price); sys->auction.price = Max((initial_price * pow(EULERS_E, decay)), floor_price);
} }
@@ -1179,8 +1169,9 @@ fn void* gameLoop(void* params) {
addSystemMessage((u8*)sbuf); addSystemMessage((u8*)sbuf);
// send a job completion message? // send a job completion message?
for (u32 i = 1; i < state.clients.length; i++) { 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.address = state.clients.items[i].address;
outgoing_message.socket_fd = state.clients.items[i].socket_fd;
outgoing_message.bytes_len = 2; outgoing_message.bytes_len = 2;
outgoing_message.bytes[0] = (u8)MessageJobComplete; outgoing_message.bytes[0] = (u8)MessageJobComplete;
outgoing_message.bytes[1] = true; outgoing_message.bytes[1] = true;
@@ -1201,8 +1192,9 @@ fn void* gameLoop(void* params) {
addSystemMessage((u8*)sbuf); addSystemMessage((u8*)sbuf);
// send a job failed message? // send a job failed message?
for (u32 i = 1; i < state.clients.length; i++) { 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.address = state.clients.items[i].address;
outgoing_message.socket_fd = state.clients.items[i].socket_fd;
outgoing_message.bytes_len = 2; outgoing_message.bytes_len = 2;
outgoing_message.bytes[0] = (u8)MessageJobComplete; outgoing_message.bytes[0] = (u8)MessageJobComplete;
outgoing_message.bytes[1] = false; outgoing_message.bytes[1] = false;
@@ -1456,9 +1448,9 @@ i32 main(i32 argc, ptr argv[]) {
state.network_send_queue = newOutgoingMessageQueue(&permanent_arena); state.network_send_queue = newOutgoingMessageQueue(&permanent_arena);
// alloc the global hashmap of rooms // alloc the global hashmap of rooms
// init + alloc clients // 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.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++) { for (i32 i = 0; i < SYSTEM_MESSAGES_LEN; i++) {
system_messages[i].capacity = MAX_SYSTEM_MESSAGE_LEN; system_messages[i].capacity = MAX_SYSTEM_MESSAGE_LEN;
system_messages[i].length = 0; system_messages[i].length = 0;
@@ -1657,13 +1649,13 @@ i32 main(i32 argc, ptr argv[]) {
} }
// 2. spin off sendNetworkUpdates() infinite loop thread // 2. spin off sendNetworkUpdates() infinite loop thread
UDPServer listener = createUDPServer(SERVER_PORT); TCPServer server = createTCPServer(SERVER_PORT);
if (!listener.ready) { if (!server.ready) {
exitWithErrorMessage("Couldn't start the udp server"); 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) // 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; u64 lane_broadcast_val = 0;
Barrier barrier = osBarrierAlloc(GAME_THREAD_CONCURRENCY); Barrier barrier = osBarrierAlloc(GAME_THREAD_CONCURRENCY);
+1 -2
View File
@@ -18,6 +18,7 @@
#define AUCTION_COMMODITY_CUTOFF (7) #define AUCTION_COMMODITY_CUTOFF (7)
#define MAX_SCREEN_HEIGHT 300 #define MAX_SCREEN_HEIGHT 300
#define MAX_SCREEN_WIDTH 800 #define MAX_SCREEN_WIDTH 800
#define SERVER_PORT 7777
#define AUCTION_PRICE_START_MULTIPLE (12) #define AUCTION_PRICE_START_MULTIPLE (12)
@@ -366,7 +367,6 @@ typedef enum Direction {
typedef enum CommandType { typedef enum CommandType {
CommandInvalid, CommandInvalid,
CommandKeepAlive,
CommandLogin, CommandLogin,
CommandCreateCharacter, CommandCreateCharacter,
CommandTransact, CommandTransact,
@@ -380,7 +380,6 @@ typedef enum CommandType {
static const char* command_type_strings[CommandType_Count] = { static const char* command_type_strings[CommandType_Count] = {
"Invalid", "Invalid",
"KeepAlive",
"Login", "Login",
"CreateCharacter", "CreateCharacter",
"ReadyStatus", "ReadyStatus",