From 8359c3af6f8d1df39c552c1ba34b4601066e7f07 Mon Sep 17 00:00:00 2001 From: Tenari Date: Sat, 14 Mar 2026 12:30:15 -0500 Subject: [PATCH] Thing > entity, also try out anton's pre-alloced with generations thing --- src/base/memory.c | 6 + src/client.c | 56 +++++---- src/render.c | 24 ++-- src/server.c | 296 +++++++++++++++++++++------------------------- src/shared.h | 27 +++-- 5 files changed, 198 insertions(+), 211 deletions(-) diff --git a/src/base/memory.c b/src/base/memory.c index 3d63540..b0655ac 100644 --- a/src/base/memory.c +++ b/src/base/memory.c @@ -40,6 +40,12 @@ fn void* arenaAlloc(Arena* arena, u64 size) { return memory; } +fn void* arenaAllocZero(Arena* a, u64 size) { + void* result = arenaAlloc(a, size); + MemoryZero(result, size); + return result; +} + fn void* arenaAllocArraySized(Arena* arena, u64 elem_size, u64 count) { return arenaAlloc(arena, elem_size * count); } diff --git a/src/client.c b/src/client.c index cb0ca33..ec7c0a6 100644 --- a/src/client.c +++ b/src/client.c @@ -49,21 +49,21 @@ typedef enum SpeechTailDirection { SpeechTailDirection_Count } SpeechTailDirection; -typedef struct Entity { - EntityType type; +typedef struct Thing { + ThingType type; u8 x; u8 y; u8 color; u64 features; u64 id; StringChunkList name; -} Entity; +} Thing; -typedef struct EntityList { +typedef struct ThingList { u64 length; // the currently used length u64 capacity; - Entity* items; -} EntityList; + Thing* items; +} ThingList; typedef struct ParsedServerMessage { Message type; @@ -74,7 +74,7 @@ typedef struct ParsedServerMessage { u64 id; u64 server_frame; XYZ xyz; - //Entity entities[PARSED_CLIENT_ENTITY_LEN]; + //Thing things[PARSED_CLIENT_THING_LEN]; //u64 ids[PARSED_IDS_LEN]; } ParsedServerMessage; @@ -105,11 +105,11 @@ typedef struct MenuState { typedef struct GameState { Screen screen; Screen old_screen; - EntityList entities; - Entity me; + ThingList things; + Thing me; u64 server_frame; u64 loop_count; - Arena entity_arena; + Arena thing_arena; StringArena string_arena; LoginState login_state; MenuState menu; @@ -175,16 +175,16 @@ fn ParsedServerMessage* psmThreadSafeNonblockingQueuePop(ParsedServerMessageThre return result; } -fn void entityPush(EntityList* list, Entity e) { +fn void thingPush(ThingList* list, Thing e) { if (list->length >= list->capacity) { - arenaAllocArray(&state.entity_arena, Entity, list->capacity); + arenaAllocArray(&state.thing_arena, Thing, list->capacity); list->capacity = list->capacity * 2; } list->items[list->length] = e; list->length += 1; } -fn bool entityDelete(EntityList* list, u64 id) { +fn bool thingDelete(ThingList* list, u64 id) { assert(list->length > 0); if (list->items[list->length - 1].id == id) { list->length -= 1; @@ -342,10 +342,10 @@ 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; - state.entities.items = arenaAllocArray(&state.entity_arena, Entity, state.entities.capacity); + arenaClear(&state.thing_arena); + state.things.capacity = 64; + state.things.length = 0; + state.things.items = arenaAllocArray(&state.thing_arena, Thing, state.things.capacity); } fn void handleIncomingMessage(u8* message, u32 len, SocketAddress sender, i32 socket) { @@ -692,10 +692,10 @@ i32 main(i32 argc, ptr argv[]) { // clear and init the state arenaInit(&permanent_arena); - arenaInit(&state.entity_arena); - state.entities.capacity = 64; - state.entities.length = 0; - state.entities.items = arenaAllocArray(&state.entity_arena, Entity, state.entities.capacity); + arenaInit(&state.thing_arena); + state.things.capacity = 64; + state.things.length = 0; + state.things.items = arenaAllocArray(&state.thing_arena, Thing, state.things.capacity); arenaInit(&state.string_arena.a); state.string_arena.mutex = newMutex(); state.message_input = stringChunkListInit(&state.string_arena); @@ -721,7 +721,19 @@ i32 main(i32 argc, ptr argv[]) { printf("bad network startup"); exit(1); } - state.client = createUDPClient(7777, argc > 1 ? argv[1] : NULL); + ptr server_address = NULL; + if (argc > 1) { + server_address = argv[1]; + } else { + printf("Server IP Address: "); + char input_string[16] = { 0 }; + scanf("%15s", input_string); + printf("%s", input_string); + if (input_string[0] && input_string[1] && input_string[2] && input_string[3]) { + server_address = input_string; + } + } + state.client = createUDPClient(7777, server_address); // "hardcoded" keep alive message to periodically send to server state.keep_alive_msg.address = state.client.server_address; diff --git a/src/render.c b/src/render.c index 4a1d009..7cbf303 100644 --- a/src/render.c +++ b/src/render.c @@ -4,17 +4,17 @@ #define MAX_SCREEN_HEIGHT 300 #define MAX_SCREEN_WIDTH 800 -fn str charForEntity(EntityType e) { +fn str charForThing(ThingType e) { switch (e) { - case EntityWall: + case ThingWall: return "# "; - case EntityDoor: + case ThingDoor: return "🚪"; - case EntityCharacter: + case ThingCharacter: return "🧙"; //return "웃"; - case EntityNull: - case EntityType_Count: + case ThingNull: + case ThingType_Count: //default: // commented out so that we get a warning for missing entity return " "; } @@ -38,21 +38,21 @@ fn void renderRoom(Pixel* buf, u32 x, u32 y, RenderableRoom* room, Dim2 screen_d } u32 bufpos = (x+1+(i*2)) + (screen_dimensions.width*(y+1+j)); - str fg_char = charForEntity(room->foreground[roompos]); + str fg_char = charForThing(room->foreground[roompos]); RGB background_color = colorForTile(room->background[roompos]); if (room->visible[roompos]) { - buf[bufpos].foreground = ansiColorForEntity(room->foreground[roompos]); + buf[bufpos].foreground = ansiColorForThing(room->foreground[roompos]); if (room->light[roompos] < VISIBLE_BRIGHTNESS_CUTOFF) { - fg_char = charForEntity(EntityMurkyUnknown); + fg_char = charForThing(ThingMurkyUnknown); background_color = rgbDarken(background_color, 0.8); } } else if (room->memory[roompos] == RememberedTileQualityDim) { background_color = rgbDarken(background_color, 0.4); - buf[bufpos].foreground = ansiColorForEntity(room->foreground[roompos]); - fg_char = charForEntity(EntityMurkyUnknown); + buf[bufpos].foreground = ansiColorForThing(room->foreground[roompos]); + fg_char = charForThing(ThingMurkyUnknown); } else if (room->memory[roompos] == RememberedTileQualityClear) { background_color = rgbDarken(background_color, 0.6); - buf[bufpos].foreground = ansiColorForEntity(room->foreground[roompos]); + buf[bufpos].foreground = ansiColorForThing(room->foreground[roompos]); } buf[bufpos].background = rgbToAnsi(background_color); Utf8Character first_character_class = classifyUtf8Character((u8)fg_char[0]); diff --git a/src/server.c b/src/server.c index 3bdef07..5e7a155 100644 --- a/src/server.c +++ b/src/server.c @@ -17,7 +17,7 @@ #include "string_chunk.c" ///// CONSTANTS -#define MAX_ENTITIES (2<<18) +#define MAX_THINGS (2<<18) #define LEFT_ROOM_ENTITES_LEN (KB(1)) #define ROOM_MAP_COLLISIONS_LEN MAX_ROOMS/8 #define CLIENT_COMMAND_LIST_LEN 8 @@ -57,22 +57,34 @@ typedef struct ParsedClientCommandThreadQueue { Cond not_full; } ParsedClientCommandThreadQueue; -typedef struct Entity { +typedef struct ThingRef { + u32 i; + u32 gen; +} ThingRef; + +typedef struct Thing { + ThingType type; bool changed; u8 x; u8 y; u8 color; - EntityType type; u32 misc; - u64 id; + ThingRef id; u64 features; -} Entity; +} Thing; + +typedef struct Things { + u32 first_free; + u32 next_free[MAX_THINGS]; + u32 gen[MAX_THINGS]; + Thing things[MAX_THINGS]; +} Things; typedef struct Account { u64 id; String name; String pw; - u64 eid; + ThingRef eid; } Account; typedef struct AccountChunk { @@ -82,30 +94,10 @@ typedef struct AccountChunk { Account* items; // the actual accounts } AccountChunk; -typedef struct EntityList { - u64 length; // the currently used length - u64 capacity; - Entity* items; -} EntityList; - -typedef struct EntityChunk { - u64 length; // the currently used # of entities in this chunk - u64 capacity; // the "chunk size" / space in this chunk - struct EntityChunk* next; // the next chunk - Entity* items; // the actual entities -} EntityChunk; - -typedef struct ChunkedEntityList { - u64 length; // the current number of entities - u64 chunk_size; // the # of entities per chunk - u64 chunks; // the # of chunks in this list so far - EntityChunk* first; // the first chunk of entities -} ChunkedEntityList; - typedef struct Client { u16 lan_port; i32 lan_ip; - u64 character_eid; + ThingRef character_eid; u64 account_id; SocketAddress address; CommandType commands[CLIENT_COMMAND_LIST_LEN]; @@ -122,23 +114,85 @@ typedef struct State { Mutex client_mutex; Mutex mutex; ClientList clients; - u64 next_eid; AccountChunk accounts; u64 frame; Arena game_scratch; StringArena string_arena; ParsedClientCommandThreadQueue* network_recv_queue; OutgoingMessageQueue* network_send_queue; + Things* things; } State; ///// Global Variables global State state = { 0 }; global Arena permanent_arena = { 0 }; -global const Entity NULL_ENTITY = { 0 }; +global const Thing NULL_THING = { 0 }; global bool debug_mode = false; -global ChunkedEntityList free_chunks = { 0, CHUNK_SIZE, 0, NULL }; ///// functionImplementations() +fn bool thingRefsEq(ThingRef a, ThingRef b) { + return a.i == b.i && a.gen == b.gen; +} + +fn void thingsInit(Things* things) { + MemoryZero(things->next_free, sizeof(things->next_free)); + MemoryZero(things->gen, sizeof(things->gen)); + MemoryZero(things->things, sizeof(things->things)); + // 0 is the NULL_THING + things->first_free = 1; + // they are all free initially, + // except the last slot has nothing to point to, and + // the first slot is for the NULL_THING + for (u32 i = 1; i < MAX_THINGS-1; i++) { + things->next_free[i] = i+1; + } +} + +fn u32 thingDeref(Things* t, ThingRef ref) { + if (ref.i > 0 && ref.i < MAX_THINGS && t->gen[ref.i] == ref.gen) { + return ref.i; + } else { + return 0; + } +} + +fn ThingRef thingAdd(Things* t, Thing thing) { + ThingRef result = {0}; + u32 slot = t->first_free; + if (slot) { + MemoryCopyStruct(&t->things[slot], &thing); + t->gen[slot] += 1; + t->first_free = t->next_free[slot]; + t->next_free[slot] = 0; // sort of indicates that this slot is used + result.gen = t->gen[slot]; + result.i = slot; + t->things[slot].id = result; + } + return result; +} + +fn Thing* thingGet(Things* t, ThingRef ref) { + u32 slot = thingDeref(t, ref); + if (slot) { + return &t->things[slot]; + } else { + return (Thing*)&NULL_THING; + } +} + +fn bool thingRemove(Things* t, ThingRef ref) { + u32 slot = thingDeref(t, ref); + if (slot) { + if (t->first_free) { + t->next_free[slot] = t->first_free; + } + t->first_free = slot; + return true; + } else { + return false; + } +} + fn ParsedClientCommandThreadQueue* newPCCThreadQueue(Arena* a) { ParsedClientCommandThreadQueue* result = arenaAlloc(a, sizeof(ParsedClientCommandThreadQueue)); MemoryZero(result, (sizeof *result)); @@ -182,13 +236,14 @@ fn ParsedClientCommand* pccThreadSafeNonblockingQueuePop(ParsedClientCommandThre return result; } -fn u64 entitySerialize(Entity current, Account* acct, u64 index, u8 bytes[]) { - // send entity header (common to all entity types) - index += writeU64ToBufferLE(bytes + index, current.id); +fn u64 thingSerialize(Thing current, Account* acct, u64 index, u8 bytes[]) { + // send thing header (common to all thing types) + index += writeU32ToBufferLE(bytes + index, current.id.i); + index += writeU32ToBufferLE(bytes + index, current.id.gen); index += writeU64ToBufferLE(bytes + index, current.features); bytes[index++] = current.x; bytes[index++] = current.y; - bytes[index++] = (u8)current.type; // EntityType + bytes[index++] = (u8)current.type; // ThingType //if (CheckFlag(current.features, FeatureRegensHp)) { // index += writeU16ToBufferLE(bytes + index, current.hp); @@ -196,7 +251,7 @@ fn u64 entitySerialize(Entity current, Account* acct, u64 index, u8 bytes[]) { //} // send character-specific details (color and name) - if (current.type == EntityCharacter) { + if (current.type == ThingCharacter) { bytes[index++] = current.color; // send name string index += writeU64ToBufferLE(bytes + index, acct->name.length); @@ -207,10 +262,10 @@ fn u64 entitySerialize(Entity current, Account* acct, u64 index, u8 bytes[]) { return index; } -fn u64 entityFeaturesFromType(EntityType type) { +fn u64 thingFeaturesFromType(ThingType type) { u64 result = 0; switch (type) { - case EntityCharacter: { + case ThingCharacter: { SetFlag(result, FeatureWalksAround); SetFlag(result, FeatureCanFight); } break; @@ -233,11 +288,11 @@ fn Account* findAccountById(u64 id) { return NULL; } -fn Account* findAccountByEId(u64 id) { +fn Account* findAccountByEId(ThingRef id) { AccountChunk* current = &state.accounts; while (current != NULL) { for (u32 i = 0; i < current->length; i++) { - if (current->items[i].eid == id) { + if (thingRefsEq(current->items[i].eid, id)) { return ¤t->items[i]; } } @@ -285,93 +340,6 @@ fn Account* newAccount(Arena* a, Account details) { return NULL; } -fn u64 pushEntity(EntityChunk* chunk, Entity e) { - assert(chunk->length < chunk->capacity); - chunk->items[chunk->length] = e; - u64 result = chunk->length; - chunk->length += 1; - return result; -} - -fn bool isSpaceInChunk(EntityChunk* chunk) { - return chunk->length < chunk->capacity; -} - -fn bool allChunksFull(ChunkedEntityList list) { - return list.length >= (list.chunk_size * list.chunks); -} - -fn EntityChunk* pushNewChunk(Arena* a, ChunkedEntityList* list) { - EntityChunk* new_chunk; - if (free_chunks.length > 0) { - new_chunk = free_chunks.first; - free_chunks.length -= 1; - free_chunks.first = new_chunk->next; - } else { - new_chunk = arenaAlloc(a, sizeof(EntityChunk)); - // alloc the new chunk of entities - new_chunk->length = 0; - new_chunk->capacity = list->chunk_size; - new_chunk->next = NULL; - new_chunk->items = arenaAllocArray(a, Entity, list->chunk_size); - } - // bookeeping in the list - list->chunks += 1; - if (list->first == NULL) { - list->first = new_chunk; - } else { - EntityChunk* last = list->first; - while (last->next != NULL) { - last = last->next; - } - last->next = new_chunk; - } - return new_chunk; -} - -fn Entity* entityPtrFromChunkList(ChunkedEntityList* list, i32 index) { - Entity* result = (Entity*)&NULL_ENTITY; - i32 chunk_index = index / list->chunk_size; - EntityChunk* chunk = list->first; - for (i32 i = 0; i < chunk_index; i++) { - chunk = chunk->next; - } - result = &chunk->items[index % list->chunk_size]; - return result; -} - -fn Entity entityFromChunkList(ChunkedEntityList* list, i32 index) { - Entity result = {0}; - i32 chunk_index = index / list->chunk_size; - EntityChunk* chunk = list->first; - for (i32 i = 0; i < chunk_index; i++) { - chunk = chunk->next; - } - result = chunk->items[index % list->chunk_size]; - return result; -} - -fn bool deleteLastEntity(ChunkedEntityList* list, EntityChunk* last_chunk, EntityChunk* second_to_last_chunk) { - last_chunk->length -= 1; - list->length -= 1; - // don't delete the room's only chunk, but otherwise move the chunk to the free-list - if (last_chunk->length == 0 && last_chunk != list->first) { - list->chunks -= 1; - second_to_last_chunk->next = NULL; - free_chunks.length += 1; - EntityChunk* last_free_chunk = free_chunks.first; - if (last_free_chunk) { - while (last_free_chunk->next != NULL) { - last_free_chunk = last_free_chunk->next; - } - last_free_chunk->next = last_chunk; - } else { - free_chunks.first = last_chunk; - } - } - return true; -} - fn void exitWithErrorMessage(ptr msg) { printf("error: %s", msg); exit(1); @@ -384,7 +352,7 @@ fn u32 pushClient(ClientList* clients, SocketAddress addr) { // first, try to overwrite an old dc'ed client for (u32 i = 1; i < clients->length; i++) { - if (clients->items[i].character_eid == 0) { + if (clients->items[i].account_id == 0) { clients->items[i] = new_client; return i; } @@ -398,12 +366,12 @@ fn u32 pushClient(ClientList* clients, SocketAddress addr) { return result; } -fn bool deleteClientByEId(ClientList* clients, u64 id) { +fn bool deleteClientByEId(ClientList* clients, ThingRef id) { bool succeeded = false; Client blank_client = {0}; // i=1 because first client is null-client for (u32 i = 1; i < clients->length && !succeeded; i++) { - if (clients->items[i].character_eid == id) { + if (thingRefsEq(clients->items[i].character_eid, id)) { clients->items[i] = blank_client; succeeded = true; } @@ -411,10 +379,10 @@ fn bool deleteClientByEId(ClientList* clients, u64 id) { return succeeded; } -fn u32 findClientHandleByEId(ClientList* clients, u64 id) { +fn u32 findClientHandleByEId(ClientList* clients, ThingRef id) { for (u32 i = 0; i < clients->length; i++) { Client c = clients->items[i]; - if (c.character_eid == id) { + if (thingRefsEq(c.character_eid, id)) { return i; } } @@ -533,7 +501,7 @@ fn void* sendNetworkUpdates(void* sock) { memset(&state.clients.items[i], 0, sizeof(Client)); continue; } - if (client.character_eid == 0) { + if (client.character_eid.i == 0) { continue; // they are still creating their character } @@ -549,6 +517,18 @@ fn void* sendNetworkUpdates(void* sock) { return NULL; } +fn bool messageSendCharacterId(SocketAddress sender, ThingRef id) { + UDPMessage outgoing_message = {0}; + outgoing_message.address = sender; + outgoing_message.bytes_len = 9; + outgoing_message.bytes[0] = (u8)MessageCharacterId; + writeU32ToBufferLE(outgoing_message.bytes + 1, id.i); + writeU32ToBufferLE(outgoing_message.bytes + 5, id.gen); + outgoingMessageQueuePush(state.network_send_queue, &outgoing_message); + printf("MessageCharacterId sent\n"); + return true; +} + fn void* gameLoop(void* params) { LaneCtx* lane_ctx = (LaneCtx*)params; ThreadContext tctx = { @@ -639,16 +619,11 @@ fn void* gameLoop(void* params) { existing_account = newAccount(&permanent_arena, new_account); } client->account_id = existing_account->id; - if (existing_account->eid != 0) { + if (existing_account->eid.i != 0) { client->character_eid = existing_account->eid; // tell the client their character id - outgoing_message.bytes[0] = (u8)MessageCharacterId; - writeU64ToBufferLE(outgoing_message.bytes + 1, existing_account->eid); - outgoing_message.bytes_len = 9; - outgoing_message.address = sender; - outgoingMessageQueuePush(state.network_send_queue, &outgoing_message); - printf("MessageCharacterId sent\n"); + messageSendCharacterId(sender, existing_account->eid); } else { // tell the client they made a new account outgoing_message.bytes[0] = (u8)MessageNewAccountCreated; @@ -657,32 +632,26 @@ fn void* gameLoop(void* params) { outgoingMessageQueuePush(state.network_send_queue, &outgoing_message); printf("MessageNewAccountCreated sent\n"); } - printf("eid=%lld, client_handle=%d, acct_id=%lld\n", existing_account->eid, client_handle, existing_account->id); + printf("eid=%d, client_handle=%d, acct_id=%lld\n", existing_account->eid.i, client_handle, existing_account->id); } break; case CommandCreateCharacter: { - if (client->character_eid == 0) { + if (client->character_eid.i == 0) { // Create new character XYZ room_xyz = {0, 0, 0 }; - Entity character = { - .type = EntityCharacter, - .id = state.next_eid++, + Thing character = { + .type = ThingCharacter, .changed = true, - .features = entityFeaturesFromType(EntityCharacter), + .features = thingFeaturesFromType(ThingCharacter), .color = msg.byte, }; dbg("made new character id=%ld\n", character.id); client->character_eid = character.id; Account* account = findAccountById(client->account_id); account->eid = character.id; - printf("character_eid=%lld, client_handle=%d, acct_id=%lld\n", account->eid, client_handle, account->id); + printf("character_eid=%d, client_handle=%d, acct_id=%lld\n", account->eid.i, client_handle, account->id); // tell the client their character id - outgoing_message.address = sender; - outgoing_message.bytes_len = 9; - outgoing_message.bytes[0] = (u8)MessageCharacterId; - writeU64ToBufferLE(outgoing_message.bytes + 1, character.id); - outgoingMessageQueuePush(state.network_send_queue, &outgoing_message); - printf("MessageCharacterId sent\n"); + messageSendCharacterId(sender, character.id); } else { printf("client tried to create a character when he already has one."); } @@ -728,18 +697,14 @@ fn void* gameLoop(void* params) { // THE SERVER i32 main(i32 argc, ptr argv[]) { osInit(); - // multi-thread architecture: + // architecture: + // - the sendNetworkUpdates() infinite loop, which sends a UDP snapshot-or-delta update to each connected client N/sec + // - the receiveNetworkUpdates() infinte loop, which waits for new UDP messages from clients // - the gameLoop() which just inifinite loops every "tick" and processes user input and updates gameworld state - // - TODO: actually make this N "lanes" as described https://www.rfleury.com/p/multi-core-by-default - // such that we can split room-work among all the various cores on our machine - // - one is the sendNetworkUpdates() infinite loop, which sends a UDP snapshot-or-delta update to each connected client N/sec - // - one is the receiveNetworkUpdates() infinte loop, which waits for new UDP messages from clients + // - this is N "lanes" as described https://www.rfleury.com/p/multi-core-by-default + // such that we can split room-work among all the various cores on our machine - // 1. initialize gameworld, and spin off infinite game-loop thread - // 2. spin off sendNetworkUpdates() infinite loop thread - // 3. infinitely wait for incoming UDP messages and process them (usually by just dropping user-commands into the relevant block of shared memory) - - // 1. initialize gameworld, and spin off infinite game-loop thread + // initialize gameworld, and spin off infinite game-loop thread arenaInit(&permanent_arena); arenaInit(&state.game_scratch); arenaInit(&state.string_arena.a); @@ -755,14 +720,14 @@ i32 main(i32 argc, ptr argv[]) { state.clients.items = (Client*)arenaAllocArray(&permanent_arena, Client, SERVER_MAX_CLIENTS); state.accounts.capacity = ACCOUNT_CHUNK_SIZE; state.accounts.items = arenaAllocArray(&permanent_arena, Account, ACCOUNT_CHUNK_SIZE); + state.things = arenaAllocZero(&permanent_arena, sizeof(Things)); - // 2. spin off sendNetworkUpdates() infinite loop thread + // networking threads (send + receive) UDPServer listener = createUDPServer(SERVER_PORT); if (!listener.ready) { exitWithErrorMessage("Couldn't start the udp server"); } Thread send_thread = spawnThread(&sendNetworkUpdates, &listener.server_socket); - // 3. infinitely wait for incoming UDP messages and process them (usually by just dropping user-commands into the relevant block of shared memory) Thread recv_thread = spawnThread(&receiveNetworkUpdates, &listener); u64 lane_broadcast_val = 0; @@ -780,6 +745,9 @@ i32 main(i32 argc, ptr argv[]) { osThreadJoin(game_threads[i], MAX_u64); } + osThreadJoin(send_thread, MAX_u64); + osThreadJoin(recv_thread, MAX_u64); + return 0; } diff --git a/src/shared.h b/src/shared.h index e2bb99f..0ab83c7 100644 --- a/src/shared.h +++ b/src/shared.h @@ -6,21 +6,24 @@ ///// #define some game-tunable constants #define GAME_CONSTANT_ONE (1) #define GAME_CONSTANT_TWO (2) +#define THING_HEADER_MESSAGE_SIZE (8+8+1+1+1) +#define THING_MESSAGE_SIZE (THING_HEADER_MESSAGE_SIZE+2+2+2+2+8+1+1) -typedef enum EntityFeature { +typedef enum ThingFeature { FeatureWalksAround, FeatureCanFight, - EntityFeature_Count -} EntityFeature; + ThingFeature_Count +} ThingFeature; -typedef enum EntityType { - EntityNull, - EntityWall, - EntityDoor, - EntityCharacter, - EntityType_Count, -} EntityType; -static const char* ENTITY_STRINGS[] = { +typedef enum ThingType { + ThingNull, + ThingWall, + ThingDoor, + ThingCharacter, + ThingType_Count, +} ThingType; + +static const char* THING_TYPE_STRINGS[] = { "NULL", "Wall", "Door", @@ -58,8 +61,6 @@ static const char* command_type_strings[] = { "CreateCharacter", }; -#define ENTITY_HEADER_MESSAGE_SIZE (8+8+1+1+1) -#define ENTITY_MESSAGE_SIZE (ENTITY_HEADER_MESSAGE_SIZE+2+2+2+2+8+1+1) typedef enum Message { MessageInvalid, MessageCharacterId,