can buy/sell crap from station

This commit is contained in:
Tenari
2026-03-03 07:29:57 -06:00
parent 1760dd9d96
commit 75c1579b1e
5 changed files with 427 additions and 78 deletions
+23
View File
@@ -12,6 +12,17 @@ fn u64 writeU64ToBufferLE(u8* buffer, u64 value) {
return 8;// number of bytes written return 8;// number of bytes written
} }
fn u64 writeF32ToBufferLE(u8* buffer, f32 value) {
u32 bits;
memcpy(&bits, &value, sizeof(u32)); // reinterpret float bits as integer
buffer[0] = (u8)(bits & 0xFF);
buffer[1] = (u8)((bits >> 8) & 0xFF);
buffer[2] = (u8)((bits >> 16) & 0xFF);
buffer[3] = (u8)((bits >> 24) & 0xFF);
return 4;// number of bytes written
}
fn u64 writeU32ToBufferLE(u8* buffer, u32 value) { fn u64 writeU32ToBufferLE(u8* buffer, u32 value) {
buffer[0] = (u8)(value & 0xFF); buffer[0] = (u8)(value & 0xFF);
buffer[1] = (u8)((value >> 8) & 0xFF); buffer[1] = (u8)((value >> 8) & 0xFF);
@@ -45,6 +56,18 @@ fn u64 readU64FromBufferLE(u8 *buffer) {
((u64)buffer[7] << 56); ((u64)buffer[7] << 56);
} }
fn f32 readF32FromBufferLE(u8 *buf) {
u32 bits = 0;
bits |= (u32)(u8)buf[0] << 0;
bits |= (u32)(u8)buf[1] << 8;
bits |= (u32)(u8)buf[2] << 16;
bits |= (u32)(u8)buf[3] << 24;
f32 value;
memcpy(&value, &bits, sizeof(f32));
return value;
}
fn u32 readU32FromBufferLE(u8 *buffer) { fn u32 readU32FromBufferLE(u8 *buffer) {
return (u32)buffer[0] | return (u32)buffer[0] |
((u32)buffer[1] << 8) | ((u32)buffer[1] << 8) |
+197 -21
View File
@@ -34,6 +34,14 @@ typedef enum Tab {
Tab_Count, Tab_Count,
} Tab; } Tab;
typedef enum StationTabStates {
StationTabStateTable,
StationTabStateTransact,
StationTabStateResult,
StationTabStateLoading,
StationTabStates_Count,
} StationTabStates;
typedef enum Screen { typedef enum Screen {
ScreenLogin, ScreenLogin,
ScreenCreateCharacter, ScreenCreateCharacter,
@@ -74,6 +82,12 @@ typedef struct EntityList {
Entity* items; Entity* items;
} EntityList; } EntityList;
typedef struct TransactionResult {
bool buying;
u32 qty;
u32 cr;
} TransactionResult;
typedef struct ParsedServerMessage { typedef struct ParsedServerMessage {
Message type; Message type;
u16 port; u16 port;
@@ -86,6 +100,8 @@ typedef struct ParsedServerMessage {
Pos2u8 positions[STAR_SYSTEM_COUNT]; Pos2u8 positions[STAR_SYSTEM_COUNT];
PlanetType planet_types[MAX_PLANETS*STAR_SYSTEM_COUNT]; PlanetType planet_types[MAX_PLANETS*STAR_SYSTEM_COUNT];
StarSystem sys; StarSystem sys;
PlayerShip ship;
TransactionResult tx_result;
//Entity entities[PARSED_CLIENT_ENTITY_LEN]; //Entity entities[PARSED_CLIENT_ENTITY_LEN];
//u64 ids[PARSED_IDS_LEN]; //u64 ids[PARSED_IDS_LEN];
} ParsedServerMessage; } ParsedServerMessage;
@@ -133,7 +149,8 @@ typedef struct GameState {
StarSystem map[STAR_SYSTEM_COUNT]; StarSystem map[STAR_SYSTEM_COUNT];
Pos2 pos; Pos2 pos;
StarSystem current; StarSystem current;
bool modal; StationTabStates station_tab_state;
TransactionResult tx_result;
} GameState; } GameState;
///// GLOBALS ///// GLOBALS
@@ -365,15 +382,22 @@ fn void clearServerSentState() {
} }
fn void handleIncomingMessage(u8* message, u32 len, SocketAddress sender, i32 socket) { fn void handleIncomingMessage(u8* message, u32 len, SocketAddress sender, i32 socket) {
Message msg_type = message[0]; u64 msg_pos = 0;
Message msg_type = message[msg_pos++];
dbg("handleIncomingMessage() of len=%d, message=%s\n", len, MESSAGE_STRINGS[msg_type]); dbg("handleIncomingMessage() of len=%d, message=%s\n", len, MESSAGE_STRINGS[msg_type]);
u8List bytes = {len, len, message}; u8List bytes = {len, len, message};
u64 msg_pos = 1;
ParsedServerMessage parsed = {0}; ParsedServerMessage parsed = {0};
parsed.type = msg_type; parsed.type = msg_type;
switch (msg_type) { switch (msg_type) {
case MessageNewAccountCreated: case MessageNewAccountCreated:
case MessageBadPw: {/*nothing to parse but the type*/} break; case MessageBadPw: {/*nothing to parse but the type*/} break;
case MessageTransactionResult: {
parsed.tx_result.buying = message[msg_pos++];
parsed.tx_result.qty = readU32FromBufferLE(message + msg_pos);
msg_pos += 4;
parsed.tx_result.cr = readU32FromBufferLE(message + msg_pos);
msg_pos += 4;
} break;
case MessageStarPositions: { case MessageStarPositions: {
u32 star_msg_size = 2+MAX_PLANETS; u32 star_msg_size = 2+MAX_PLANETS;
for (u32 i = 0; i < STAR_SYSTEM_COUNT; i++) { for (u32 i = 0; i < STAR_SYSTEM_COUNT; i++) {
@@ -393,6 +417,36 @@ fn void handleIncomingMessage(u8* message, u32 len, SocketAddress sender, i32 so
} }
} }
} break; } break;
case MessagePlayerDetails: {
parsed.ship.type = 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);
msg_pos += 2;
parsed.ship.climate_cargo_slots = readU16FromBufferLE(message + msg_pos);
msg_pos += 2;
parsed.ship.passenger_berths = readU16FromBufferLE(message + msg_pos);
msg_pos += 2;
parsed.ship.passenger_amenities_flags = readU16FromBufferLE(message + msg_pos);
msg_pos += 2;
parsed.ship.smugglers_hold_cu_m = readU16FromBufferLE(message + msg_pos);
msg_pos += 2;
parsed.ship.remaining_mortgage = readU32FromBufferLE(message + msg_pos);
msg_pos += 4;
parsed.ship.interest_rate = readF32FromBufferLE(message + msg_pos);
msg_pos += 4;
parsed.ship.cu_m_fuel = readU32FromBufferLE(message + msg_pos);
msg_pos += 4;
parsed.ship.cu_m_o2 = readU32FromBufferLE(message + msg_pos);
msg_pos += 4;
parsed.ship.credits = readF32FromBufferLE(message + msg_pos);
msg_pos += 4;
parsed.ship.id = readU64FromBufferLE(message + msg_pos);
msg_pos += 8;
for (u32 i = 0; i < Commodity_Count; i++, msg_pos += 4) {
parsed.ship.commodities[i] = readU32FromBufferLE(message + msg_pos);
}
} break;
case MessageCharacterId: { case MessageCharacterId: {
parsed.id = readU64FromBufferLE(message + 1); parsed.id = readU64FromBufferLE(message + 1);
} break; } break;
@@ -446,6 +500,7 @@ fn bool updateAndRender(TuiState* tui, void* s, u8* input_buffer, u64 loop_count
fflush(stdout); fflush(stdout);
return should_quit; return should_quit;
} }
char sbuf[512] = {0};
// process server messages // process server messages
u32 msg_iters = 0; u32 msg_iters = 0;
@@ -454,6 +509,30 @@ fn bool updateAndRender(TuiState* tui, void* s, u8* input_buffer, u64 loop_count
while (next_net_msg != NULL) { while (next_net_msg != NULL) {
msg_iters += 0; msg_iters += 0;
switch (msg.type) { switch (msg.type) {
case MessageTransactionResult: {
state->station_tab_state = StationTabStateResult;
MemoryCopyStruct(&state->tx_result, &msg.tx_result);
} break;
case MessagePlayerDetails: {
if (msg.ship.id == state->me.id) {
state->me.type = msg.ship.type;
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;
state->me.climate_cargo_slots = msg.ship.climate_cargo_slots;
state->me.passenger_berths = msg.ship.passenger_berths;
state->me.passenger_amenities_flags = msg.ship.passenger_amenities_flags;
state->me.smugglers_hold_cu_m = msg.ship.smugglers_hold_cu_m;
state->me.remaining_mortgage = msg.ship.remaining_mortgage;
state->me.interest_rate = msg.ship.interest_rate;
state->me.cu_m_fuel = msg.ship.cu_m_fuel;
state->me.cu_m_o2 = msg.ship.cu_m_o2;
state->me.credits = msg.ship.credits;
for (u32 i = 0; i < Commodity_Count; i++) {
state->me.commodities[i] = msg.ship.commodities[i];
}
}
} break;
case MessageStarPositions: { case MessageStarPositions: {
for (u32 i = 0; i < STAR_SYSTEM_COUNT; i++) { for (u32 i = 0; i < STAR_SYSTEM_COUNT; i++) {
state->map[i].x = msg.positions[i].x; state->map[i].x = msg.positions[i].x;
@@ -557,7 +636,6 @@ fn bool updateAndRender(TuiState* tui, void* s, u8* input_buffer, u64 loop_count
} }
//// RENDERING //// RENDERING
char tmp_buffer[64] = {0};
u16 line = 2; u16 line = 2;
// 1. draw the outline // 1. draw the outline
Box b = { .x = 2, .y = line, .width = screen_dimensions.width - 5, .height = screen_dimensions.height - 5 }; Box b = { .x = 2, .y = line, .width = screen_dimensions.width - 5, .height = screen_dimensions.height - 5 };
@@ -581,8 +659,8 @@ fn bool updateAndRender(TuiState* tui, void* s, u8* input_buffer, u64 loop_count
f32 rm = r / 12.0; f32 rm = r / 12.0;
u32 n = 12 * 4; u32 n = 12 * 4;
u32 payment = principal_borrowed * (rm*pow((1.0+rm),n))/(pow((1+rm),n) - 1); u32 payment = principal_borrowed * (rm*pow((1.0+rm),n))/(pow((1+rm),n) - 1);
sprintf(tmp_buffer, "Mortgage: $%d @ %f%% = $%d minimum payment per turn", selected_ship_cost - STARTING_DOWN_PAYMENT, mortgage_rate, payment); sprintf(sbuf, "Mortgage: $%d @ %f%% = $%d minimum payment per turn", selected_ship_cost - STARTING_DOWN_PAYMENT, mortgage_rate, payment);
renderStrToBuffer(tui->frame_buffer, 5, ++line, tmp_buffer, screen_dimensions); renderStrToBuffer(tui->frame_buffer, 5, ++line, sbuf, screen_dimensions);
line++; line++;
// the table // the table
TableDrawInfo info = { TableDrawInfo info = {
@@ -701,13 +779,33 @@ fn bool updateAndRender(TuiState* tui, void* s, u8* input_buffer, u64 loop_count
if (input_buffer[0] == 'q' || user_pressed_esc) { if (input_buffer[0] == 'q' || user_pressed_esc) {
should_quit = true; should_quit = true;
} }
renderStrToBuffer(tui->frame_buffer, box.x+2, box.y+1, SHIP_TYPE_STRINGS[state->me.type], screen_dimensions);
renderStrToBuffer(tui->frame_buffer, box.x+2, box.y+2, "Credits: ", screen_dimensions);
MemoryZero(sbuf, 512);
sprintf(sbuf, "%.2f", state->me.credits);
renderStrToBuffer(tui->frame_buffer, box.x+15, box.y+2, sbuf, screen_dimensions);
for (u32 i = 0; i < Commodity_Count; i++) {
MemoryZero(sbuf, 512);
sprintf(sbuf, "%-42s %d", COMMODITY_STRINGS[i], state->me.commodities[i]);
renderStrToBuffer(tui->frame_buffer, box.x+4, box.y+4+i, sbuf, screen_dimensions);
}
} break; } break;
case TabStation: { case TabStation: {
if (input_buffer[0] == 'q' || user_pressed_esc) { if (input_buffer[0] == 'q' || user_pressed_esc) {
if (state->modal) { switch (state->station_tab_state) {
state->modal = !state->modal; case StationTabStateTable:
} else {
should_quit = true; should_quit = true;
break;
case StationTabStateTransact:
state->station_tab_state = StationTabStateTable;
break;
case StationTabStateResult:
state->station_tab_state = StationTabStateTransact;
break;
case StationTabStateLoading:
state->station_tab_state = StationTabStateTable;
break;
case StationTabStates_Count: assert(false && "something has gone horribly wrong");
} }
} }
if (user_pressed_up) { if (user_pressed_up) {
@@ -744,35 +842,67 @@ fn bool updateAndRender(TuiState* tui, void* s, u8* input_buffer, u64 loop_count
} }
renderTable(tui, info, state->row.selected_index, MARKET_COMMODITY_FIELDS, 1, rows, sizeof(MarketCommodity)); renderTable(tui, info, state->row.selected_index, MARKET_COMMODITY_FIELDS, 1, rows, sizeof(MarketCommodity));
if (user_pressed_enter) { if (state->station_tab_state != StationTabStateTable) {
state->modal = !state->modal;
if (state->modal) {
state->modal_choice.len = 2;
state->modal_choice.selected_index = 0;
}
}
if (state->modal) {
if (user_pressed_right) { if (user_pressed_right) {
state->modal_choice.selected_index = 1; state->modal_choice.selected_index = 1;
} else if (user_pressed_left) { } else if (user_pressed_left) {
state->modal_choice.selected_index = 0; state->modal_choice.selected_index = 0;
} else if (user_pressed_a_number) {
String next_char = { 1, 2, (char*)input_buffer };
stringChunkListAppend(&state->string_arena, &state->message_input, next_char);
} else if (user_pressed_backspace) {
stringChunkListDeleteLast(&state->string_arena, &state->message_input);
} }
// draw the "purchase order" modal
// draw the modal
bool buy_selected = state->modal_choice.selected_index == 0;
Box modal_outline = { Box modal_outline = {
.x = (tui->screen_dimensions.width - 50) / 2, .x = (tui->screen_dimensions.width - 50) / 2,
.y = (tui->screen_dimensions.height - 15) / 2, .y = (tui->screen_dimensions.height - 15) / 2,
.height = 15, .height = 15,
.width = 50, .width = 50,
}; };
clearBox(tui, modal_outline);
drawAnsiBox(tui->frame_buffer, modal_outline, tui->screen_dimensions, false); drawAnsiBox(tui->frame_buffer, modal_outline, tui->screen_dimensions, false);
str cname = COMMODITIES[state->row.selected_index].name;
renderStrToBuffer(tui->frame_buffer, modal_outline.x+(modal_outline.width/2)-strlen(cname), modal_outline.y+1, cname, screen_dimensions);
u32 y_off = modal_outline.y+2; u32 y_off = modal_outline.y+2;
str cname = COMMODITIES[state->row.selected_index].name;
if (state->station_tab_state == StationTabStateResult) {
if (state->tx_result.buying) {
renderStrToBuffer(tui->frame_buffer, modal_outline.x+((modal_outline.width-10)/2), y_off++, "Purchased!", screen_dimensions);
} else {
renderStrToBuffer(tui->frame_buffer, modal_outline.x+((modal_outline.width-5)/2), y_off++, "Sold!", screen_dimensions);
}
MemoryZero(sbuf, 512);
sprintf(sbuf, "%d %s", state->tx_result.qty, cname);
renderStrToBuffer(tui->frame_buffer, modal_outline.x+((modal_outline.width-strlen(sbuf))/2), y_off++, sbuf, screen_dimensions);
renderStrToBuffer(tui->frame_buffer, modal_outline.x+((modal_outline.width-3)/2), y_off++, "for", screen_dimensions);
MemoryZero(sbuf, 512);
sprintf(sbuf, "%d credits", state->tx_result.cr);
renderStrToBuffer(tui->frame_buffer, modal_outline.x+((modal_outline.width-strlen(sbuf))/2), y_off++, sbuf, screen_dimensions);
y_off++;
u32 exit_x = modal_outline.x+((modal_outline.width-4)/2);
u32 pos = XYToPos(exit_x, y_off, tui->screen_dimensions.width);
for (u32 i = 0; i < 4; i++) {
tui->frame_buffer[pos+i].background = ANSI_WHITE;
tui->frame_buffer[pos+i].foreground = ANSI_BLACK;
}
tui->cursor.x = exit_x;
tui->cursor.y = y_off;
renderStrToBuffer(tui->frame_buffer, exit_x, y_off++, "EXIT", screen_dimensions);
if (user_pressed_enter) {
state->station_tab_state = StationTabStateTable;
}
} else if (state->station_tab_state == StationTabStateLoading) {
renderStrToBuffer(tui->frame_buffer, modal_outline.x+2, y_off, "Loading...", screen_dimensions);
} else { // StationTabStateTransact
// draw the "purchase order" modal
renderStrToBuffer(tui->frame_buffer, modal_outline.x+(modal_outline.width/2)-strlen(cname), modal_outline.y+1, cname, screen_dimensions);
u32 buy_x = modal_outline.x+2; u32 buy_x = modal_outline.x+2;
u32 sell_x = modal_outline.x+modal_outline.width-6; u32 sell_x = modal_outline.x+modal_outline.width-6;
renderStrToBuffer(tui->frame_buffer, buy_x, y_off, "Buy", screen_dimensions); renderStrToBuffer(tui->frame_buffer, buy_x, y_off, "Buy", screen_dimensions);
renderStrToBuffer(tui->frame_buffer, sell_x, y_off, "Sell", screen_dimensions); renderStrToBuffer(tui->frame_buffer, sell_x, y_off, "Sell", screen_dimensions);
if (state->modal_choice.selected_index == 0) { if (buy_selected) {
u32 pos = XYToPos(buy_x, y_off, tui->screen_dimensions.width); u32 pos = XYToPos(buy_x, y_off, tui->screen_dimensions.width);
tui->frame_buffer[pos].background = ANSI_WHITE; tui->frame_buffer[pos].background = ANSI_WHITE;
tui->frame_buffer[pos].foreground = ANSI_BLACK; tui->frame_buffer[pos].foreground = ANSI_BLACK;
@@ -791,6 +921,52 @@ fn bool updateAndRender(TuiState* tui, void* s, u8* input_buffer, u64 loop_count
tui->frame_buffer[pos+3].background = ANSI_WHITE; tui->frame_buffer[pos+3].background = ANSI_WHITE;
tui->frame_buffer[pos+3].foreground = ANSI_BLACK; tui->frame_buffer[pos+3].foreground = ANSI_BLACK;
} }
y_off++;
renderStrToBuffer(tui->frame_buffer, modal_outline.x+((modal_outline.width-8)/2), y_off++, "Quantity", screen_dimensions);
renderStringChunkList(tui, &state->message_input, modal_outline.x+8, y_off);
tui->cursor.x = modal_outline.x + 8 + state->message_input.total_size;
tui->cursor.y = y_off++;
if (buy_selected) {
renderStrToBuffer(tui->frame_buffer, modal_outline.x+((modal_outline.width-4)/2), y_off++, "Cost", screen_dimensions);
} else {
renderStrToBuffer(tui->frame_buffer, modal_outline.x+((modal_outline.width-6)/2), y_off++, "Profit", screen_dimensions);
}
String input_q_str = stringChunkToString(&permanent_arena, state->message_input);
u32 input_quantity = atoi(input_q_str.bytes);
u32 credits = input_quantity * (buy_selected ? rows[state->row.selected_index].ask : rows[state->row.selected_index].bid);
arenaDealloc(&permanent_arena, input_q_str.capacity);
MemoryZero(sbuf, 512);
sprintf(sbuf, "%d", credits);
renderStrToBuffer(tui->frame_buffer, modal_outline.x+8, y_off++, sbuf, screen_dimensions);
if (user_pressed_enter) {
// send the "purchase" or "sell" message to server
// get back new "status" of things
u32 msg_idx = 0;
UDPMessage msg = {0};
msg.address = udp->server_address;
// 1. msg type/CommandType
msg.bytes[msg_idx++] = CommandTransact;
// 2. buy?
msg.bytes[msg_idx++] = buy_selected;
// 3. quantity
msg_idx += writeU32ToBufferLE(msg.bytes + msg_idx, input_quantity);
// 4. commodity
msg.bytes[msg_idx++] = rows[state->row.selected_index].type;
msg.bytes_len = msg_idx;
outgoingMessageQueuePush(network_send_queue, &msg);
state->station_tab_state = StationTabStateLoading;
}
}
} else {
// we are on the table
if (user_pressed_enter) {
state->station_tab_state = StationTabStateTransact;
state->modal_choice.len = 2;
state->modal_choice.selected_index = 0;
}
} }
} break; } break;
case Tab_Count: {} break; case Tab_Count: {} break;
+14 -9
View File
@@ -95,15 +95,6 @@ fn RGB rgbDarken(RGB color, f32 factor) {
return result; return result;
} }
/*
fn RGB ansiToRGB(u8 ansi) {
ansi -= 16;
u8 b = ansi % 51;
ansi -= ()
u8 g =
}
*/
fn bool rgbEq(RGB a, RGB b) { fn bool rgbEq(RGB a, RGB b) {
return a.r == b.r && a.g == b.g && a.b == b.b; return a.r == b.r && a.g == b.g && a.b == b.b;
} }
@@ -137,6 +128,20 @@ fn void copyStr(u8* bytes, str cstring) {
} }
} }
fn void clearBox(TuiState* tui, Box box) {
for (u32 i = 0; i < box.height; i++) {
for (u32 ii = 0; ii < box.width; ii++) {
u32 pos = XYToPos(box.x+ii, box.y+i, tui->screen_dimensions.width);
tui->frame_buffer[pos].background = 0;
tui->frame_buffer[pos].foreground = 0;
tui->frame_buffer[pos].bytes[0] = 0;
tui->frame_buffer[pos].bytes[1] = 0;
tui->frame_buffer[pos].bytes[2] = 0;
tui->frame_buffer[pos].bytes[3] = 0;
}
}
}
fn void drawAnsiBox(Pixel* buf, Box box, Dim2 sd, bool bold) { fn void drawAnsiBox(Pixel* buf, Box box, Dim2 sd, bool bold) {
str items[] = {"","","","","",""}; str items[] = {"","","","","",""};
str b_items[] = {"","","","","",""}; str b_items[] = {"","","","","",""};
+164 -26
View File
@@ -42,6 +42,8 @@ typedef struct ParsedClientCommand {
u16 alt_port; u16 alt_port;
u32 sender_ip; u32 sender_ip;
u32 alt_ip; u32 alt_ip;
u32 qty;
CommodityType commodity;
StringChunkList name; StringChunkList name;
StringChunkList pass; StringChunkList pass;
u64 id; u64 id;
@@ -369,6 +371,80 @@ fn u32 findClientHandleBySocketAddress(ClientList* clients, SocketAddress addres
return 0; return 0;
} }
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) {
return &state.map[i];
}
}
return &state.map[0];
}
fn u32 starSystemPlanetCount(StarSystem* sys) {
u32 planet_count = 0;
for (u32 i = 0; i < MAX_PLANETS; i++) {
if (sys->planets[i].type != PlanetTypeNull) {
planet_count++;
}
}
return planet_count;
}
fn UDPMessage makeMessageSystemCommodities(StarSystem* sys) {
UDPMessage outgoing_message = {0};
u32 planet_count = starSystemPlanetCount(sys);
u32 msg_i = 0;
outgoing_message.bytes[msg_i++] = (u8)MessageSystemCommodities;
outgoing_message.bytes[msg_i++] = (u8)sys->idx;
outgoing_message.bytes[msg_i++] = (u8)planet_count;
for (u32 i = 0; i < planet_count; i++) {
for (u32 ii = 0; ii < Commodity_Count; ii++) {
outgoing_message.bytes[msg_i++] = sys->planets[i].commodities[ii];
}
}
outgoing_message.bytes_len = msg_i;
return outgoing_message;
}
fn void sendMessageTransactionResult(SocketAddress addr, u32 qty, bool buying, u32 credits) {
UDPMessage outgoing_message = {.address = addr};
u32 msg_i = 0;
outgoing_message.bytes[msg_i++] = (u8)MessageTransactionResult;
outgoing_message.bytes[msg_i++] = buying;
msg_i += writeU32ToBufferLE(outgoing_message.bytes + msg_i, qty);
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]]);
}
fn void sendMessagePlayerDetails(PlayerShip ship, SocketAddress addr) {
UDPMessage outgoing_message = {.address = addr};
u32 msg_i = 0;
outgoing_message.bytes[msg_i++] = (u8)MessagePlayerDetails;
outgoing_message.bytes[msg_i++] = ship.type;
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);
msg_i += writeU16ToBufferLE(outgoing_message.bytes + msg_i, ship.climate_cargo_slots);
msg_i += writeU16ToBufferLE(outgoing_message.bytes + msg_i, ship.passenger_berths);
msg_i += writeU16ToBufferLE(outgoing_message.bytes + msg_i, ship.passenger_amenities_flags);
msg_i += writeU16ToBufferLE(outgoing_message.bytes + msg_i, ship.smugglers_hold_cu_m);
msg_i += writeU32ToBufferLE(outgoing_message.bytes + msg_i, ship.remaining_mortgage);
msg_i += writeF32ToBufferLE(outgoing_message.bytes + msg_i, ship.interest_rate);
msg_i += writeU32ToBufferLE(outgoing_message.bytes + msg_i, ship.cu_m_fuel);
msg_i += writeU32ToBufferLE(outgoing_message.bytes + msg_i, ship.cu_m_o2);
msg_i += writeF32ToBufferLE(outgoing_message.bytes + msg_i, ship.credits);
msg_i += writeU64ToBufferLE(outgoing_message.bytes + msg_i, ship.id);
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;
outgoingMessageQueuePush(state.network_send_queue, &outgoing_message);
printf("%s sent\n", MESSAGE_STRINGS[outgoing_message.bytes[0]]);
}
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); dbg("%d: %s from %s:%d\n", len, command_type_strings[message[0]], inet_ntoa(sender.sin_addr), sender.sin_port);
u32 msg_idx = 0; u32 msg_idx = 0;
@@ -420,6 +496,12 @@ fn void handleIncomingMessage(u8* message, u32 len, SocketAddress sender, i32 so
} break; } break;
case CommandKeepAlive: case CommandKeepAlive:
break; break;
case CommandTransact: {
printf("command transact received\n");
parsed.byte = message[1]; // buy?
parsed.qty = readU32FromBufferLE(message + 2);
parsed.commodity = message[6];
} break;
case CommandCreateCharacter: { case CommandCreateCharacter: {
printf("command create character received\n"); printf("command create character received\n");
parsed.byte = message[1]; parsed.byte = message[1];
@@ -475,9 +557,25 @@ fn void* sendNetworkUpdates(void* sock) {
// continue; // they are still creating their character // 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]);
u8List msg = {
.capacity = UDP_MAX_MESSAGE_LEN,
.items = msg_data.bytes,
.length = msg_data.bytes_len,
};
sendUDPu8List(socket, &client.address, &msg);
}
}
} }
} unlockMutex(&state.client_mutex); } unlockMutex(&state.client_mutex);
for (u32 i = 0; i < STAR_SYSTEM_COUNT; i++) {
state.map[i].changed = false;
}
u32 loop_duration = osTimeMicrosecondsNow() - loop_start; u32 loop_duration = osTimeMicrosecondsNow() - loop_start;
i32 remaining_time = GOAL_NETWORK_SEND_LOOP_US - loop_duration; i32 remaining_time = GOAL_NETWORK_SEND_LOOP_US - loop_duration;
if (remaining_time > 0) { if (remaining_time > 0) {
@@ -522,6 +620,56 @@ fn void* gameLoop(void* params) {
u32 client_handle = findClientHandleBySocketAddress(&state.clients, sender); u32 client_handle = findClientHandleBySocketAddress(&state.clients, sender);
Client* client = &state.clients.items[client_handle]; Client* client = &state.clients.items[client_handle];
switch (msg.type) { switch (msg.type) {
case CommandTransact: {
printf("Transact for client_handle=%d, on #%lld", client_handle, state.frame);
bool is_buying_from_system = msg.byte;
Account* account = &state.accounts[client->account_id];
StarSystem* sys = findAccountsSystem(account);
printf("%d %lld %s\n", account->id, account->ship.id, sys->name);
u32 total_available = sys->planets[0].commodities[msg.commodity] + sys->planets[1].commodities[msg.commodity] + sys->planets[2].commodities[msg.commodity];
u32 qty_traded = 0;
u32 credit_value = 0;
if (is_buying_from_system) {
printf("is buying from system\n");
for (u32 amount_to_buy = Min(msg.qty, total_available); amount_to_buy > 0; amount_to_buy--, total_available--) {
u32 price = priceForCommodity(msg.commodity, total_available, false);
if (price > account->ship.credits) {
amount_to_buy = 0;
break;
}
credit_value += price;
account->ship.credits -= price;
account->ship.commodities[msg.commodity] += 1;
qty_traded += 1;
if (sys->planets[0].commodities[msg.commodity]) {
sys->planets[0].commodities[msg.commodity] -= 1;
} else if (sys->planets[1].commodities[msg.commodity]) {
sys->planets[1].commodities[msg.commodity] -= 1;
} else if (sys->planets[2].commodities[msg.commodity]) {
sys->planets[2].commodities[msg.commodity] -= 1;
}
}
} else {
// selling to system logic
qty_traded = Min(msg.qty, account->ship.commodities[msg.commodity]);
u32 planet_count = starSystemPlanetCount(sys);
for (
u32 amount_to_sell = qty_traded;
amount_to_sell > 0;
amount_to_sell--, total_available++
) {
u32 price = priceForCommodity(msg.commodity, total_available, true);
credit_value += price;
account->ship.credits += price;
account->ship.commodities[msg.commodity] -= 1;
u8 planet_idx = rand() % planet_count;
sys->planets[planet_idx].commodities[msg.commodity] += 1;
}
}
sys->changed = true;
sendMessagePlayerDetails(account->ship, sender);
sendMessageTransactionResult(sender, qty_traded, is_buying_from_system, credit_value);
} break;
case CommandKeepAlive: { case CommandKeepAlive: {
dbg("KeepAlive for client_handle=%d, %ld", client_handle, state.frame); dbg("KeepAlive for client_handle=%d, %ld", client_handle, state.frame);
state.clients.items[client_handle].last_ping = state.frame; state.clients.items[client_handle].last_ping = state.frame;
@@ -573,6 +721,7 @@ fn void* gameLoop(void* params) {
if (state.accounts[i].id == 0 && state.accounts[i].name.length == 0) { if (state.accounts[i].id == 0 && state.accounts[i].name.length == 0) {
existing_account = &state.accounts[i]; existing_account = &state.accounts[i];
existing_account->id = i; existing_account->id = i;
break;
} }
} }
existing_account->name = name; existing_account->name = name;
@@ -582,7 +731,7 @@ fn void* gameLoop(void* params) {
if (existing_account->ship.id != 0) { if (existing_account->ship.id != 0) {
// tell the client their account id // tell the client their account id
outgoing_message.bytes[0] = (u8)MessageCharacterId; outgoing_message.bytes[0] = (u8)MessageCharacterId;
writeU64ToBufferLE(outgoing_message.bytes + 1, existing_account->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;
outgoingMessageQueuePush(state.network_send_queue, &outgoing_message); outgoingMessageQueuePush(state.network_send_queue, &outgoing_message);
@@ -598,8 +747,8 @@ fn void* gameLoop(void* params) {
printf("client_handle=%d, acct_id=%d\n", client_handle, existing_account->id); printf("client_handle=%d, acct_id=%d\n", client_handle, existing_account->id);
} break; } break;
case CommandCreateCharacter: { case CommandCreateCharacter: {
Account account = state.accounts[client->account_id]; Account* account = &state.accounts[client->account_id];
if (account.ship.id == 0) { if (account->ship.id == 0) {
ShipTemplate template = SHIPS[msg.byte]; ShipTemplate template = SHIPS[msg.byte];
PlayerShip player_ship = { PlayerShip player_ship = {
.type = msg.byte, .type = msg.byte,
@@ -613,26 +762,29 @@ 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 = 10000.0,
//.cu_m_fuel; // we are just saying you can buy as much fuel as you want //.cu_m_fuel; // we are just saying you can buy as much fuel as you want
//.cu_m_o2; // we are just saying you can buy as much o2 as you want //.cu_m_o2; // we are just saying you can buy as much o2 as you want
.id = ++state.next_eid, // start from 1 .id = ++state.next_eid, // start from 1
}; };
account.ship = player_ship; account->ship = player_ship;
printf("ship_type=%s, client_handle=%d, acct_id=%d\n", SHIP_TYPE_STRINGS[msg.byte], client_handle, account.id); 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; u32 starting_system_idx = rand() % STAR_SYSTEM_COUNT;
StarSystem starting_system = state.map[starting_system_idx]; StarSystem starting_system = state.map[starting_system_idx];
account.pos.x = starting_system.x; account->pos.x = starting_system.x;
account.pos.y = starting_system.y; account->pos.y = starting_system.y;
// tell the client their account id // tell the client their account id
outgoing_message.address = sender; outgoing_message.address = sender;
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.id); writeU64ToBufferLE(outgoing_message.bytes + 1, account->ship.id);
outgoingMessageQueuePush(state.network_send_queue, &outgoing_message); outgoingMessageQueuePush(state.network_send_queue, &outgoing_message);
printf("MessageCharacterId sent\n"); printf("MessageCharacterId sent\n");
// TODO tell the client about the map sendMessagePlayerDetails(account->ship, sender);
// tell the client about the map
u32 star_msg_size = 2+MAX_PLANETS; u32 star_msg_size = 2+MAX_PLANETS;
outgoing_message.address = sender; outgoing_message.address = sender;
outgoing_message.bytes_len = 1+(star_msg_size*STAR_SYSTEM_COUNT); outgoing_message.bytes_len = 1+(star_msg_size*STAR_SYSTEM_COUNT);
@@ -647,25 +799,10 @@ fn void* gameLoop(void* params) {
outgoingMessageQueuePush(state.network_send_queue, &outgoing_message); outgoingMessageQueuePush(state.network_send_queue, &outgoing_message);
printf("%s sent\n", MESSAGE_STRINGS[outgoing_message.bytes[0]]); printf("%s sent\n", MESSAGE_STRINGS[outgoing_message.bytes[0]]);
u32 planet_count = 0; outgoing_message = makeMessageSystemCommodities(&starting_system);
for (u32 i = 0; i < MAX_PLANETS; i++) { outgoing_message.address = sender;
if (starting_system.planets[i].type != PlanetTypeNull) {
planet_count++;
}
}
outgoing_message.bytes_len = 1 + 1 + 1 + (planet_count * Commodity_Count);
u32 msg_i = 0;
outgoing_message.bytes[msg_i++] = (u8)MessageSystemCommodities;
outgoing_message.bytes[msg_i++] = (u8)starting_system_idx;
outgoing_message.bytes[msg_i++] = (u8)planet_count;
for (u32 i = 0; i < planet_count; i++) {
for (u32 ii = 0; ii < Commodity_Count; ii++) {
outgoing_message.bytes[msg_i++] = starting_system.planets[i].commodities[ii];
}
}
outgoingMessageQueuePush(state.network_send_queue, &outgoing_message); outgoingMessageQueuePush(state.network_send_queue, &outgoing_message);
printf("%s sent\n", MESSAGE_STRINGS[outgoing_message.bytes[0]]); printf("%s sent\n", MESSAGE_STRINGS[outgoing_message.bytes[0]]);
} else { } else {
printf("client tried to create a character when he already has one."); printf("client tried to create a character when he already has one.");
} }
@@ -738,6 +875,7 @@ i32 main(i32 argc, ptr argv[]) {
state.clients.items = (Client*)arenaAllocArray(&permanent_arena, Client, SERVER_MAX_CLIENTS); state.clients.items = (Client*)arenaAllocArray(&permanent_arena, Client, SERVER_MAX_CLIENTS);
// set position of all star systems // set position of all star systems
for (u32 i = 0; i < STAR_SYSTEM_COUNT; i++) { for (u32 i = 0; i < STAR_SYSTEM_COUNT; i++) {
state.map[i].idx = i;
state.map[i].name = STAR_NAMES[i]; state.map[i].name = STAR_NAMES[i];
state.map[i].crime = rand() % 100; state.map[i].crime = rand() % 100;
state.map[i].x = rand() % MAP_WIDTH; state.map[i].x = rand() % MAP_WIDTH;
+7
View File
@@ -341,9 +341,11 @@ typedef struct Planet {
} Planet; } Planet;
typedef struct StarSystem { typedef struct StarSystem {
bool changed;
u32 x; u32 x;
u32 y; u32 y;
u32 crime; u32 crime;
u32 idx;
str name; str name;
Planet planets[MAX_PLANETS]; Planet planets[MAX_PLANETS];
} StarSystem; } StarSystem;
@@ -413,6 +415,7 @@ typedef enum CommandType {
CommandKeepAlive, CommandKeepAlive,
CommandLogin, CommandLogin,
CommandCreateCharacter, CommandCreateCharacter,
CommandTransact,
CommandType_Count, CommandType_Count,
} CommandType; } CommandType;
static const char* command_type_strings[] = { static const char* command_type_strings[] = {
@@ -431,6 +434,8 @@ typedef enum Message {
MessageNewAccountCreated, MessageNewAccountCreated,
MessageStarPositions, MessageStarPositions,
MessageSystemCommodities, MessageSystemCommodities,
MessagePlayerDetails,
MessageTransactionResult,
Message_Count, Message_Count,
} Message; } Message;
static const char* MESSAGE_STRINGS[] = { static const char* MESSAGE_STRINGS[] = {
@@ -440,6 +445,8 @@ static const char* MESSAGE_STRINGS[] = {
"NewAccountCreated", "NewAccountCreated",
"StarPositions", "StarPositions",
"SystemCommodities", "SystemCommodities",
"PlayerDetails",
"TransactionResult",
}; };
#endif //GAMESHARED_H #endif //GAMESHARED_H