COMMON = array.o list.o map.o signal.o util.o types.o node.o
-SERVER = $(COMMON) server.o servercommands.o servermap.o
+SERVER = $(COMMON) server.o servercommands.o servermap.o perlin.o facecache.o mapgen.o
CLIENT = $(COMMON) client.o clientcommands.o clientmap.o mesh.o scene.o shaders.o
LIBRARIES = -lpthread -lm
FLAGS = -g -fmax-errors=4
if (*lptr) {
pthread_mutex_lock(&meshgen.mtx);
MapBlock *block = (*lptr)->key;
- block->state = MBS_READY;
ListPair *next = (*lptr)->next;
free(*lptr);
*lptr = next;
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);
- }
+ list_put(&meshgen.queue, block, NULL);
pthread_mutex_unlock(&meshgen.mtx);
}
--- /dev/null
+#include <stdlib.h>
+#include "array.h"
+#include "facecache.h"
+
+static struct
+{
+ Array positions;
+ u32 size;
+ pthread_mutex_t mtx;
+} facecache;
+
+__attribute((constructor)) static void init_face_cache()
+{
+ facecache.size = 0;
+ facecache.positions = array_create(sizeof(v3s32));
+ v3s32 pos = {0};
+ array_append(&facecache.positions, &pos);
+ pthread_mutex_init(&facecache.mtx, NULL);
+}
+
+__attribute((destructor)) void deinit_face_cache()
+{
+ if (facecache.positions.ptr)
+ free(facecache.positions.ptr);
+ pthread_mutex_destroy(&facecache.mtx);
+}
+
+static void calculate_face_cache(s32 size)
+{
+#define ADDPOS(a, b, c, va, vb, vc) \
+ { \
+ v3s32 pos; \
+ *(s32 *) ((char *) &pos + offsetof(v3s32, a)) = va; \
+ *(s32 *) ((char *) &pos + offsetof(v3s32, b)) = vb; \
+ *(s32 *) ((char *) &pos + offsetof(v3s32, c)) = vc; \
+ array_append(&facecache.positions, &pos); \
+ }
+#define SQUARES(a, b, c) \
+ for (s32 va = -size + 1; va < size; va++) { \
+ for (s32 vb = -size + 1; vb < size; vb++) { \
+ ADDPOS(a, b, c, va, vb, size) \
+ ADDPOS(a, b, c, va, vb, -size) \
+ } \
+ }
+ SQUARES(x, z, y)
+ SQUARES(x, y, z)
+ SQUARES(z, y, x)
+#undef SQUARES
+#define EDGES(a, b, c) \
+ for (s32 va = -size + 1; va < size; va++) { \
+ ADDPOS(a, b, c, va, size, size) \
+ ADDPOS(a, b, c, va, size, -size) \
+ ADDPOS(a, b, c, va, -size, size) \
+ ADDPOS(a, b, c, va, -size, -size) \
+ }
+ EDGES(x, y, z)
+ EDGES(z, x, y)
+ EDGES(y, x, z)
+#undef EDGES
+ ADDPOS(x, y, z, size, size, size)
+ ADDPOS(x, y, z, size, size, -size)
+ ADDPOS(x, y, z, size, -size, size)
+ ADDPOS(x, y, z, size, -size, -size)
+ ADDPOS(x, y, z, -size, size, size)
+ ADDPOS(x, y, z, -size, size, -size)
+ ADDPOS(x, y, z, -size, -size, size)
+ ADDPOS(x, y, z, -size, -size, -size)
+#undef ADDPOS
+}
+
+v3s32 get_face(size_t i, v3s32 *base)
+{
+ pthread_mutex_lock(&facecache.mtx);
+ while (facecache.positions.siz <= i)
+ calculate_face_cache(++facecache.size);
+ v3s32 pos = ((v3s32 *) facecache.positions.ptr)[i];
+ pthread_mutex_unlock(&facecache.mtx);
+ if (base) {
+ pos.x += base->x;
+ pos.y += base->y;
+ pos.z += base->z;
+ }
+ return pos;
+}
+
+size_t get_face_count(u32 size)
+{
+ size_t len = 1 + size * 2;
+ return len * len * len;
+}
--- /dev/null
+#ifndef _FACECACHE_H_
+#define _FACECACHE_H_
+
+#include <pthread.h>
+#include "types.h"
+
+v3s32 get_face(size_t i, v3s32 *base);
+size_t get_face_count(u32 size);
+
+#endif
return block;
}
+static MapBlock *create_block(MapSector *sector, size_t idx, v3s32 pos)
+{
+ MapBlock *block = allocate_block(pos);
+ array_insert(§or->blocks, &block, idx);
+ return block;
+}
+
+static bool read_block_data(int fd, MapBlockData data)
+{
+ size_t n_read_total = 0;
+ int n_read;
+
+ while (n_read_total < sizeof(MapBlockData)) {
+ if ((n_read = read(fd, (char *) data + n_read_total, sizeof(MapBlockData) - n_read_total)) == -1) {
+ perror("read");
+ return false;
+ }
+
+ n_read_total += n_read;
+ }
+
+ return true;
+}
+
Map *map_create()
{
Map *map = malloc(sizeof(Map));
MapBlock *block = NULL;
if (res.success) {
- MapBlock *rawblock = map_get_block_raw(sector, res.index);
- if (rawblock->state == MBS_READY || create)
- block = rawblock;
+ block = map_get_block_raw(sector, res.index);
+ if (block->state < MBS_READY && ! create)
+ block = NULL;
} else if (create) {
- block = allocate_block(pos);
- array_insert(§or->blocks, &block, res.index);
+ block = create_block(sector, res.index, pos);
}
pthread_rwlock_unlock(§or->rwlck);
MapBlock *block;
- if (dummy) {
+ if (dummy)
block = allocate_block(pos);
- } else {
- MapSector *sector = map_get_sector(map, (v2s32) {pos.x, pos.z}, true);
-
- pthread_rwlock_wrlock(§or->rwlck);
- ArraySearchResult res = array_search(§or->blocks, &pos.y);
-
- if (res.success) {
- block = map_get_block_raw(sector, res.index);
- } else {
- block = allocate_block(pos);
- array_insert(§or->blocks, &block, res.index);
- }
-
- pthread_rwlock_unlock(§or->rwlck);
+ else
+ block = map_get_block(map, pos, true);
- if (res.success)
- map_clear_meta(block);
- }
+ if (block->state != MBS_CREATED)
+ map_clear_meta(block);
pthread_mutex_lock(&block->mtx);
+ block->state = MBS_INITIALIZING;
- bool ret = true;
- size_t n_read_total = 0;
- int n_read;
MapBlockData data;
- while (n_read_total < sizeof(MapBlockData)) {
- if ((n_read = read(fd, (char *) data + n_read_total, sizeof(MapBlockData) - n_read_total)) == -1) {
- perror("read");
- ret = false;
- break;
- }
-
- n_read_total += n_read;
- }
+ bool success = read_block_data(fd, data);
ITERATE_MAPBLOCK {
- if (ret) {
+ if (success) {
MapNode node = data[x][y][z];
node.type = be32toh(node.type);
block->metadata[x][y][z] = list_create(&list_compare_string);
}
+ block->state = MBS_UNSENT;
if (dummy)
map_free_block(block);
- else if (blockptr && ret)
+ else if (blockptr && success)
*blockptr = block;
pthread_mutex_unlock(&block->mtx);
- return ret;
+ return success;
}
bool map_serialize(FILE *file, Map *map)
list_clear(&block->metadata[offset.x][offset.y][offset.z]);
block->data[offset.x][offset.y][offset.z] = node;
- block->state = MBS_MODIFIED;
+ block->state = MBS_UNSENT;
}
}
typedef enum
{
MBS_CREATED,
- MBS_PROCESSING,
+ MBS_INITIALIZING,
MBS_READY,
- MBS_MODIFIED,
+ MBS_UNSENT,
+ MBS_SENDING,
} MapBlockState;
typedef MapNode MapBlockData[16][16][16];
--- /dev/null
+#include "mapgen.h"
+#include "perlin.h"
+
+void mapgen_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, 0) * 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->metadata[x][y][z] = list_create(&list_compare_string);
+ }
+ }
+ }
+}
--- /dev/null
+#ifndef _MAPGEN_H_
+#define _MAPGEN_H_
+
+#include "map.h"
+
+void mapgen_generate_block(MapBlock *block);
+
+#endif
return ret;
}
-static void handle_packets(Client *client) {
+static bool handle_packets(Client *client) {
while (client->state != CS_DISCONNECTED || ! interrupted) {
struct pollfd pfd = {
.fd = client->fd,
if (pstate == -1) {
perror("poll");
- return;
+ break;
}
if (pstate == 0) {
}
if (! (pfd.revents & POLLIN))
- return;
+ return false;
HostCommand command;
if (! read_u32(client->fd, &command))
- return;
+ break;
CommandHandler *handler = NULL;
if (! good)
printf("Recieved %s command, but client is in invalid state: %d\n", handler->name, client->state);
if (! handler->func(client, good))
- return;
+ break;
} else {
printf("Recieved invalid command %d\n", command);
}
}
+
+ return client->state == CS_DISCONNECTED || errno == EINTR;
}
--- /dev/null
+#include <perlin/perlin.c>
+
--- /dev/null
+#ifndef _PERLIN_H_
+#define _PERLIN_H_
+
+#include <perlin/perlin.h>
+
+#endif
void server_disconnect_client(Client *client, int flags, const char *detail)
{
+ ClientState cs = client->state;
client->state = CS_DISCONNECTED;
- if (client->name && ! (flags & DISCO_NO_REMOVE))
- list_delete(&server.clients, client->name);
+ if (! (flags & DISCO_NO_REMOVE)) {
+ if (client->name) {
+ pthread_rwlock_wrlock(&server.players_rwlck);
+ list_delete(&server.players, client->name);
+ pthread_rwlock_unlock(&server.players_rwlck);
+ }
+ pthread_rwlock_wrlock(&server.clients_rwlck);
+ list_delete(&server.clients, client);
+ pthread_rwlock_unlock(&server.clients_rwlck);
+ }
if (! (flags & DISCO_NO_MESSAGE))
printf("Disconnected %s %s%s%s\n", client->name, INBRACES(detail));
pthread_mutex_lock(&client->mtx);
close(client->fd);
pthread_mutex_unlock(&client->mtx);
-}
-
-#include "network.c"
-static void *server_reciever_thread(void *clientptr)
-{
- Client *client = clientptr;
-
- handle_packets(client);
+ if (! (flags & DISCO_NO_JOIN))
+ pthread_join(client->net_thread, NULL);
- if (client->state != CS_DISCONNECTED)
- server_disconnect_client(client, DISCO_NO_SEND, "network error");
+ if (cs == CS_ACTIVE)
+ pthread_join(client->map_thread, NULL);
if (client->name != client->address)
free(client->name);
-
free(client->address);
pthread_mutex_destroy(&client->mtx);
-
free(client);
+}
+
+#include "network.c"
+
+static void *server_reciever_thread(void *cli)
+{
+ Client *client = cli;
+
+ if (! handle_packets(client))
+ server_disconnect_client(client, DISCO_NO_SEND | DISCO_NO_JOIN, "network error");
return NULL;
}
client->name = client->address;
client->server = &server;
client->pos = (v3f) {0.0f, 0.0f, 0.0f};
- pthread_create(&client->thread, NULL, &server_reciever_thread, client);
+ pthread_create(&client->net_thread, NULL, &server_reciever_thread, client);
+
+ pthread_rwlock_wrlock(&server.clients_rwlck);
+ list_put(&server.clients, client, NULL);
+ pthread_rwlock_unlock(&server.clients_rwlck);
printf("Connected %s\n", client->address);
}
{
server.sockfd = fd;
server.map = map_create(NULL);
- server.clients = list_create(&list_compare_string);
+ pthread_rwlock_init(&server.clients_rwlck, NULL);
+ server.clients = list_create(NULL);
+ pthread_rwlock_init(&server.players_rwlck, NULL);
+ server.players = list_create(&list_compare_string);
FILE *mapfile = fopen("map", "r");
if (mapfile) {
printf("Shutting down\n");
- ITERATE_LIST(&server.clients, pair) server_disconnect_client(pair->value, DISCO_NO_REMOVE | DISCO_NO_MESSAGE, "");
+ pthread_rwlock_wrlock(&server.clients_rwlck);
+ ITERATE_LIST(&server.clients, pair) server_disconnect_client(pair->key, DISCO_NO_REMOVE | DISCO_NO_MESSAGE, "");
list_clear(&server.clients);
+ pthread_rwlock_unlock(&server.clients_rwlck);
+ pthread_rwlock_wrlock(&server.players_rwlck);
+ list_clear(&server.players);
+ pthread_rwlock_unlock(&server.players_rwlck);
+
+ pthread_rwlock_destroy(&server.clients_rwlck);
+ pthread_rwlock_destroy(&server.players_rwlck);
shutdown(server.sockfd, SHUT_RDWR);
close(server.sockfd);
typedef struct
{
int sockfd;
+ pthread_rwlock_t clients_rwlck;
List clients;
+ pthread_rwlock_t players_rwlck;
+ List players;
Map *map;
} Server;
char *address;
char *name;
Server *server;
- pthread_t thread;
+ pthread_t net_thread;
+ pthread_t map_thread;
v3f pos;
} Client;
DISCO_NO_REMOVE = 0x01,
DISCO_NO_SEND = 0x02,
DISCO_NO_MESSAGE = 0x04,
+ DISCO_NO_JOIN = 0x08,
} DiscoFlag;
void server_disconnect_client(Client *client, int flags, const char *detail);
return true;
}
- u8 success = list_put(&client->server->clients, name, client);
+ pthread_rwlock_wrlock(&client->server->players_rwlck);
+ u8 success = list_put(&client->server->players, name, client);
+ pthread_rwlock_unlock(&client->server->players_rwlck);
printf("Authentication %s: %s -> %s\n", success ? "success" : "failure", client->address, name);
return true;
}
-static bool kick_handler(Client *client, bool good)
-{
- char *target_name = read_string(client->fd, NAME_MAX);
-
- if (! target_name)
- return false;
-
- if (good) {
- Client *target = list_get(&client->server->clients, target_name);
- if (target)
- server_disconnect_client(target, 0, "kicked");
- }
-
- free(target_name);
- return true;
-}
-
static bool pos_handler(Client *client, bool good)
{
v3f pos;
{&disconnect_handler, "DISCONNECT", CS_CREATED | CS_ACTIVE},
{&auth_handler, "AUTH", CS_CREATED},
{&setnode_handler, "SETNODE", CS_ACTIVE},
- {&kick_handler, "KICK", CS_ACTIVE},
{&pos_handler, "POS", CS_ACTIVE},
};
SC_DISCONNECT,
SC_AUTH,
SC_SETNODE,
- SC_KICK,
SC_POS,
SERVER_COMMAND_COUNT,
} ServerCommand;
#include <stdio.h>
#include <string.h>
#include <unistd.h>
-#include <perlin/perlin.h>
+#include "facecache.h"
+#include "mapgen.h"
+#include "map.h"
#include "servermap.h"
-int seed = 0;
-static Server *server = NULL;
+static size_t max_blocks;
-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->metadata[x][y][z] = list_create(&list_compare_string);
- }
- }
- }
- block->state = MBS_READY;
-}
+static Server *server = NULL;
typedef struct
{
size_t size;
} MapBlockBuffer;
+static void generate_block(MapBlock *block)
+{
+ pthread_mutex_lock(&block->mtx);
+ if (block->state == MBS_CREATED) {
+ block->state = MBS_INITIALIZING;
+ mapgen_generate_block(block);
+ block->state = MBS_UNSENT;
+ }
+ pthread_mutex_unlock(&block->mtx);
+}
+
static void serialize_block(Client *client, MapBlock *block)
{
MapBlockBuffer *buffer = block->extra;
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)
- serialize_block(client, block);
- }
- 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 init_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) \
- } \
+ if (block->state == MBS_UNSENT) {
+ block->state = MBS_SENDING;
+ if (block->extra) {
+ free(((MapBlockBuffer *) block->extra)->ptr);
+ free(block->extra);
+ block->extra = NULL;
}
- 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) \
+ // ToDo: only send to near clients
+ pthread_rwlock_rdlock(&server->players_rwlck);
+ ITERATE_LIST(&server->players, pair) {
+ if (block->state != MBS_SENDING)
+ break;
+ serialize_block(pair->value, block);
}
- 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
+ pthread_rwlock_unlock(&server->players_rwlck);
+ if (block->state == MBS_SENDING)
+ block->state = MBS_READY;
}
+ pthread_mutex_unlock(&block->mtx);
}
-static void send_blocks(Client *client, bool init)
+static void send_blocks(Client *client)
{
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) {
- if (block)
- serialize_block(client, block);
- } else switch (block->state) {
+ for (size_t i = 0; i < max_blocks; i++) {
+ MapBlock *block = map_get_block(client->server->map, get_face(i, &pos), true);
+ switch (block->state) {
case MBS_CREATED:
generate_block(block);
__attribute__ ((fallthrough));
- case MBS_MODIFIED:
- if (block->extra) {
- free(((MapBlockBuffer *) block->extra)->ptr);
- free(block->extra);
- block->extra = NULL;
- }
+ case MBS_UNSENT:
send_block(block);
__attribute__ ((fallthrough));
- case MBS_PROCESSING:
- i = -1;
- sched_yield();
- __attribute__ ((fallthrough));
+ case MBS_INITIALIZING:
+ case MBS_SENDING:
+ return;
case MBS_READY:
break;
}
}
}
-static void *block_send_thread(void *cli)
+static void *map_thread(void *cli)
{
Client *client = cli;
- send_blocks(client, true);
-
- while (client->state != CS_DISCONNECTED)
- send_blocks(client, false);
+ while (client->state != CS_DISCONNECTED) {
+ send_blocks(client);
+ sched_yield();
+ }
return NULL;
}
void servermap_init(Server *srv)
{
server = srv;
- init_pos_cache();
+ max_blocks = get_face_count(3);
}
void servermap_add_client(Client *client)
{
- pthread_t thread;
- pthread_create(&thread, NULL, &block_send_thread, client);
+ pthread_create(&client->map_thread, NULL, &map_thread, client);
}
-
-#include <perlin/perlin.c>
-#ifndef _MAPGEN_H_
-#define _MAPGEN_H_
+#ifndef _SERVERMAP_H_
+#define _SERVERMAP_H_
#include "server.h"
-#include "map.h"
void servermap_init(Server *srv);