diff --git a/src/client.c b/src/client.c index d602683..f025dba 100644 --- a/src/client.c +++ b/src/client.c @@ -21,6 +21,8 @@ #define MAX_SYSTEM_MESSAGE_LEN 512 #define GOAL_LOOPS_PER_S 50 #define GOAL_LOOP_US 1000000/GOAL_LOOPS_PER_S +#define TURN_TICK_S (3) +#define TURN_TICK_LEN (GOAL_LOOPS_PER_S * TURN_TICK_S) #define LOGIN_NAME_BUFFER_LEN 16 #define PARSED_SERVER_MESSAGE_THREAD_QUEUE_LEN 16 #define SBUFLEN (512) @@ -49,6 +51,7 @@ typedef enum Screen { ScreenMainGame, ScreenDefeat, ScreenVictory, + ScreenTurnTick, Screen_Count } Screen; @@ -149,10 +152,10 @@ typedef struct GameState { StringChunkList message_input; StarSystem map[STAR_SYSTEM_COUNT]; Pos2 pos; - u32 destination_sys_idx; - StarSystem current; + u8 destination_sys_idx; StationTabStates station_tab_state; TransactionResult tx_result; + u64 turn_tick_started_on; } GameState; ///// GLOBALS @@ -375,8 +378,6 @@ fn void renderStaticAssetToPixelBuffer(TuiState* tui, u8* asset, u32 len, u16 x, } fn void clearServerSentState() { - //memset(&state.current_room, 0, sizeof(RenderableRoom)); - arenaClear(&state.entity_arena); state.entities.capacity = 64; state.entities.length = 0; @@ -391,6 +392,7 @@ fn void handleIncomingMessage(u8* message, u32 len, SocketAddress sender, i32 so ParsedServerMessage parsed = {0}; parsed.type = msg_type; switch (msg_type) { + case MessageTurnTick: case MessageNewAccountCreated: case MessageBadPw: {/*nothing to parse but the type*/} break; case MessageTransactionResult: { @@ -421,6 +423,8 @@ fn void handleIncomingMessage(u8* message, u32 len, SocketAddress sender, i32 so } break; case MessagePlayerDetails: { parsed.ship.type = message[msg_pos++]; + parsed.ship.system_idx = message[msg_pos++]; + parsed.ship.ready_to_depart = message[msg_pos++]; parsed.ship.drive_efficiency = message[msg_pos++]; parsed.ship.life_support_efficiency = message[msg_pos++]; parsed.ship.vacuum_cargo_slots = readU16FromBufferLE(message + msg_pos); @@ -511,6 +515,10 @@ fn bool updateAndRender(TuiState* tui, void* s, u8* input_buffer, u64 loop_count while (next_net_msg != NULL) { msg_iters += 0; switch (msg.type) { + case MessageTurnTick: { + state->screen = ScreenTurnTick; + state->turn_tick_started_on = loop_count; + } break; case MessageTransactionResult: { state->station_tab_state = StationTabStateResult; MemoryCopyStruct(&state->tx_result, &msg.tx_result); @@ -518,6 +526,8 @@ fn bool updateAndRender(TuiState* tui, void* s, u8* input_buffer, u64 loop_count case MessagePlayerDetails: { if (msg.ship.id == state->me.id) { state->me.type = msg.ship.type; + state->me.system_idx = msg.ship.system_idx; + state->me.ready_to_depart = msg.ship.ready_to_depart; state->me.drive_efficiency = msg.ship.drive_efficiency; state->me.life_support_efficiency = msg.ship.life_support_efficiency; state->me.vacuum_cargo_slots = msg.ship.vacuum_cargo_slots; @@ -547,10 +557,10 @@ fn bool updateAndRender(TuiState* tui, void* s, u8* input_buffer, u64 loop_count } break; case MessageSystemCommodities: { u32 sys_idx = msg.id; - MemoryCopyStruct(&state->current, &state->map[sys_idx]); + StarSystem* sys = &state->map[sys_idx]; for (u32 i = 0; i < MAX_PLANETS; i++) { for (u32 ii = 0; ii < Commodity_Count; ii++) { - state->current.planets[i].commodities[ii] = msg.sys.planets[i].commodities[ii]; + sys->planets[i].commodities[ii] = msg.sys.planets[i].commodities[ii]; } } } break; @@ -582,7 +592,7 @@ fn bool updateAndRender(TuiState* tui, void* s, u8* input_buffer, u64 loop_count 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_esc = input_buffer[0] == ASCII_ESCAPE && input_buffer[1] == 0; - bool user_pressed_a_number = input_buffer[0] >= '1' && input_buffer[0] <= '9' && 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; @@ -590,6 +600,25 @@ fn bool updateAndRender(TuiState* tui, void* s, u8* input_buffer, u64 loop_count 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; switch (state->screen) { + case ScreenTurnTick: { + u32 line = 2; + StarSystem dest = state->map[state->destination_sys_idx]; + MemoryZero(sbuf, SBUFLEN); + sprintf(sbuf, "Warping to %s...", dest.name); + renderStrToBuffer(tui->frame_buffer, (screen_dimensions.width - (strlen(sbuf)+4))/2, line++, sbuf, screen_dimensions); + + MemoryZero(sbuf, SBUFLEN); + u32 frames_left = (TURN_TICK_LEN - (loop_count - state->turn_tick_started_on)); + u32 seconds_left = (frames_left / GOAL_LOOPS_PER_S + 1); + sprintf(sbuf, "in %d", seconds_left); + renderStrToBuffer(tui->frame_buffer, (screen_dimensions.width - (strlen(sbuf)+4))/2, ++line, sbuf, screen_dimensions); + + if (state->turn_tick_started_on + TURN_TICK_LEN < loop_count) { + state->screen = ScreenMainGame; + state->menu.len = Tab_Count; + state->menu.selected_index = TabMap; + } + } break; case ScreenCreateCharacter: { //// SIMULATION if (state->me.id != 0) { @@ -607,7 +636,9 @@ fn bool updateAndRender(TuiState* tui, void* s, u8* input_buffer, u64 loop_count } else if (user_pressed_up || user_pressed_left) { state->menu.selected_index--; } else if (user_pressed_a_number) { - state->menu.selected_index = input_buffer[0] - '1'; + if (input_buffer[0] != '0') { + state->menu.selected_index = input_buffer[0] - '1'; + } } else if (user_pressed_enter) { // save ship choice to our gamestate ShipTemplate template = SHIPS[state->menu.selected_index]; @@ -717,6 +748,7 @@ fn bool updateAndRender(TuiState* tui, void* s, u8* input_buffer, u64 loop_count .height = screen_dimensions.height - box_y - 2, }; drawAnsiBox(tui->frame_buffer, box, screen_dimensions, true); + StarSystem curr = state->map[state->me.system_idx]; switch (tab) { case TabDebug: { if (input_buffer[0] == 'q' || user_pressed_esc) { @@ -762,7 +794,7 @@ fn bool updateAndRender(TuiState* tui, void* s, u8* input_buffer, u64 loop_count if (state->pos.x == sysx && state->pos.y == sysy) { tui->frame_buffer[bufpos].background = ANSI_WHITE; tui->frame_buffer[bufpos].foreground = ANSI_BLACK; - } else if (state->current.x == sysx && state->current.y == sysy) { + } else if (curr.x == sysx && curr.y == sysy) { // highlight the system we are currently at tui->frame_buffer[bufpos].background = ANSI_HIGHLIGHT_BLUE; tui->frame_buffer[bufpos].foreground = ANSI_BLACK; @@ -809,25 +841,16 @@ fn bool updateAndRender(TuiState* tui, void* s, u8* input_buffer, u64 loop_count // map key y_off += 1+(MAP_HEIGHT*yh); - for (u32 i = 0; i < xw; i++) { - for (u32 ii = 0; ii < yh; ii++) { - u32 bufpos = XYToPos(x_off+i, y_off+ii, screen_dimensions.width); - tui->frame_buffer[bufpos].background = ANSI_HIGHLIGHT_BLUE; - tui->frame_buffer[bufpos].bytes[0] = ' '; - } - } + Box current_key = { .height = yh, .width = xw, .x = x_off, .y = y_off }; + colorizeBox(tui, current_key, ANSI_HIGHLIGHT_BLUE, 0, ' '); renderStrToBuffer(tui->frame_buffer, x_off+5, y_off++, "Your current star system:", screen_dimensions); - if (state->current.name) { - renderStrToBuffer(tui->frame_buffer, x_off+5, y_off++, state->current.name, screen_dimensions); + if (curr.name) { + renderStrToBuffer(tui->frame_buffer, x_off+5, y_off++, curr.name, screen_dimensions); } y_off++; - for (u32 i = 0; i < xw; i++) { - for (u32 ii = 0; ii < yh; ii++) { - u32 bufpos = XYToPos(x_off+i, y_off+ii, screen_dimensions.width); - tui->frame_buffer[bufpos].background = ANSI_HIGHLIGHT_RED; - tui->frame_buffer[bufpos].bytes[0] = ' '; - } - } + + Box flight_dest_key = { .height = yh, .width = xw, .x = x_off, .y = y_off }; + colorizeBox(tui, flight_dest_key, ANSI_HIGHLIGHT_RED, 0, ' '); renderStrToBuffer(tui->frame_buffer, x_off+5, y_off++, "Flight Destination:", screen_dimensions); if (dest.name) { renderStrToBuffer(tui->frame_buffer, x_off+5, y_off++, dest.name, screen_dimensions); @@ -835,7 +858,7 @@ fn bool updateAndRender(TuiState* tui, void* s, u8* input_buffer, u64 loop_count y_off++; Pos2 dest_pos = {dest.x, dest.y}; - Pos2 curr_pos = {state->current.x, state->current.y}; + Pos2 curr_pos = {curr.x, curr.y}; u32 projected_fuel_cost = fuelCostForTravel(state->me.drive_efficiency, curr_pos, dest_pos); bool have_enough_fuel = projected_fuel_cost < state->me.commodities[CommodityHydrogenFuel]; MemoryZero(sbuf, SBUFLEN); @@ -852,10 +875,11 @@ fn bool updateAndRender(TuiState* tui, void* s, u8* input_buffer, u64 loop_count "Not enough Fuel! Need %dkg more", projected_fuel_cost - state->me.commodities[CommodityHydrogenFuel] ); - for (u32 i = 0; i < strlen(sbuf); i++) { - u32 bufpos = XYToPos(x_off+i, y_off, screen_dimensions.width); - tui->frame_buffer[bufpos].background = ANSI_HIGHLIGHT_RED; - } + Range1u32 range = { + .min = XYToPos(x_off, y_off, screen_dimensions.width), + .max = XYToPos(x_off+strlen(sbuf), y_off, screen_dimensions.width), + }; + colorizeRange(tui, range, ANSI_HIGHLIGHT_RED, 0); } renderStrToBuffer(tui->frame_buffer, x_off, y_off++, sbuf, screen_dimensions); @@ -863,6 +887,17 @@ fn bool updateAndRender(TuiState* tui, void* s, u8* input_buffer, u64 loop_count for (u32 i = 0; i < STAR_SYSTEM_COUNT; i++) { if (state->pos.x == state->map[i].x && state->pos.y == state->map[i].y) { state->destination_sys_idx = i; + // send the destination to the server + u32 msg_idx = 0; + UDPMessage msg = {0}; + msg.address = udp->server_address; + // 1. msg type/CommandType + msg.bytes[msg_idx++] = CommandSetDestination; + // 2. buy? + msg.bytes[msg_idx++] = state->destination_sys_idx; + msg.bytes_len = msg_idx; + + outgoingMessageQueuePush(network_send_queue, &msg); } } } @@ -889,9 +924,34 @@ fn bool updateAndRender(TuiState* tui, void* s, u8* input_buffer, u64 loop_count sprintf(sbuf, "Cargo: %d / %d", used_cargo, state->me.vacuum_cargo_slots); renderStrToBuffer(tui->frame_buffer, box.x+2, yoff++, sbuf, screen_dimensions); + MemoryZero(sbuf, SBUFLEN); + sprintf(sbuf, "Ready To Depart: %s", state->me.ready_to_depart ? "yes" : "no"); + Range1u32 range = { + .min = XYToPos(box.x+2, yoff, screen_dimensions.width), + .max = XYToPos(box.x+2+strlen(sbuf), yoff, screen_dimensions.width), + }; + tui->cursor.x = box.x+2; + tui->cursor.y = yoff; + colorizeRange(tui, range, ANSI_WHITE, ANSI_BLACK); + renderStrToBuffer(tui->frame_buffer, box.x+2, yoff++, sbuf, screen_dimensions); + + if (user_pressed_enter) { + // send the "ready" or "not ready" message to the server + u32 msg_idx = 0; + UDPMessage msg = {0}; + msg.address = udp->server_address; + // 1. msg type/CommandType + msg.bytes[msg_idx++] = CommandReadyStatus; + // 2. buy? + msg.bytes[msg_idx++] = !state->me.ready_to_depart; + msg.bytes_len = msg_idx; + + outgoingMessageQueuePush(network_send_queue, &msg); + } + for (u32 i = 0; i < Commodity_Count; i++) { MemoryZero(sbuf, SBUFLEN); - sprintf(sbuf, "%-42s %d", COMMODITY_STRINGS[i], state->me.commodities[i]); + sprintf(sbuf, "%-42s %d", COMMODITIES[i].name, state->me.commodities[i]); renderStrToBuffer(tui->frame_buffer, box.x+4, yoff+4+i, sbuf, screen_dimensions); } } break; @@ -927,7 +987,7 @@ fn bool updateAndRender(TuiState* tui, void* s, u8* input_buffer, u64 loop_count } } renderStrToBuffer(tui->frame_buffer, box.x+2, box.y+1, "Welcome to ", screen_dimensions); - renderStrToBuffer(tui->frame_buffer, box.x+2+11, box.y+1, state->current.name, screen_dimensions); + renderStrToBuffer(tui->frame_buffer, box.x+2+11, box.y+1, curr.name, screen_dimensions); // the table TableDrawInfo info = { @@ -937,7 +997,7 @@ fn bool updateAndRender(TuiState* tui, void* s, u8* input_buffer, u64 loop_count MarketCommodity rows[Commodity_Count] = { 0 }; for (u32 i = 0; i < Commodity_Count; i++) { Commodity c = COMMODITIES[i]; - u32 qty = state->current.planets[0].commodities[i] + state->current.planets[1].commodities[i] + state->current.planets[2].commodities[i]; + u32 qty = curr.planets[0].commodities[i] + curr.planets[1].commodities[i] + curr.planets[2].commodities[i]; rows[i].type = c.type; rows[i].unit = c.unit; rows[i].bid = priceForCommodity(i, qty, true); diff --git a/src/lib/tui.c b/src/lib/tui.c index 57d3d79..c87d458 100644 --- a/src/lib/tui.c +++ b/src/lib/tui.c @@ -254,6 +254,27 @@ fn void renderStringChunkList(TuiState* tui, StringChunkList* list, u16 x, u16 y } } +fn void colorizeBox(TuiState* tui, Box box, u8 background, u8 foreground, u8 byte) { + for (u32 i = 0; i < box.width; i++) { + for (u32 ii = 0; ii < box.height; ii++) { + u32 bufpos = XYToPos(box.x+i, box.y+ii, tui->screen_dimensions.width); + tui->frame_buffer[bufpos].background = background; + tui->frame_buffer[bufpos].foreground = foreground; + if (byte) { + tui->frame_buffer[bufpos].bytes[0] = byte; + } + } + } +} + +fn void colorizeRange(TuiState* tui, Range1u32 range, u8 background, u8 foreground) { + assert(range.max > range.min); + for (u32 i = 0; i < (range.max - range.min); i++) { + tui->frame_buffer[range.min+i].background = background; + tui->frame_buffer[range.min+i].foreground = foreground; + } +} + fn u32 sprintfAnsiMoveCursorTo(ptr output, u16 x, u16 y) { return sprintf(output, "\x1b[%d;%df",y,x); } diff --git a/src/server.c b/src/server.c index 531646e..baf4883 100644 --- a/src/server.c +++ b/src/server.c @@ -24,10 +24,10 @@ #define SERVER_PORT 7777 #define SERVER_MAX_HEAP_MEMORY MB(256) #define SERVER_MAX_CLIENTS 16 -#define GAME_THREAD_CONCURRENCY 4 -#define GOAL_NETWORK_SEND_LOOPS_PER_S 4 +#define GAME_THREAD_CONCURRENCY 2 +#define GOAL_NETWORK_SEND_LOOPS_PER_S 8 #define GOAL_NETWORK_SEND_LOOP_US 1000000/GOAL_NETWORK_SEND_LOOPS_PER_S -#define GOAL_GAME_LOOPS_PER_S 4 +#define GOAL_GAME_LOOPS_PER_S 24 #define GOAL_GAME_LOOP_US 1000000/GOAL_GAME_LOOPS_PER_S #define CLIENT_TIMEOUT_FRAMES GOAL_GAME_LOOPS_PER_S*3 #define CHUNK_SIZE 64 @@ -71,8 +71,9 @@ typedef struct Entity { } Entity; typedef struct Account { + u8 destination_sys_idx; + bool changed; u32 id; // the index in the array - Pos2 pos; String name; String pw; PlayerShip ship; @@ -114,6 +115,7 @@ typedef struct ClientList { } ClientList; typedef struct State { + bool all_accounts_ready; Mutex client_mutex; Mutex mutex; ClientList clients; @@ -373,7 +375,7 @@ fn u32 findClientHandleBySocketAddress(ClientList* clients, SocketAddress addres fn StarSystem* findAccountsSystem(Account* a) { for (u32 i = 0; i < STAR_SYSTEM_COUNT; i++) { - if (state.map[i].x == a->pos.x && state.map[i].y == a->pos.y) { + if (i == a->ship.system_idx) { return &state.map[i]; } } @@ -418,11 +420,13 @@ fn void sendMessageTransactionResult(SocketAddress addr, u32 qty, bool buying, u printf("%s sent\n", MESSAGE_STRINGS[outgoing_message.bytes[0]]); } -fn void sendMessagePlayerDetails(PlayerShip ship, SocketAddress addr) { - UDPMessage outgoing_message = {.address = addr}; +fn UDPMessage makeMessagePlayerDetails(PlayerShip ship) { + UDPMessage outgoing_message = {0}; u32 msg_i = 0; outgoing_message.bytes[msg_i++] = (u8)MessagePlayerDetails; outgoing_message.bytes[msg_i++] = ship.type; + outgoing_message.bytes[msg_i++] = ship.system_idx; + outgoing_message.bytes[msg_i++] = ship.ready_to_depart; outgoing_message.bytes[msg_i++] = ship.drive_efficiency; outgoing_message.bytes[msg_i++] = ship.life_support_efficiency; msg_i += writeU16ToBufferLE(outgoing_message.bytes + msg_i, ship.vacuum_cargo_slots); @@ -439,8 +443,13 @@ fn void sendMessagePlayerDetails(PlayerShip ship, SocketAddress addr) { for (u32 i = 0; i < Commodity_Count; i++) { msg_i += writeU32ToBufferLE(outgoing_message.bytes + msg_i, ship.commodities[i]); } - outgoing_message.bytes_len = msg_i; + return outgoing_message; +} + +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]]); } @@ -496,6 +505,12 @@ fn void handleIncomingMessage(u8* message, u32 len, SocketAddress sender, i32 so } break; case CommandKeepAlive: break; + case CommandSetDestination: { + parsed.byte = message[1]; // index of system in array + } break; + case CommandReadyStatus: { + parsed.byte = message[1]; // boolean ready_to_depart + } break; case CommandTransact: { printf("command transact received\n"); parsed.byte = message[1]; // buy? @@ -557,10 +572,23 @@ fn void* sendNetworkUpdates(void* sock) { // continue; // they are still creating their character //} - // update allthe changed systems - for (u32 i = 0; i < STAR_SYSTEM_COUNT; i++) { - if (state.map[i].changed == true) { - UDPMessage msg_data = makeMessageSystemCommodities(&state.map[i]); + // update all the changed systems + UDPMessage msg_data; + for (u32 ii = 0; ii < STAR_SYSTEM_COUNT; ii++) { + if (state.map[ii].changed == true) { + msg_data = makeMessageSystemCommodities(&state.map[ii]); + u8List msg = { + .capacity = UDP_MAX_MESSAGE_LEN, + .items = msg_data.bytes, + .length = msg_data.bytes_len, + }; + sendUDPu8List(socket, &client.address, &msg); + } + } + // update all the changed accounts + for (u32 ii = 0; ii < ACCOUNT_LEN; ii++) { + if (state.accounts[ii].changed == true) { + msg_data = makeMessagePlayerDetails(state.accounts[ii].ship); u8List msg = { .capacity = UDP_MAX_MESSAGE_LEN, .items = msg_data.bytes, @@ -572,9 +600,14 @@ fn void* sendNetworkUpdates(void* sock) { } } unlockMutex(&state.client_mutex); - for (u32 i = 0; i < STAR_SYSTEM_COUNT; i++) { - state.map[i].changed = false; - } + lockMutex(&state.mutex); { + for (u32 i = 0; i < STAR_SYSTEM_COUNT; i++) { + state.map[i].changed = false; + } + for (u32 i = 0; i < ACCOUNT_LEN; i++) { + state.accounts[i].changed = false; + } + } unlockMutex(&state.mutex); u32 loop_duration = osTimeMicrosecondsNow() - loop_start; i32 remaining_time = GOAL_NETWORK_SEND_LOOP_US - loop_duration; @@ -585,6 +618,10 @@ fn void* sendNetworkUpdates(void* sock) { return NULL; } +fn bool accountIsEmpty(Account* a) { + return a->id == 0 && a->name.length == 0; +} + fn void* gameLoop(void* params) { LaneCtx* lane_ctx = (LaneCtx*)params; ThreadContext tctx = { @@ -620,6 +657,23 @@ fn void* gameLoop(void* params) { u32 client_handle = findClientHandleBySocketAddress(&state.clients, sender); Client* client = &state.clients.items[client_handle]; switch (msg.type) { + case CommandSetDestination: { + Account* account = &state.accounts[client->account_id]; + account->destination_sys_idx = msg.byte; + } break; + case CommandReadyStatus: { + Account* account = &state.accounts[client->account_id]; + account->ship.ready_to_depart = msg.byte; + sendMessagePlayerDetails(account->ship, sender); + state.all_accounts_ready = true; + for (u32 i = 0; i < ACCOUNT_LEN; i++) { + if (!accountIsEmpty(&state.accounts[i])) { + if (state.accounts[i].ship.ready_to_depart == false) { + state.all_accounts_ready = false; + } + } + } + } break; case CommandTransact: { printf("Transact for client_handle=%d, on #%lld", client_handle, state.frame); bool is_buying_from_system = msg.byte; @@ -629,7 +683,10 @@ fn void* gameLoop(void* params) { u32 qty_traded = 0; u32 credit_value = 0; if (is_buying_from_system) { - u32 ship_space = account->ship.vacuum_cargo_slots - usedVacuumCargoSlots(account->ship); + u32 ship_space = MAX_u32; + if (COMMODITIES[msg.commodity].unit == StorageUnitContainer) { + ship_space = account->ship.vacuum_cargo_slots - usedVacuumCargoSlots(account->ship); + } for (u32 amount_to_buy = Min(msg.qty, total_available); amount_to_buy > 0; amount_to_buy--, total_available--, ship_space--) { u32 price = priceForCommodity(msg.commodity, total_available, false); if (price > account->ship.credits || ship_space == 0) { @@ -717,7 +774,7 @@ fn void* gameLoop(void* params) { } } else { for (u32 i = 0; i < ACCOUNT_LEN; i++) { - if (state.accounts[i].id == 0 && state.accounts[i].name.length == 0) { + if (accountIsEmpty(&state.accounts[i])) { existing_account = &state.accounts[i]; existing_account->id = i; break; @@ -772,8 +829,7 @@ fn void* gameLoop(void* params) { printf("ship_type=%s, client_handle=%d, acct_id=%d\n", SHIP_TYPE_STRINGS[msg.byte], client_handle, account->id); u32 starting_system_idx = rand() % STAR_SYSTEM_COUNT; StarSystem starting_system = state.map[starting_system_idx]; - account->pos.x = starting_system.x; - account->pos.y = starting_system.y; + account->ship.system_idx = starting_system_idx; // tell the client their account id outgoing_message.address = sender; @@ -817,21 +873,73 @@ fn void* gameLoop(void* params) { msg_iters++; } + if (state.all_accounts_ready) { + for (u32 i = 1; i < SERVER_MAX_CLIENTS; i++) { + if (state.clients.items[i].last_ping != 0) { + outgoing_message.address = state.clients.items[i].address; + outgoing_message.bytes_len = 1; + outgoing_message.bytes[0] = (u8)MessageTurnTick; + outgoingMessageQueuePush(state.network_send_queue, &outgoing_message); + } + } + } + } unlockMutex(&state.mutex); unlockMutex(&state.client_mutex); } LaneSync(); // 2. tick non-user entities - // iterate all the rooms - /* - Room* room = NULL; - Range1u64 room_range = LaneRange(MAX_ROOMS); - for (u32 i = room_range.min; i < room_range.max; i++) { - room = &state.rooms->items[i]; - simulateRoom(&scratch_arena, room, burn_tick, regen_hp_tick); + if (state.all_accounts_ready) { + // tick all the star systems + StarSystem* sys = NULL; + Range1u64 sys_range = LaneRange(STAR_SYSTEM_COUNT); + for (u32 i = sys_range.min; i < sys_range.max; i++) { + sys = &state.map[i]; + sys->changed = true; + for (u32 ii = 0; ii < Commodity_Count; ii++) { + for (u32 iii = 0; iii < MAX_PLANETS; iii++) { + if (sys->planets[iii].type != PlanetTypeNull) { + if (sys->planets[iii].commodities[ii] > COMMODITIES[ii].consumption) { + sys->planets[iii].commodities[ii] -= COMMODITIES[ii].consumption; + } else { + sys->planets[iii].commodities[ii] = 0; + } + sys->planets[iii].commodities[ii] += sys->planets[iii].production[ii]; + } + } + } + } + + // move all the players + u32 player_count = 0; + for (u32 i = 0; i < ACCOUNT_LEN; i++) { + if (!accountIsEmpty(&state.accounts[i])) { + player_count += 1; + } + } + Range1u64 ship_range = LaneRange(player_count); + for (u32 i = ship_range.min; i < ship_range.max; i++) { + Account* acct = &state.accounts[i]; + StarSystem dest = state.map[acct->destination_sys_idx]; + StarSystem current = state.map[acct->ship.system_idx]; + Pos2 dest_pos = {dest.x, dest.y}; + Pos2 curr_pos = {current.x, current.y}; + u32 projected_fuel_cost = fuelCostForTravel(acct->ship.drive_efficiency, curr_pos, dest_pos); + bool have_enough_fuel = projected_fuel_cost < acct->ship.commodities[CommodityHydrogenFuel]; + if (have_enough_fuel) { + // remove the fuel they used on the journey + acct->ship.commodities[CommodityHydrogenFuel] -= projected_fuel_cost; + // set the pos to be the system they had as destination + acct->ship.system_idx = acct->destination_sys_idx; + } + acct->ship.ready_to_depart = false; + acct->changed = true; + } + + // TODO pay the mortgage payment } - */ + state.all_accounts_ready = false; // 3. scratch cleanup arenaClear(&scratch_arena); diff --git a/src/shared.h b/src/shared.h index 0994840..e8c0056 100644 --- a/src/shared.h +++ b/src/shared.h @@ -314,6 +314,8 @@ fn f32 priceForCommodity(CommodityType type, u32 quantity, bool bid) { typedef struct PlayerShip { ShipType type; + bool ready_to_depart; + u8 system_idx; u8 drive_efficiency; u8 life_support_efficiency; u16 vacuum_cargo_slots; @@ -334,7 +336,9 @@ typedef struct PlayerShip { fn u32 usedVacuumCargoSlots(PlayerShip ship) { u32 used_cargo = 0; for (u32 i = 0; i < Commodity_Count; i++) { - used_cargo += ship.commodities[i]; + if (COMMODITIES[i].unit == StorageUnitContainer) { + used_cargo += ship.commodities[i]; + } } return used_cargo; } @@ -431,14 +435,18 @@ typedef enum CommandType { CommandLogin, CommandCreateCharacter, CommandTransact, + CommandReadyStatus, + CommandSetDestination, CommandType_Count, } CommandType; -static const char* command_type_strings[] = { +static const char* command_type_strings[CommandType_Count] = { "Invalid", "KeepAlive", "Login", "CreateCharacter", + "ReadyStatus", + "SetDestination", }; #define ENTITY_HEADER_MESSAGE_SIZE (8+8+1+1+1) @@ -452,6 +460,7 @@ typedef enum Message { MessageSystemCommodities, MessagePlayerDetails, MessageTransactionResult, + MessageTurnTick, Message_Count, } Message; @@ -464,6 +473,7 @@ static const char* MESSAGE_STRINGS[] = { "SystemCommodities", "PlayerDetails", "TransactionResult", + "TurnTick", }; fn u32 fuelCostForTravel(u32 drive_efficiency, Pos2 current, Pos2 dest) {