diff --git a/src/Makefile b/src/Makefile index 0b6d0d8..e0bc214 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,6 +1,6 @@ COMMON = array.o list.o map.o signal.o util.o types.o node.o -SERVER = $(COMMON) server.o servercommands.o mapgen.o perlin.o -CLIENT = $(COMMON) client.o clientcommands.o mesh.o scene.o mapblock_meshgen.o shaders.o +SERVER = $(COMMON) server.o servercommands.o servermap.o +CLIENT = $(COMMON) client.o clientcommands.o clientmap.o mesh.o scene.o shaders.o LIBRARIES = -lpthread -lm CLIENT_LIBRARIES = -lGL -lGLEW -lglfw FLAGS = -g -fmax-errors=4 diff --git a/src/client.c b/src/client.c index a2e197e..0d581c6 100644 --- a/src/client.c +++ b/src/client.c @@ -10,7 +10,7 @@ #include #include #include "client.h" -#include "mapblock_meshgen.h" +#include "clientmap.h" #include "signal.h" #include "shaders.h" #include "util.h" @@ -200,7 +200,7 @@ static void client_start(int fd) client.scene = scene_create(); client.pos = (v3f) {0.0f, 0.0f, 0.0f}; - mapblock_meshgen_init(client.map, client.scene); + clientmap_init(&client); pthread_t recv_thread; pthread_create(&recv_thread, NULL, &reciever_thread, NULL); @@ -214,7 +214,7 @@ static void client_start(int fd) if (client.name) free(client.name); - mapblock_meshgen_stop(); + clientmap_deinit(); map_delete(client.map); scene_delete(client.scene); diff --git a/src/clientcommands.c b/src/clientcommands.c index 5b99d34..d3d816b 100644 --- a/src/clientcommands.c +++ b/src/clientcommands.c @@ -1,5 +1,6 @@ #include #include "client.h" +#include "clientmap.h" #include "types.h" static bool disconnect_handler(Client *client, bool good) @@ -33,7 +34,12 @@ static bool auth_handler(Client *client, bool good) static bool block_handler(Client *client, bool good) { - return map_deserialize_block(client->fd, client->map, ! good); + MapBlock *block; + if (! map_deserialize_block(client->fd, client->map, &block, ! good)) + return false; + if (good) + clientmap_block_changed(block); + return true; } CommandHandler command_handlers[CLIENT_COMMAND_COUNT] = { diff --git a/src/mapblock_meshgen.c b/src/clientmap.c similarity index 88% rename from src/mapblock_meshgen.c rename to src/clientmap.c index 10bf853..236785b 100644 --- a/src/mapblock_meshgen.c +++ b/src/clientmap.c @@ -1,17 +1,16 @@ #include -#include "node.h" -#include "mapblock_meshgen.h" +#include "clientmap.h" static struct { - Map *map; - Scene *scene; List queue; pthread_mutex_t mtx; pthread_t thread; bool cancel; } meshgen; +static Client *client = NULL; + static v3f vpos[6][6] = { { {-0.5f, -0.5f, -0.5f}, @@ -122,9 +121,9 @@ static void *meshgen_thread(void *unused) while (! meshgen.cancel) { ListPair **lptr = &meshgen.queue.first; if (*lptr) { - MapBlock *block = (*lptr)->key; - pthread_mutex_lock(&meshgen.mtx); + MapBlock *block = (*lptr)->key; + block->state = MBS_READY; ListPair *next = (*lptr)->next; free(*lptr); *lptr = next; @@ -137,7 +136,7 @@ static void *meshgen_thread(void *unused) mesh = mesh_create(vertices.ptr, vertices.siz); mesh->pos = (v3f) {block->pos.x * 16.0f - 8.0f, block->pos.y * 16.0f - 8.0f, block->pos.z * 16.0f - 8.0f}; mesh_transform(mesh); - scene_add_mesh(meshgen.scene, mesh); + scene_add_mesh(client->scene, mesh); } if (block->extra) @@ -152,29 +151,28 @@ static void *meshgen_thread(void *unused) return NULL; } -static void enqueue_block(MapBlock *block) +void clientmap_init(Client *cli) { - pthread_mutex_lock(&meshgen.mtx); - list_put(&meshgen.queue, block, NULL); - pthread_mutex_unlock(&meshgen.mtx); -} - -void mapblock_meshgen_init(Map *map, Scene *scene) -{ - meshgen.map = map; - meshgen.scene = scene; + client = cli; meshgen.queue = list_create(NULL); pthread_mutex_init(&meshgen.mtx, NULL); - map->on_block_add = &enqueue_block; - map->on_block_change = &enqueue_block; pthread_create(&meshgen.thread, NULL, &meshgen_thread, NULL); } -void mapblock_meshgen_stop() +void clientmap_deinit() { meshgen.cancel = true; pthread_join(meshgen.thread, NULL); pthread_mutex_destroy(&meshgen.mtx); - ITERATE_LIST(&meshgen.queue, pair) free(pair->key); list_clear(&meshgen.queue); } + +void clientmap_block_changed(MapBlock *block) +{ + pthread_mutex_lock(&meshgen.mtx); + if (block->state != MBS_PROCESSING) { + block->state = MBS_PROCESSING; + list_put(&meshgen.queue, block, NULL); + } + pthread_mutex_unlock(&meshgen.mtx); +} diff --git a/src/clientmap.h b/src/clientmap.h new file mode 100644 index 0000000..e46c374 --- /dev/null +++ b/src/clientmap.h @@ -0,0 +1,11 @@ +#ifndef _CLIENTMAP_H_ +#define _CLIENTMAP_H_ + +#include "client.h" + +void clientmap_init(Client *cli); +void clientmap_deinit(); + +void clientmap_block_changed(MapBlock *block); + +#endif diff --git a/src/map.c b/src/map.c index 94d5ea8..840b6ac 100644 --- a/src/map.c +++ b/src/map.c @@ -22,8 +22,9 @@ static MapBlock *allocate_block(v3s32 pos) { MapBlock *block = malloc(sizeof(MapBlock)); block->pos = pos; - block->ready = false; + block->state = MBS_CREATED; block->extra = NULL; + pthread_mutex_init(&block->mtx, NULL); return block; } @@ -96,19 +97,17 @@ MapBlock *map_get_block(Map *map, v3s32 pos, bool create) } else if (create) { block = allocate_block(pos); array_insert(§or->blocks, &block, res.index); - - if (map->on_block_create) - map->on_block_create(block); } else { return NULL; } - return block->ready ? block : NULL; + return (create || block->state == MBS_READY) ? block : NULL; } void map_free_block(MapBlock *block) { ITERATE_MAPBLOCK map_node_clear(&block->data[x][y][z]); + pthread_mutex_destroy(&block->mtx); free(block); } @@ -140,7 +139,7 @@ bool map_serialize_block(int fd, MapBlock *block) return true; } -bool map_deserialize_block(int fd, Map *map, bool dummy) +bool map_deserialize_block(int fd, Map *map, MapBlock **blockptr, bool dummy) { v3s32 pos; @@ -162,18 +161,17 @@ bool map_deserialize_block(int fd, Map *map, bool dummy) } ITERATE_MAPBLOCK { - if (! map_deserialize_node(fd, &block->data[x][y][z])) + if (! map_deserialize_node(fd, &block->data[x][y][z])) { + if (dummy) + map_free_block(block); return false; + } } - if (dummy) { + if (dummy) map_free_block(block); - } else { - block->ready = true; - - if (map->on_block_add) - map->on_block_add(block); - } + else if (blockptr) + *blockptr = block; return true; } @@ -191,7 +189,7 @@ bool map_serialize(int fd, Map *map) void map_deserialize(int fd, Map *map) { - while (map_deserialize_block(fd, map, false)) + while (map_deserialize_block(fd, map, NULL, false)) ; } @@ -220,6 +218,8 @@ void map_set_node(Map *map, v3s32 pos, MapNode node) MapNode *current_node = &block->data[offset.x][offset.y][offset.z]; map_node_clear(current_node); *current_node = node; + + block->state = MBS_MODIFIED; } } diff --git a/src/map.h b/src/map.h index 2341379..f659e2f 100644 --- a/src/map.h +++ b/src/map.h @@ -2,6 +2,7 @@ #define _MAP_H_ #include +#include #include "array.h" #include "list.h" #include "node.h" @@ -15,11 +16,20 @@ typedef struct List meta; } MapNode; +typedef enum +{ + MBS_CREATED, + MBS_PROCESSING, + MBS_READY, + MBS_MODIFIED, +} MapBlockState; + typedef struct { MapNode data[16][16][16]; v3s32 pos; - bool ready; + MapBlockState state; + pthread_mutex_t mtx; void *extra; } MapBlock; @@ -30,14 +40,9 @@ typedef struct u64 hash; } MapSector; -typedef void (*MapBlockCallback)(MapBlock *block); - typedef struct { Array sectors; - MapBlockCallback on_block_create; - MapBlockCallback on_block_add; - MapBlockCallback on_block_change; } Map; Map *map_create(); @@ -52,7 +57,7 @@ void map_free_block(MapBlock *block); bool map_deserialize_node(int fd, MapNode *buf); bool map_serialize_block(int fd, MapBlock *block); -bool map_deserialize_block(int fd, Map *map, bool dummy); +bool map_deserialize_block(int fd, Map *map, MapBlock **blockptr, bool dummy); bool map_serialize(int fd, Map *map); void map_deserialize(int fd, Map *map); diff --git a/src/mapblock_meshgen.h b/src/mapblock_meshgen.h deleted file mode 100644 index 2be1016..0000000 --- a/src/mapblock_meshgen.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef _MAPBLOCK_MESHGEN_H_ -#define _MAPBLOCK_MESHGEN_H_ - -#include "map.h" -#include "scene.h" - -void mapblock_meshgen_init(Map *map, Scene *scene); -void mapblock_meshgen_stop(); - -#endif diff --git a/src/mapgen.c b/src/mapgen.c deleted file mode 100644 index b25f58e..0000000 --- a/src/mapgen.c +++ /dev/null @@ -1,71 +0,0 @@ -#include -#include -#include "mapgen.h" - -int seed = 0; -static Server *server = NULL; - -// mapgen prototype -static void generate_block(MapBlock *block) -{ - for (u8 x = 0; x < 16; x++) { - u32 ux = x + block->pos.x * 16 + ((u32) 1 << 31); - for (u8 z = 0; z < 16; z++) { - u32 uz = z + block->pos.z * 16 + ((u32) 1 << 31); - s32 height = smooth2d((double) ux / 32.0f, (double) uz / 32.0f, 0, seed) * 16.0f; - for (u8 y = 0; y < 16; y++) { - s32 ay = y + block->pos.y * 16; - Node type; - if (ay > height) - type = NODE_AIR; - else if (ay == height) - type = NODE_GRASS; - else if (ay >= height - 4) - type = NODE_DIRT; - else - type = NODE_STONE; - block->data[x][y][z] = map_node_create(type); - } - } - } - ITERATE_LIST(&server->clients, pair) { - Client *client = pair->value; - if (client->state == CS_ACTIVE) { - pthread_mutex_lock(&client->mtx); - (void) (write_u32(client->fd, CC_BLOCK) && map_serialize_block(client->fd, block)); - pthread_mutex_unlock(&client->mtx); - } - } - block->ready = true; -} - -void mapgen_init(Server *srv) -{ - server = srv; - server->map->on_block_create = &generate_block; -} - -#define RANGE 3 - -static void *mapgen_thread(void *cliptr) -{ - Client *client = cliptr; - - while (client->state != CS_DISCONNECTED) { - v3s32 pos = map_node_to_block_pos((v3s32) {client->pos.x, client->pos.y, client->pos.z}, NULL); - for (s32 x = pos.x - RANGE; x <= pos.x + RANGE; x++) - for (s32 y = pos.y - RANGE; y <= pos.y + RANGE; y++) - for (s32 z = pos.z - RANGE; z <= pos.z + RANGE; z++) - map_get_block(client->server->map, (v3s32) {x, y, z}, true); - } - - return NULL; -} - -void mapgen_start_thread(Client *client) -{ - (void) client; - (void) mapgen_thread; - pthread_t thread; - pthread_create(&thread, NULL, &mapgen_thread, client); -} diff --git a/src/perlin.c b/src/perlin.c deleted file mode 100644 index 47aa06c..0000000 --- a/src/perlin.c +++ /dev/null @@ -1 +0,0 @@ -#include diff --git a/src/server.c b/src/server.c index ee5a068..0816dba 100644 --- a/src/server.c +++ b/src/server.c @@ -3,8 +3,8 @@ #include #include #include -#include "mapgen.h" #include "server.h" +#include "servermap.h" #include "signal.h" #include "util.h" @@ -91,7 +91,7 @@ void server_start(int fd) perror("fopen"); } - mapgen_init(&server); + servermap_init(&server); while (! interrupted) server_accept_client(); diff --git a/src/servercommands.c b/src/servercommands.c index 8975d6a..f8b4dd7 100644 --- a/src/servercommands.c +++ b/src/servercommands.c @@ -1,7 +1,7 @@ #include #include -#include "mapgen.h" #include "server.h" +#include "servermap.h" #include "util.h" static bool disconnect_handler(Client *client, bool good) @@ -11,17 +11,6 @@ static bool disconnect_handler(Client *client, bool good) return true; } -static bool send_map(Client *client) -{ - for (size_t s = 0; s < client->server->map->sectors.siz; s++) { - MapSector *sector = map_get_sector_raw(client->server->map, s); - for (size_t b = 0; b < sector->blocks.siz; b++) - if (! (write_u32(client->fd, CC_BLOCK) && map_serialize_block(client->fd, map_get_block_raw(sector, b)))) - return false; - } - return true; -} - static bool auth_handler(Client *client, bool good) { char *name = read_string(client->fd, NAME_MAX); @@ -41,13 +30,13 @@ static bool auth_handler(Client *client, bool good) if (success) { client->name = name; client->state = CS_ACTIVE; - mapgen_start_thread(client); + servermap_add_client(client); } else { free(name); } pthread_mutex_lock(&client->mtx); - bool ret = write_u32(client->fd, CC_AUTH) && write_u8(client->fd, success) && (success ? send_map(client) : true); + bool ret = write_u32(client->fd, CC_AUTH) && write_u8(client->fd, success); pthread_mutex_unlock(&client->mtx); return ret; diff --git a/src/servermap.c b/src/servermap.c new file mode 100644 index 0000000..3a93885 --- /dev/null +++ b/src/servermap.c @@ -0,0 +1,152 @@ +#include +#include +#include "servermap.h" + +int seed = 0; +static Server *server = NULL; + +static void generate_block(MapBlock *block) +{ + pthread_mutex_lock(&block->mtx); + if (block->state != MBS_CREATED) + return; + block->state = MBS_PROCESSING; + pthread_mutex_unlock(&block->mtx); + for (u8 x = 0; x < 16; x++) { + u32 ux = x + block->pos.x * 16 + ((u32) 1 << 31); + for (u8 z = 0; z < 16; z++) { + u32 uz = z + block->pos.z * 16 + ((u32) 1 << 31); + s32 height = smooth2d((double) ux / 32.0f, (double) uz / 32.0f, 0, seed) * 16.0f; + for (u8 y = 0; y < 16; y++) { + s32 ay = y + block->pos.y * 16; + Node type; + if (ay > height) + type = NODE_AIR; + else if (ay == height) + type = NODE_GRASS; + else if (ay >= height - 4) + type = NODE_DIRT; + else + type = NODE_STONE; + block->data[x][y][z] = map_node_create(type); + } + } + } + block->state = MBS_READY; +} + +static void send_block(MapBlock *block) +{ + pthread_mutex_lock(&block->mtx); + block->state = MBS_READY; + // ToDo: only send to near clients + ITERATE_LIST(&server->clients, pair) { + if (block->state != MBS_READY) + break; + Client *client = pair->value; + if (client->state == CS_ACTIVE) { + pthread_mutex_lock(&client->mtx); + (void) (write_u32(client->fd, CC_BLOCK) && map_serialize_block(client->fd, block)); + pthread_mutex_unlock(&client->mtx); + } + } + pthread_mutex_unlock(&block->mtx); +} + +#define RANGE 3 +#define POS_CACHE_COUNT ((1 + RANGE * 2) * (1 + RANGE * 2) * (1 + RANGE * 2)) +static size_t pos_chache_count = POS_CACHE_COUNT; +static v3s32 pos_cache[POS_CACHE_COUNT]; + +static void create_pos_cache() +{ + size_t i = -1; +#define ADDPOS(a, b, c, va, vb, vc) \ + *(s32 *) ((char *) &pos_cache[++i] + offsetof(v3s32, a)) = va; \ + *(s32 *) ((char *) &pos_cache[i] + offsetof(v3s32, b)) = vb; \ + *(s32 *) ((char *) &pos_cache[i] + offsetof(v3s32, c)) = vc; + ADDPOS(x, y, z, 0, 0, 0) + for (s32 l = 1; l <= RANGE ; l++) { +#define SQUARES(a, b, c) \ + for (s32 va = -l + 1; va < l; va++) { \ + for (s32 vb = -l + 1; vb < l; vb++) { \ + ADDPOS(a, b, c, va, vb, l) \ + ADDPOS(a, b, c, va, vb, -l) \ + } \ + } + SQUARES(x, z, y) + SQUARES(x, y, z) + SQUARES(z, y, x) +#undef SQUARES +#define EDGES(a, b, c) \ + for (s32 va = -l + 1; va < l; va++) { \ + ADDPOS(a, b, c, va, l, l) \ + ADDPOS(a, b, c, va, l, -l) \ + ADDPOS(a, b, c, va, -l, l) \ + ADDPOS(a, b, c, va, -l, -l) \ + } + EDGES(x, y, z) + EDGES(z, x, y) + EDGES(y, x, z) +#undef EDGES + ADDPOS(x, y, z, l, l, l) + ADDPOS(x, y, z, l, l, -l) + ADDPOS(x, y, z, l, -l, l) + ADDPOS(x, y, z, l, -l, -l) + ADDPOS(x, y, z, -l, l, l) + ADDPOS(x, y, z, -l, l, -l) + ADDPOS(x, y, z, -l, -l, l) + ADDPOS(x, y, z, -l, -l, -l) +#undef ADDPOS + } +} + +static void send_blocks(Client *client, bool init) +{ + v3s32 pos = map_node_to_block_pos((v3s32) {client->pos.x, client->pos.y, client->pos.z}, NULL); + for (size_t i = 0; i < pos_chache_count; i++) { + MapBlock *block = map_get_block(client->server->map, (v3s32) {pos.x + pos_cache[i].x, pos.y + pos_cache[i].y, pos.z + pos_cache[i].z}, ! init); + if (init) + (void) (block && write_u32(client->fd, CC_BLOCK) && map_serialize_block(client->fd, block)); + else switch (block->state) { + case MBS_CREATED: + generate_block(block); + __attribute__ ((fallthrough)); + case MBS_MODIFIED: + send_block(block); + __attribute__ ((fallthrough)); + case MBS_PROCESSING: + i = -1; + sched_yield(); + __attribute__ ((fallthrough)); + case MBS_READY: + break; + } + } +} + +static void *block_send_thread(void *cli) +{ + Client *client = cli; + + send_blocks(client, true); + + while (client->state != CS_DISCONNECTED) + send_blocks(client, false); + + return NULL; +} + +void servermap_init(Server *srv) +{ + server = srv; + create_pos_cache(); +} + +void servermap_add_client(Client *client) +{ + pthread_t thread; + pthread_create(&thread, NULL, &block_send_thread, client); +} + +#include diff --git a/src/mapgen.h b/src/servermap.h similarity index 52% rename from src/mapgen.h rename to src/servermap.h index 0c686c0..c40b9f9 100644 --- a/src/mapgen.h +++ b/src/servermap.h @@ -4,7 +4,8 @@ #include "server.h" #include "map.h" -void mapgen_init(Server *srv); -void mapgen_start_thread(Client *client); +void servermap_init(Server *srv); + +void servermap_add_client(Client *client); #endif