diff --git a/Makefile b/Makefile index 7747c50..3a15712 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/src/common/world.c b/src/common/world.c new file mode 100644 index 0000000..2a9cebc --- /dev/null +++ b/src/common/world.c @@ -0,0 +1,57 @@ +#include "world.h" + +void +tile_type_forward_init_interned(TileTypeForwardInternation *reg) +{ + if (!reg) { + return; + } + hash_table_init(®->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(®->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(®->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(®->table, hash_table_key_from_cstr(name)); +} + +void +tile_type_forward_clear_interned(TileTypeForwardInternation *reg) +{ + if (!reg) { + return; + } + hash_table_deinit(®->table); +} diff --git a/src/common/world.h b/src/common/world.h new file mode 100644 index 0000000..caca887 --- /dev/null +++ b/src/common/world.h @@ -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_ */ diff --git a/src/server/client_connection.h b/src/server/client_connection.h new file mode 100644 index 0000000..bb0e44d --- /dev/null +++ b/src/server/client_connection.h @@ -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_ */ diff --git a/src/server/main.c b/src/server/main.c index 2b7f76a..80b3212 100644 --- a/src/server/main.c +++ b/src/server/main.c @@ -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; } diff --git a/src/server/world.c b/src/server/world.c new file mode 100644 index 0000000..9bd1502 --- /dev/null +++ b/src/server/world.c @@ -0,0 +1,124 @@ +#include "world.h" + +#include + +#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; +} diff --git a/src/server/world.h b/src/server/world.h new file mode 100644 index 0000000..341e4a0 --- /dev/null +++ b/src/server/world.h @@ -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_ */