buddy progress

This commit is contained in:
jacob 2025-02-07 19:17:27 -06:00
parent 6f7c19e053
commit 78ace4b38a
8 changed files with 296 additions and 47 deletions

View File

@ -51,7 +51,7 @@ void arena_release(struct arena *arena)
} }
/* NOTE: Application will exit if arena fails to commit memory */ /* NOTE: Application will exit if arena fails to commit memory */
void *_arena_push_bytes(struct arena *arena, u64 size, u64 align) void *arena_push_bytes(struct arena *arena, u64 size, u64 align)
{ {
ASSERT(align > 0); ASSERT(align > 0);
ASSERT(!arena->readonly); ASSERT(!arena->readonly);
@ -101,7 +101,7 @@ void arena_copy_replace(struct arena *dest, struct arena *src)
arena_reset(dest); arena_reset(dest);
u64 data_size = src->pos; u64 data_size = src->pos;
u8 *data_src = src->base; u8 *data_src = src->base;
u8 *data_dest = _arena_push_bytes(dest, data_size, 1); u8 *data_dest = arena_push_bytes(dest, data_size, 1);
MEMCPY(data_dest, data_src, data_size); MEMCPY(data_dest, data_src, data_size);
} }

View File

@ -3,21 +3,19 @@
#include "memory.h" #include "memory.h"
#define arena_push(a, type) ((type *)_arena_push_bytes((a), sizeof(type), alignof(type))) #define arena_push(a, type) ((type *)arena_push_bytes((a), sizeof(type), alignof(type)))
#define arena_push_zero(a, type) ((type *)_arena_push_bytes_zero((a), sizeof(type), alignof(type))) #define arena_push_zero(a, type) ((type *)arena_push_bytes_zero((a), sizeof(type), alignof(type)))
#define arena_push_array(a, type, n) ((type *)_arena_push_bytes((a), (sizeof(type) * (n)), alignof(type))) #define arena_push_array(a, type, n) ((type *)arena_push_bytes((a), (sizeof(type) * (n)), alignof(type)))
#define arena_push_array_zero(a, type, n) ((type *)_arena_push_bytes_zero((a), (sizeof(type) * (n)), alignof(type))) #define arena_push_array_zero(a, type, n) ((type *)arena_push_bytes_zero((a), (sizeof(type) * (n)), alignof(type)))
#define arena_pop(a, type, dest) _arena_pop_struct((a), sizeof(type), dest) #define arena_pop(a, type, dest) arena_pop_struct((a), sizeof(type), dest)
#define arena_pop_array(a, type, n, dest) _arena_pop_struct((a), sizeof(type) * (n), dest) #define arena_pop_array(a, type, n, dest) arena_pop_struct((a), sizeof(type) * (n), dest)
/* Returns a pointer to where the next allocation would be (at alignment of type). /* Returns a pointer to where the next allocation would be (at alignment of type).
* Equivalent arena_push but without actually allocating anything. */ * Equivalent arena_push but without actually allocating anything. */
#define arena_dry_push(a, type) (type *)(_arena_dry_push((a), alignof(type))) #define arena_dry_push(a, type) (type *)(_arena_dry_push((a), alignof(type)))
#define arena_align(a, align) (void *)(_arena_align((a), align))
struct temp_arena { struct temp_arena {
struct arena *arena; struct arena *arena;
u64 start_pos; u64 start_pos;
@ -29,15 +27,15 @@ struct temp_arena {
struct arena arena_alloc(u64 reserve); struct arena arena_alloc(u64 reserve);
void arena_release(struct arena *arena); void arena_release(struct arena *arena);
void *_arena_push_bytes(struct arena *arena, u64 size, u64 align); void *arena_push_bytes(struct arena *arena, u64 size, u64 align);
void arena_copy_replace(struct arena *dest, struct arena *src); void arena_copy_replace(struct arena *dest, struct arena *src);
void arena_decommit_unused_blocks(struct arena *arena); void arena_decommit_unused_blocks(struct arena *arena);
void arena_set_readonly(struct arena *arena); void arena_set_readonly(struct arena *arena);
void arena_set_readwrite(struct arena *arena); void arena_set_readwrite(struct arena *arena);
INLINE void *_arena_push_bytes_zero(struct arena *arena, u64 size, u64 align) INLINE void *arena_push_bytes_zero(struct arena *arena, u64 size, u64 align)
{ {
void *p = _arena_push_bytes(arena, size, align); void *p = arena_push_bytes(arena, size, align);
MEMZERO(p, size); MEMZERO(p, size);
return p; return p;
} }
@ -51,7 +49,7 @@ INLINE void arena_pop_to(struct arena *arena, u64 pos)
arena->pos = pos; arena->pos = pos;
} }
INLINE void _arena_pop_struct(struct arena *arena, u64 size, void *copy_dest) INLINE void arena_pop_struct(struct arena *arena, u64 size, void *copy_dest)
{ {
ASSERT(arena->pos >= size); ASSERT(arena->pos >= size);
ASSERT(!arena->readonly); ASSERT(!arena->readonly);
@ -64,7 +62,7 @@ INLINE void _arena_pop_struct(struct arena *arena, u64 size, void *copy_dest)
arena->pos = new_pos; arena->pos = new_pos;
} }
INLINE void *_arena_align(struct arena *arena, u64 align) INLINE void *arena_align(struct arena *arena, u64 align)
{ {
ASSERT(!arena->readonly); ASSERT(!arena->readonly);

200
src/buddy.c Normal file
View File

@ -0,0 +1,200 @@
#include "buddy.h"
#include "arena.h"
/* ========================== *
* Ctx
* ========================== */
struct buddy_ctx *buddy_ctx_alloc(u64 reserve)
{
/* TODO: Determine meta reserve dynamically */
struct arena meta_arena = arena_alloc(GIGABYTE(64));
struct buddy_ctx *ctx = arena_push_zero(&meta_arena, struct buddy_ctx);
ctx->meta_arena = meta_arena;
ctx->data_arena = arena_alloc(reserve);
/* TODO: Minimum block size */
ctx->levels = arena_push_array_zero(&ctx->meta_arena, struct buddy_level, 64);
for (u64 i = 0; i < 64; ++i) {
struct buddy_level *level = &ctx->levels[i];
level->ctx = ctx;
level->tier = i;
level->size = (u64)1 << i;
}
return ctx;
}
void buddy_ctx_release(struct buddy_ctx *ctx)
{
arena_release(&ctx->data_arena);
arena_release(&ctx->meta_arena);
}
/* ========================== *
* Block
* ========================== */
INTERNAL struct buddy_block *buddy_block_alloc_internal(struct buddy_ctx *ctx)
{
struct buddy_block *block;
if (ctx->first_free_block) {
block = ctx->first_free_block;
ctx->first_free_block = block->next;
} else {
block = arena_push(&ctx->meta_arena, struct buddy_block);
}
MEMZERO_STRUCT(block);
return block;
}
INTERNAL void buddy_block_release_internal(struct buddy_ctx *ctx, struct buddy_level *level, struct buddy_block *block)
{
/* Remove from unused list */
{
struct buddy_block *prev = block->prev;
struct buddy_block *next = block->next;
if (prev) {
prev->next = next;
} else {
level->first_unused_block = next;
}
if (next) {
next->prev = prev;
}
}
block->next = ctx->first_free_block;
ctx->first_free_block = block;
}
INTERNAL struct buddy_block *buddy_block_get_unused(struct buddy_ctx *ctx, struct buddy_level *level)
{
struct buddy_block *block = NULL;
/* TODO: Tier oob check */
if (level->first_unused_block) {
block = level->first_unused_block;
level->first_unused_block = block->next;
if (level->first_unused_block) {
level->first_unused_block->prev = NULL;
}
block->used = true;
block->next = NULL;
ASSERT(block->memory);
} else {
if (level->backed) {
struct buddy_level *parent_level = &ctx->levels[level->tier + 1];
struct buddy_block *parent_block = buddy_block_get_unused(ctx, parent_level);
ASSERT(parent_block->memory);
/* Create left block from parent block */
struct buddy_block *left = buddy_block_alloc_internal(ctx);
left->used = true;
left->level = level;
left->parent = parent_block;
left->memory = parent_block->memory;
/* Create right (unused) block from parent block */
struct buddy_block *right = buddy_block_alloc_internal(ctx);
right->used = false;
right->level = level;
right->parent = parent_block;
right->memory = left->memory + level->size;
if (level->first_unused_block) {
right->next = level->first_unused_block;
level->first_unused_block->prev = right;
}
level->first_unused_block = right;
left->sibling = right;
right->sibling = left;
block = left;
ASSERT(block->memory);
} else {
#if 0
/* Create left block from arena */
struct buddy_block *left = buddy_block_alloc_internal(ctx);
left->used = true;
left->level = level;
left->memory = arena_push_array(&ctx->data_arena, u8, level->size * 2);
/* Create right (unused) block from arena */
struct buddy_block *right = buddy_block_alloc_internal(ctx);
right->used = false;
right->level = level;
right->memory = left->memory + level->size;
if (level->first_unused_block) {
right->next = level->first_unused_block;
level->first_unused_block->prev = right;
}
level->first_unused_block = right;
left->sibling = right;
right->sibling = left;
block = left;
level->backed = true;
ASSERT(block->memory);
#else
/* Create block by growing arena */
block = buddy_block_alloc_internal(ctx);
block->used = true;
block->memory = arena_push_array(&ctx->data_arena, u8, level->size);
block->level = level;
level->backed = true;
ASSERT(block->memory);
#endif
}
}
ASSERT(block->memory);
return block;
}
INTERNAL void buddy_block_mark_unused(struct buddy_block *block)
{
block->used = false;
struct buddy_block *sibling = block->sibling;
struct buddy_level *level = block->level;
if (sibling && !sibling->used) {
/* Merge siblings */
struct buddy_ctx *ctx = level->ctx;
struct buddy_block *parent = block->parent;
buddy_block_release_internal(ctx, level, block);
buddy_block_release_internal(ctx, level, sibling);
if (parent) {
buddy_block_mark_unused(parent);
}
} else {
if (level->first_unused_block) {
block->next = level->first_unused_block;
level->first_unused_block->prev = block;
}
level->first_unused_block = block;
}
}
struct buddy_block *buddy_alloc(struct buddy_ctx *ctx, u64 size)
{
if (size > 0x00FFFFFFFFFFFFFFULL) {
/* TODO: Error */
}
u64 desired_block_size = 1;
u64 desired_level_tier = 0;
while (desired_block_size < size && desired_level_tier < 64) {
desired_block_size <<= 1;
++desired_level_tier;
}
struct buddy_block *block = buddy_block_get_unused(ctx, &ctx->levels[desired_level_tier]);
return block;
}
void buddy_release(struct buddy_block *block)
{
buddy_block_mark_unused(block);
}

38
src/buddy.h Normal file
View File

@ -0,0 +1,38 @@
#ifndef BUDDY_H
#define BUDDY_H
struct buddy_block {
b32 used;
struct buddy_level *level;
struct buddy_block *parent;
struct buddy_block *sibling;
/* Links to block in level's unused list or in ctx's free list */
struct buddy_block *prev;
struct buddy_block *next;
u8 *memory;
};
struct buddy_level {
struct buddy_ctx *ctx;
b32 backed; /* Is this level backed by memory in the ctx arena */
u32 tier;
u64 size;
struct buddy_block *first_unused_block;
};
struct buddy_ctx {
struct arena meta_arena;
struct arena data_arena;
struct buddy_level *levels;
struct buddy_block *first_free_block;
};
struct buddy_ctx *buddy_ctx_alloc(u64 reserve);
void buddy_ctx_release(struct buddy_ctx *ctx);
struct buddy_block *buddy_alloc(struct buddy_ctx *ctx, u64 size);
void buddy_release(struct buddy_block *block);
#endif

View File

@ -1327,7 +1327,7 @@ INTERNAL void game_update(void)
l.last = &snapshot_event; l.last = &snapshot_event;
struct string msg = game_string_from_events(temp.arena, l); struct string msg = game_string_from_events(temp.arena, l);
host_queue_write(G.host, HOST_CHANNEL_ID_ALL, msg); host_queue_write(G.host, HOST_CHANNEL_ID_ALL, msg, 0);
arena_temp_end(temp); arena_temp_end(temp);
} }

View File

@ -5,6 +5,7 @@
#include "sys.h" #include "sys.h"
#include "util.h" #include "util.h"
#include "log.h" #include "log.h"
#include "buddy.h"
//#define HOST_NETWORK_ADDRESS_STRING(str) //#define HOST_NETWORK_ADDRESS_STRING(str)
//#define HOST_NETWORK_ADDRESS_ALL_LOCAL_INTERFACES(port) //#define HOST_NETWORK_ADDRESS_ALL_LOCAL_INTERFACES(port)
@ -104,13 +105,9 @@ struct host_recv_buffer {
}; };
struct host_msg_assembler { struct host_msg_assembler {
struct host *host;
struct host_channel *channel; struct host_channel *channel;
b32 is_reliable; b32 is_reliable;
/* TODO: Remove this (testing) */
struct arena testarena;
/* Free list */ /* Free list */
struct host_msg_assembler *next_free; struct host_msg_assembler *next_free;
@ -131,8 +128,9 @@ struct host_msg_assembler {
i64 touched_ns; i64 touched_ns;
u8 *chunks_received_bitmap; struct buddy_block *buddy_block;
u8 *data; u8 *chunk_bitmap;
u8 *chunk_data;
}; };
struct host_msg_assembler_lookup_bucket { struct host_msg_assembler_lookup_bucket {
@ -173,6 +171,11 @@ struct host *host_alloc(u16 listen_port)
host->cmd_arena = arena_alloc(GIGABYTE(64)); host->cmd_arena = arena_alloc(GIGABYTE(64));
host->queued_event_arena = arena_alloc(GIGABYTE(64)); host->queued_event_arena = arena_alloc(GIGABYTE(64));
host->channel_arena = arena_alloc(GIGABYTE(64)); host->channel_arena = arena_alloc(GIGABYTE(64));
host->recv_buffer_read = arena_push_zero(&host->arena, struct host_recv_buffer);
host->recv_buffer_write = arena_push_zero(&host->arena, struct host_recv_buffer);
host->recv_buffer_read->arena = arena_alloc(GIGABYTE(64));
host->recv_buffer_write->arena = arena_alloc(GIGABYTE(64));
host->buddy = buddy_ctx_alloc(GIGABYTE(64));
host->channels = arena_dry_push(&host->channel_arena, struct host_channel); host->channels = arena_dry_push(&host->channel_arena, struct host_channel);
@ -182,12 +185,6 @@ struct host *host_alloc(u16 listen_port)
host->num_msg_assembler_lookup_buckets = NUM_MSG_ASSEMBLER_LOOKUP_BUCKETS; host->num_msg_assembler_lookup_buckets = NUM_MSG_ASSEMBLER_LOOKUP_BUCKETS;
host->msg_assembler_lookup_buckets = arena_push_array_zero(&host->arena, struct host_msg_assembler_lookup_bucket, host->num_msg_assembler_lookup_buckets); host->msg_assembler_lookup_buckets = arena_push_array_zero(&host->arena, struct host_msg_assembler_lookup_bucket, host->num_msg_assembler_lookup_buckets);
host->recv_buffer_read = arena_push_zero(&host->arena, struct host_recv_buffer);
host->recv_buffer_write = arena_push_zero(&host->arena, struct host_recv_buffer);
host->recv_buffer_read->arena = arena_alloc(GIGABYTE(64));
host->recv_buffer_write->arena = arena_alloc(GIGABYTE(64));
host->sock = sock_alloc(listen_port); host->sock = sock_alloc(listen_port);
host->recv_buffer_write_mutex = sys_mutex_alloc(); host->recv_buffer_write_mutex = sys_mutex_alloc();
@ -205,6 +202,7 @@ void host_release(struct host *host)
sock_release(host->sock); sock_release(host->sock);
buddy_ctx_release(host->buddy);
arena_release(&host->recv_buffer_write->arena); arena_release(&host->recv_buffer_write->arena);
arena_release(&host->recv_buffer_read->arena); arena_release(&host->recv_buffer_read->arena);
arena_release(&host->channel_arena); arena_release(&host->channel_arena);
@ -390,19 +388,27 @@ INTERNAL struct host_msg_assembler *host_msg_assembler_alloc(struct host_channel
ma = arena_push(&host->arena, struct host_msg_assembler); ma = arena_push(&host->arena, struct host_msg_assembler);
} }
MEMZERO_STRUCT(ma); MEMZERO_STRUCT(ma);
ma->host = channel->host;
ma->channel = channel; ma->channel = channel;
ma->msg_id = msg_id; ma->msg_id = msg_id;
ma->num_chunks_total = chunk_count; ma->num_chunks_total = chunk_count;
/* FIXME: Use buddy allocator or something */ u64 chunk_bitmap_size = ((chunk_count - 1) / 8) + 1;
u64 data_size = chunk_count * PACKET_MSG_CHUNK_MAX_LEN; if ((chunk_bitmap_size % 16) != 0) {
u64 bitmap_size = ((chunk_count - 1) / 8) + 1; /* Align chunk bitmap to 16 so msg data is aligned */
ma->testarena = arena_alloc(data_size + bitmap_size); chunk_bitmap_size += 16 - (chunk_bitmap_size % 16);
}
u64 chunk_data_size = chunk_count * PACKET_MSG_CHUNK_MAX_LEN;
/* Allocate msg data using buddy allocator since the assembler has
* arbitrary lifetime and data needs to stay contiguous for random
* access as packets are received */
ma->buddy_block = buddy_alloc(host->buddy, chunk_bitmap_size + chunk_data_size);
ma->chunk_bitmap = ma->buddy_block->memory;
MEMZERO(ma->chunk_bitmap, chunk_bitmap_size);
ma->chunk_data = ma->chunk_bitmap + chunk_bitmap_size;
/* FIXME: Ensure chunk_count > 0 */ /* FIXME: Ensure chunk_count > 0 */
ma->chunks_received_bitmap = arena_push_array_zero(&ma->testarena, u8, bitmap_size);
ma->data = arena_push_array(&ma->testarena, u8, data_size);
ma->is_reliable = is_reliable; ma->is_reliable = is_reliable;
/* Insert into channel list */ /* Insert into channel list */
@ -432,11 +438,9 @@ INTERNAL struct host_msg_assembler *host_msg_assembler_alloc(struct host_channel
INTERNAL void host_msg_assembler_release(struct host_msg_assembler *ma) INTERNAL void host_msg_assembler_release(struct host_msg_assembler *ma)
{ {
struct host *host = ma->host;
struct host_channel *channel = ma->channel; struct host_channel *channel = ma->channel;
struct host *host = channel->host;
/* FIXME: Data should be in buddy allocator or something */ buddy_release(ma->buddy_block);
arena_release(&ma->testarena);
/* Release from channel list */ /* Release from channel list */
{ {
@ -471,7 +475,6 @@ INTERNAL void host_msg_assembler_release(struct host_msg_assembler *ma)
} }
} }
ma->next_free = host->first_free_msg_assembler; ma->next_free = host->first_free_msg_assembler;
host->first_free_msg_assembler = ma; host->first_free_msg_assembler = ma;
} }
@ -513,7 +516,7 @@ INTERNAL void host_msg_assembler_touch(struct host_msg_assembler *ma, i64 now_ns
INTERNAL b32 host_msg_assembler_is_chunk_filled(struct host_msg_assembler *ma, u64 chunk_id) INTERNAL b32 host_msg_assembler_is_chunk_filled(struct host_msg_assembler *ma, u64 chunk_id)
{ {
if (chunk_id < ma->num_chunks_total) { if (chunk_id < ma->num_chunks_total) {
return (ma->chunks_received_bitmap[chunk_id / 8] & (1 << (chunk_id % 8))) != 0; return (ma->chunk_bitmap[chunk_id / 8] & (1 << (chunk_id % 8))) != 0;
} }
return false; return false;
} }
@ -521,7 +524,7 @@ INTERNAL b32 host_msg_assembler_is_chunk_filled(struct host_msg_assembler *ma, u
INTERNAL void host_msg_assembler_set_chunk_received(struct host_msg_assembler *ma, u64 chunk_id) INTERNAL void host_msg_assembler_set_chunk_received(struct host_msg_assembler *ma, u64 chunk_id)
{ {
if (chunk_id < ma->num_chunks_total) { if (chunk_id < ma->num_chunks_total) {
ma->chunks_received_bitmap[chunk_id / 8] |= (1 << (chunk_id % 8)); ma->chunk_bitmap[chunk_id / 8] |= (1 << (chunk_id % 8));
} }
} }
@ -593,12 +596,13 @@ void host_queue_disconnect(struct host *host, struct host_channel_id channel_id)
cmd->channel_id = channel_id; cmd->channel_id = channel_id;
} }
void host_queue_write(struct host *host, struct host_channel_id channel_id, struct string msg) void host_queue_write(struct host *host, struct host_channel_id channel_id, struct string msg, u32 flags)
{ {
struct host_cmd *cmd = host_cmd_alloc_and_append(host); struct host_cmd *cmd = host_cmd_alloc_and_append(host);
cmd->kind = HOST_CMD_KIND_WRITE; cmd->kind = HOST_CMD_KIND_WRITE;
cmd->channel_id = channel_id; cmd->channel_id = channel_id;
cmd->write_msg = string_copy(&host->cmd_arena, msg); cmd->write_msg = string_copy(&host->cmd_arena, msg);
cmd->write_reliable = flags & HOST_WRITE_FLAG_RELIABLE;
} }
/* ========================== * /* ========================== *
@ -728,7 +732,7 @@ void host_update(struct host *host)
if (!host_msg_assembler_is_chunk_filled(ma, chunk_id)) { if (!host_msg_assembler_is_chunk_filled(ma, chunk_id)) {
u8 *src = br_seek(&br, data_len); u8 *src = br_seek(&br, data_len);
if (src) { if (src) {
u8 *dst = &ma->data[chunk_id * PACKET_MSG_CHUNK_MAX_LEN]; u8 *dst = &ma->chunk_data[chunk_id * PACKET_MSG_CHUNK_MAX_LEN];
MEMCPY(dst, src, data_len); MEMCPY(dst, src, data_len);
if (is_last_chunk) { if (is_last_chunk) {
ma->last_chunk_len = data_len; ma->last_chunk_len = data_len;
@ -743,7 +747,7 @@ void host_update(struct host *host)
struct string data = ZI; struct string data = ZI;
data.len = ((chunk_count - 1) * PACKET_MSG_CHUNK_MAX_LEN) + ma->last_chunk_len; data.len = ((chunk_count - 1) * PACKET_MSG_CHUNK_MAX_LEN) + ma->last_chunk_len;
data.text = arena_push_array(&host->queued_event_arena, u8, data.len); data.text = arena_push_array(&host->queued_event_arena, u8, data.len);
MEMCPY(data.text, ma->data, data.len); MEMCPY(data.text, ma->chunk_data, data.len);
queued_event->event.kind = HOST_EVENT_KIND_MSG; queued_event->event.kind = HOST_EVENT_KIND_MSG;
queued_event->event.msg = data; queued_event->event.msg = data;
queued_event->event.channel_id = channel->id; queued_event->event.channel_id = channel->id;

View File

@ -7,6 +7,7 @@
#define HOST_CHANNEL_ID_NIL (struct host_channel_id) { .gen = 0, .idx = 0 } #define HOST_CHANNEL_ID_NIL (struct host_channel_id) { .gen = 0, .idx = 0 }
#define HOST_CHANNEL_ID_ALL (struct host_channel_id) { .gen = U32_MAX, .idx = U32_MAX } #define HOST_CHANNEL_ID_ALL (struct host_channel_id) { .gen = U32_MAX, .idx = U32_MAX }
struct buddy_ctx;
struct host_packet; struct host_packet;
struct host_channel_lookup_bucket; struct host_channel_lookup_bucket;
struct host_recv_buffer; struct host_recv_buffer;
@ -28,6 +29,12 @@ enum host_event_kind {
HOST_EVENT_KIND_MSG HOST_EVENT_KIND_MSG
}; };
enum host_write_flag {
HOST_WRITE_FLAG_NONE = 0,
HOST_WRITE_FLAG_RELIABLE = (1 << 0)
};
struct host_cmd { struct host_cmd {
enum host_cmd_kind kind; enum host_cmd_kind kind;
struct host_channel_id channel_id; struct host_channel_id channel_id;
@ -53,6 +60,8 @@ struct host {
struct arena arena; struct arena arena;
struct sock *sock; struct sock *sock;
struct buddy_ctx *buddy; /* For storing msg assembler data */
struct arena cmd_arena; struct arena cmd_arena;
struct host_cmd *first_cmd; struct host_cmd *first_cmd;
struct host_cmd *last_cmd; struct host_cmd *last_cmd;
@ -108,7 +117,7 @@ void host_queue_connect_to_address(struct host *host, struct sock_address connec
void host_queue_disconnect(struct host *host, struct host_channel_id channel_id); void host_queue_disconnect(struct host *host, struct host_channel_id channel_id);
void host_queue_write(struct host *host, struct host_channel_id channel_id, struct string msg); void host_queue_write(struct host *host, struct host_channel_id channel_id, struct string msg, u32 flags);
/* ========================== * /* ========================== *
* Update * Update

View File

@ -1668,7 +1668,7 @@ INTERNAL void user_update(void)
struct temp_arena temp = arena_temp_begin(scratch.arena); struct temp_arena temp = arena_temp_begin(scratch.arena);
struct string cmds_str = game_string_from_cmds(temp.arena, cmd_list); struct string cmds_str = game_string_from_cmds(temp.arena, cmd_list);
host_queue_write(G.host, HOST_CHANNEL_ID_ALL, cmds_str); host_queue_write(G.host, HOST_CHANNEL_ID_ALL, cmds_str, 0);
arena_temp_end(temp); arena_temp_end(temp);
} }