Compare commits

...

3 Commits

Author SHA1 Message Date
Vftdan b119dbc129 Server tile type registry 2024-10-25 17:47:24 +02:00
Vftdan 05ae5bb258 Fix macros used by hash table 2024-10-25 17:44:14 +02:00
Vftdan 1120424949 Hide ThreadResult.pointer usages 2024-10-25 15:33:58 +02:00
12 changed files with 292 additions and 9 deletions

View File

@ -5,7 +5,7 @@ all: server
$(BUILD_DIR)/common/util/thread.o: $(BUILD_DIR)/common/util/thread.std.o
cp $< $@
server: $(BUILD_DIR)/server/main.o $(BUILD_DIR)/common/util/hash_table.o $(BUILD_DIR)/common/util/byte_stream.o $(BUILD_DIR)/common/util/thread.o
server: $(BUILD_DIR)/server/main.o $(BUILD_DIR)/server/world.o $(BUILD_DIR)/common/world.o $(BUILD_DIR)/common/util/hash_table.o $(BUILD_DIR)/common/util/byte_stream.o $(BUILD_DIR)/common/util/thread.o
$(COMPILE_EXE)
run: server

View File

@ -11,6 +11,8 @@
// Assuming child type has a field for the base type
// So for structs it is usually actual downcast, but for unions it is an upcast
#define DOWNCAST(contype, basename, ptr) containerof(ptr, contype, as_##basename)
// Expects ptr to be of type srctype* or void*, returns (dsttype*)ptr
#define EXPECT_AND_CAST(dsttype, srctype, ptr) (((union { typeof(srctype) *src; typeof(dsttype) *dst; }){.src = (ptr)}).dst)
#define T_ALLOC(count, T) ((T*)calloc((count), sizeof(T)))
#define HEADER_FN __attribute__((unused)) inline static

View File

@ -61,9 +61,9 @@ hash_table_delete_by_key_impl(HashTableDynamicData * dyndata, const HashTableKey
return hash_table_delete_at_index_impl(dyndata, hash_table_find_impl(dyndata, key));
}
#define hash_table_init(ht, value_deinit) hash_table_init_impl(&(ht)->as_HashTableDynamicData, sizeof(*(ht)->value_array), IMPLICIT_CAST(void(void*), void(typeof((ht)->value_array)), value_deinit))
#define hash_table_init(ht, value_deinit) hash_table_init_impl(&(ht)->as_HashTableDynamicData, sizeof(*(ht)->value_array), EXPECT_AND_CAST(void(void*), void(typeof((ht)->value_array)), value_deinit))
#define hash_table_deinit(ht) hash_table_deinit_impl(&(ht)->as_HashTableDynamicData)
#define hash_table_insert(ht, key, value_ptr) hash_table_insert_impl(&(ht)->as_HashTableDynamicData, key, IMPLICIT_CAST(const void, const typeof(*(ht)->value_array), value_ptr))
#define hash_table_insert(ht, key, value_ptr) hash_table_insert_impl(&(ht)->as_HashTableDynamicData, key, EXPECT_AND_CAST(const void, const typeof(*(ht)->value_array), value_ptr))
#define hash_table_find(ht, key) hash_table_find_impl(&(ht)->as_HashTableDynamicData, key)
#define hash_table_delete_at_index(ht, index) hash_table_delete_at_index_impl(&(ht)->as_HashTableDynamicData, index)
#define hash_table_delete_by_key(ht, key) hash_table_delete_by_key_impl(&(ht)->as_HashTableDynamicData, key)

View File

@ -11,11 +11,19 @@ typedef union {
typedef union {
uint32_t value;
void *pointer; // Bits outside of .value may be reset
void *_pointer; // Bits outside of .value may be reset
} ThreadResult;
#define THREAD_NONE (Thread) { .handle = 0, }
HEADER_FN ThreadResult
thread_wrap_result(uint32_t value)
{
ThreadResult res = { ._pointer = NULL };
res.value = value;
return res;
}
typedef CLOSURE_T(ThreadResult) ThreadEntry;
/**

View File

@ -53,13 +53,13 @@ run_entry(void *arg)
assert(arg != NULL);
NativeEntryArg casted_arg = *(NativeEntryArg*) arg;
ThreadEntry entry = casted_arg.entry;
void *error_result = casted_arg.error_result.pointer;
void *error_result = casted_arg.error_result._pointer;
free(arg);
if (!entry.callback) {
return error_result;
}
ThreadResult result = entry.callback(entry.env);
return result.pointer;
return result._pointer;
}
Thread
@ -106,7 +106,7 @@ thread_join(Thread th, ThreadResult *result_ptr)
pthread_t handle = get_handle(th);
void **retval = NULL;
if (result_ptr) {
retval = &result_ptr->pointer;
retval = &result_ptr->_pointer;
}
int error = pthread_join(handle, retval);
if (error) {
@ -119,7 +119,7 @@ thread_join(Thread th, ThreadResult *result_ptr)
void
thread_exit(ThreadResult result)
{
pthread_exit(result.pointer);
pthread_exit(result._pointer);
}
bool

View File

@ -111,7 +111,7 @@ thread_join(Thread th, ThreadResult *result_ptr)
thrd_t handle = get_handle(th);
int32_t *retval = NULL;
if (result_ptr) {
result_ptr->pointer = NULL;
*result_ptr = thread_wrap_result(0);
retval = (int32_t*) &result_ptr->value;
}
int error = thrd_join(handle, retval);

57
src/common/world.c Normal file
View File

@ -0,0 +1,57 @@
#include "world.h"
void
tile_type_forward_init_interned(TileTypeForwardInternation *reg)
{
if (!reg) {
return;
}
hash_table_init(&reg->table, NULL);
}
bool
tile_type_forward_intern_as(TileTypeForwardInternation *reg, const char *name, TileTypeId id)
{
if (!reg) {
return false;
}
if (!name) {
return false;
}
HashTableIndex idx = hash_table_insert(&reg->table, hash_table_key_from_cstr(name), &id);
return idx >= 0;
}
bool
tile_type_forward_get_interned(TileTypeForwardInternation *reg, const char *name, TileTypeId *outptr)
{
if (!reg) {
return false;
}
HashTableIndex idx = hash_table_find(&reg->table, hash_table_key_from_cstr(name));
if (idx < 0) {
return false;
}
if (outptr) {
*outptr = reg->table.value_array[idx];
}
return true;
}
bool
tile_type_forward_delete_interned(TileTypeForwardInternation *reg, const char *name)
{
if (!reg) {
return false;
}
return hash_table_delete_by_key(&reg->table, hash_table_key_from_cstr(name));
}
void
tile_type_forward_clear_interned(TileTypeForwardInternation *reg)
{
if (!reg) {
return;
}
hash_table_deinit(&reg->table);
}

20
src/common/world.h Normal file
View File

@ -0,0 +1,20 @@
#ifndef COMMON_WORLD_H_
#define COMMON_WORLD_H_
#include "common/defs.h"
#include "common/util/hash_table.h"
typedef uint16_t TileTypeId;
typedef uint16_t TileVariant;
typedef struct {
TYPED_HASH_TABLE(TileTypeId) table;
} TileTypeForwardInternation;
void tile_type_forward_init_interned(TileTypeForwardInternation *reg);
bool tile_type_forward_intern_as(TileTypeForwardInternation *reg, const char *name, TileTypeId id);
bool tile_type_forward_get_interned(TileTypeForwardInternation *reg, const char *name, TileTypeId *outptr);
bool tile_type_forward_delete_interned(TileTypeForwardInternation *reg, const char *name);
void tile_type_forward_clear_interned(TileTypeForwardInternation *reg);
#endif /* end of include guard: COMMON_WORLD_H_ */

View File

@ -0,0 +1,10 @@
#ifndef SERVER_CLIENT_CONNECTION_H_
#define SERVER_CLIENT_CONNECTION_H_
#include "common/defs.h"
typedef struct {
uint64_t id;
} ServerClientConnection;
#endif /* end of include guard: SERVER_CLIENT_CONNECTION_H_ */

View File

@ -5,6 +5,7 @@
#include "common/util/byte_serdes.h"
#include "common/util/thread.h"
#include "common/util/spin_lock.h"
#include "server/world.h"
int
main(int argc, char **argv)
@ -12,5 +13,28 @@ main(int argc, char **argv)
(void) argc;
(void) argv;
printf("Hello, server!\n");
server_init_tile_type_registry();
const char *name = "core:example";
const char *name2 = "core:example2";
ServerTileType example = {
.name = name,
.on_deregister = NULL,
.read_tile = NULL,
.write_tile = NULL,
.send_tile = NULL,
};
TileTypeId example_id, example2_id, check_id;
assert(server_register_tile_type(&example, &example_id));
example.name = name2;
assert(server_register_tile_type(&example, &example2_id));
printf("%s registered as %d\n", name, example_id);
printf("%s registered as %d\n", name2, example2_id);
assert(server_tile_type_by_id(example_id)->name == name);
assert(server_tile_type_by_id(example2_id)->name == name2);
server_tile_id_by_name(name, &check_id);
assert(check_id == example_id);
server_tile_id_by_name(name2, &check_id);
assert(check_id == example2_id);
server_deinit_tile_type_registry();
return 0;
}

124
src/server/world.c Normal file
View File

@ -0,0 +1,124 @@
#include "world.h"
#include <assert.h>
#include "common/util/spin_lock.h"
TileTypeId next_id = 0;
TileTypeForwardInternation server_tile_type_internation;
TYPED_HASH_TABLE(ServerTileType) server_tile_type_registry;
SpinLock lock = SPIN_LOCK_UNLOCKED;
static void deinit_tile_type(ServerTileType *tile_type) __attribute__((nothrow));
void
deinit_tile_type(ServerTileType *tile_type)
{
if (!tile_type) {
return;
}
if (!tile_type->on_deregister) {
return;
}
tile_type->on_deregister(tile_type);
}
void
server_init_tile_type_registry()
{
spin_lock_acquire(&lock);
tile_type_forward_init_interned(&server_tile_type_internation);
hash_table_init(&server_tile_type_registry, &deinit_tile_type);
spin_lock_release(&lock);
}
void
server_deinit_tile_type_registry()
{
spin_lock_acquire(&lock);
hash_table_deinit(&server_tile_type_registry);
tile_type_forward_clear_interned(&server_tile_type_internation);
next_id = 0;
spin_lock_release(&lock);
}
bool
server_register_tile_type(const ServerTileType *tile_type, TileTypeId *outptr)
{
if (!tile_type) {
return false;
}
const char *name = tile_type->name;
if (!name) {
return false;
}
spin_lock_acquire(&lock);
TileTypeId id = next_id;
HashTableKey id_key = hash_table_key_from_bytes((void*) &id, sizeof(id));
if (hash_table_find(&server_tile_type_registry, id_key) >= 0) {
for(++id; id != next_id; ++id) {
if (hash_table_find(&server_tile_type_registry, (id_key = hash_table_key_from_bytes((void*) &id, sizeof(id)))) < 0) {
break;
}
}
if (id == next_id) {
spin_lock_release(&lock);
return false; // Ran out of ids
}
}
next_id = id;
if (!tile_type_forward_intern_as(&server_tile_type_internation, name, id)) {
spin_lock_release(&lock);
return false;
}
if (hash_table_insert(&server_tile_type_registry, id_key, tile_type) < 0) {
tile_type_forward_delete_interned(&server_tile_type_internation, name);
spin_lock_release(&lock);
return false;
}
*outptr = id;
++next_id;
spin_lock_release(&lock);
return true;
}
bool
server_deregister_tile_type(TileTypeId id)
{
spin_lock_acquire(&lock);
HashTableKey id_key = hash_table_key_from_bytes((void*) &id, sizeof(id));
HashTableIndex reg_idx = hash_table_find(&server_tile_type_registry, id_key);
if (reg_idx < 0) {
spin_lock_release(&lock);
return false;
}
const char *name = server_tile_type_registry.value_array[reg_idx].name;
assert(name != NULL);
tile_type_forward_delete_interned(&server_tile_type_internation, name);
hash_table_delete_at_index(&server_tile_type_registry, reg_idx);
spin_lock_release(&lock);
return true;
}
bool
server_tile_id_by_name(const char *name, TileTypeId *outptr)
{
spin_lock_acquire(&lock);
bool result = tile_type_forward_get_interned(&server_tile_type_internation, name, outptr);
spin_lock_release(&lock);
return result;
}
const ServerTileType*
server_tile_type_by_id(TileTypeId id)
{
spin_lock_acquire(&lock);
HashTableKey id_key = hash_table_key_from_bytes((void*) &id, sizeof(id));
HashTableIndex reg_idx = hash_table_find(&server_tile_type_registry, id_key);
if (reg_idx < 0) {
spin_lock_release(&lock);
return NULL;
}
const ServerTileType *tile_type = &server_tile_type_registry.value_array[reg_idx];
spin_lock_release(&lock);
return tile_type;
}

38
src/server/world.h Normal file
View File

@ -0,0 +1,38 @@
#ifndef SERVER_WORLD_H_
#define SERVER_WORLD_H_
#include "common/world.h"
#include "common/util/byte_stream.h"
#include "common/util/hash_table.h"
#include "server/client_connection.h"
typedef struct server_tile_complex_data ServerTileComplexData;
struct server_tile_complex_data {
void (*destroy)(ServerTileComplexData *self); // Calls free
};
typedef struct {
TileTypeId type;
TileVariant variant;
ServerTileComplexData *complex_data;
} ServerTile;
// Relocateable
typedef struct server_tile_type ServerTileType;
struct server_tile_type {
const char *name;
void (*on_deregister)(const ServerTileType *self) __attribute__((nothrow));
ServerTile (*read_tile)(AbstractInputByteStream *stream); // Full data
void (*write_tile)(AbstractOutputByteStream *stream, ServerTile tile); // Full data
void (*send_tile)(AbstractOutputByteStream *stream, ServerClientConnection *client, ServerTile tile); // Client data
// Behavior handlers go here
};
void server_init_tile_type_registry();
void server_deinit_tile_type_registry();
bool server_register_tile_type(const ServerTileType *tile_type, TileTypeId *outptr); // Relocates tile_type
bool server_deregister_tile_type(TileTypeId id);
bool server_tile_id_by_name(const char *name, TileTypeId *outptr);
const ServerTileType *server_tile_type_by_id(TileTypeId id); // FIXME ensure thread safety?
#endif /* end of include guard: SERVER_WORLD_H_ */