can buy/sell crap from station
This commit is contained in:
@@ -12,6 +12,17 @@ fn u64 writeU64ToBufferLE(u8* buffer, u64 value) {
|
||||
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) {
|
||||
buffer[0] = (u8)(value & 0xFF);
|
||||
buffer[1] = (u8)((value >> 8) & 0xFF);
|
||||
@@ -45,6 +56,18 @@ fn u64 readU64FromBufferLE(u8 *buffer) {
|
||||
((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) {
|
||||
return (u32)buffer[0] |
|
||||
((u32)buffer[1] << 8) |
|
||||
|
||||
+219
-43
@@ -34,6 +34,14 @@ typedef enum Tab {
|
||||
Tab_Count,
|
||||
} Tab;
|
||||
|
||||
typedef enum StationTabStates {
|
||||
StationTabStateTable,
|
||||
StationTabStateTransact,
|
||||
StationTabStateResult,
|
||||
StationTabStateLoading,
|
||||
StationTabStates_Count,
|
||||
} StationTabStates;
|
||||
|
||||
typedef enum Screen {
|
||||
ScreenLogin,
|
||||
ScreenCreateCharacter,
|
||||
@@ -74,6 +82,12 @@ typedef struct EntityList {
|
||||
Entity* items;
|
||||
} EntityList;
|
||||
|
||||
typedef struct TransactionResult {
|
||||
bool buying;
|
||||
u32 qty;
|
||||
u32 cr;
|
||||
} TransactionResult;
|
||||
|
||||
typedef struct ParsedServerMessage {
|
||||
Message type;
|
||||
u16 port;
|
||||
@@ -86,6 +100,8 @@ typedef struct ParsedServerMessage {
|
||||
Pos2u8 positions[STAR_SYSTEM_COUNT];
|
||||
PlanetType planet_types[MAX_PLANETS*STAR_SYSTEM_COUNT];
|
||||
StarSystem sys;
|
||||
PlayerShip ship;
|
||||
TransactionResult tx_result;
|
||||
//Entity entities[PARSED_CLIENT_ENTITY_LEN];
|
||||
//u64 ids[PARSED_IDS_LEN];
|
||||
} ParsedServerMessage;
|
||||
@@ -133,7 +149,8 @@ typedef struct GameState {
|
||||
StarSystem map[STAR_SYSTEM_COUNT];
|
||||
Pos2 pos;
|
||||
StarSystem current;
|
||||
bool modal;
|
||||
StationTabStates station_tab_state;
|
||||
TransactionResult tx_result;
|
||||
} GameState;
|
||||
|
||||
///// GLOBALS
|
||||
@@ -365,15 +382,22 @@ fn void clearServerSentState() {
|
||||
}
|
||||
|
||||
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]);
|
||||
u8List bytes = {len, len, message};
|
||||
u64 msg_pos = 1;
|
||||
ParsedServerMessage parsed = {0};
|
||||
parsed.type = msg_type;
|
||||
switch (msg_type) {
|
||||
case MessageNewAccountCreated:
|
||||
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: {
|
||||
u32 star_msg_size = 2+MAX_PLANETS;
|
||||
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;
|
||||
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: {
|
||||
parsed.id = readU64FromBufferLE(message + 1);
|
||||
} break;
|
||||
@@ -446,6 +500,7 @@ fn bool updateAndRender(TuiState* tui, void* s, u8* input_buffer, u64 loop_count
|
||||
fflush(stdout);
|
||||
return should_quit;
|
||||
}
|
||||
char sbuf[512] = {0};
|
||||
|
||||
// process server messages
|
||||
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) {
|
||||
msg_iters += 0;
|
||||
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: {
|
||||
for (u32 i = 0; i < STAR_SYSTEM_COUNT; i++) {
|
||||
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
|
||||
char tmp_buffer[64] = {0};
|
||||
u16 line = 2;
|
||||
// 1. draw the outline
|
||||
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;
|
||||
u32 n = 12 * 4;
|
||||
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);
|
||||
renderStrToBuffer(tui->frame_buffer, 5, ++line, tmp_buffer, screen_dimensions);
|
||||
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, sbuf, screen_dimensions);
|
||||
line++;
|
||||
// the table
|
||||
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) {
|
||||
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;
|
||||
case TabStation: {
|
||||
if (input_buffer[0] == 'q' || user_pressed_esc) {
|
||||
if (state->modal) {
|
||||
state->modal = !state->modal;
|
||||
} else {
|
||||
should_quit = true;
|
||||
switch (state->station_tab_state) {
|
||||
case StationTabStateTable:
|
||||
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) {
|
||||
@@ -744,52 +842,130 @@ 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));
|
||||
|
||||
if (user_pressed_enter) {
|
||||
state->modal = !state->modal;
|
||||
if (state->modal) {
|
||||
state->modal_choice.len = 2;
|
||||
state->modal_choice.selected_index = 0;
|
||||
}
|
||||
}
|
||||
if (state->modal) {
|
||||
if (state->station_tab_state != StationTabStateTable) {
|
||||
if (user_pressed_right) {
|
||||
state->modal_choice.selected_index = 1;
|
||||
} else if (user_pressed_left) {
|
||||
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 = {
|
||||
.x = (tui->screen_dimensions.width - 50) / 2,
|
||||
.y = (tui->screen_dimensions.height - 15) / 2,
|
||||
.height = 15,
|
||||
.width = 50,
|
||||
};
|
||||
clearBox(tui, modal_outline);
|
||||
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 buy_x = modal_outline.x+2;
|
||||
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, sell_x, y_off, "Sell", screen_dimensions);
|
||||
if (state->modal_choice.selected_index == 0) {
|
||||
u32 pos = XYToPos(buy_x, y_off, tui->screen_dimensions.width);
|
||||
tui->frame_buffer[pos].background = ANSI_WHITE;
|
||||
tui->frame_buffer[pos].foreground = ANSI_BLACK;
|
||||
tui->frame_buffer[pos+1].background = ANSI_WHITE;
|
||||
tui->frame_buffer[pos+1].foreground = ANSI_BLACK;
|
||||
tui->frame_buffer[pos+2].background = ANSI_WHITE;
|
||||
tui->frame_buffer[pos+2].foreground = ANSI_BLACK;
|
||||
} else {
|
||||
u32 pos = XYToPos(sell_x, y_off, tui->screen_dimensions.width);
|
||||
tui->frame_buffer[pos].background = ANSI_WHITE;
|
||||
tui->frame_buffer[pos].foreground = ANSI_BLACK;
|
||||
tui->frame_buffer[pos+1].background = ANSI_WHITE;
|
||||
tui->frame_buffer[pos+1].foreground = ANSI_BLACK;
|
||||
tui->frame_buffer[pos+2].background = ANSI_WHITE;
|
||||
tui->frame_buffer[pos+2].foreground = ANSI_BLACK;
|
||||
tui->frame_buffer[pos+3].background = ANSI_WHITE;
|
||||
tui->frame_buffer[pos+3].foreground = ANSI_BLACK;
|
||||
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 sell_x = modal_outline.x+modal_outline.width-6;
|
||||
renderStrToBuffer(tui->frame_buffer, buy_x, y_off, "Buy", screen_dimensions);
|
||||
renderStrToBuffer(tui->frame_buffer, sell_x, y_off, "Sell", screen_dimensions);
|
||||
if (buy_selected) {
|
||||
u32 pos = XYToPos(buy_x, y_off, tui->screen_dimensions.width);
|
||||
tui->frame_buffer[pos].background = ANSI_WHITE;
|
||||
tui->frame_buffer[pos].foreground = ANSI_BLACK;
|
||||
tui->frame_buffer[pos+1].background = ANSI_WHITE;
|
||||
tui->frame_buffer[pos+1].foreground = ANSI_BLACK;
|
||||
tui->frame_buffer[pos+2].background = ANSI_WHITE;
|
||||
tui->frame_buffer[pos+2].foreground = ANSI_BLACK;
|
||||
} else {
|
||||
u32 pos = XYToPos(sell_x, y_off, tui->screen_dimensions.width);
|
||||
tui->frame_buffer[pos].background = ANSI_WHITE;
|
||||
tui->frame_buffer[pos].foreground = ANSI_BLACK;
|
||||
tui->frame_buffer[pos+1].background = ANSI_WHITE;
|
||||
tui->frame_buffer[pos+1].foreground = ANSI_BLACK;
|
||||
tui->frame_buffer[pos+2].background = ANSI_WHITE;
|
||||
tui->frame_buffer[pos+2].foreground = ANSI_BLACK;
|
||||
tui->frame_buffer[pos+3].background = ANSI_WHITE;
|
||||
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;
|
||||
|
||||
+14
-9
@@ -95,15 +95,6 @@ fn RGB rgbDarken(RGB color, f32 factor) {
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
fn RGB ansiToRGB(u8 ansi) {
|
||||
ansi -= 16;
|
||||
u8 b = ansi % 51;
|
||||
ansi -= ()
|
||||
u8 g =
|
||||
}
|
||||
*/
|
||||
|
||||
fn bool rgbEq(RGB a, RGB 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) {
|
||||
str items[] = {"┌","─","┐","│","└","┘"};
|
||||
str b_items[] = {"┏","━","┓","┃","┗","┛"};
|
||||
|
||||
+164
-26
@@ -42,6 +42,8 @@ typedef struct ParsedClientCommand {
|
||||
u16 alt_port;
|
||||
u32 sender_ip;
|
||||
u32 alt_ip;
|
||||
u32 qty;
|
||||
CommodityType commodity;
|
||||
StringChunkList name;
|
||||
StringChunkList pass;
|
||||
u64 id;
|
||||
@@ -369,6 +371,80 @@ fn u32 findClientHandleBySocketAddress(ClientList* clients, SocketAddress addres
|
||||
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) {
|
||||
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;
|
||||
@@ -420,6 +496,12 @@ fn void handleIncomingMessage(u8* message, u32 len, SocketAddress sender, i32 so
|
||||
} break;
|
||||
case CommandKeepAlive:
|
||||
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: {
|
||||
printf("command create character received\n");
|
||||
parsed.byte = message[1];
|
||||
@@ -475,9 +557,25 @@ 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]);
|
||||
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);
|
||||
|
||||
for (u32 i = 0; i < STAR_SYSTEM_COUNT; i++) {
|
||||
state.map[i].changed = false;
|
||||
}
|
||||
|
||||
u32 loop_duration = osTimeMicrosecondsNow() - loop_start;
|
||||
i32 remaining_time = GOAL_NETWORK_SEND_LOOP_US - loop_duration;
|
||||
if (remaining_time > 0) {
|
||||
@@ -522,6 +620,56 @@ fn void* gameLoop(void* params) {
|
||||
u32 client_handle = findClientHandleBySocketAddress(&state.clients, sender);
|
||||
Client* client = &state.clients.items[client_handle];
|
||||
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: {
|
||||
dbg("KeepAlive for client_handle=%d, %ld", client_handle, 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) {
|
||||
existing_account = &state.accounts[i];
|
||||
existing_account->id = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
existing_account->name = name;
|
||||
@@ -582,7 +731,7 @@ fn void* gameLoop(void* params) {
|
||||
if (existing_account->ship.id != 0) {
|
||||
// tell the client their account id
|
||||
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.address = sender;
|
||||
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);
|
||||
} break;
|
||||
case CommandCreateCharacter: {
|
||||
Account account = state.accounts[client->account_id];
|
||||
if (account.ship.id == 0) {
|
||||
Account* account = &state.accounts[client->account_id];
|
||||
if (account->ship.id == 0) {
|
||||
ShipTemplate template = SHIPS[msg.byte];
|
||||
PlayerShip player_ship = {
|
||||
.type = msg.byte,
|
||||
@@ -613,26 +762,29 @@ fn void* gameLoop(void* params) {
|
||||
.base_cost = template.base_cost,
|
||||
.remaining_mortgage = template.base_cost - STARTING_DOWN_PAYMENT,
|
||||
.interest_rate = calcInterestRate(template.base_cost, STARTING_DOWN_PAYMENT),
|
||||
.credits = 10000.0,
|
||||
//.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
|
||||
.id = ++state.next_eid, // start from 1
|
||||
};
|
||||
account.ship = player_ship;
|
||||
printf("ship_type=%s, client_handle=%d, acct_id=%d\n", SHIP_TYPE_STRINGS[msg.byte], client_handle, account.id);
|
||||
account->ship = player_ship;
|
||||
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->pos.x = starting_system.x;
|
||||
account->pos.y = starting_system.y;
|
||||
|
||||
// tell the client their account id
|
||||
outgoing_message.address = sender;
|
||||
outgoing_message.bytes_len = 9;
|
||||
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);
|
||||
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;
|
||||
outgoing_message.address = sender;
|
||||
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);
|
||||
printf("%s sent\n", MESSAGE_STRINGS[outgoing_message.bytes[0]]);
|
||||
|
||||
u32 planet_count = 0;
|
||||
for (u32 i = 0; i < MAX_PLANETS; i++) {
|
||||
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];
|
||||
}
|
||||
}
|
||||
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]]);
|
||||
|
||||
} else {
|
||||
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);
|
||||
// set position of all star systems
|
||||
for (u32 i = 0; i < STAR_SYSTEM_COUNT; i++) {
|
||||
state.map[i].idx = i;
|
||||
state.map[i].name = STAR_NAMES[i];
|
||||
state.map[i].crime = rand() % 100;
|
||||
state.map[i].x = rand() % MAP_WIDTH;
|
||||
|
||||
@@ -341,9 +341,11 @@ typedef struct Planet {
|
||||
} Planet;
|
||||
|
||||
typedef struct StarSystem {
|
||||
bool changed;
|
||||
u32 x;
|
||||
u32 y;
|
||||
u32 crime;
|
||||
u32 idx;
|
||||
str name;
|
||||
Planet planets[MAX_PLANETS];
|
||||
} StarSystem;
|
||||
@@ -413,6 +415,7 @@ typedef enum CommandType {
|
||||
CommandKeepAlive,
|
||||
CommandLogin,
|
||||
CommandCreateCharacter,
|
||||
CommandTransact,
|
||||
CommandType_Count,
|
||||
} CommandType;
|
||||
static const char* command_type_strings[] = {
|
||||
@@ -431,6 +434,8 @@ typedef enum Message {
|
||||
MessageNewAccountCreated,
|
||||
MessageStarPositions,
|
||||
MessageSystemCommodities,
|
||||
MessagePlayerDetails,
|
||||
MessageTransactionResult,
|
||||
Message_Count,
|
||||
} Message;
|
||||
static const char* MESSAGE_STRINGS[] = {
|
||||
@@ -440,6 +445,8 @@ static const char* MESSAGE_STRINGS[] = {
|
||||
"NewAccountCreated",
|
||||
"StarPositions",
|
||||
"SystemCommodities",
|
||||
"PlayerDetails",
|
||||
"TransactionResult",
|
||||
};
|
||||
|
||||
#endif //GAMESHARED_H
|
||||
|
||||
Reference in New Issue
Block a user