From 711a812145f2df0c072f08e009e8236bc5769f37 Mon Sep 17 00:00:00 2001 From: Tenari Date: Thu, 19 Mar 2026 19:19:19 -0500 Subject: [PATCH] server show map --- src/client.c | 42 ------- src/server.c | 342 +++++++++++++++++++++++++++++++++++++++++++++------ src/shared.h | 23 ++-- 3 files changed, 315 insertions(+), 92 deletions(-) diff --git a/src/client.c b/src/client.c index 48a9cbc..e4ceee3 100644 --- a/src/client.c +++ b/src/client.c @@ -187,7 +187,6 @@ global FieldDescriptor COMPARE_COMMODITY_FIELDS[STAR_SYSTEM_COUNT] = { { "Travel Fuel kg", FieldTypeU32, offsetof(CompareCommodity, fuel_distance), 16 }, }; - ///// FUNCTIONS fn ParsedServerMessageThreadQueue* newPSMThreadQueue(Arena* a) { ParsedServerMessageThreadQueue* result = arenaAlloc(a, sizeof(ParsedServerMessageThreadQueue)); @@ -266,47 +265,6 @@ fn void renderSystemMessages(Pixel* buf, Dim2 screen_dimensions, Box sys_msg_box } } -fn void renderPercentBar(TuiState* tui, u16 x, u16 y, u16 width, u8 ansi_color, u64 value, u64 max) { - Pixel* buf = tui->frame_buffer; - Dim2 screen_dimensions = tui->screen_dimensions; - u16 pos = x + (screen_dimensions.width * y); - buf[pos].bytes[0] = '['; - pos = x+width + (screen_dimensions.width * y); - buf[pos].bytes[0] = ']'; - pos = x+1 + (screen_dimensions.width * y); - f32 base_ratio = 0; - if (max != 0) { - base_ratio = ((f32)value / (f32)max); - } - f32 raw_ratio = base_ratio * (width-2); - u32 full_spaces_count = (u32) raw_ratio; - f32 remainder = raw_ratio - full_spaces_count; - for (u32 i = 0; i < full_spaces_count; i++) { - buf[pos+i].foreground = ansi_color; - renderUtf8CharToBuffer(buf, x+1+i, y, "█", screen_dimensions); - } - buf[pos+full_spaces_count].foreground = ansi_color; - if (value == max) { - renderUtf8CharToBuffer(buf, x+1+full_spaces_count, y, "█", screen_dimensions); - } else if (remainder > 0.875) { - renderUtf8CharToBuffer(buf, x+1+full_spaces_count, y, "▉", screen_dimensions); - } else if (remainder > 0.75) { - renderUtf8CharToBuffer(buf, x+1+full_spaces_count, y, "▊", screen_dimensions); - } else if (remainder > 0.625) { - renderUtf8CharToBuffer(buf, x+1+full_spaces_count, y, "▋", screen_dimensions); - } else if (remainder > 0.5) { - renderUtf8CharToBuffer(buf, x+1+full_spaces_count, y, "▌", screen_dimensions); - } else if (remainder > 0.375) { - renderUtf8CharToBuffer(buf, x+1+full_spaces_count, y, "▍", screen_dimensions); - } else if (remainder > 0.25) { - renderUtf8CharToBuffer(buf, x+1+full_spaces_count, y, "▎", screen_dimensions); - } else if (remainder > 0.125) { - renderUtf8CharToBuffer(buf, x+1+full_spaces_count, y, "▏", screen_dimensions); - } else { - renderUtf8CharToBuffer(buf, x+1+full_spaces_count, y, " ", screen_dimensions); - } -} - fn void renderStaticAssetToPixelBuffer(TuiState* tui, u8* asset, u32 len, u16 x, u16 y) { u16 line = 0; u16 x_in_line = 0; diff --git a/src/server.c b/src/server.c index 5be9b9a..e4bd45c 100644 --- a/src/server.c +++ b/src/server.c @@ -32,8 +32,17 @@ #define CHUNK_SIZE 64 #define ACCOUNT_LEN (16) #define PARSED_CLIENT_COMMAND_THREAD_QUEUE_LEN 64 +#define SYSTEM_MESSAGES_LEN 32 +#define MAX_SYSTEM_MESSAGE_LEN 512 +#define SBUFLEN (512) ///// TypeDefs +typedef enum Tab { + TabDebug, + TabMap, + Tab_Count, +} Tab; + typedef struct ParsedClientCommand { CommandType type; u8 byte; @@ -86,6 +95,7 @@ typedef struct ClientList { typedef struct State { bool all_accounts_ready; + Tab tab; u8 winner_id; bool someone_won; Mutex client_mutex; @@ -103,10 +113,48 @@ typedef struct State { ///// Global Variables global State state = { 0 }; +global str TAB_STRS[Tab_Count] = {"Debug", "Map"}; global Arena permanent_arena = { 0 }; global bool debug_mode = false; +global bool should_quit = false; +global u8List system_messages[SYSTEM_MESSAGES_LEN] = {0}; +global u8 system_message_index = 0; ///// functionImplementations() +fn void addSystemMessage(u8* msg) { + // save the message to our system_messages ring buffer + memset(system_messages[system_message_index].items, 0, SYSTEM_MESSAGES_LEN); + sprintf((char*)system_messages[system_message_index].items, "%s", msg); + system_messages[system_message_index].length = strlen((char*)system_messages[system_message_index].items); + system_message_index += 1; + if (system_message_index == SYSTEM_MESSAGES_LEN) { + system_message_index = 0; + } +} + +fn void renderSystemMessages(Pixel* buf, Dim2 screen_dimensions, Box sys_msg_box) { + i32 printable_lines = sys_msg_box.height - 2; + if (printable_lines > SYSTEM_MESSAGES_LEN) { + printable_lines = SYSTEM_MESSAGES_LEN; + } + for (i32 i = 0; i < printable_lines; i++) { + i32 index = (system_message_index - 1 - i); + if (index < 0) { + index = SYSTEM_MESSAGES_LEN + index; + } + u32 y = sys_msg_box.y + (sys_msg_box.height - i) - 1; + u8List sys_msg = system_messages[index]; + for (i32 j = 0; j < MAX_SYSTEM_MESSAGE_LEN && j < sys_msg_box.width-4; j++) { + u32 pos = (sys_msg_box.x + 2+j) + (screen_dimensions.width * y); + if (j < sys_msg.length) { + if (sys_msg.items[j] != '\n') { + buf[pos].bytes[0] = sys_msg.items[j]; + } + } + } + } +} + fn ParsedClientCommandThreadQueue* newPCCThreadQueue(Arena* a) { ParsedClientCommandThreadQueue* result = arenaAlloc(a, sizeof(ParsedClientCommandThreadQueue)); MemoryZero(result, (sizeof *result)); @@ -279,7 +327,7 @@ fn void sendMessageTransactionResult(SocketAddress addr, u32 qty, bool buying, u msg_i += writeU32ToBufferLE(outgoing_message.bytes + msg_i, credits); outgoing_message.bytes_len = msg_i; outgoingMessageQueuePush(state.network_send_queue, &outgoing_message); - printf("%s sent\n", MESSAGE_STRINGS[outgoing_message.bytes[0]]); + addSystemMessage((u8*)"Message TransactionResult sent"); } fn UDPMessage makeMessagePlayerDetails(PlayerShip ship) { @@ -323,7 +371,7 @@ fn void sendMessageJobAcceptResult(SocketAddress addr, bool result) { outgoing_message.bytes[msg_i++] = result; outgoing_message.bytes_len = msg_i; outgoingMessageQueuePush(state.network_send_queue, &outgoing_message); - printf("%s sent\n", MESSAGE_STRINGS[outgoing_message.bytes[0]]); + addSystemMessage((u8*)"MessageJobAcceptResult sent"); } fn void sendMessagePayoffResult(SocketAddress addr) { @@ -333,7 +381,7 @@ fn void sendMessagePayoffResult(SocketAddress addr) { outgoing_message.bytes[msg_i++] = (u8)MessagePayoffResult; outgoing_message.bytes_len = msg_i; outgoingMessageQueuePush(state.network_send_queue, &outgoing_message); - printf("%s sent\n", MESSAGE_STRINGS[outgoing_message.bytes[0]]); + addSystemMessage((u8*)"MessagePayoffResult sent"); } fn void sendMessageStarPositions(SocketAddress addr) { @@ -351,18 +399,19 @@ fn void sendMessageStarPositions(SocketAddress addr) { } } outgoingMessageQueuePush(state.network_send_queue, &outgoing_message); - printf("%s sent\n", MESSAGE_STRINGS[outgoing_message.bytes[0]]); + addSystemMessage((u8*)"MessageStarPositions sent"); } fn void sendMessagePlayerDetails(PlayerShip ship, SocketAddress addr) { UDPMessage outgoing_message = makeMessagePlayerDetails(ship); outgoing_message.address = addr; outgoingMessageQueuePush(state.network_send_queue, &outgoing_message); - printf("%s sent\n", MESSAGE_STRINGS[outgoing_message.bytes[0]]); + addSystemMessage((u8*)"MessagePlayerDetails sent"); } 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}; u32 msg_idx = 0; ParsedClientCommand parsed = { .type = (CommandType)message[msg_idx++], @@ -408,12 +457,16 @@ fn void handleIncomingMessage(u8* message, u32 len, SocketAddress sender, i32 so parsed.pass = allocStringChunkList(&state.string_arena, temp_str); msg_idx += temp_str.length; - printf("Logging in player: %s %d %s\n", MESSAGE_STRINGS[parsed.type], name_len, message + 7); + MemoryZero(sbuf, SBUFLEN); + 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); - printf("paying off %lld\n", parsed.id); + MemoryZero(sbuf, SBUFLEN); + sprintf(sbuf, "paying off %lld", parsed.id); + addSystemMessage((u8*)sbuf); } break; case CommandSetDestination: { parsed.byte = message[1]; // index of system in array @@ -422,17 +475,17 @@ fn void handleIncomingMessage(u8* message, u32 len, SocketAddress sender, i32 so parsed.byte = message[1]; // boolean ready_to_depart } break; case CommandTransact: { - printf("command transact received\n"); + addSystemMessage((u8*)"command transact received"); parsed.byte = message[1]; // buy? parsed.qty = readU32FromBufferLE(message + 2); parsed.commodity = message[6]; } break; case CommandCreateCharacter: { - printf("command create character received\n"); + addSystemMessage((u8*)"command create character received"); parsed.byte = message[1]; } break; case CommandAcceptPassengerJob: { - printf("command accept passenger job received\n"); + addSystemMessage((u8*)"command accept passenger job received\n"); parsed.byte = message[1]; parsed.people = message[2]; parsed.time_limit = message[3]; @@ -445,7 +498,6 @@ fn void handleIncomingMessage(u8* message, u32 len, SocketAddress sender, i32 so } pccThreadSafeQueuePush(state.network_recv_queue, &parsed); - fflush(stdout); } fn void* receiveNetworkUpdates(void* udp) { @@ -460,8 +512,9 @@ fn void* sendNetworkUpdates(void* sock) { tctxInit(&tctx); i32* socket_ptr = (i32*)sock; i32 socket = *socket_ptr; + char sbuf[SBUFLEN] = {0}; u64 send_loop = 0; - while (true) { + while (!should_quit) { send_loop += 1; u64 loop_start = osTimeMicrosecondsNow(); @@ -524,7 +577,9 @@ fn void* sendNetworkUpdates(void* sock) { sendUDPu8List(socket, &client.address, &msg); // send passenger jobs - printf("sending passenger jobs for %s\n", STAR_NAMES[ii]); + 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; @@ -579,15 +634,16 @@ fn void* gameLoop(void* params) { .lane_ctx = *lane_ctx, }; tctxInit(&tctx); - printf("Lane %lld (%lld) of %lld starting.\n", lane_ctx->lane_idx, LaneIdx(), lane_ctx->lane_count); - fflush(stdout); + 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}; u64 loop_start; u64 last_burn = 0; u64 last_hp_regen = 0; Arena scratch_arena = {0}; arenaInit(&scratch_arena); - while (true) { + while (!should_quit) { loop_start = osTimeMicrosecondsNow(); if (LaneIdx() == 0) { // narrow @@ -684,7 +740,9 @@ fn void* gameLoop(void* params) { } } break; case CommandTransact: { - printf("Transact for client_handle=%d, on #%lld", client_handle, state.frame); + MemoryZero(sbuf, SBUFLEN); + sprintf(sbuf, "Transact for client_handle=%d, on #%lld", client_handle, state.frame); + addSystemMessage((u8*)sbuf); bool is_buying_from_system = msg.byte; Account* account = &state.accounts[client->account_id]; StarSystem* sys = findAccountsSystem(account); @@ -745,7 +803,9 @@ fn void* gameLoop(void* params) { if (client_handle == 0) { client_handle = pushClient(&state.clients, sender); client = &state.clients.items[client_handle]; - printf("pushed new client handle = %d\n", 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); @@ -765,15 +825,16 @@ fn void* gameLoop(void* params) { releaseStringChunkList(&state.string_arena, &msg.pass); Account* existing_account = findAccountByName(name); - printf("name(%d): %s pw(%d): %s acct?: %d\n", name.length, name.bytes, pw.length, pw.bytes, existing_account != NULL); - fflush(stdout); + MemoryZero(sbuf, SBUFLEN); + sprintf(sbuf, "name(%d): %s pw(%d): %s acct?: %d\n", name.length, name.bytes, pw.length, pw.bytes, existing_account != NULL); + addSystemMessage((u8*)sbuf); if (existing_account) { - printf(" existing account\n"); + addSystemMessage((u8*)" existing account\n"); bool pw_matches = stringsEq(&pw, &existing_account->pw); arenaDealloc(&permanent_arena, pw.capacity); arenaDealloc(&permanent_arena, name.capacity); if (pw_matches) { - printf(" pw matched\n"); + addSystemMessage((u8*)" pw matched\n"); existing_account->changed = true; sendMessageStarPositions(sender); } else { @@ -782,7 +843,7 @@ fn void* gameLoop(void* params) { outgoing_message.bytes_len = 1; outgoing_message.address = sender; outgoingMessageQueuePush(state.network_send_queue, &outgoing_message); - printf("MessageBadPw sent\n"); + addSystemMessage((u8*)"MessageBadPw sent\n"); break; } } else { @@ -793,7 +854,9 @@ fn void* gameLoop(void* params) { break; } } - printf("new account id=%d\n", existing_account->id); + MemoryZero(sbuf, SBUFLEN); + sprintf(sbuf, "new account id=%d\n", existing_account->id); + addSystemMessage((u8*)sbuf); existing_account->name = name; existing_account->pw = pw; } @@ -805,20 +868,24 @@ fn void* gameLoop(void* params) { outgoing_message.bytes_len = 9; outgoing_message.address = sender; outgoingMessageQueuePush(state.network_send_queue, &outgoing_message); - printf("MessageCharacterId sent\n"); + addSystemMessage((u8*)"MessageCharacterId sent\n"); } else { // tell the client they made a new account outgoing_message.bytes[0] = (u8)MessageNewAccountCreated; outgoing_message.bytes_len = 1; outgoing_message.address = sender; outgoingMessageQueuePush(state.network_send_queue, &outgoing_message); - printf("MessageNewAccountCreated sent\n"); + addSystemMessage((u8*)"MessageNewAccountCreated sent\n"); } - printf("client_handle=%d, acct_id=%d\n", client_handle, existing_account->id); + MemoryZero(sbuf, SBUFLEN); + sprintf(sbuf, "client_handle=%d, acct_id=%d\n", client_handle, existing_account->id); + addSystemMessage((u8*)sbuf); } break; case CommandCreateCharacter: { Account* account = &state.accounts[client->account_id]; - printf("creating character for account id=%d, %s\n", account->id, SHIP_TYPE_STRINGS[msg.byte]); + MemoryZero(sbuf, SBUFLEN); + sprintf(sbuf, "creating character for account id=%d, %s\n", account->id, SHIP_TYPE_STRINGS[msg.byte]); + addSystemMessage((u8*)sbuf); if (shipIsNull(&account->ship)) { ShipTemplate template = SHIPS[msg.byte]; PlayerShip player_ship = { @@ -841,7 +908,11 @@ fn void* gameLoop(void* params) { player_ship.commodities[CommodityHydrogenFuel] = player_ship.cu_m_fuel; player_ship.commodities[CommodityOxygen] = player_ship.cu_m_o2; account->ship = player_ship; - printf("ship_type=%s, client_handle=%d, acct_id=%d\n", SHIP_TYPE_STRINGS[msg.byte], client_handle, account->id); + + MemoryZero(sbuf, SBUFLEN); + sprintf(sbuf, "ship_type=%s, client_handle=%d, acct_id=%d\n", SHIP_TYPE_STRINGS[msg.byte], client_handle, account->id); + addSystemMessage((u8*)sbuf); + u32 starting_system_idx = rand() % STAR_SYSTEM_COUNT; StarSystem starting_system = state.map[starting_system_idx]; account->ship.system_idx = starting_system_idx; @@ -852,7 +923,7 @@ fn void* gameLoop(void* params) { outgoing_message.bytes[0] = (u8)MessageCharacterId; writeU64ToBufferLE(outgoing_message.bytes + 1, account->ship.id); outgoingMessageQueuePush(state.network_send_queue, &outgoing_message); - printf("MessageCharacterId sent\n"); + addSystemMessage((u8*)"MessageCharacterId sent\n"); sendMessagePlayerDetails(account->ship, sender); @@ -861,9 +932,11 @@ fn void* gameLoop(void* params) { outgoing_message = makeMessageSystemCommodities(&starting_system); outgoing_message.address = sender; outgoingMessageQueuePush(state.network_send_queue, &outgoing_message); - printf("%s sent\n", MESSAGE_STRINGS[outgoing_message.bytes[0]]); + MemoryZero(sbuf, SBUFLEN); + sprintf(sbuf, "%s sent\n", MESSAGE_STRINGS[outgoing_message.bytes[0]]); + addSystemMessage((u8*)sbuf); } else { - printf("client tried to create a character when he already has one."); + addSystemMessage((u8*)"client tried to create a character when he already has one."); } } break; case CommandType_Count: @@ -916,7 +989,7 @@ fn void* gameLoop(void* params) { // 2. tick non-user entities if (state.all_accounts_ready) { - printf("NEW TURN: ticking all_accounts_ready\n"); + addSystemMessage((u8*)"NEW TURN: ticking all_accounts_ready"); // tick all the star systems StarSystem* sys = NULL; Range1u64 sys_range = LaneRange(STAR_SYSTEM_COUNT); @@ -995,9 +1068,13 @@ fn void* gameLoop(void* params) { if (is_valid_job) { if (is_at_job_destination) { acct->ship.credits += job->reward; - printf("%d credits awarded to %s for finishing passenger job\n", job->reward, acct->name.bytes); + MemoryZero(sbuf, SBUFLEN); + sprintf(sbuf, "%d credits awarded to %s for finishing passenger job\n", job->reward, acct->name.bytes); + addSystemMessage((u8*)sbuf); MemoryZero(job, sizeof(Passenger)); - printf("%s FINISHED a passenger job!\n", acct->name.bytes); + MemoryZero(sbuf, SBUFLEN); + sprintf(sbuf, "%s FINISHED a passenger job!\n", acct->name.bytes); + 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) { @@ -1006,7 +1083,9 @@ fn void* gameLoop(void* params) { outgoing_message.bytes[0] = (u8)MessageJobComplete; outgoing_message.bytes[1] = true; outgoingMessageQueuePush(state.network_send_queue, &outgoing_message); - printf("sent MessageJobComplete to %s\n", acct->name.bytes); + MemoryZero(sbuf, SBUFLEN); + sprintf(sbuf, "sent MessageJobComplete to %s\n", acct->name.bytes); + addSystemMessage((u8*)sbuf); break; } } @@ -1015,7 +1094,9 @@ fn void* gameLoop(void* params) { if (job->turns_remaining == 0) { // they failed the job MemoryZero(job, sizeof(Passenger)); - printf("%s failed a passenger job\n", acct->name.bytes); + MemoryZero(sbuf, SBUFLEN); + sprintf(sbuf, "%s failed a passenger job\n", acct->name.bytes); + 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) { @@ -1024,7 +1105,9 @@ fn void* gameLoop(void* params) { outgoing_message.bytes[0] = (u8)MessageJobComplete; outgoing_message.bytes[1] = false; outgoingMessageQueuePush(state.network_send_queue, &outgoing_message); - printf("sent MessageJobComplete to %s\n", acct->name.bytes); + MemoryZero(sbuf, SBUFLEN); + sprintf(sbuf, "sent MessageJobComplete to %s\n", acct->name.bytes); + addSystemMessage((u8*)sbuf); break; } } @@ -1043,7 +1126,9 @@ fn void* gameLoop(void* params) { if (acct->ship.remaining_mortgage == 0) { state.winner_id = acct->id; state.someone_won = true; - printf("%s WON!!! %d\n", acct->name.bytes, i); + MemoryZero(sbuf, SBUFLEN); + sprintf(sbuf, "%s WON!!! %d\n", acct->name.bytes, i); + addSystemMessage((u8*)sbuf); acct->changed = true; break; } @@ -1073,6 +1158,170 @@ fn void setLowProductionCommodity(Planet* p, CommodityType t) { p->production[t] = rand() % (COMMODITIES[t].consumption / 2); } +fn bool updateAndRender(TuiState* tui, void* s, u8* input_buffer, u64 loop_count) { + State* state = (State*) s; + Dim2 screen_dimensions = tui->screen_dimensions; + if (screen_dimensions.height > MAX_SCREEN_HEIGHT || screen_dimensions.width > MAX_SCREEN_WIDTH) { + printf("\033[2J\033[%d;%df Your screen is too damn big. Shrink it to %dx%d max, bro... geez", 1,1, MAX_SCREEN_WIDTH, MAX_SCREEN_HEIGHT); + fflush(stdout); + return should_quit; + } + char sbuf[SBUFLEN] = {0}; + + // operate on screen-based input+state + bool user_pressed_tab = input_buffer[0] == ASCII_TAB && input_buffer[1] == 0; + bool user_pressed_shift_tab = input_buffer[0] == '\x1b' && input_buffer[1] == '[' && input_buffer[2] == 'Z'; + bool user_pressed_space = input_buffer[0] == ' ' && input_buffer[1] == 0; + bool user_pressed_esc = input_buffer[0] == ASCII_ESCAPE && input_buffer[1] == 0; + bool user_pressed_a_number = input_buffer[0] >= '0' && input_buffer[0] <= '9' && input_buffer[1] == 0; + bool user_pressed_up = input_buffer[0] == 27 && input_buffer[1] == 91 && input_buffer[2] == 65; + bool user_pressed_down = input_buffer[0] == 27 && input_buffer[1] == 91 && input_buffer[2] == 66; + bool user_pressed_left = input_buffer[0] == 27 && input_buffer[1] == 91 && input_buffer[2] == 68; + bool user_pressed_right = input_buffer[0] == 27 && input_buffer[1] == 91 && input_buffer[2] == 67; + bool user_pressed_backspace = input_buffer[0] == ASCII_BACKSPACE || input_buffer[0] == ASCII_DEL; + bool user_pressed_enter = input_buffer[0] == ASCII_RETURN || input_buffer[0] == ASCII_LINE_FEED; + + if (user_pressed_tab || user_pressed_shift_tab) { + state->tab = state->tab == TabDebug ? TabMap : TabDebug; + } + + // draw the tabs box + u32 tabs_y = 1; + u32 box_y = tabs_y + 2; + Box box = { + .x = 1, + .y = box_y, + .width = screen_dimensions.width - 3, + .height = screen_dimensions.height - box_y - 2, + }; + drawAnsiBox(tui->frame_buffer, box, screen_dimensions, true); + u32 tx = 2; + // draw the actual tabs + for (u32 i = 0; i < Tab_Count; i++) { + u32 tab_len = strlen(TAB_STRS[i]); + Box b = { .x = tx, .y = tabs_y, .width = tab_len+1, .height = 1 }; + drawAnsiBox(tui->frame_buffer, b, screen_dimensions, state->tab == i); + renderStrToBuffer(tui->frame_buffer, tx+1, tabs_y+ 1, TAB_STRS[i], screen_dimensions); + tx += (tab_len + 3); + } + + switch (state->tab) { + case TabDebug: { + if (input_buffer[0] == 'q' || user_pressed_esc) { + should_quit = true; + } + renderSystemMessages(tui->frame_buffer, tui->screen_dimensions, box); + } break; + case TabMap: { + u8 COLORS[] = {ANSI_HIGHLIGHT_RED, ANSI_HIGHLIGHT_BLUE, ANSI_HIGHLIGHT_YELLOW, ANSI_HIGHLIGHT_GREEN, ANSI_HIGHLIGHT_GRAY}; + u32 xw = 4; + u32 yh = 2; + if (box.width <= MAP_WIDTH*xw) { + renderStrToBuffer(tui->frame_buffer, box.x+2, box.y+2, "Your screen needs to be wider...", screen_dimensions); + break; + } + if (box.height < MAP_HEIGHT*yh) { + renderStrToBuffer(tui->frame_buffer, box.x+2, box.y+2, "Your screen needs to be taller...", screen_dimensions); + break; + } + + // render the damn map + u32 x_off = box.x+((box.width - MAP_WIDTH*xw)/2); + u32 y_off = box.y+1; + for (u32 i = 0; i < MAP_WIDTH*xw; i++) { + for (u32 ii = 0; ii < MAP_HEIGHT*yh; ii++) { + u32 sysx = i / xw; + u32 sysy = ii / yh; + u32 bufpos = XYToPos(x_off+i, y_off+ii, screen_dimensions.width); + tui->frame_buffer[bufpos].bytes[0] = ' '; + tui->frame_buffer[bufpos].foreground = ANSI_WHITE; + tui->frame_buffer[bufpos].background = ANSI_BLACK; + // try to colorize the map cell if a player is on it + for (u32 iii = 0; iii < ACCOUNT_LEN; iii++) { + Account* acct = &state->accounts[iii]; + if (acct->name.length > 0) { + StarSystem sys = state->map[acct->ship.system_idx]; + if (sys.x == sysx && sys.y == sysy) { + Box current_key = { .height = yh, .width = xw, .x = x_off+i, .y = y_off+ii }; + colorizeBox(tui, current_key, COLORS[iii], 0, ' '); + } + } + } + //if (state->pos.x == sysx && state->pos.y == sysy) { + // tui->frame_buffer[bufpos].background = ANSI_WHITE; + // tui->frame_buffer[bufpos].foreground = ANSI_BLACK; + //} else { + //} + } + } + for (u32 i = 0; i < STAR_SYSTEM_COUNT; i++) { + StarSystem sys = state->map[i]; + u32 sysx = x_off + xw*sys.x; + u32 sysy = y_off + yh*sys.y; + // clear the render-grid for this "tile" position + for (u32 ii = 0; ii < xw; ii++) { + for (u32 iii = 0; iii < yh; iii++) { + u32 bufpos = XYToPos(sysx+ii, sysy+iii, screen_dimensions.width); + tui->frame_buffer[bufpos].bytes[0] = 0; + } + } + renderUtf8CodePoint(tui, sysx, sysy, "⭐"); + renderUtf8CodePoint( + tui, + sysx +2, + sysy, + strForPlanet(sys.planets[0].type) + ); + renderUtf8CodePoint( + tui, + sysx, + sysy +1, + strForPlanet(sys.planets[1].type) + ); + renderUtf8CodePoint( + tui, + sysx +2, + sysy +1, + strForPlanet(sys.planets[2].type) + ); + } + for (u32 i = 0; i < STAR_SYSTEM_COUNT; i++) { + StarSystem sys = state->map[i]; + u32 sysx = x_off + xw*sys.x; + u32 sysy = y_off + yh*sys.y; + if (sysx == tui->cursor.x && sysy == tui->cursor.y) { + renderStrToBuffer(tui->frame_buffer, sysx, sysy+2, STAR_NAMES[i], screen_dimensions); + } + } + // TODO fix the background bleed from previous line + + // map key + y_off += 1+(MAP_HEIGHT*yh); + u32 original_x_off = x_off; + for (u32 i = 0; i < ACCOUNT_LEN; i++) { + Account* acct = &state->accounts[i]; + if (acct->name.length > 0) { + Box current_key = { .height = yh, .width = xw, .x = x_off, .y = y_off }; + colorizeBox(tui, current_key, COLORS[i], 0, ' '); + renderStrToBuffer(tui->frame_buffer, x_off+5, y_off, acct->name.bytes, screen_dimensions); + + if (i % 2 == 0) { + x_off += 20; + } else if (i % 2 == 1) { + y_off += 3; + x_off = original_x_off; + } + } + } + + } break; + case Tab_Count: {} break; + } + + return should_quit; +} + + // THE SERVER i32 main(i32 argc, ptr argv[]) { osInit(); @@ -1101,6 +1350,14 @@ i32 main(i32 argc, ptr argv[]) { state.clients.capacity = 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); + for (i32 i = 0; i < SYSTEM_MESSAGES_LEN; i++) { + system_messages[i].capacity = MAX_SYSTEM_MESSAGE_LEN; + system_messages[i].length = 0; + system_messages[i].items = arenaAllocArraySized(&permanent_arena, sizeof(u8), MAX_SYSTEM_MESSAGE_LEN); + } + + // GAME LOGIC SETUP + // set position of all star systems for (u32 i = 0; i < STAR_SYSTEM_COUNT; i++) { state.map[i].idx = i; @@ -1260,6 +1517,13 @@ i32 main(i32 argc, ptr argv[]) { lane_ctxs[i].broadcast_memory = &lane_broadcast_val; game_threads[i] = spawnThread(&gameLoop, &lane_ctxs[i]); } + infiniteUILoop( + MAX_SCREEN_WIDTH, + MAX_SCREEN_HEIGHT, + GOAL_GAME_LOOP_US, + &state, + updateAndRender + ); for (u32 i = 0; i < GAME_THREAD_CONCURRENCY; i++) { osThreadJoin(game_threads[i], MAX_u64); } diff --git a/src/shared.h b/src/shared.h index 21ead18..733690a 100644 --- a/src/shared.h +++ b/src/shared.h @@ -47,28 +47,28 @@ typedef struct ShipTemplate { global ShipTemplate SHIPS[] = { { - .type = ShipSparrow, .drive_efficiency = 10, .life_support_efficiency = 1, + .type = ShipSparrow, .drive_efficiency = 1, .life_support_efficiency = 1, .vacuum_cargo_slots = 5, .climate_cargo_slots = 0, .passenger_berths = 0, .passenger_amenities_flags = 0, .smugglers_hold_cu_m = 1, .base_cost = 100000, - .cu_m_fuel = 2500, .cu_m_o2 = 1000, + .cu_m_fuel = 2500, .cu_m_o2 = 500, }, { - .type = ShipDart, .drive_efficiency = 70, .life_support_efficiency = 3, + .type = ShipDart, .drive_efficiency = 7, .life_support_efficiency = 3, .vacuum_cargo_slots = 6, .climate_cargo_slots = 0, .passenger_berths = 1, .passenger_amenities_flags = 0, .smugglers_hold_cu_m = 0, .base_cost = 150000, .cu_m_fuel = 2000, .cu_m_o2 = 2000, }, { - .type = ShipHaulerPrimeZ1, .drive_efficiency = 50, .life_support_efficiency = 2, + .type = ShipHaulerPrimeZ1, .drive_efficiency = 5, .life_support_efficiency = 2, .vacuum_cargo_slots = 30, .climate_cargo_slots = 0, .passenger_berths = 0, .passenger_amenities_flags = 0, .smugglers_hold_cu_m = 0, .base_cost = 250000, .cu_m_fuel = 2500, .cu_m_o2 = 1000, }, { - .type = ShipNX400, .drive_efficiency = 60, .life_support_efficiency = 5, + .type = ShipNX400, .drive_efficiency = 6, .life_support_efficiency = 5, .vacuum_cargo_slots = 3, .climate_cargo_slots = 0, .passenger_berths = 3, .passenger_amenities_flags = 0, .smugglers_hold_cu_m = 0, .base_cost = 250000, @@ -488,8 +488,8 @@ fn u32 usedVacuumCargoSlots(PlayerShip ship) { fn f32 priceForCommodity(CommodityType type, u32 quantity, bool bid) { f32 result = 0.0; Commodity details = COMMODITIES[type]; - f32 max = details.price * 5; - f32 min = (f32)details.price / 5.0; + f32 max = details.price * 3; + f32 min = (f32)details.price / 3.0; if (quantity == 0) { return max; } @@ -502,16 +502,17 @@ fn f32 priceForCommodity(CommodityType type, u32 quantity, bool bid) { } // the bid/ask spread if (bid) { - result = result * 0.98; + result = result * 0.97; } else { - result = result * 1.02; + result = result * 1.03; } return result; } fn f32 calcInterestRate(u32 cost, u32 down_payment) { - f32 mortgage_rate = 10.0 *(1.0 - (3.0*(f32)down_payment / ((f32)cost/3.0))); - return mortgage_rate; + return 3.0; + //f32 mortgage_rate = 10.0 *(1.0 - (3.0*(f32)down_payment / ((f32)cost/3.0))); + //return mortgage_rate; } fn bool passengerJobEq(PassengerJobOffer a, PassengerJobOffer b) {