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
#include <GL/gl.h>
#include <GLFW/glfw3.h>
#include "client.h"
-#include "mapblock_meshgen.h"
+#include "clientmap.h"
#include "signal.h"
#include "shaders.h"
#include "util.h"
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);
if (client.name)
free(client.name);
- mapblock_meshgen_stop();
+ clientmap_deinit();
map_delete(client.map);
scene_delete(client.scene);
#include <stdio.h>
#include "client.h"
+#include "clientmap.h"
#include "types.h"
static bool disconnect_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] = {
--- /dev/null
+#include <stdlib.h>
+#include "clientmap.h"
+
+static struct
+{
+ 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},
+ {+0.5f, -0.5f, -0.5f},
+ {+0.5f, +0.5f, -0.5f},
+ {+0.5f, +0.5f, -0.5f},
+ {-0.5f, +0.5f, -0.5f},
+ {-0.5f, -0.5f, -0.5f},
+ },
+ {
+ {-0.5f, -0.5f, +0.5f},
+ {+0.5f, +0.5f, +0.5f},
+ {+0.5f, -0.5f, +0.5f},
+ {+0.5f, +0.5f, +0.5f},
+ {-0.5f, -0.5f, +0.5f},
+ {-0.5f, +0.5f, +0.5f},
+ },
+ {
+ {-0.5f, +0.5f, +0.5f},
+ {-0.5f, -0.5f, -0.5f},
+ {-0.5f, +0.5f, -0.5f},
+ {-0.5f, -0.5f, -0.5f},
+ {-0.5f, +0.5f, +0.5f},
+ {-0.5f, -0.5f, +0.5f},
+ },
+ {
+ {+0.5f, +0.5f, +0.5f},
+ {+0.5f, +0.5f, -0.5f},
+ {+0.5f, -0.5f, -0.5f},
+ {+0.5f, -0.5f, -0.5f},
+ {+0.5f, -0.5f, +0.5f},
+ {+0.5f, +0.5f, +0.5f},
+ },
+ {
+ {-0.5f, -0.5f, -0.5f},
+ {+0.5f, -0.5f, -0.5f},
+ {+0.5f, -0.5f, +0.5f},
+ {+0.5f, -0.5f, +0.5f},
+ {-0.5f, -0.5f, +0.5f},
+ {-0.5f, -0.5f, -0.5f},
+ },
+ {
+ {-0.5f, +0.5f, -0.5f},
+ {+0.5f, +0.5f, -0.5f},
+ {+0.5f, +0.5f, +0.5f},
+ {+0.5f, +0.5f, +0.5f},
+ {-0.5f, +0.5f, +0.5f},
+ {-0.5f, +0.5f, -0.5f},
+ },
+};
+
+static v3s8 fdir[6] = {
+ {+0, +0, -1},
+ {+0, +0, +1},
+ {-1, +0, +0},
+ {+1, +0, +0},
+ {+0, -1, +0},
+ {+0, +1, +0},
+};
+
+#define GNODDEF(block, x, y, z) node_definitions[block->data[x][y][z].type]
+#define VALIDPOS(pos) (pos.x >= 0 && pos.x < 16 && pos.y >= 0 && pos.y < 16 && pos.z >= 0 && pos.z < 16)
+
+static Array make_vertices(MapBlock *block)
+{
+ Array vertices = array_create(sizeof(Vertex));
+
+ ITERATE_MAPBLOCK {
+ NodeDefintion *def = &GNODDEF(block, x, y, z);
+ if (def->visible) {
+ v3u8 pos = {x, y, z};
+ v3f offset = {x + 8.5f, y + 8.5f, z + 8.5f};
+ v3f color = get_node_color(def);
+ for (int f = 0; f < 6; f++) {
+ v3s8 *noff = &fdir[f];
+ v3s8 npos = {
+ pos.x + noff->x,
+ pos.y + noff->y,
+ pos.z + noff->z,
+ };
+ if (! VALIDPOS(npos) || ! GNODDEF(block, npos.x, npos.y, npos.z).visible) {
+ for (int v = 0; v < 6; v++) {
+ Vertex vertex = {
+ vpos[f][v].x + offset.x,
+ vpos[f][v].y + offset.y,
+ vpos[f][v].z + offset.z,
+ color.x,
+ color.y,
+ color.z,
+ };
+ array_append(&vertices, &vertex);
+ }
+ }
+ }
+ }
+ }
+
+ return vertices;
+}
+
+#undef GNODDEF
+#undef VALIDPOS
+
+static void *meshgen_thread(void *unused)
+{
+ (void) unused;
+
+ while (! meshgen.cancel) {
+ ListPair **lptr = &meshgen.queue.first;
+ if (*lptr) {
+ pthread_mutex_lock(&meshgen.mtx);
+ MapBlock *block = (*lptr)->key;
+ block->state = MBS_READY;
+ ListPair *next = (*lptr)->next;
+ free(*lptr);
+ *lptr = next;
+ pthread_mutex_unlock(&meshgen.mtx);
+
+ Array vertices = make_vertices(block);
+ Mesh *mesh = NULL;
+
+ if (vertices.siz > 0) {
+ 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(client->scene, mesh);
+ }
+
+ if (block->extra)
+ ((Mesh *) block->extra)->remove = true;
+
+ block->extra = mesh;
+ } else {
+ sched_yield();
+ }
+ }
+
+ return NULL;
+}
+
+void clientmap_init(Client *cli)
+{
+ client = cli;
+ meshgen.queue = list_create(NULL);
+ pthread_mutex_init(&meshgen.mtx, NULL);
+ pthread_create(&meshgen.thread, NULL, &meshgen_thread, NULL);
+}
+
+void clientmap_deinit()
+{
+ meshgen.cancel = true;
+ pthread_join(meshgen.thread, NULL);
+ pthread_mutex_destroy(&meshgen.mtx);
+ 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);
+}
--- /dev/null
+#ifndef _CLIENTMAP_H_
+#define _CLIENTMAP_H_
+
+#include "client.h"
+
+void clientmap_init(Client *cli);
+void clientmap_deinit();
+
+void clientmap_block_changed(MapBlock *block);
+
+#endif
{
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;
}
} 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);
}
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;
}
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;
}
void map_deserialize(int fd, Map *map)
{
- while (map_deserialize_block(fd, map, false))
+ while (map_deserialize_block(fd, map, NULL, false))
;
}
MapNode *current_node = &block->data[offset.x][offset.y][offset.z];
map_node_clear(current_node);
*current_node = node;
+
+ block->state = MBS_MODIFIED;
}
}
#define _MAP_H_
#include <stdbool.h>
+#include <pthread.h>
#include "array.h"
#include "list.h"
#include "node.h"
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;
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();
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);
+++ /dev/null
-#include <stdlib.h>
-#include "node.h"
-#include "mapblock_meshgen.h"
-
-static struct
-{
- Map *map;
- Scene *scene;
- List queue;
- pthread_mutex_t mtx;
- pthread_t thread;
- bool cancel;
-} meshgen;
-
-static v3f vpos[6][6] = {
- {
- {-0.5f, -0.5f, -0.5f},
- {+0.5f, -0.5f, -0.5f},
- {+0.5f, +0.5f, -0.5f},
- {+0.5f, +0.5f, -0.5f},
- {-0.5f, +0.5f, -0.5f},
- {-0.5f, -0.5f, -0.5f},
- },
- {
- {-0.5f, -0.5f, +0.5f},
- {+0.5f, +0.5f, +0.5f},
- {+0.5f, -0.5f, +0.5f},
- {+0.5f, +0.5f, +0.5f},
- {-0.5f, -0.5f, +0.5f},
- {-0.5f, +0.5f, +0.5f},
- },
- {
- {-0.5f, +0.5f, +0.5f},
- {-0.5f, -0.5f, -0.5f},
- {-0.5f, +0.5f, -0.5f},
- {-0.5f, -0.5f, -0.5f},
- {-0.5f, +0.5f, +0.5f},
- {-0.5f, -0.5f, +0.5f},
- },
- {
- {+0.5f, +0.5f, +0.5f},
- {+0.5f, +0.5f, -0.5f},
- {+0.5f, -0.5f, -0.5f},
- {+0.5f, -0.5f, -0.5f},
- {+0.5f, -0.5f, +0.5f},
- {+0.5f, +0.5f, +0.5f},
- },
- {
- {-0.5f, -0.5f, -0.5f},
- {+0.5f, -0.5f, -0.5f},
- {+0.5f, -0.5f, +0.5f},
- {+0.5f, -0.5f, +0.5f},
- {-0.5f, -0.5f, +0.5f},
- {-0.5f, -0.5f, -0.5f},
- },
- {
- {-0.5f, +0.5f, -0.5f},
- {+0.5f, +0.5f, -0.5f},
- {+0.5f, +0.5f, +0.5f},
- {+0.5f, +0.5f, +0.5f},
- {-0.5f, +0.5f, +0.5f},
- {-0.5f, +0.5f, -0.5f},
- },
-};
-
-static v3s8 fdir[6] = {
- {+0, +0, -1},
- {+0, +0, +1},
- {-1, +0, +0},
- {+1, +0, +0},
- {+0, -1, +0},
- {+0, +1, +0},
-};
-
-#define GNODDEF(block, x, y, z) node_definitions[block->data[x][y][z].type]
-#define VALIDPOS(pos) (pos.x >= 0 && pos.x < 16 && pos.y >= 0 && pos.y < 16 && pos.z >= 0 && pos.z < 16)
-
-static Array make_vertices(MapBlock *block)
-{
- Array vertices = array_create(sizeof(Vertex));
-
- ITERATE_MAPBLOCK {
- NodeDefintion *def = &GNODDEF(block, x, y, z);
- if (def->visible) {
- v3u8 pos = {x, y, z};
- v3f offset = {x + 8.5f, y + 8.5f, z + 8.5f};
- v3f color = get_node_color(def);
- for (int f = 0; f < 6; f++) {
- v3s8 *noff = &fdir[f];
- v3s8 npos = {
- pos.x + noff->x,
- pos.y + noff->y,
- pos.z + noff->z,
- };
- if (! VALIDPOS(npos) || ! GNODDEF(block, npos.x, npos.y, npos.z).visible) {
- for (int v = 0; v < 6; v++) {
- Vertex vertex = {
- vpos[f][v].x + offset.x,
- vpos[f][v].y + offset.y,
- vpos[f][v].z + offset.z,
- color.x,
- color.y,
- color.z,
- };
- array_append(&vertices, &vertex);
- }
- }
- }
- }
- }
-
- return vertices;
-}
-
-#undef GNODDEF
-#undef VALIDPOS
-
-static void *meshgen_thread(void *unused)
-{
- (void) unused;
-
- while (! meshgen.cancel) {
- ListPair **lptr = &meshgen.queue.first;
- if (*lptr) {
- MapBlock *block = (*lptr)->key;
-
- pthread_mutex_lock(&meshgen.mtx);
- ListPair *next = (*lptr)->next;
- free(*lptr);
- *lptr = next;
- pthread_mutex_unlock(&meshgen.mtx);
-
- Array vertices = make_vertices(block);
- Mesh *mesh = NULL;
-
- if (vertices.siz > 0) {
- 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);
- }
-
- if (block->extra)
- ((Mesh *) block->extra)->remove = true;
-
- block->extra = mesh;
- } else {
- sched_yield();
- }
- }
-
- return NULL;
-}
-
-static void enqueue_block(MapBlock *block)
-{
- 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;
- 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()
-{
- 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);
-}
+++ /dev/null
-#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
+++ /dev/null
-#include <stdlib.h>
-#include <perlin/perlin.h>
-#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);
-}
+++ /dev/null
-#ifndef _MAPGEN_H_
-#define _MAPGEN_H_
-
-#include "server.h"
-#include "map.h"
-
-void mapgen_init(Server *srv);
-void mapgen_start_thread(Client *client);
-
-#endif
+++ /dev/null
-#include <perlin/perlin.c>
#include <unistd.h>
#include <errno.h>
#include <netdb.h>
-#include "mapgen.h"
#include "server.h"
+#include "servermap.h"
#include "signal.h"
#include "util.h"
perror("fopen");
}
- mapgen_init(&server);
+ servermap_init(&server);
while (! interrupted)
server_accept_client();
#include <stdio.h>
#include <stdlib.h>
-#include "mapgen.h"
#include "server.h"
+#include "servermap.h"
#include "util.h"
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);
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;
--- /dev/null
+#include <stdlib.h>
+#include <perlin/perlin.h>
+#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 <perlin/perlin.c>
--- /dev/null
+#ifndef _MAPGEN_H_
+#define _MAPGEN_H_
+
+#include "server.h"
+#include "map.h"
+
+void servermap_init(Server *srv);
+
+void servermap_add_client(Client *client);
+
+#endif