# Building instructions
+The code and the Makefile are located in the src/ directory
+
## Available targets
- `all` (default)
- `Dragonblocks`
+++ /dev/null
-COMMON = array.o binsearch.o linkedlist.o map.o signal.o util.o types.o
-SERVER = $(COMMON) server.o servercommands.o
-CLIENT = $(COMMON) client.o clientcommands.o
-LIBRARIES = -lpthread -lm
-FLAGS = -g
-
-ifdef RELEASE
-FLAGS = -O3
-endif
-
-all: Dragonblocks DragonblocksServer
-
-Dragonblocks: $(CLIENT)
- cc $(FLAGS) -o Dragonblocks $(CLIENT) $(LIBRARIES)
-
-DragonblocksServer: $(SERVER)
- cc $(FLAGS) -o DragonblocksServer $(SERVER) $(LIBRARIES)
-
-%.o: %.c
- cc $(FLAGS) -o $@ -c -Wall -Wextra -Wpedantic -Werror $<
-
-clean:
- rm -rf *.o
-
-clobber: clean
- rm -rf Dragonblocks DragonblocksServer
+++ /dev/null
-#include <string.h>
-#include <stdlib.h>
-#include "array.h"
-
-static void array_realloc(Array *array)
-{
- if (array->siz > array->cap) {
- array->cap = array->siz + ARRAY_REALLOC_EXTRA;
- array->ptr = realloc(array->ptr, array->cap * sizeof(void *));
- }
-}
-
-static void array_alloc(Array *array, size_t siz)
-{
- array->siz += siz;
- array_realloc(array);
-}
-
-void array_insert(Array *array, void *elem, size_t idx)
-{
- size_t oldsiz = array->siz;
- array_alloc(array, 1);
-
- void **iptr = array->ptr + idx;
- memmove(iptr + 1, iptr, (oldsiz - idx) * sizeof(void *));
- *iptr = elem;
-}
-
-void array_append(Array *array, void *elem)
-{
- size_t oldsiz = array->siz;
- array_alloc(array, 1);
-
- array->ptr[oldsiz] = elem;
-}
-
-Array array_create()
-{
- return (Array) {0, 0, NULL};
-}
+++ /dev/null
-#ifndef _ARRAY_H_
-#define _ARRAY_H_
-
-#define ARRAY_REALLOC_EXTRA 25
-
-#include <stddef.h>
-
-typedef struct
-{
- size_t siz, cap;
- void **ptr;
-} Array;
-
-void array_insert(Array *array, void *elem, size_t idx);
-void array_append(Array *array, void *elem);
-Array array_create();
-
-#endif
+++ /dev/null
-#include "binsearch.h"
-
-BinsearchResult binsearch(void *search, void **array, size_t size, BinsearchComparator cmp)
-{
- size_t min, max, index;
-
- min = index = 0;
- max = size;
-
- while (min < max) {
- index = min;
-
- size_t mid = (max + min) / 2;
- s8 state = cmp(search, array[mid]);
-
- if (state == 0)
- return (BinsearchResult) {true, mid};
- else if (state > 0)
- max = mid;
- else
- min = mid;
- }
-
- return (BinsearchResult) {false, index};
-}
+++ /dev/null
-#ifndef _BINSEARCH_H_
-#define _BINSEARCH_H_
-
-#include <stddef.h>
-#include <stdbool.h>
-#include "types.h"
-
-typedef s8 (*BinsearchComparator)(void *search, void *element);
-
-typedef struct {
- bool success;
- size_t index;
-} BinsearchResult;
-
-BinsearchResult binsearch(void *search, void **array, size_t size, BinsearchComparator cmp);
-
-#endif
+++ /dev/null
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <arpa/inet.h>
-#include <unistd.h>
-#include <errno.h>
-#include <netdb.h>
-#include "client.h"
-#include "signal.h"
-#include "util.h"
-
-#include "network.c"
-
-void client_disconnect(Client *client, bool send, const char *detail)
-{
- pthread_mutex_lock(&client->mtx);
- if (client->state != CS_DISCONNECTED) {
- if (send)
- write_u32(client->fd, SC_DISCONNECT);
-
- client->state = CS_DISCONNECTED;
- printf("Disconnected %s%s%s\n", INBRACES(detail));
- close(client->fd);
- }
- pthread_mutex_unlock(&client->mtx);
-}
-
-static void *reciever_thread(void *cliptr)
-{
- Client *client = cliptr;
-
- handle_packets(client);
-
- if (errno == EINTR)
- client_disconnect(client, true, NULL);
- else
- client_disconnect(client, false, "network error");
-
- if (client->name)
- free(client->name);
-
- pthread_mutex_destroy(&client->mtx);
-
- exit(EXIT_SUCCESS);
- return NULL;
-}
-
-static void client_loop(Client *client)
-{
- while (client->state != CS_DISCONNECTED) {
- if (client->state == CS_CREATED) {
- printf("Enter name: ");
- fflush(stdout);
- char name[NAME_MAX];
- if (scanf("%s", name) == EOF)
- return;
- client->name = strdup(name);
- pthread_mutex_lock(&client->mtx);
- if (write_u32(client->fd, SC_AUTH) && write(client->fd, client->name, strlen(name) + 1)) {
- client->state = CS_AUTH;
- printf("Authenticating...\n");
- }
- pthread_mutex_unlock(&client->mtx);
- } else if (client->state == CS_ACTIVE) {
- printf("%s: ", client->name);
- fflush(stdout);
- char buffer[BUFSIZ] = {0};
- if (scanf("%s", buffer) == EOF)
- return;
- if (strcmp(buffer, "disconnect") == 0) {
- return;
- } else if (strcmp(buffer, "setnode") == 0) {
- v3s32 pos;
- char node[BUFSIZ] = {0};
- if (scanf("%d %d %d %s", &pos.x, &pos.y, &pos.z, node) == EOF)
- return;
- Node node_type = NODE_INVALID;
- if (strcmp(node, "air") == 0)
- node_type = NODE_AIR;
- else if (strcmp(node, "grass") == 0)
- node_type = NODE_GRASS;
- else if (strcmp(node, "dirt") == 0)
- node_type = NODE_DIRT;
- else if (strcmp(node, "stone") == 0)
- node_type = NODE_STONE;
- if (node_type == NODE_INVALID) {
- printf("Invalid node\n");
- } else {
- pthread_mutex_lock(&client->mtx);
- (void) (write_u32(client->fd, SC_SETNODE) && write_v3s32(client->fd, pos) && write_u32(client->fd, node_type));
- pthread_mutex_unlock(&client->mtx);
- }
- } else if (strcmp(buffer, "getnode") == 0) {
- v3s32 pos;
- if (scanf("%d %d %d", &pos.x, &pos.y, &pos.z) == EOF)
- return;
- pthread_mutex_lock(&client->mtx);
- (void) (write_u32(client->fd, SC_GETBLOCK) && write_v3s32(client->fd, map_node_to_block_pos(pos, NULL)));
- pthread_mutex_unlock(&client->mtx);
- } else if (strcmp(buffer, "printnode") == 0) {
- v3s32 pos;
- if (scanf("%d %d %d", &pos.x, &pos.y, &pos.z) == EOF)
- return;
- MapNode node = map_get_node(client->map, pos);
- const char *nodename;
- switch (node.type) {
- case NODE_UNLOADED:
- nodename = "unloaded";
- break;
- case NODE_AIR:
- nodename = "air";
- break;
- case NODE_GRASS:
- nodename = "grass";
- break;
- case NODE_DIRT:
- nodename = "dirt";
- break;
- case NODE_STONE:
- nodename = "stone";
- break;
- case NODE_INVALID:
- nodename = "invalid";
- break;
- }
- printf("%s\n", nodename);
- } else if (strcmp(buffer, "kick") == 0) {
- char target_name[NAME_MAX];
- if (scanf("%s", target_name) == EOF)
- return;
- pthread_mutex_lock(&client->mtx);
- (void) (write_u32(client->fd, SC_KICK) && write(client->fd, target_name, strlen(target_name) + 1) != -1);
- pthread_mutex_unlock(&client->mtx);
- } else {
- printf("Invalid command: %s\n", buffer);
- }
- } else {
- sched_yield();
- }
- }
-}
-
-int main(int argc, char **argv)
-{
- program_name = argv[0];
-
- if (argc < 3)
- internal_error("missing address or port");
-
- struct addrinfo hints = {
- .ai_family = AF_UNSPEC, // support both IPv4 and IPv6
- .ai_socktype = SOCK_STREAM,
- .ai_protocol = 0,
- .ai_flags = AI_NUMERICSERV,
- };
-
- struct addrinfo *info = NULL;
-
- int gai_state = getaddrinfo(argv[1], argv[2], &hints, &info);
-
- if (gai_state != 0)
- internal_error(gai_strerror(gai_state));
-
- Client client = {
- .fd = -1,
- .map = NULL,
- .name = NULL,
- .state = CS_CREATED,
- };
-
- pthread_mutex_init(&client.mtx, NULL);
-
- client.fd = socket(info->ai_family, info->ai_socktype, info->ai_protocol);
-
- if (client.fd == -1)
- syscall_error("socket");
-
- if (connect(client.fd, info->ai_addr, info->ai_addrlen) == -1)
- syscall_error("connect");
-
- freeaddrinfo(info);
-
- init_signal_handlers();
-
- client.map = map_create();
-
- pthread_t recv_thread;
- pthread_create(&recv_thread, NULL, &reciever_thread, &client);
-
- client_loop(&client);
-
- client_disconnect(&client, true, NULL);
-
- pthread_join(recv_thread, NULL);
-}
+++ /dev/null
-#ifndef _CLIENT_H_
-#define _CLIENT_H_
-
-#include <stdbool.h>
-#include <pthread.h>
-#include "servercommands.h"
-#include "clientcommands.h"
-#include "network.h"
-#include "map.h"
-
-typedef struct Client
-{
- int fd;
- char *name;
- Map *map;
- ClientState state;
- pthread_mutex_t mtx;
-} Client;
-
-void client_disconnect(Client *client, bool send, const char *detail);
-
-#endif
+++ /dev/null
-#include <stdio.h>
-#include "client.h"
-#include "types.h"
-
-static bool disconnect_handler(Client *client, bool good)
-{
- if (good)
- client_disconnect(client, false, NULL);
- return true;
-}
-
-static bool auth_handler(Client *client, bool good)
-{
- u8 success;
- if (! read_u8(client->fd, &success))
- return false;
-
- if (! good)
- return true;
-
- if (success) {
- printf("Authenticated successfully\n");
- client->state = CS_ACTIVE;
- } else {
- printf("Authentication failed, please try again\n");
- client->state = CS_CREATED;
- }
-
- return true;
-}
-
-static bool block_handler(Client *client, bool good)
-{
- if (good)
- return map_deserialize_block(client->fd, client->map);
- return true;
-}
-
-CommandHandler command_handlers[CLIENT_COMMAND_COUNT] = {
- {0},
- {&disconnect_handler, "DISCONNECT", CS_CREATED | CS_AUTH | CS_ACTIVE},
- {&auth_handler, "AUTH", CS_AUTH},
- {&block_handler, "BLOCK", CS_ACTIVE},
-};
+++ /dev/null
-#ifndef _CLIENT_COMMAND_H_
-#define _CLIENT_COMMAND_H_
-
-typedef enum
-{
- CLIENT_COMMAND_NULL,
- CC_DISCONNECT,
- CC_AUTH,
- CC_BLOCK,
- CLIENT_COMMAND_COUNT
-} ClientCommand;
-
-#ifdef _SERVER_H_
-typedef ClientCommand RemoteCommand;
-#endif
-
-#ifdef _CLIENT_H_
-typedef ClientCommand HostCommand;
-#define HOST_COMMAND_COUNT CLIENT_COMMAND_COUNT
-#endif
-
-#endif
+++ /dev/null
-#include <stdlib.h>
-#include <string.h>
-#include "linkedlist.h"
-
-LinkedList linked_list_create()
-{
- return (LinkedList) {NULL};
-}
-
-void linked_list_clear(LinkedList *list)
-{
- for (LinkedListPair *pair = list->first; pair != NULL;) {
- LinkedListPair *next = pair->next;
- free(pair);
- pair = next;
- }
- list->first = NULL;
-}
-
-static LinkedListPair *make_pair(const char *key, void *value)
-{
- LinkedListPair *pair = malloc(sizeof(LinkedListPair));
- pair->key = key;
- pair->value = value;
- pair->next = NULL;
- return pair;
-}
-
-bool linked_list_put(LinkedList *list, const char *key, void *value)
-{
- LinkedListPair **pairptr;
- for (pairptr = &list->first; *pairptr != NULL; pairptr = &(*pairptr)->next) {
- if (strcmp((*pairptr)->key, key) == 0)
- return false;
- }
- *pairptr = make_pair(key, value);
- return true;
-}
-
-void linked_list_set(LinkedList *list, const char *key, void *value)
-{
- LinkedListPair **pairptr;
- for (pairptr = &list->first; *pairptr != NULL; pairptr = &(*pairptr)->next) {
- if (strcmp((*pairptr)->key, key) == 0)
- break;
- }
- *pairptr = make_pair(key, value);
-}
-
-void linked_list_delete(LinkedList *list, const char *key)
-{
- for (LinkedListPair **pairptr = &list->first; *pairptr != NULL; pairptr = &(*pairptr)->next) {
- if (strcmp((*pairptr)->key, key) == 0) {
- LinkedListPair *pair = *pairptr;
- *pairptr = pair->next;
- free(pair);
- return;
- }
- }
-}
-
-void *linked_list_get(LinkedList *list, const char *key)
-{
- for (LinkedListPair *pair = list->first; pair != NULL; pair = pair->next)
- if (strcmp(pair->key, key) == 0)
- return pair->value;
- return NULL;
-}
+++ /dev/null
-#ifndef _LINKEDLIST_H_
-#define _LINKEDLIST_H_
-
-#include <stdbool.h>
-
-#define ITERATE_LINKEDLIST(list, pair) for (LinkedListPair *pair = (list)->first; pair != NULL; pair = pair->next)
-
-typedef struct LinkedListPair
-{
- struct LinkedListPair *next;
- const char *key;
- void *value;
-} LinkedListPair;
-
-typedef struct
-{
- LinkedListPair *first;
-} LinkedList;
-
-LinkedList linked_list_create();
-void linked_list_clear(LinkedList *list);
-
-bool linked_list_put(LinkedList *list, const char *key, void *value);
-void linked_list_set(LinkedList *list, const char *key, void *value);
-void *linked_list_get(LinkedList *list, const char *key);
-void linked_list_delete(LinkedList *list, const char *key);
-
-void linked_list_serialize(int fd); // ToDo
-void linked_list_deserialize(int fd, LinkedList *); // ToDo
-
-#endif
+++ /dev/null
-#include <stdlib.h>
-#include <stdbool.h>
-#include <math.h>
-#include "binsearch.h"
-#include "map.h"
-#include "util.h"
-
-#define CMPBOUNDS(x) x == 0 ? 0 : x > 0 ? 1 : -1
-
-static void raw_delete_block(MapBlock *block)
-{
- ITERATE_MAPBLOCK map_node_clear(&block->data[x][y][z]);
- free(block);
-}
-
-static s8 sector_compare(void *hash, void *sector)
-{
- s64 d = *((u64 *) hash) - ((MapSector *) sector)->hash;
- return CMPBOUNDS(d);
-}
-
-MapSector *map_get_sector(Map *map, v2s32 pos, bool create)
-{
- u64 hash = ((u64) pos.x << 32) + (u64) pos.y;
- BinsearchResult res = binsearch(&hash, map->sectors.ptr, map->sectors.siz, §or_compare);
-
- if (res.success)
- return map->sectors.ptr[res.index];
- if (! create)
- return NULL;
-
- MapSector *sector = malloc(sizeof(MapSector));
- sector->pos = pos;
- sector->hash = hash;
- sector->blocks = array_create();
-
- array_insert(&map->sectors, sector, res.index);
-
- return sector;
-}
-
-static s8 block_compare(void *level, void *block)
-{
- s32 d = *((s32 *) level) - ((MapBlock *) block)->pos.y;
- return CMPBOUNDS(d);
-}
-
-MapBlock *map_get_block(Map *map, v3s32 pos, bool create)
-{
- MapSector *sector = map_get_sector(map, (v2s32) {pos.x, pos.z}, create);
- if (! sector)
- return NULL;
-
- BinsearchResult res = binsearch(&pos.y, sector->blocks.ptr, sector->blocks.siz, &block_compare);
-
- if (res.success)
- return sector->blocks.ptr[res.index];
- if (! create)
- return NULL;
-
- MapBlock *block = malloc(sizeof(MapBlock));
- block->pos = pos;
-
- MapNode air = map_node_create(NODE_AIR);
- ITERATE_MAPBLOCK block->data[x][y][z] = air;
-
- array_insert(§or->blocks, block, res.index);
-
- return block;
-}
-
-bool map_deserialize_node(int fd, MapNode *node)
-{
- Node type;
-
- if (! read_u32(fd, &type))
- return false;
-
- if (type > NODE_INVALID)
- type = NODE_INVALID;
-
- *node = map_node_create(type);
-
- return true;
-}
-
-bool map_serialize_block(int fd, MapBlock *block)
-{
- if (! write_v3s32(fd, block->pos))
- return false;
-
- ITERATE_MAPBLOCK {
- if (! write_u32(fd, block->data[x][y][z].type))
- return false;
- }
-
- return true;
-}
-
-bool map_deserialize_block(int fd, Map *map)
-{
- MapBlock *block = malloc(sizeof(MapBlock));
-
- if (! read_v3s32(fd, &block->pos))
- return false;
-
- ITERATE_MAPBLOCK {
- if (! map_deserialize_node(fd, &block->data[x][y][z])) {
- free(block);
- return false;
- }
- }
-
- MapSector *sector = map_get_sector(map, (v2s32) {block->pos.x, block->pos.z}, true);
- BinsearchResult res = binsearch(&block->pos.y, sector->blocks.ptr, sector->blocks.siz, &block_compare);
- if (res.success) {
- raw_delete_block(sector->blocks.ptr[res.index]);
- sector->blocks.ptr[res.index] = block;
- } else {
- array_insert(§or->blocks, block, res.index);
- }
-
- return true;
-}
-
-
-v3s32 map_node_to_block_pos(v3s32 pos, v3u8 *offset)
-{
- if (offset)
- *offset = (v3u8) {(u32) pos.x % 16, (u32) pos.y % 16, (u32) pos.z % 16};
- return (v3s32) {floor((double) pos.x / 16), floor((double) pos.y / 16), floor((double) pos.z / 16)};
-}
-
-
-MapNode map_get_node(Map *map, v3s32 pos)
-{
- v3u8 offset;
- v3s32 blockpos = map_node_to_block_pos(pos, &offset);
- MapBlock *block = map_get_block(map, blockpos, false);
- if (! block)
- return map_node_create(NODE_UNLOADED);
- return block->data[offset.x][offset.y][offset.z];
-}
-
-void map_set_node(Map *map, v3s32 pos, MapNode node)
-{
- v3u8 offset;
- MapBlock *block = map_get_block(map, map_node_to_block_pos(pos, &offset), true);
- MapNode *current_node = &block->data[offset.x][offset.y][offset.z];
- map_node_clear(current_node);
- *current_node = node;
-}
-
-MapNode map_node_create(Node type)
-{
- return (MapNode) {type, linked_list_create()};
-}
-
-void map_node_clear(MapNode *node)
-{
- linked_list_clear(&node->meta);
-}
-
-Map *map_create()
-{
- Map *map = malloc(sizeof(Map));
- map->sectors = array_create();
-
- return map;
-}
-
-void map_delete(Map *map)
-{
- for (size_t s = 0; s < map->sectors.siz; s++) {
- MapSector *sector = map->sectors.ptr[s];
- for (size_t b = 0; b < sector->blocks.siz; b++)
- raw_delete_block(sector->blocks.ptr[b]);
- if (sector->blocks.ptr)
- free(sector->blocks.ptr);
- free(sector);
- }
- if (map->sectors.ptr)
- free(map->sectors.ptr);
- free(map);
-}
+++ /dev/null
-#ifndef _MAP_H_
-#define _MAP_H_
-
-#include <stdbool.h>
-#include "array.h"
-#include "linkedlist.h"
-#include "node.h"
-#include "types.h"
-
-#define ITERATE_MAPBLOCK for (u8 x = 0; x < 16; x++) for (u8 y = 0; y < 16; y++) for (u8 z = 0; z < 16; z++)
-
-typedef struct
-{
- Node type;
- LinkedList meta;
-} MapNode;
-
-typedef struct
-{
- MapNode data[16][16][16];
- v3s32 pos;
-} MapBlock;
-
-typedef struct
-{
- Array blocks;
- v2s32 pos;
- u64 hash;
-} MapSector;
-
-typedef struct
-{
- Array sectors;
-} Map;
-
-MapSector *map_get_sector(Map *map, v2s32 pos, bool create);
-MapBlock *map_get_block(Map *map, v3s32 pos, bool 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);
-
-void map_delete_block(MapBlock *); // ToDo
-void map_unload_block(MapBlock *); // ToDo
-
-v3s32 map_node_to_block_pos(v3s32 pos, v3u8 *offset);
-
-MapNode map_get_node(Map *map, v3s32 pos);
-void map_set_node(Map *map, v3s32 pos, MapNode node);
-MapNode map_node_create(Node type);
-void map_node_clear(MapNode *node);
-
-Map *map_create();
-void map_delete(Map *map);
-
-#endif
+++ /dev/null
-#include <poll.h>
-
-bool send_command(Client *client, RemoteCommand cmd)
-{
- pthread_mutex_lock(&client->mtx);
- bool ret = write_u32(client->fd, cmd);
- pthread_mutex_unlock(&client->mtx);
- return ret;
-}
-
-static void handle_packets(Client *client) {
- while (client->state != CS_DISCONNECTED) {
- struct pollfd pfd = {
- .fd = client->fd,
- .events = POLLIN,
- .revents = 0,
- };
-
- int pstate = poll(&pfd, 1, 0);
-
- if (pstate == -1) {
- perror("poll");
- return;
- }
-
- if (client->state == CS_DISCONNECTED)
- return;
-
- if (pstate == 0)
- continue;
-
- if (! (pfd.revents & POLLIN))
- return;
-
- HostCommand command;
- if (! read_u32(client->fd, &command))
- return;
-
- CommandHandler *handler = NULL;
-
- if (command < HOST_COMMAND_COUNT)
- handler = &command_handlers[command];
-
- if (handler && handler->func) {
- bool good = client->state & handler->state_flags;
- if (! good)
- printf("Recieved %s command, but client is in invalid state: %d\n", handler->name, client->state);
- if (! handler->func(client, good))
- return;
- } else {
- printf("Recieved invalid command %d\n", command);
- }
- }
-}
+++ /dev/null
-#ifndef _NETWORK_H_
-#define _NETWORK_H_
-
-#include <stdbool.h>
-#define NAME_MAX 64
-
-typedef enum
-{
- CS_CREATED = 0x01,
- CS_AUTH = 0x02,
- CS_ACTIVE = 0x04,
- CS_DISCONNECTED = 0x08,
-} ClientState;
-
-struct Client;
-
-typedef struct {
- bool (*func)(struct Client *client, bool good);
- const char *name;
- int state_flags;
-} CommandHandler;
-
-extern CommandHandler command_handlers[];
-
-bool send_command(struct Client *client, RemoteCommand cmd);
-
-#endif
+++ /dev/null
-#ifndef _NODE_H_
-#define _NODE_H_
-
-typedef enum
-{
- NODE_UNLOADED, // Used for nodes in unloaded blocks
- NODE_AIR,
- NODE_GRASS,
- NODE_DIRT,
- NODE_STONE,
- NODE_INVALID, // Used for invalid nodes received from server (caused by outdated clients)
-} Node;
-
-#endif
+++ /dev/null
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <errno.h>
-#include <netdb.h>
-#include "server.h"
-#include "signal.h"
-#include "util.h"
-
-#include "network.c"
-
-char *server_get_client_name(Client *client)
-{
- return client->name ? client->name : client->address;
-}
-
-void server_disconnect_client(Client *client, int flags, const char *detail)
-{
- client->state = CS_DISCONNECTED;
-
- if (client->name && ! (flags & DISCO_NO_REMOVE))
- linked_list_delete(&client->server->clients, client->name);
-
- if (! (flags & DISCO_NO_MESSAGE))
- printf("Disconnected %s %s%s%s\n", server_get_client_name(client), INBRACES(detail));
-
- if (! (flags & DISCO_NO_SEND))
- send_command(client, CC_DISCONNECT);
-
- pthread_mutex_lock(&client->mtx);
- close(client->fd);
- pthread_mutex_unlock(&client->mtx);
-}
-
-void server_shutdown(Server *srv)
-{
- printf("Shutting down\n");
-
- ITERATE_LINKEDLIST(&srv->clients, pair) server_disconnect_client(pair->value, DISCO_NO_REMOVE | DISCO_NO_MESSAGE, "");
- linked_list_clear(&srv->clients);
-
- shutdown(srv->sockfd, SHUT_RDWR);
- close(srv->sockfd);
-
- map_delete(srv->map);
-
- exit(EXIT_SUCCESS);
-}
-
-static void *reciever_thread(void *clientptr)
-{
- Client *client = clientptr;
-
- handle_packets(client);
-
- if (client->state != CS_DISCONNECTED)
- server_disconnect_client(client, DISCO_NO_SEND, "network error");
-
- if (client->name)
- free(client->name);
-
- free(client->address);
-
- pthread_mutex_destroy(&client->mtx);
- free(client);
-
- return NULL;
-}
-
-static void accept_client(Server *srv)
-{
- struct sockaddr_storage client_address = {0};
- socklen_t client_addrlen = sizeof(client_address);
-
- int fd = accept(srv->sockfd, (struct sockaddr *)&client_address, &client_addrlen);
-
- if (fd == -1) {
- if (errno == EINTR)
- server_shutdown(srv);
- else
- syscall_error("accept");
- }
-
- Client *client = malloc(sizeof(Client));
- client->server = srv;
- client->state = CS_CREATED;
- client->fd = fd;
- client->name = NULL;
- client->address = address_string((struct sockaddr_in6 *) &client_address);
-
- printf("Connected %s\n", client->address);
-
- pthread_mutex_init(&client->mtx, NULL);
-
- pthread_t thread;
- pthread_create(&thread, NULL, &reciever_thread, client);
-}
-
-int main(int argc, char **argv)
-{
- program_name = argv[0];
-
- if (argc < 2)
- internal_error("missing port");
-
- struct addrinfo hints = {
- .ai_family = AF_INET6,
- .ai_socktype = SOCK_STREAM,
- .ai_protocol = 0,
- .ai_flags = AI_NUMERICSERV | AI_PASSIVE,
- };
-
- struct addrinfo *info = NULL;
-
- int gai_state = getaddrinfo(NULL, argv[1], &hints, &info);
-
- if (gai_state != 0)
- internal_error(gai_strerror(gai_state));
-
- Server server = {
- .sockfd = -1,
- .map = NULL,
- .clients = linked_list_create(),
- };
-
- server.sockfd = socket(info->ai_family, info->ai_socktype, 0);
-
- if (server.sockfd == -1)
- syscall_error("socket");
-
- int flag = 1;
-
- if (setsockopt(server.sockfd, SOL_SOCKET, SO_REUSEADDR, &flag, 4) == -1)
- syscall_error("setsockopt");
-
- if (bind(server.sockfd, info->ai_addr, info->ai_addrlen) == -1)
- syscall_error("bind");
-
- if (listen(server.sockfd, 3) == -1)
- syscall_error("listen");
-
- freeaddrinfo(info);
-
- init_signal_handlers();
-
- server.map = map_create(NULL);
-
- for ever accept_client(&server);
-}
+++ /dev/null
-#ifndef _SERVER_H_
-#define _SERVER_H_
-
-#include <pthread.h>
-#include <netinet/in.h>
-#include "clientcommands.h"
-#include "servercommands.h"
-#include "linkedlist.h"
-#include "map.h"
-#include "network.h"
-
-typedef struct
-{
- int sockfd;
- Map *map;
- LinkedList clients;
-} Server;
-
-typedef struct Client
-{
- int fd;
- char *name;
- char *address;
- Server *server;
- ClientState state;
- pthread_mutex_t mtx;
-} Client;
-
-typedef enum
-{
- DISCO_NO_REMOVE = 0x01,
- DISCO_NO_SEND = 0x02,
- DISCO_NO_MESSAGE = 0x04,
-} DiscoFlag;
-
-char *server_get_client_name(Client *client);
-void server_disconnect_client(Client *client, int flags, const char *detail);
-void server_shutdown(Server *srv);
-
-#endif
+++ /dev/null
-#include <stdio.h>
-#include <stdlib.h>
-#include "server.h"
-#include "util.h"
-
-static bool disconnect_handler(Client *client, bool good)
-{
- if (good)
- server_disconnect_client(client, DISCO_NO_SEND, NULL);
- return true;
-}
-
-static bool auth_handler(Client *client, bool good)
-{
- char *name = read_string(client->fd, NAME_MAX);
-
- if (! name)
- return false;
-
- if (! good) {
- free(name);
- return true;
- }
-
- u8 success = linked_list_put(&client->server->clients, name, client);
-
- printf("Authentication %s: %s -> %s\n", success ? "success" : "failure", client->address, name);
-
- if (success) {
- client->name = name;
- client->state = CS_ACTIVE;
- } else {
- free(name);
- }
-
- pthread_mutex_lock(&client->mtx);
- bool ret = write_u32(client->fd, CC_AUTH) && write_u8(client->fd, success);
- pthread_mutex_unlock(&client->mtx);
-
- return ret;
-}
-
-static bool getblock_handler(Client *client, bool good)
-{
- v3s32 pos;
-
- if (! read_v3s32(client->fd, &pos))
- return false;
-
- if (! good)
- return true;
-
- MapBlock *block = map_get_block(client->server->map, pos, false);
- if (block) {
- pthread_mutex_lock(&client->mtx);
- bool ret = write_u32(client->fd, CC_BLOCK) && map_serialize_block(client->fd, block);
- pthread_mutex_unlock(&client->mtx);
-
- return ret;
- }
-
- return true;
-}
-
-static bool setnode_handler(Client *client, bool good)
-{
- v3s32 pos;
-
- if (! read_v3s32(client->fd, &pos))
- return false;
-
- MapNode node;
-
- if (! map_deserialize_node(client->fd, &node))
- return false;
-
- if (good)
- map_set_node(client->server->map, pos, node);
-
- 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 = linked_list_get(&client->server->clients, target_name);
- if (target)
- server_disconnect_client(target, 0, "kicked");
- }
-
- free(target_name);
- return true;
-}
-
-CommandHandler command_handlers[SERVER_COMMAND_COUNT] = {
- {0},
- {&disconnect_handler, "DISCONNECT", CS_CREATED | CS_ACTIVE},
- {&auth_handler, "AUTH", CS_CREATED},
- {&getblock_handler, "GETBLOCK", CS_ACTIVE},
- {&setnode_handler, "SETNODE", CS_ACTIVE},
- {&kick_handler, "KICK", CS_ACTIVE},
-};
+++ /dev/null
-#ifndef _SERVER_COMMAND_H_
-#define _SERVER_COMMAND_H_
-
-typedef enum
-{
- SERVER_COMMAND_NULL,
- SC_DISCONNECT,
- SC_AUTH,
- SC_GETBLOCK,
- SC_SETNODE,
- SC_KICK,
- SERVER_COMMAND_COUNT,
-} ServerCommand;
-
-#ifdef _CLIENT_H_
-typedef ServerCommand RemoteCommand;
-#endif
-
-#ifdef _SERVER_H_
-typedef ServerCommand HostCommand;
-#define HOST_COMMAND_COUNT SERVER_COMMAND_COUNT
-#endif
-
-#endif
+++ /dev/null
-#include <signal.h>
-#include <stdio.h>
-#include <string.h>
-
-static void interrupt_handler(int sig)
-{
- fprintf(stderr, "%s\n", strsignal(sig));
-}
-
-static void silent_handler(int sig)
-{
- (void) sig;
-}
-
-static struct sigaction sigact_interrupt = {0};
-static struct sigaction sigact_silent = {0};
-
-void init_signal_handlers()
-{
- sigact_interrupt.sa_handler = &interrupt_handler;
- sigaction(SIGINT, &sigact_interrupt, NULL);
- sigaction(SIGTERM, &sigact_interrupt, NULL);
-
- sigact_silent.sa_handler = &silent_handler;
- sigaction(SIGPIPE, &sigact_silent, NULL);
-}
+++ /dev/null
-#ifndef _SIGNAL_H_
-#define _SIGNAL_H_
-
-void init_signal_handlers();
-
-#endif
--- /dev/null
+COMMON = array.o binsearch.o linkedlist.o map.o signal.o util.o types.o
+SERVER = $(COMMON) server.o servercommands.o
+CLIENT = $(COMMON) client.o clientcommands.o
+LIBRARIES = -lpthread -lm
+FLAGS = -g
+
+ifdef RELEASE
+FLAGS = -O3
+endif
+
+all: Dragonblocks DragonblocksServer
+
+Dragonblocks: $(CLIENT)
+ cc $(FLAGS) -o Dragonblocks $(CLIENT) $(LIBRARIES)
+
+DragonblocksServer: $(SERVER)
+ cc $(FLAGS) -o DragonblocksServer $(SERVER) $(LIBRARIES)
+
+%.o: %.c
+ cc $(FLAGS) -o $@ -c -Wall -Wextra -Wpedantic -Werror $<
+
+clean:
+ rm -rf *.o
+
+clobber: clean
+ rm -rf Dragonblocks DragonblocksServer
--- /dev/null
+#include <string.h>
+#include <stdlib.h>
+#include "array.h"
+
+static void array_realloc(Array *array)
+{
+ if (array->siz > array->cap) {
+ array->cap = array->siz + ARRAY_REALLOC_EXTRA;
+ array->ptr = realloc(array->ptr, array->cap * sizeof(void *));
+ }
+}
+
+static void array_alloc(Array *array, size_t siz)
+{
+ array->siz += siz;
+ array_realloc(array);
+}
+
+void array_insert(Array *array, void *elem, size_t idx)
+{
+ size_t oldsiz = array->siz;
+ array_alloc(array, 1);
+
+ void **iptr = array->ptr + idx;
+ memmove(iptr + 1, iptr, (oldsiz - idx) * sizeof(void *));
+ *iptr = elem;
+}
+
+void array_append(Array *array, void *elem)
+{
+ size_t oldsiz = array->siz;
+ array_alloc(array, 1);
+
+ array->ptr[oldsiz] = elem;
+}
+
+Array array_create()
+{
+ return (Array) {0, 0, NULL};
+}
--- /dev/null
+#ifndef _ARRAY_H_
+#define _ARRAY_H_
+
+#define ARRAY_REALLOC_EXTRA 25
+
+#include <stddef.h>
+
+typedef struct
+{
+ size_t siz, cap;
+ void **ptr;
+} Array;
+
+void array_insert(Array *array, void *elem, size_t idx);
+void array_append(Array *array, void *elem);
+Array array_create();
+
+#endif
--- /dev/null
+#include "binsearch.h"
+
+BinsearchResult binsearch(void *search, void **array, size_t size, BinsearchComparator cmp)
+{
+ size_t min, max, index;
+
+ min = index = 0;
+ max = size;
+
+ while (min < max) {
+ index = min;
+
+ size_t mid = (max + min) / 2;
+ s8 state = cmp(search, array[mid]);
+
+ if (state == 0)
+ return (BinsearchResult) {true, mid};
+ else if (state > 0)
+ max = mid;
+ else
+ min = mid;
+ }
+
+ return (BinsearchResult) {false, index};
+}
--- /dev/null
+#ifndef _BINSEARCH_H_
+#define _BINSEARCH_H_
+
+#include <stddef.h>
+#include <stdbool.h>
+#include "types.h"
+
+typedef s8 (*BinsearchComparator)(void *search, void *element);
+
+typedef struct {
+ bool success;
+ size_t index;
+} BinsearchResult;
+
+BinsearchResult binsearch(void *search, void **array, size_t size, BinsearchComparator cmp);
+
+#endif
--- /dev/null
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <errno.h>
+#include <netdb.h>
+#include "client.h"
+#include "signal.h"
+#include "util.h"
+
+#include "network.c"
+
+void client_disconnect(Client *client, bool send, const char *detail)
+{
+ pthread_mutex_lock(&client->mtx);
+ if (client->state != CS_DISCONNECTED) {
+ if (send)
+ write_u32(client->fd, SC_DISCONNECT);
+
+ client->state = CS_DISCONNECTED;
+ printf("Disconnected %s%s%s\n", INBRACES(detail));
+ close(client->fd);
+ }
+ pthread_mutex_unlock(&client->mtx);
+}
+
+static void *reciever_thread(void *cliptr)
+{
+ Client *client = cliptr;
+
+ handle_packets(client);
+
+ if (errno == EINTR)
+ client_disconnect(client, true, NULL);
+ else
+ client_disconnect(client, false, "network error");
+
+ if (client->name)
+ free(client->name);
+
+ pthread_mutex_destroy(&client->mtx);
+
+ exit(EXIT_SUCCESS);
+ return NULL;
+}
+
+static void client_loop(Client *client)
+{
+ while (client->state != CS_DISCONNECTED) {
+ if (client->state == CS_CREATED) {
+ printf("Enter name: ");
+ fflush(stdout);
+ char name[NAME_MAX];
+ if (scanf("%s", name) == EOF)
+ return;
+ client->name = strdup(name);
+ pthread_mutex_lock(&client->mtx);
+ if (write_u32(client->fd, SC_AUTH) && write(client->fd, client->name, strlen(name) + 1)) {
+ client->state = CS_AUTH;
+ printf("Authenticating...\n");
+ }
+ pthread_mutex_unlock(&client->mtx);
+ } else if (client->state == CS_ACTIVE) {
+ printf("%s: ", client->name);
+ fflush(stdout);
+ char buffer[BUFSIZ] = {0};
+ if (scanf("%s", buffer) == EOF)
+ return;
+ if (strcmp(buffer, "disconnect") == 0) {
+ return;
+ } else if (strcmp(buffer, "setnode") == 0) {
+ v3s32 pos;
+ char node[BUFSIZ] = {0};
+ if (scanf("%d %d %d %s", &pos.x, &pos.y, &pos.z, node) == EOF)
+ return;
+ Node node_type = NODE_INVALID;
+ if (strcmp(node, "air") == 0)
+ node_type = NODE_AIR;
+ else if (strcmp(node, "grass") == 0)
+ node_type = NODE_GRASS;
+ else if (strcmp(node, "dirt") == 0)
+ node_type = NODE_DIRT;
+ else if (strcmp(node, "stone") == 0)
+ node_type = NODE_STONE;
+ if (node_type == NODE_INVALID) {
+ printf("Invalid node\n");
+ } else {
+ pthread_mutex_lock(&client->mtx);
+ (void) (write_u32(client->fd, SC_SETNODE) && write_v3s32(client->fd, pos) && write_u32(client->fd, node_type));
+ pthread_mutex_unlock(&client->mtx);
+ }
+ } else if (strcmp(buffer, "getnode") == 0) {
+ v3s32 pos;
+ if (scanf("%d %d %d", &pos.x, &pos.y, &pos.z) == EOF)
+ return;
+ pthread_mutex_lock(&client->mtx);
+ (void) (write_u32(client->fd, SC_GETBLOCK) && write_v3s32(client->fd, map_node_to_block_pos(pos, NULL)));
+ pthread_mutex_unlock(&client->mtx);
+ } else if (strcmp(buffer, "printnode") == 0) {
+ v3s32 pos;
+ if (scanf("%d %d %d", &pos.x, &pos.y, &pos.z) == EOF)
+ return;
+ MapNode node = map_get_node(client->map, pos);
+ const char *nodename;
+ switch (node.type) {
+ case NODE_UNLOADED:
+ nodename = "unloaded";
+ break;
+ case NODE_AIR:
+ nodename = "air";
+ break;
+ case NODE_GRASS:
+ nodename = "grass";
+ break;
+ case NODE_DIRT:
+ nodename = "dirt";
+ break;
+ case NODE_STONE:
+ nodename = "stone";
+ break;
+ case NODE_INVALID:
+ nodename = "invalid";
+ break;
+ }
+ printf("%s\n", nodename);
+ } else if (strcmp(buffer, "kick") == 0) {
+ char target_name[NAME_MAX];
+ if (scanf("%s", target_name) == EOF)
+ return;
+ pthread_mutex_lock(&client->mtx);
+ (void) (write_u32(client->fd, SC_KICK) && write(client->fd, target_name, strlen(target_name) + 1) != -1);
+ pthread_mutex_unlock(&client->mtx);
+ } else {
+ printf("Invalid command: %s\n", buffer);
+ }
+ } else {
+ sched_yield();
+ }
+ }
+}
+
+int main(int argc, char **argv)
+{
+ program_name = argv[0];
+
+ if (argc < 3)
+ internal_error("missing address or port");
+
+ struct addrinfo hints = {
+ .ai_family = AF_UNSPEC, // support both IPv4 and IPv6
+ .ai_socktype = SOCK_STREAM,
+ .ai_protocol = 0,
+ .ai_flags = AI_NUMERICSERV,
+ };
+
+ struct addrinfo *info = NULL;
+
+ int gai_state = getaddrinfo(argv[1], argv[2], &hints, &info);
+
+ if (gai_state != 0)
+ internal_error(gai_strerror(gai_state));
+
+ Client client = {
+ .fd = -1,
+ .map = NULL,
+ .name = NULL,
+ .state = CS_CREATED,
+ };
+
+ pthread_mutex_init(&client.mtx, NULL);
+
+ client.fd = socket(info->ai_family, info->ai_socktype, info->ai_protocol);
+
+ if (client.fd == -1)
+ syscall_error("socket");
+
+ if (connect(client.fd, info->ai_addr, info->ai_addrlen) == -1)
+ syscall_error("connect");
+
+ freeaddrinfo(info);
+
+ init_signal_handlers();
+
+ client.map = map_create();
+
+ pthread_t recv_thread;
+ pthread_create(&recv_thread, NULL, &reciever_thread, &client);
+
+ client_loop(&client);
+
+ client_disconnect(&client, true, NULL);
+
+ pthread_join(recv_thread, NULL);
+}
--- /dev/null
+#ifndef _CLIENT_H_
+#define _CLIENT_H_
+
+#include <stdbool.h>
+#include <pthread.h>
+#include "servercommands.h"
+#include "clientcommands.h"
+#include "network.h"
+#include "map.h"
+
+typedef struct Client
+{
+ int fd;
+ char *name;
+ Map *map;
+ ClientState state;
+ pthread_mutex_t mtx;
+} Client;
+
+void client_disconnect(Client *client, bool send, const char *detail);
+
+#endif
--- /dev/null
+#include <stdio.h>
+#include "client.h"
+#include "types.h"
+
+static bool disconnect_handler(Client *client, bool good)
+{
+ if (good)
+ client_disconnect(client, false, NULL);
+ return true;
+}
+
+static bool auth_handler(Client *client, bool good)
+{
+ u8 success;
+ if (! read_u8(client->fd, &success))
+ return false;
+
+ if (! good)
+ return true;
+
+ if (success) {
+ printf("Authenticated successfully\n");
+ client->state = CS_ACTIVE;
+ } else {
+ printf("Authentication failed, please try again\n");
+ client->state = CS_CREATED;
+ }
+
+ return true;
+}
+
+static bool block_handler(Client *client, bool good)
+{
+ if (good)
+ return map_deserialize_block(client->fd, client->map);
+ return true;
+}
+
+CommandHandler command_handlers[CLIENT_COMMAND_COUNT] = {
+ {0},
+ {&disconnect_handler, "DISCONNECT", CS_CREATED | CS_AUTH | CS_ACTIVE},
+ {&auth_handler, "AUTH", CS_AUTH},
+ {&block_handler, "BLOCK", CS_ACTIVE},
+};
--- /dev/null
+#ifndef _CLIENT_COMMAND_H_
+#define _CLIENT_COMMAND_H_
+
+typedef enum
+{
+ CLIENT_COMMAND_NULL,
+ CC_DISCONNECT,
+ CC_AUTH,
+ CC_BLOCK,
+ CLIENT_COMMAND_COUNT
+} ClientCommand;
+
+#ifdef _SERVER_H_
+typedef ClientCommand RemoteCommand;
+#endif
+
+#ifdef _CLIENT_H_
+typedef ClientCommand HostCommand;
+#define HOST_COMMAND_COUNT CLIENT_COMMAND_COUNT
+#endif
+
+#endif
--- /dev/null
+#include <stdlib.h>
+#include <string.h>
+#include "linkedlist.h"
+
+LinkedList linked_list_create()
+{
+ return (LinkedList) {NULL};
+}
+
+void linked_list_clear(LinkedList *list)
+{
+ for (LinkedListPair *pair = list->first; pair != NULL;) {
+ LinkedListPair *next = pair->next;
+ free(pair);
+ pair = next;
+ }
+ list->first = NULL;
+}
+
+static LinkedListPair *make_pair(const char *key, void *value)
+{
+ LinkedListPair *pair = malloc(sizeof(LinkedListPair));
+ pair->key = key;
+ pair->value = value;
+ pair->next = NULL;
+ return pair;
+}
+
+bool linked_list_put(LinkedList *list, const char *key, void *value)
+{
+ LinkedListPair **pairptr;
+ for (pairptr = &list->first; *pairptr != NULL; pairptr = &(*pairptr)->next) {
+ if (strcmp((*pairptr)->key, key) == 0)
+ return false;
+ }
+ *pairptr = make_pair(key, value);
+ return true;
+}
+
+void linked_list_set(LinkedList *list, const char *key, void *value)
+{
+ LinkedListPair **pairptr;
+ for (pairptr = &list->first; *pairptr != NULL; pairptr = &(*pairptr)->next) {
+ if (strcmp((*pairptr)->key, key) == 0)
+ break;
+ }
+ *pairptr = make_pair(key, value);
+}
+
+void linked_list_delete(LinkedList *list, const char *key)
+{
+ for (LinkedListPair **pairptr = &list->first; *pairptr != NULL; pairptr = &(*pairptr)->next) {
+ if (strcmp((*pairptr)->key, key) == 0) {
+ LinkedListPair *pair = *pairptr;
+ *pairptr = pair->next;
+ free(pair);
+ return;
+ }
+ }
+}
+
+void *linked_list_get(LinkedList *list, const char *key)
+{
+ for (LinkedListPair *pair = list->first; pair != NULL; pair = pair->next)
+ if (strcmp(pair->key, key) == 0)
+ return pair->value;
+ return NULL;
+}
--- /dev/null
+#ifndef _LINKEDLIST_H_
+#define _LINKEDLIST_H_
+
+#include <stdbool.h>
+
+#define ITERATE_LINKEDLIST(list, pair) for (LinkedListPair *pair = (list)->first; pair != NULL; pair = pair->next)
+
+typedef struct LinkedListPair
+{
+ struct LinkedListPair *next;
+ const char *key;
+ void *value;
+} LinkedListPair;
+
+typedef struct
+{
+ LinkedListPair *first;
+} LinkedList;
+
+LinkedList linked_list_create();
+void linked_list_clear(LinkedList *list);
+
+bool linked_list_put(LinkedList *list, const char *key, void *value);
+void linked_list_set(LinkedList *list, const char *key, void *value);
+void *linked_list_get(LinkedList *list, const char *key);
+void linked_list_delete(LinkedList *list, const char *key);
+
+void linked_list_serialize(int fd); // ToDo
+void linked_list_deserialize(int fd, LinkedList *); // ToDo
+
+#endif
--- /dev/null
+#include <stdlib.h>
+#include <stdbool.h>
+#include <math.h>
+#include "binsearch.h"
+#include "map.h"
+#include "util.h"
+
+#define CMPBOUNDS(x) x == 0 ? 0 : x > 0 ? 1 : -1
+
+static void raw_delete_block(MapBlock *block)
+{
+ ITERATE_MAPBLOCK map_node_clear(&block->data[x][y][z]);
+ free(block);
+}
+
+static s8 sector_compare(void *hash, void *sector)
+{
+ s64 d = *((u64 *) hash) - ((MapSector *) sector)->hash;
+ return CMPBOUNDS(d);
+}
+
+MapSector *map_get_sector(Map *map, v2s32 pos, bool create)
+{
+ u64 hash = ((u64) pos.x << 32) + (u64) pos.y;
+ BinsearchResult res = binsearch(&hash, map->sectors.ptr, map->sectors.siz, §or_compare);
+
+ if (res.success)
+ return map->sectors.ptr[res.index];
+ if (! create)
+ return NULL;
+
+ MapSector *sector = malloc(sizeof(MapSector));
+ sector->pos = pos;
+ sector->hash = hash;
+ sector->blocks = array_create();
+
+ array_insert(&map->sectors, sector, res.index);
+
+ return sector;
+}
+
+static s8 block_compare(void *level, void *block)
+{
+ s32 d = *((s32 *) level) - ((MapBlock *) block)->pos.y;
+ return CMPBOUNDS(d);
+}
+
+MapBlock *map_get_block(Map *map, v3s32 pos, bool create)
+{
+ MapSector *sector = map_get_sector(map, (v2s32) {pos.x, pos.z}, create);
+ if (! sector)
+ return NULL;
+
+ BinsearchResult res = binsearch(&pos.y, sector->blocks.ptr, sector->blocks.siz, &block_compare);
+
+ if (res.success)
+ return sector->blocks.ptr[res.index];
+ if (! create)
+ return NULL;
+
+ MapBlock *block = malloc(sizeof(MapBlock));
+ block->pos = pos;
+
+ MapNode air = map_node_create(NODE_AIR);
+ ITERATE_MAPBLOCK block->data[x][y][z] = air;
+
+ array_insert(§or->blocks, block, res.index);
+
+ return block;
+}
+
+bool map_deserialize_node(int fd, MapNode *node)
+{
+ Node type;
+
+ if (! read_u32(fd, &type))
+ return false;
+
+ if (type > NODE_INVALID)
+ type = NODE_INVALID;
+
+ *node = map_node_create(type);
+
+ return true;
+}
+
+bool map_serialize_block(int fd, MapBlock *block)
+{
+ if (! write_v3s32(fd, block->pos))
+ return false;
+
+ ITERATE_MAPBLOCK {
+ if (! write_u32(fd, block->data[x][y][z].type))
+ return false;
+ }
+
+ return true;
+}
+
+bool map_deserialize_block(int fd, Map *map)
+{
+ MapBlock *block = malloc(sizeof(MapBlock));
+
+ if (! read_v3s32(fd, &block->pos))
+ return false;
+
+ ITERATE_MAPBLOCK {
+ if (! map_deserialize_node(fd, &block->data[x][y][z])) {
+ free(block);
+ return false;
+ }
+ }
+
+ MapSector *sector = map_get_sector(map, (v2s32) {block->pos.x, block->pos.z}, true);
+ BinsearchResult res = binsearch(&block->pos.y, sector->blocks.ptr, sector->blocks.siz, &block_compare);
+ if (res.success) {
+ raw_delete_block(sector->blocks.ptr[res.index]);
+ sector->blocks.ptr[res.index] = block;
+ } else {
+ array_insert(§or->blocks, block, res.index);
+ }
+
+ return true;
+}
+
+
+v3s32 map_node_to_block_pos(v3s32 pos, v3u8 *offset)
+{
+ if (offset)
+ *offset = (v3u8) {(u32) pos.x % 16, (u32) pos.y % 16, (u32) pos.z % 16};
+ return (v3s32) {floor((double) pos.x / 16), floor((double) pos.y / 16), floor((double) pos.z / 16)};
+}
+
+
+MapNode map_get_node(Map *map, v3s32 pos)
+{
+ v3u8 offset;
+ v3s32 blockpos = map_node_to_block_pos(pos, &offset);
+ MapBlock *block = map_get_block(map, blockpos, false);
+ if (! block)
+ return map_node_create(NODE_UNLOADED);
+ return block->data[offset.x][offset.y][offset.z];
+}
+
+void map_set_node(Map *map, v3s32 pos, MapNode node)
+{
+ v3u8 offset;
+ MapBlock *block = map_get_block(map, map_node_to_block_pos(pos, &offset), true);
+ MapNode *current_node = &block->data[offset.x][offset.y][offset.z];
+ map_node_clear(current_node);
+ *current_node = node;
+}
+
+MapNode map_node_create(Node type)
+{
+ return (MapNode) {type, linked_list_create()};
+}
+
+void map_node_clear(MapNode *node)
+{
+ linked_list_clear(&node->meta);
+}
+
+Map *map_create()
+{
+ Map *map = malloc(sizeof(Map));
+ map->sectors = array_create();
+
+ return map;
+}
+
+void map_delete(Map *map)
+{
+ for (size_t s = 0; s < map->sectors.siz; s++) {
+ MapSector *sector = map->sectors.ptr[s];
+ for (size_t b = 0; b < sector->blocks.siz; b++)
+ raw_delete_block(sector->blocks.ptr[b]);
+ if (sector->blocks.ptr)
+ free(sector->blocks.ptr);
+ free(sector);
+ }
+ if (map->sectors.ptr)
+ free(map->sectors.ptr);
+ free(map);
+}
--- /dev/null
+#ifndef _MAP_H_
+#define _MAP_H_
+
+#include <stdbool.h>
+#include "array.h"
+#include "linkedlist.h"
+#include "node.h"
+#include "types.h"
+
+#define ITERATE_MAPBLOCK for (u8 x = 0; x < 16; x++) for (u8 y = 0; y < 16; y++) for (u8 z = 0; z < 16; z++)
+
+typedef struct
+{
+ Node type;
+ LinkedList meta;
+} MapNode;
+
+typedef struct
+{
+ MapNode data[16][16][16];
+ v3s32 pos;
+} MapBlock;
+
+typedef struct
+{
+ Array blocks;
+ v2s32 pos;
+ u64 hash;
+} MapSector;
+
+typedef struct
+{
+ Array sectors;
+} Map;
+
+MapSector *map_get_sector(Map *map, v2s32 pos, bool create);
+MapBlock *map_get_block(Map *map, v3s32 pos, bool 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);
+
+void map_delete_block(MapBlock *); // ToDo
+void map_unload_block(MapBlock *); // ToDo
+
+v3s32 map_node_to_block_pos(v3s32 pos, v3u8 *offset);
+
+MapNode map_get_node(Map *map, v3s32 pos);
+void map_set_node(Map *map, v3s32 pos, MapNode node);
+MapNode map_node_create(Node type);
+void map_node_clear(MapNode *node);
+
+Map *map_create();
+void map_delete(Map *map);
+
+#endif
--- /dev/null
+#include <poll.h>
+
+bool send_command(Client *client, RemoteCommand cmd)
+{
+ pthread_mutex_lock(&client->mtx);
+ bool ret = write_u32(client->fd, cmd);
+ pthread_mutex_unlock(&client->mtx);
+ return ret;
+}
+
+static void handle_packets(Client *client) {
+ while (client->state != CS_DISCONNECTED) {
+ struct pollfd pfd = {
+ .fd = client->fd,
+ .events = POLLIN,
+ .revents = 0,
+ };
+
+ int pstate = poll(&pfd, 1, 0);
+
+ if (pstate == -1) {
+ perror("poll");
+ return;
+ }
+
+ if (client->state == CS_DISCONNECTED)
+ return;
+
+ if (pstate == 0)
+ continue;
+
+ if (! (pfd.revents & POLLIN))
+ return;
+
+ HostCommand command;
+ if (! read_u32(client->fd, &command))
+ return;
+
+ CommandHandler *handler = NULL;
+
+ if (command < HOST_COMMAND_COUNT)
+ handler = &command_handlers[command];
+
+ if (handler && handler->func) {
+ bool good = client->state & handler->state_flags;
+ if (! good)
+ printf("Recieved %s command, but client is in invalid state: %d\n", handler->name, client->state);
+ if (! handler->func(client, good))
+ return;
+ } else {
+ printf("Recieved invalid command %d\n", command);
+ }
+ }
+}
--- /dev/null
+#ifndef _NETWORK_H_
+#define _NETWORK_H_
+
+#include <stdbool.h>
+#define NAME_MAX 64
+
+typedef enum
+{
+ CS_CREATED = 0x01,
+ CS_AUTH = 0x02,
+ CS_ACTIVE = 0x04,
+ CS_DISCONNECTED = 0x08,
+} ClientState;
+
+struct Client;
+
+typedef struct {
+ bool (*func)(struct Client *client, bool good);
+ const char *name;
+ int state_flags;
+} CommandHandler;
+
+extern CommandHandler command_handlers[];
+
+bool send_command(struct Client *client, RemoteCommand cmd);
+
+#endif
--- /dev/null
+#ifndef _NODE_H_
+#define _NODE_H_
+
+typedef enum
+{
+ NODE_UNLOADED, // Used for nodes in unloaded blocks
+ NODE_AIR,
+ NODE_GRASS,
+ NODE_DIRT,
+ NODE_STONE,
+ NODE_INVALID, // Used for invalid nodes received from server (caused by outdated clients)
+} Node;
+
+#endif
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <netdb.h>
+#include "server.h"
+#include "signal.h"
+#include "util.h"
+
+#include "network.c"
+
+char *server_get_client_name(Client *client)
+{
+ return client->name ? client->name : client->address;
+}
+
+void server_disconnect_client(Client *client, int flags, const char *detail)
+{
+ client->state = CS_DISCONNECTED;
+
+ if (client->name && ! (flags & DISCO_NO_REMOVE))
+ linked_list_delete(&client->server->clients, client->name);
+
+ if (! (flags & DISCO_NO_MESSAGE))
+ printf("Disconnected %s %s%s%s\n", server_get_client_name(client), INBRACES(detail));
+
+ if (! (flags & DISCO_NO_SEND))
+ send_command(client, CC_DISCONNECT);
+
+ pthread_mutex_lock(&client->mtx);
+ close(client->fd);
+ pthread_mutex_unlock(&client->mtx);
+}
+
+void server_shutdown(Server *srv)
+{
+ printf("Shutting down\n");
+
+ ITERATE_LINKEDLIST(&srv->clients, pair) server_disconnect_client(pair->value, DISCO_NO_REMOVE | DISCO_NO_MESSAGE, "");
+ linked_list_clear(&srv->clients);
+
+ shutdown(srv->sockfd, SHUT_RDWR);
+ close(srv->sockfd);
+
+ map_delete(srv->map);
+
+ exit(EXIT_SUCCESS);
+}
+
+static void *reciever_thread(void *clientptr)
+{
+ Client *client = clientptr;
+
+ handle_packets(client);
+
+ if (client->state != CS_DISCONNECTED)
+ server_disconnect_client(client, DISCO_NO_SEND, "network error");
+
+ if (client->name)
+ free(client->name);
+
+ free(client->address);
+
+ pthread_mutex_destroy(&client->mtx);
+ free(client);
+
+ return NULL;
+}
+
+static void accept_client(Server *srv)
+{
+ struct sockaddr_storage client_address = {0};
+ socklen_t client_addrlen = sizeof(client_address);
+
+ int fd = accept(srv->sockfd, (struct sockaddr *)&client_address, &client_addrlen);
+
+ if (fd == -1) {
+ if (errno == EINTR)
+ server_shutdown(srv);
+ else
+ syscall_error("accept");
+ }
+
+ Client *client = malloc(sizeof(Client));
+ client->server = srv;
+ client->state = CS_CREATED;
+ client->fd = fd;
+ client->name = NULL;
+ client->address = address_string((struct sockaddr_in6 *) &client_address);
+
+ printf("Connected %s\n", client->address);
+
+ pthread_mutex_init(&client->mtx, NULL);
+
+ pthread_t thread;
+ pthread_create(&thread, NULL, &reciever_thread, client);
+}
+
+int main(int argc, char **argv)
+{
+ program_name = argv[0];
+
+ if (argc < 2)
+ internal_error("missing port");
+
+ struct addrinfo hints = {
+ .ai_family = AF_INET6,
+ .ai_socktype = SOCK_STREAM,
+ .ai_protocol = 0,
+ .ai_flags = AI_NUMERICSERV | AI_PASSIVE,
+ };
+
+ struct addrinfo *info = NULL;
+
+ int gai_state = getaddrinfo(NULL, argv[1], &hints, &info);
+
+ if (gai_state != 0)
+ internal_error(gai_strerror(gai_state));
+
+ Server server = {
+ .sockfd = -1,
+ .map = NULL,
+ .clients = linked_list_create(),
+ };
+
+ server.sockfd = socket(info->ai_family, info->ai_socktype, 0);
+
+ if (server.sockfd == -1)
+ syscall_error("socket");
+
+ int flag = 1;
+
+ if (setsockopt(server.sockfd, SOL_SOCKET, SO_REUSEADDR, &flag, 4) == -1)
+ syscall_error("setsockopt");
+
+ if (bind(server.sockfd, info->ai_addr, info->ai_addrlen) == -1)
+ syscall_error("bind");
+
+ if (listen(server.sockfd, 3) == -1)
+ syscall_error("listen");
+
+ freeaddrinfo(info);
+
+ init_signal_handlers();
+
+ server.map = map_create(NULL);
+
+ for ever accept_client(&server);
+}
--- /dev/null
+#ifndef _SERVER_H_
+#define _SERVER_H_
+
+#include <pthread.h>
+#include <netinet/in.h>
+#include "clientcommands.h"
+#include "servercommands.h"
+#include "linkedlist.h"
+#include "map.h"
+#include "network.h"
+
+typedef struct
+{
+ int sockfd;
+ Map *map;
+ LinkedList clients;
+} Server;
+
+typedef struct Client
+{
+ int fd;
+ char *name;
+ char *address;
+ Server *server;
+ ClientState state;
+ pthread_mutex_t mtx;
+} Client;
+
+typedef enum
+{
+ DISCO_NO_REMOVE = 0x01,
+ DISCO_NO_SEND = 0x02,
+ DISCO_NO_MESSAGE = 0x04,
+} DiscoFlag;
+
+char *server_get_client_name(Client *client);
+void server_disconnect_client(Client *client, int flags, const char *detail);
+void server_shutdown(Server *srv);
+
+#endif
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include "server.h"
+#include "util.h"
+
+static bool disconnect_handler(Client *client, bool good)
+{
+ if (good)
+ server_disconnect_client(client, DISCO_NO_SEND, NULL);
+ return true;
+}
+
+static bool auth_handler(Client *client, bool good)
+{
+ char *name = read_string(client->fd, NAME_MAX);
+
+ if (! name)
+ return false;
+
+ if (! good) {
+ free(name);
+ return true;
+ }
+
+ u8 success = linked_list_put(&client->server->clients, name, client);
+
+ printf("Authentication %s: %s -> %s\n", success ? "success" : "failure", client->address, name);
+
+ if (success) {
+ client->name = name;
+ client->state = CS_ACTIVE;
+ } else {
+ free(name);
+ }
+
+ pthread_mutex_lock(&client->mtx);
+ bool ret = write_u32(client->fd, CC_AUTH) && write_u8(client->fd, success);
+ pthread_mutex_unlock(&client->mtx);
+
+ return ret;
+}
+
+static bool getblock_handler(Client *client, bool good)
+{
+ v3s32 pos;
+
+ if (! read_v3s32(client->fd, &pos))
+ return false;
+
+ if (! good)
+ return true;
+
+ MapBlock *block = map_get_block(client->server->map, pos, false);
+ if (block) {
+ pthread_mutex_lock(&client->mtx);
+ bool ret = write_u32(client->fd, CC_BLOCK) && map_serialize_block(client->fd, block);
+ pthread_mutex_unlock(&client->mtx);
+
+ return ret;
+ }
+
+ return true;
+}
+
+static bool setnode_handler(Client *client, bool good)
+{
+ v3s32 pos;
+
+ if (! read_v3s32(client->fd, &pos))
+ return false;
+
+ MapNode node;
+
+ if (! map_deserialize_node(client->fd, &node))
+ return false;
+
+ if (good)
+ map_set_node(client->server->map, pos, node);
+
+ 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 = linked_list_get(&client->server->clients, target_name);
+ if (target)
+ server_disconnect_client(target, 0, "kicked");
+ }
+
+ free(target_name);
+ return true;
+}
+
+CommandHandler command_handlers[SERVER_COMMAND_COUNT] = {
+ {0},
+ {&disconnect_handler, "DISCONNECT", CS_CREATED | CS_ACTIVE},
+ {&auth_handler, "AUTH", CS_CREATED},
+ {&getblock_handler, "GETBLOCK", CS_ACTIVE},
+ {&setnode_handler, "SETNODE", CS_ACTIVE},
+ {&kick_handler, "KICK", CS_ACTIVE},
+};
--- /dev/null
+#ifndef _SERVER_COMMAND_H_
+#define _SERVER_COMMAND_H_
+
+typedef enum
+{
+ SERVER_COMMAND_NULL,
+ SC_DISCONNECT,
+ SC_AUTH,
+ SC_GETBLOCK,
+ SC_SETNODE,
+ SC_KICK,
+ SERVER_COMMAND_COUNT,
+} ServerCommand;
+
+#ifdef _CLIENT_H_
+typedef ServerCommand RemoteCommand;
+#endif
+
+#ifdef _SERVER_H_
+typedef ServerCommand HostCommand;
+#define HOST_COMMAND_COUNT SERVER_COMMAND_COUNT
+#endif
+
+#endif
--- /dev/null
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+
+static void interrupt_handler(int sig)
+{
+ fprintf(stderr, "%s\n", strsignal(sig));
+}
+
+static void silent_handler(int sig)
+{
+ (void) sig;
+}
+
+static struct sigaction sigact_interrupt = {0};
+static struct sigaction sigact_silent = {0};
+
+void init_signal_handlers()
+{
+ sigact_interrupt.sa_handler = &interrupt_handler;
+ sigaction(SIGINT, &sigact_interrupt, NULL);
+ sigaction(SIGTERM, &sigact_interrupt, NULL);
+
+ sigact_silent.sa_handler = &silent_handler;
+ sigaction(SIGPIPE, &sigact_silent, NULL);
+}
--- /dev/null
+#ifndef _SIGNAL_H_
+#define _SIGNAL_H_
+
+void init_signal_handlers();
+
+#endif
--- /dev/null
+#include <stdio.h>
+#include <unistd.h>
+#include <endian.h>
+#include <poll.h>
+#include "types.h"
+
+#define htobe8(x) x
+#define be8toh(x) x
+
+#define READVEC(type, n) \
+ type buf[n]; \
+ for (int i = 0; i < n; i++) { \
+ if (! read_ ## type(fd, &buf[i])) \
+ return false; \
+ }
+
+#define WRITEVEC(type, n) \
+ for (int i = 0; i < n; i++) { \
+ if (! write_ ## type(fd, vec[i])) \
+ return false; \
+ } \
+ return true;
+
+#define DEFVEC(type) \
+ bool read_v2 ## type(int fd, v2 ## type *ptr) \
+ { \
+ READVEC(type, 2) \
+ ptr->x = buf[0]; \
+ ptr->y = buf[1]; \
+ return true; \
+ } \
+ bool write_v2 ## type(int fd, v2 ## type val) \
+ { \
+ type vec[2] = {val.x, val.y}; \
+ WRITEVEC(type, 2) \
+ } \
+ bool read_v3 ## type(int fd, v3 ## type *ptr) \
+ { \
+ READVEC(type, 3) \
+ ptr->x = buf[0]; \
+ ptr->y = buf[1]; \
+ ptr->z = buf[2]; \
+ return true; \
+ } \
+ bool write_v3 ## type(int fd, v3 ## type val) \
+ { \
+ type vec[3] = {val.x, val.y, val.z}; \
+ WRITEVEC(type, 3) \
+ }
+
+#define DEFTYP(type, bits) \
+ bool read_ ## type(int fd, type *buf) \
+ { \
+ u ## bits encoded; \
+ int n_read; \
+ if ((n_read = read(fd, &encoded, sizeof(encoded))) != sizeof(encoded)) { \
+ if (n_read == -1) \
+ perror("read"); \
+ return false; \
+ } \
+ *buf = be ## bits ## toh(encoded); \
+ return true; \
+ } \
+ bool write_ ## type(int fd, type val) \
+ { \
+ u ## bits encoded = htobe ## bits(val); \
+ if (write(fd, &encoded, sizeof(encoded)) == -1) { \
+ perror("write"); \
+ return false; \
+ } \
+ return true; \
+ } \
+ DEFVEC(type)
+
+#define DEFTYPES(bits) \
+ DEFTYP(s ## bits, bits) \
+ DEFTYP(u ## bits, bits)
+
+DEFTYPES(8)
+DEFTYPES(16)
+DEFTYPES(32)
+DEFTYPES(64)
--- /dev/null
+#ifndef _TYPES_H_
+#define _TYPES_H_
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#define DEFRW(type) \
+ bool read_ ## type(int fd, type *ptr); \
+ bool write_ ## type(int fd, type val);
+
+#define DEFVEC(type) \
+ typedef struct {type x, y;} v2 ## type; \
+ DEFRW(v2 ## type) \
+ typedef struct {type x, y, z;} v3 ## type; \
+ DEFRW(v3 ## type)
+
+#define DEFTYP(from, to) \
+ typedef from to; \
+ DEFRW(to) \
+ DEFVEC(to)
+
+#define DEFTYPES(bits) \
+ DEFTYP(int ## bits ## _t, s ## bits) \
+ DEFTYP(uint ## bits ## _t, u ## bits)
+
+DEFTYPES(8)
+DEFTYPES(16)
+DEFTYPES(32)
+DEFTYPES(64)
+
+typedef float f32;
+typedef double f64;
+
+DEFVEC(f32)
+DEFVEC(f64)
+
+typedef v2f32 v2f;
+typedef v3f32 v3f;
+
+#undef DEFRW
+#undef DEFVEC
+#undef DEFTYP
+#undef DEFTYPES
+
+#endif
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include "util.h"
+
+const char *program_name;
+
+void syscall_error(const char *err)
+{
+ perror(err);
+ exit(EXIT_FAILURE);
+}
+
+void internal_error(const char *err)
+{
+ fprintf(stderr, "%s: %s\n", program_name, err);
+ exit(EXIT_FAILURE);
+}
+
+char *read_string(int fd, size_t bufsiz)
+{
+ char buf[bufsiz + 1];
+ buf[bufsiz] = 0;
+ for (size_t i = 0;; i++) {
+ char c;
+ if (read(fd, &c, 1) == -1) {
+ perror("read");
+ return NULL;
+ }
+ if (i < bufsiz)
+ buf[i] = c;
+ if (c == EOF || c == 0)
+ break;
+ }
+ return strdup(buf);
+}
+
+char *address_string(struct sockaddr_in6 *addr)
+{
+ char address[INET6_ADDRSTRLEN] = {0};
+ char port[6] = {0};
+
+ if (inet_ntop(addr->sin6_family, &addr->sin6_addr, address, INET6_ADDRSTRLEN) == NULL)
+ perror("inet_ntop");
+ sprintf(port, "%d", ntohs(addr->sin6_port));
+
+ char *result = malloc(strlen(address) + 1 + strlen(port) + 1);
+ sprintf(result, "%s:%s", address, port);
+ return result;
+}
--- /dev/null
+#ifndef _UTIL_H_
+#define _UTIL_H_
+
+#include <arpa/inet.h>
+#include "types.h"
+
+#define ever (;;)
+#define INBRACES(str) str ? "(" : "", str ? str : "", str ? ")" : ""
+
+extern const char *program_name;
+
+void syscall_error(const char *err);
+void internal_error(const char *err);
+char *read_string(int fd, size_t bufsiz);
+char *address_string(struct sockaddr_in6 *addr);
+
+#endif
+++ /dev/null
-#include <stdio.h>
-#include <unistd.h>
-#include <endian.h>
-#include <poll.h>
-#include "types.h"
-
-#define htobe8(x) x
-#define be8toh(x) x
-
-#define READVEC(type, n) \
- type buf[n]; \
- for (int i = 0; i < n; i++) { \
- if (! read_ ## type(fd, &buf[i])) \
- return false; \
- }
-
-#define WRITEVEC(type, n) \
- for (int i = 0; i < n; i++) { \
- if (! write_ ## type(fd, vec[i])) \
- return false; \
- } \
- return true;
-
-#define DEFVEC(type) \
- bool read_v2 ## type(int fd, v2 ## type *ptr) \
- { \
- READVEC(type, 2) \
- ptr->x = buf[0]; \
- ptr->y = buf[1]; \
- return true; \
- } \
- bool write_v2 ## type(int fd, v2 ## type val) \
- { \
- type vec[2] = {val.x, val.y}; \
- WRITEVEC(type, 2) \
- } \
- bool read_v3 ## type(int fd, v3 ## type *ptr) \
- { \
- READVEC(type, 3) \
- ptr->x = buf[0]; \
- ptr->y = buf[1]; \
- ptr->z = buf[2]; \
- return true; \
- } \
- bool write_v3 ## type(int fd, v3 ## type val) \
- { \
- type vec[3] = {val.x, val.y, val.z}; \
- WRITEVEC(type, 3) \
- }
-
-#define DEFTYP(type, bits) \
- bool read_ ## type(int fd, type *buf) \
- { \
- u ## bits encoded; \
- int n_read; \
- if ((n_read = read(fd, &encoded, sizeof(encoded))) != sizeof(encoded)) { \
- if (n_read == -1) \
- perror("read"); \
- return false; \
- } \
- *buf = be ## bits ## toh(encoded); \
- return true; \
- } \
- bool write_ ## type(int fd, type val) \
- { \
- u ## bits encoded = htobe ## bits(val); \
- if (write(fd, &encoded, sizeof(encoded)) == -1) { \
- perror("write"); \
- return false; \
- } \
- return true; \
- } \
- DEFVEC(type)
-
-#define DEFTYPES(bits) \
- DEFTYP(s ## bits, bits) \
- DEFTYP(u ## bits, bits)
-
-DEFTYPES(8)
-DEFTYPES(16)
-DEFTYPES(32)
-DEFTYPES(64)
+++ /dev/null
-#ifndef _TYPES_H_
-#define _TYPES_H_
-
-#include <stdint.h>
-#include <stdbool.h>
-
-#define DEFRW(type) \
- bool read_ ## type(int fd, type *ptr); \
- bool write_ ## type(int fd, type val);
-
-#define DEFVEC(type) \
- typedef struct {type x, y;} v2 ## type; \
- DEFRW(v2 ## type) \
- typedef struct {type x, y, z;} v3 ## type; \
- DEFRW(v3 ## type)
-
-#define DEFTYP(from, to) \
- typedef from to; \
- DEFRW(to) \
- DEFVEC(to)
-
-#define DEFTYPES(bits) \
- DEFTYP(int ## bits ## _t, s ## bits) \
- DEFTYP(uint ## bits ## _t, u ## bits)
-
-DEFTYPES(8)
-DEFTYPES(16)
-DEFTYPES(32)
-DEFTYPES(64)
-
-typedef float f32;
-typedef double f64;
-
-DEFVEC(f32)
-DEFVEC(f64)
-
-typedef v2f32 v2f;
-typedef v3f32 v3f;
-
-#undef DEFRW
-#undef DEFVEC
-#undef DEFTYP
-#undef DEFTYPES
-
-#endif
+++ /dev/null
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-#include "util.h"
-
-const char *program_name;
-
-void syscall_error(const char *err)
-{
- perror(err);
- exit(EXIT_FAILURE);
-}
-
-void internal_error(const char *err)
-{
- fprintf(stderr, "%s: %s\n", program_name, err);
- exit(EXIT_FAILURE);
-}
-
-char *read_string(int fd, size_t bufsiz)
-{
- char buf[bufsiz + 1];
- buf[bufsiz] = 0;
- for (size_t i = 0;; i++) {
- char c;
- if (read(fd, &c, 1) == -1) {
- perror("read");
- return NULL;
- }
- if (i < bufsiz)
- buf[i] = c;
- if (c == EOF || c == 0)
- break;
- }
- return strdup(buf);
-}
-
-char *address_string(struct sockaddr_in6 *addr)
-{
- char address[INET6_ADDRSTRLEN] = {0};
- char port[6] = {0};
-
- if (inet_ntop(addr->sin6_family, &addr->sin6_addr, address, INET6_ADDRSTRLEN) == NULL)
- perror("inet_ntop");
- sprintf(port, "%d", ntohs(addr->sin6_port));
-
- char *result = malloc(strlen(address) + 1 + strlen(port) + 1);
- sprintf(result, "%s:%s", address, port);
- return result;
-}
+++ /dev/null
-#ifndef _UTIL_H_
-#define _UTIL_H_
-
-#include <arpa/inet.h>
-#include "types.h"
-
-#define ever (;;)
-#define INBRACES(str) str ? "(" : "", str ? str : "", str ? ")" : ""
-
-extern const char *program_name;
-
-void syscall_error(const char *err);
-void internal_error(const char *err);
-char *read_string(int fd, size_t bufsiz);
-char *address_string(struct sockaddr_in6 *addr);
-
-#endif