+# CMake
CMakeLists.txt.user
CMakeCache.txt
CMakeFiles
compile_commands.json
CTestTestfile.cmake
_deps
-DragonblocksServer
+
+# Binaries
Dragonblocks
+DragonblocksServer
+DragonblocksAlpha-*.zip
+
+# Data
+client.conf
+server.conf
world.sqlite
world.sqlite-journal
-DragonblocksAlpha-*.zip
-src/version.h
screenshot-*.png
-*.conf
+
+# Generated code
+src/version.h
+src/types.c
+src/types.h
[submodule "deps/stb"]
path = deps/stb
url = https://github.com/nothings/stb
-[submodule "deps/dragontype"]
- path = deps/dragontype
- url = https://github.com/dragonblocks/dragontype
+[submodule "deps/dragonstd"]
+ path = deps/dragonstd
+ url = https://github.com/dragonblocks/dragonstd
[submodule "deps/endian.h"]
path = deps/endian.h
url = https://github.com/dragonblocks/endian.h
[submodule "deps/dragonport"]
path = deps/dragonport
url = https://github.com/dragonblocks/dragonport
+[submodule "deps/dragonnet"]
+ path = deps/dragonnet
+ url = https://github.com/dragonblocks/dragonnet
+[submodule "deps/linenoise"]
+ path = deps/linenoise
+ url = https://github.com/antirez/linenoise
+[submodule "deps/dragontype"]
+ path = deps/dragontype
+ url = https://github.com/dragonblocks/dragontype
--- /dev/null
+Subproject commit b2fd5e955c0f910fdd1abf89626cfe324efbfe90
--- /dev/null
+Subproject commit 39b52e8cfa889d55008f2edd0933d6b421556c34
-Subproject commit b3c245ba9b111462f2fff6178b08b486cd553f1b
+Subproject commit 61292ea8a973ba03f93ebf4acde705071e15ccaf
--- /dev/null
+Subproject commit 97d2850af13c339369093b78abe5265845d78220
#! /bin/bash
-./DragonblocksServer 4000 & echo "singleplayer" | ./Dragonblocks localhost 4000; killall DragonblocksServer
+./DragonblocksServer "[::1]:4000" & echo "singleplayer" | ./Dragonblocks "[::1]:4000"; killall DragonblocksServer
cd .build/
mkdir build
cd build
-if ! (cmake -B . -S ../src -DCMAKE_BUILD_TYPE=Release && make clean && make -j$(nproc)); then
+if ! (cmake -B . -S ../src -DCMAKE_BUILD_TYPE=Release -DRESSOURCE_PATH="" && make clean && make -j$(nproc)); then
cd ../..
rm -rf .build
exit 1
cmake_minimum_required(VERSION 3.12)
-
project(Dragonblocks)
+# Variables
+
+set(DEPS_DIR "${CMAKE_SOURCE_DIR}/../deps/")
+
if(NOT CMAKE_BUILD_TYPE)
- set(CMAKE_BUILD_TYPE Debug)
+ set(CMAKE_BUILD_TYPE Debug)
endif()
+if(NOT RESSOURCE_PATH)
+ set(RESSOURCE_PATH "../")
+endif()
+
+# Dependencies
+
find_package(OpenGL REQUIRED)
find_package(GLEW REQUIRED)
find_package(glfw3 3.3 REQUIRED)
find_package(Freetype REQUIRED)
+# Options
+
+add_compile_definitions("RESSOURCE_PATH=\"${RESSOURCE_PATH}\"")
+add_compile_definitions("USE_DRAGONNET")
+
+add_compile_options(-Wall -Wextra -Werror -fmax-errors=4)
+
link_libraries(
pthread
m
z
)
-set(DEPS_DIR "${CMAKE_SOURCE_DIR}/../deps/")
+include_directories(SYSTEM ${DEPS_DIR})
+include_directories(BEFORE ${CMAKE_SOURCE_DIR})
-include_directories(SYSTEM
- ${DEPS_DIR}
-)
-
-include_directories(BEFORE
- ${CMAKE_SOURCE_DIR}
-)
+# System specific options
if ("${CMAKE_SYSTEM_NAME}" STREQUAL "FreeBSD" OR "${CMAKE_SYSTEM_NAME}" STREQUAL "OpenBSD")
link_directories("/usr/local/lib")
include_directories("/usr/X11R6/include")
endif()
-add_compile_options(-Wall -Wextra -Werror)
+# Common sources
-set(DEPS_SOURCES
- "${DEPS_DIR}/dragontype/array.c"
- "${DEPS_DIR}/dragontype/bintree.c"
- "${DEPS_DIR}/dragontype/list.c"
- "${DEPS_DIR}/dragontype/number.c"
- "${DEPS_DIR}/dragontype/queue.c"
+set(COMMON_SOURCES
+ "${DEPS_DIR}/dragonnet/addr.c"
+ "${DEPS_DIR}/dragonnet/listen.c"
+ "${DEPS_DIR}/dragonnet/peer.c"
+ "${DEPS_DIR}/dragonnet/recv.c"
+ "${DEPS_DIR}/dragonnet/recv_thread.c"
+ "${DEPS_DIR}/dragonnet/send.c"
"${DEPS_DIR}/dragonport/asprintf.c"
+ "${DEPS_DIR}/dragonstd/array.c"
+ "${DEPS_DIR}/dragonstd/bintree.c"
+ "${DEPS_DIR}/dragonstd/list.c"
+ "${DEPS_DIR}/dragonstd/queue.c"
+ "${DEPS_DIR}/linenoise/linenoise.c"
"${DEPS_DIR}/perlin/perlin.c"
-)
-
-set(COMMON_SOURCES
- ${DEPS_SOURCES}
config.c
day.c
environment.c
node.c
perlin.c
signal_handlers.c
+ types.c
util.c
)
+# Client
+
add_executable(Dragonblocks
${COMMON_SOURCES}
client/blockmesh.c
client/camera.c
client/client.c
- client/client_commands.c
+ client/client_auth.c
client/client_config.c
client/client_map.c
client/client_node.c
${FREETYPE_INCLUDE_DIRS}
)
+# Server
+
add_executable(DragonblocksServer
${COMMON_SOURCES}
server/biomes.c
server/database.c
server/mapgen.c
server/server.c
- server/server_commands.c
server/server_config.c
server/server_map.c
+ server/server_player.c
server/trees.c
server/voxelctx.c
)
sqlite3
)
-add_custom_target(version
+# Version
+
+add_custom_target(Version
COMMAND ${CMAKE_COMMAND} -DSOURCE_DIR=${CMAKE_SOURCE_DIR} -P ${CMAKE_SOURCE_DIR}/version.cmake
)
-add_dependencies(Dragonblocks version)
-add_dependencies(DragonblocksServer version)
+add_dependencies(Dragonblocks Version)
+add_dependencies(DragonblocksServer Version)
-if (CMAKE_BUILD_TYPE STREQUAL "Release")
- add_compile_definitions(RELEASE)
-endif()
+# Types
+
+add_custom_command(
+ OUTPUT "${CMAKE_SOURCE_DIR}/types.c" "${CMAKE_SOURCE_DIR}/types.h"
+ COMMAND "${CMAKE_SOURCE_DIR}/mktypes.sh"
+ MAIN_DEPENDENCY "${CMAKE_SOURCE_DIR}/types.def"
+ WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
+)
+
+add_custom_target(DragonnetTypes
+ DEPENDS "${CMAKE_SOURCE_DIR}/types.c" "${CMAKE_SOURCE_DIR}/types.h"
+)
+
+add_dependencies(Dragonblocks DragonnetTypes)
+add_dependencies(DragonblocksServer DragonnetTypes)
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <linmath.h/linmath.h>
-#include <dragontype/number.h>
+#include "types.h"
extern struct Camera
{
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
-#include <errno.h>
-#include <netdb.h>
#include "client/client.h"
+#include "client/client_auth.h"
#include "client/client_map.h"
#include "client/client_player.h"
#include "client/game.h"
#include "client/input.h"
+#include "day.h"
#include "signal_handlers.h"
+#include "perlin.h"
+#include "types.h"
#include "util.h"
-static Client client;
+DragonnetPeer *client;
-void client_disconnect(bool send, const char *detail)
+static bool finished = false;
+
+static bool on_recv(unused DragonnetPeer *peer, DragonnetTypeId type, unused void *pkt)
{
- 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);
+ while (client_auth.state == AUTH_INIT)
+ ;
+
+ return (client_auth.state == AUTH_WAIT) == (type == DRAGONNET_TYPE_ToClientAuth);
}
-void client_send_position(v3f64 pos)
+static void on_disconnect(unused DragonnetPeer *peer)
{
- pthread_mutex_lock(&client.mtx);
- (void) (write_u32(client.fd, SC_POS) && write_v3f64(client.fd, pos));
- pthread_mutex_unlock(&client.mtx);
+ interrupted = true;
+ while (! finished)
+ ;
}
-#include "network.c"
-
-static void *reciever_thread(unused void *arg)
+static void on_ToClientAuth(unused DragonnetPeer *peer, ToClientAuth *pkt)
{
- handle_packets(&client);
+ if (pkt->success) {
+ client_auth.state = AUTH_SUCCESS;
+ printf("Authenticated successfully\n");
+ } else {
+ client_auth.state = AUTH_INIT;
+ printf("Authentication failed, please try again\n");
+ }
+}
- if (errno != EINTR)
- client_disconnect(false, "network error");
+static void on_ToClientBlock(unused DragonnetPeer *peer, ToClientBlock *pkt)
+{
+ MapBlock *block = map_get_block(client_map.map, pkt->pos, true);
- return NULL;
+ map_deserialize_block(block, pkt->data);
+ client_map_block_received(block);
}
-static bool client_name_prompt()
+static void on_ToClientInfo(unused DragonnetPeer *peer, ToClientInfo *pkt)
{
- printf("Enter name: ");
- fflush(stdout);
- char name[PLAYER_NAME_MAX];
- if (scanf("%s", name) == EOF)
- return false;
- client.name = strdup(name);
- pthread_mutex_lock(&client.mtx);
- if (write_u32(client.fd, SC_AUTH) && write(client.fd, client.name, strlen(client.name) + 1)) {
- client.state = CS_AUTH;
- printf("Authenticating...\n");
- }
- pthread_mutex_unlock(&client.mtx);
- return true;
+ client_map_set_simulation_distance(pkt->simulation_distance);
+ seed = pkt->seed;
}
-static bool client_authenticate()
+static void on_ToClientPos(unused DragonnetPeer *peer, ToClientPos *pkt)
{
- for ever {
- switch (client.state) {
- case CS_CREATED:
- if (client_name_prompt())
- break;
- else
- return false;
- case CS_AUTH:
- if (interrupted)
- return false;
- else
- sched_yield();
- break;
- case CS_ACTIVE:
- return true;
- case CS_DISCONNECTED:
- return false;
- }
- }
- return false;
+ client_player_set_position(pkt->pos);
}
-static bool client_start(int fd)
+static void on_ToClientTimeOfDay(unused DragonnetPeer *peer, ToClientTimeOfDay *pkt)
{
- client.fd = fd;
- pthread_mutex_init(&client.mtx, NULL);
- client.state = CS_CREATED;
- client.name = NULL;
-
- client_map_init(&client);
- client_player_init();
-
- pthread_t recv_thread;
- pthread_create(&recv_thread, NULL, &reciever_thread, NULL);
-
- bool return_value = client_authenticate() && game(&client);
-
- if (client.state != CS_DISCONNECTED)
- client_disconnect(true, NULL);
-
- if (client.name)
- free(client.name);
-
- pthread_join(recv_thread, NULL);
-
- client_player_deinit();
- client_map_deinit();
-
- pthread_mutex_destroy(&client.mtx);
-
- return return_value;
+ set_time_of_day(pkt->time_of_day);
}
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,
- .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 (argc < 2) {
+ fprintf(stderr, "Missing address\n");
+ return EXIT_FAILURE;
+ }
- if (gai_state != 0)
- internal_error(gai_strerror(gai_state));
+ if (! (client = dragonnet_connect(argv[1]))) {
+ fprintf(stderr, "Failed to connect to server\n");
+ return EXIT_FAILURE;
+ }
- int fd = socket(info->ai_family, info->ai_socktype, info->ai_protocol);
+ client->on_disconnect = &on_disconnect;
+ client->on_recv = &on_recv;
+ client->on_recv_type[DRAGONNET_TYPE_ToClientAuth ] = (void *) &on_ToClientAuth;
+ client->on_recv_type[DRAGONNET_TYPE_ToClientBlock ] = (void *) &on_ToClientBlock;
+ client->on_recv_type[DRAGONNET_TYPE_ToClientInfo ] = (void *) &on_ToClientInfo;
+ client->on_recv_type[DRAGONNET_TYPE_ToClientPos ] = (void *) &on_ToClientPos;
+ client->on_recv_type[DRAGONNET_TYPE_ToClientTimeOfDay] = (void *) &on_ToClientTimeOfDay;
- if (fd == -1)
- syscall_error("socket");
+ signal_handlers_init();
+ client_map_init();
+ client_player_init();
+ dragonnet_peer_run(client);
- if (connect(fd, info->ai_addr, info->ai_addrlen) == -1)
- syscall_error("connect");
+ if (! client_auth_init())
+ return EXIT_FAILURE;
- char *addrstr = address_string((struct sockaddr_in6 *) info->ai_addr);
- printf("Connected to %s\n", addrstr);
- free(addrstr);
+ if (! game())
+ return EXIT_FAILURE;
- freeaddrinfo(info);
+ dragonnet_peer_shutdown(client);
+ client_auth_deinit();
+ client_player_deinit();
+ client_map_deinit();
- signal_handlers_init();
+ pthread_t recv_thread = client->recv_thread;
+ finished = true;
+ pthread_join(recv_thread, NULL);
- return client_start(fd) ? EXIT_SUCCESS : EXIT_FAILURE;
+ return EXIT_SUCCESS;
}
#ifndef _CLIENT_H_
#define _CLIENT_H_
-#include <stdbool.h>
-#include <pthread.h>
-#include <dragontype/number.h>
-#include "client/client_commands.h"
-#include "client/scene.h"
-#include "server/server_commands.h"
-#include "network.h"
+#include <dragonnet/peer.h>
-#ifdef RELEASE
- #define RESSOURCEPATH ""
-#else
- #define RESSOURCEPATH "../"
-#endif
-
-typedef struct Client
-{
- int fd;
- pthread_mutex_t mtx;
- ClientState state;
- char *name;
-} Client;
-
-void client_disconnect(bool send, const char *detail);
-void client_send_position(v3f64 pos);
+extern DragonnetPeer *client;
#endif
--- /dev/null
+#include <stddef.h>
+#include <stdio.h>
+#include <linenoise/linenoise.h>
+#include "client.h"
+#include "client_auth.h"
+#include "signal_handlers.h"
+#include "types.h"
+
+struct ClientAuth client_auth;
+
+static bool name_prompt()
+{
+ if (! (client_auth.name = linenoise("Enter name: ")))
+ return false;
+
+ dragonnet_peer_send_ToServerAuth(client, &(ToServerAuth) {
+ .name = client_auth.name,
+ });
+
+ printf("Authenticating as %s...\n", client_auth.name);
+ client_auth.state = AUTH_WAIT;
+
+ return true;
+}
+
+bool client_auth_init()
+{
+ client_auth.state = AUTH_INIT;
+
+ while (! interrupted) {
+ switch (client_auth.state) {
+ case AUTH_INIT:
+ if (name_prompt())
+ break;
+ else
+ return false;
+
+ case AUTH_WAIT:
+ sched_yield();
+ break;
+
+ case AUTH_SUCCESS:
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void client_auth_deinit()
+{
+ linenoiseFree(client_auth.name);
+}
--- /dev/null
+#ifndef _CLIENT_AUTH_H_
+#define _CLIENT_AUTH_H_
+
+typedef enum
+{
+ AUTH_INIT,
+ AUTH_WAIT,
+ AUTH_SUCCESS,
+} ClientAuthState;
+
+extern struct ClientAuth
+{
+ char *name;
+ ClientAuthState state;
+} client_auth;
+
+bool client_auth_init();
+void client_auth_assert_state(ClientAuthState state, const char *pkt);
+void client_auth_deinit();
+
+#endif
+++ /dev/null
-#include <stdio.h>
-#include <unistd.h>
-#include <dragontype/number.h>
-#include "client/client.h"
-#include "client/client_map.h"
-#include "client/client_player.h"
-#include "day.h"
-#include "perlin.h"
-#include "util.h"
-
-static bool disconnect_handler(unused Client *client, bool good)
-{
- if (good)
- client_disconnect(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)
-{
- v3s32 pos;
-
- if (! read_v3s32(client->fd, &pos))
- return false;
-
- u64 size;
-
- if (! read_u64(client->fd, &size))
- return false;
-
- if (size > 1 << 16)
- return false;
-
- u64 rawsize;
-
- if (! read_u64(client->fd, &rawsize))
- return false;
-
- char data[size];
- if (! read_full(client->fd, data, size))
- return false;
-
- MapBlock *block;
-
- if (good)
- block = map_get_block(client_map.map, pos, true);
- else
- block = map_allocate_block(pos);
-
- bool ret = map_deserialize_block(block, data, size, rawsize);
-
- if (good)
- client_map_block_received(block);
- else
- map_free_block(block);
-
- return ret;
-}
-
-static bool info_handler(Client *client, bool good)
-{
- u32 simulation_distance;
- s32 server_seed;
-
- if (! (read_u32(client->fd, &simulation_distance) && read_s32(client->fd, &server_seed)))
- return false;
-
- if (good) {
- client_map_set_simulation_distance(simulation_distance);
- seed = server_seed;
- }
-
- return true;
-}
-
-static bool setpos_handler(Client *client, bool good)
-{
- v3f64 pos;
-
- if (! read_v3f64(client->fd, &pos))
- return false;
-
- if (good)
- client_player_set_position(pos);
-
- return true;
-}
-
-static bool timeofday_handler(Client *client, bool good)
-{
- u64 time_of_day;
-
- if (! read_u64(client->fd, &time_of_day))
- return false;
-
- if (good)
- set_time_of_day(time_of_day);
-
- 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},
- {&info_handler, "INFO", CS_ACTIVE},
- {&setpos_handler, "SETPOS", CS_ACTIVE},
- {&timeofday_handler, "TIMEOFDAY", CS_ACTIVE},
-};
+++ /dev/null
-#ifndef _CLIENT_COMMANDS_H_
-#define _CLIENT_COMMANDS_H_
-
-typedef enum
-{
- CLIENT_COMMAND_NULL,
- CC_DISCONNECT,
- CC_AUTH,
- CC_BLOCK,
- CC_INFO,
- CC_SETPOS,
- CC_TIMEOFDAY,
- 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
#define MAX_BLOCK_REQUESTS 4
struct ClientMap client_map;
-Client *client;
// meshgen functions
// send block request command to server
static void request_position(v3s32 pos)
{
- pthread_mutex_lock(&client->mtx);
- (void) (write_u32(client->fd, SC_REQUEST_BLOCK) && write_v3s32(client->fd, pos));
- pthread_mutex_unlock(&client->mtx);
+ dragonnet_peer_send_ToServerRequestBlock(client, &(ToServerRequestBlock) {
+ .pos = pos
+ });
}
// mapblock synchronisation step
// public functions
// ClientMap singleton constructor
-void client_map_init(Client *cli)
+void client_map_init()
{
- client = cli;
-
client_map.map = map_create((MapCallbacks) {
.create_block = &on_create_block,
.delete_block = &on_delete_block,
#include <stdbool.h>
#include <pthread.h>
-#include <dragontype/queue.h>
+#include <dragonstd/queue.h>
#include "map.h"
#include "client/object.h"
#define NUM_MESHGEN_THREADS 4
hum_max = 0.33f;
temp_max = 0.45f;
- f32 temp_f = clamp(0.3f - get_temperature(pos), 0.0f, 0.3f) / 0.3f;
+ f32 temp_f = f64_clamp(0.3f - get_temperature(pos), 0.0f, 0.3f) / 0.3f;
vertex->color = hsl_to_rgb((v3f32) {(get_humidity(pos) * (hum_max - hum_min) + hum_min) * (1.0f - temp_f) + temp_max * temp_f, 1.0f, 0.5f});
}
ClientNodeDefinition client_node_definitions[NODE_UNLOADED] = {
// unknown
{
- .tiles = TILES_SIMPLE(RESSOURCEPATH "textures/unknown.png"),
+ .tiles = TILES_SIMPLE(RESSOURCE_PATH "textures/unknown.png"),
.visibility = NV_SOLID,
.mipmap = true,
.render = NULL,
},
// grass
{
- .tiles = TILES_SIMPLE(RESSOURCEPATH "textures/grass.png"),
+ .tiles = TILES_SIMPLE(RESSOURCE_PATH "textures/grass.png"),
.visibility = NV_SOLID,
.mipmap = true,
.render = &render_grass,
},
// dirt
{
- .tiles = TILES_SIMPLE(RESSOURCEPATH "textures/dirt.png"),
+ .tiles = TILES_SIMPLE(RESSOURCE_PATH "textures/dirt.png"),
.visibility = NV_SOLID,
.mipmap = true,
.render = NULL,
},
// stone
{
- .tiles = TILES_SIMPLE(RESSOURCEPATH "textures/stone.png"),
+ .tiles = TILES_SIMPLE(RESSOURCE_PATH "textures/stone.png"),
.visibility = NV_SOLID,
.mipmap = true,
.render = &render_stone,
},
// snow
{
- .tiles = TILES_SIMPLE(RESSOURCEPATH "textures/snow.png"),
+ .tiles = TILES_SIMPLE(RESSOURCE_PATH "textures/snow.png"),
.visibility = NV_SOLID,
.mipmap = true,
.render = NULL,
// oak wood
{
.tiles = {
- .paths = {RESSOURCEPATH "textures/oak_wood.png", RESSOURCEPATH "textures/oak_wood_top.png", NULL, NULL, NULL, NULL},
+ .paths = {RESSOURCE_PATH "textures/oak_wood.png", RESSOURCE_PATH "textures/oak_wood_top.png", NULL, NULL, NULL, NULL},
.indices = {0, 0, 0, 0, 1, 1},
.textures = {NULL},
},
},
// oak leaves
{
- .tiles = TILES_SIMPLE(RESSOURCEPATH "textures/oak_leaves.png"),
+ .tiles = TILES_SIMPLE(RESSOURCE_PATH "textures/oak_leaves.png"),
.visibility = NV_SOLID,
.mipmap = true,
.render = &render_hsl,
// pine wood
{
.tiles = {
- .paths = {RESSOURCEPATH "textures/pine_wood.png", RESSOURCEPATH "textures/pine_wood_top.png", NULL, NULL, NULL, NULL},
+ .paths = {RESSOURCE_PATH "textures/pine_wood.png", RESSOURCE_PATH "textures/pine_wood_top.png", NULL, NULL, NULL, NULL},
.indices = {0, 0, 0, 0, 1, 1},
.textures = {NULL},
},
},
// pine leaves
{
- .tiles = TILES_SIMPLE(RESSOURCEPATH "textures/pine_leaves.png"),
+ .tiles = TILES_SIMPLE(RESSOURCE_PATH "textures/pine_leaves.png"),
.visibility = NV_CLIP,
.mipmap = true,
.render = &render_hsl,
// palm wood
{
.tiles = {
- .paths = {RESSOURCEPATH "textures/palm_wood.png", RESSOURCEPATH "textures/palm_wood_top.png", NULL, NULL, NULL, NULL},
+ .paths = {RESSOURCE_PATH "textures/palm_wood.png", RESSOURCE_PATH "textures/palm_wood_top.png", NULL, NULL, NULL, NULL},
.indices = {0, 0, 0, 0, 1, 1},
.textures = {NULL},
},
},
// palm leaves
{
- .tiles = TILES_SIMPLE(RESSOURCEPATH "textures/palm_leaves.png"),
+ .tiles = TILES_SIMPLE(RESSOURCE_PATH "textures/palm_leaves.png"),
.visibility = NV_SOLID,
.mipmap = true,
.render = &render_hsl,
},
// sand
{
- .tiles = TILES_SIMPLE(RESSOURCEPATH "textures/sand.png"),
+ .tiles = TILES_SIMPLE(RESSOURCE_PATH "textures/sand.png"),
.visibility = NV_SOLID,
.mipmap = true,
.render = NULL,
},
// water
{
- .tiles = TILES_SIMPLE(RESSOURCEPATH "textures/water.png"),
+ .tiles = TILES_SIMPLE(RESSOURCE_PATH "textures/water.png"),
.visibility = NV_BLEND,
.mipmap = true,
.render = NULL,
},
// lava
{
- .tiles = TILES_SIMPLE(RESSOURCEPATH "textures/lava.png"),
+ .tiles = TILES_SIMPLE(RESSOURCE_PATH "textures/lava.png"),
.visibility = NV_BLEND,
.mipmap = true,
.render = NULL,
},
// vulcano_stone
{
- .tiles = TILES_SIMPLE(RESSOURCEPATH "textures/vulcano_stone.png"),
+ .tiles = TILES_SIMPLE(RESSOURCE_PATH "textures/vulcano_stone.png"),
.visibility = NV_SOLID,
.mipmap = true,
.render = NULL,
static void update_pos()
{
camera_set_position((v3f32) {client_player.pos.x, client_player.pos.y + client_player.eye_height, client_player.pos.z});
- client_send_position(client_player.pos);
+ dragonnet_peer_send_ToServerPos(client, &(ToServerPos) {
+ .pos = client_player.pos,
+ });
client_player.obj->pos = (v3f32) {client_player.pos.x, client_player.pos.y, client_player.pos.z};
object_transform(client_player.obj);
client_player.obj->scale = (v3f32) {0.6, 1.75, 0.6};
client_player.obj->visible = false;
- object_set_texture(client_player.obj, texture_load(RESSOURCEPATH "textures/player.png", true));
+ object_set_texture(client_player.obj, texture_load(RESSOURCE_PATH "textures/player.png", true));
for (int f = 0; f < 6; f++) {
for (int v = 0; v < 6; v++) {
} \
} \
} \
- a ## _physics_done: (void) 0;\
+ a ## _physics_done: (void) 0; \
}
PHYSICS(x, y, z)
#define _CLIENT_PLAYER_H_
#include <pthread.h>
-#include <dragontype/number.h>
#include "client/client.h"
#include "client/object.h"
+#include "types.h"
extern struct ClientPlayer
{
#include <stdlib.h>
-#include <dragontype/array.h>
+#include <dragonstd/array.h>
#include "client/facecache.h"
static struct
#define _FACECACHE_H_
#include <pthread.h>
-#include <dragontype/number.h>
+#include "types.h"
v3s32 facecache_face(size_t i, v3s32 *base);
size_t facecache_count(u32 size);
#include FT_FREETYPE_H
#include "client/client.h"
#include "client/font.h"
+#include "client/texture.h"
#define NUM_CHARS 128
-
typedef struct
{
Texture *texture;
return false;
}
- if (FT_New_Face(font.library, RESSOURCEPATH "fonts/Minecraftia.ttf", 0, &font.face)) {
+ if (FT_New_Face(font.library, RESSOURCE_PATH "fonts/Minecraftia.ttf", 0, &font.face)) {
fprintf(stderr, "Failed to load Minecraftia.ttf\n");
return false;
}
#include <stdbool.h>
#include <stddef.h>
#include "client/mesh.h"
+#include "types.h"
typedef struct
{
#include <stdbool.h>
#include <linmath.h/linmath.h>
-#include <dragontype/number.h>
+#include "types.h"
void frustum_update(mat4x4 view_proj);
bool frustum_is_visible(aabb3f32 box);
#include "client/font.h"
#include "client/gui.h"
#include "client/input.h"
+#include "client/scene.h"
#include "client/sky.h"
#include "client/window.h"
#include "day.h"
.scale_type = GST_IMAGE,
.affect_parent_scale = false,
.text = NULL,
- .image = texture_load(RESSOURCEPATH "textures/crosshair.png", false),
+ .image = texture_load(RESSOURCE_PATH "textures/crosshair.png", false),
.text_color = (v4f32) {0.0f, 0.0f, 0.0f, 0.0f},
.bg_color = (v4f32) {0.0f, 0.0f, 0.0f, 0.0f},
});
gui_render();
}
-static void game_loop(Client *client)
+static void game_loop()
{
f64 fps_update_timer = 1.0f;
int frames = 0;
struct timespec ts, ts_old;
clock_gettime(CLOCK_REALTIME, &ts_old);
- while (! glfwWindowShouldClose(window.handle) && client->state != CS_DISCONNECTED && ! interrupted) {
+ while (! glfwWindowShouldClose(window.handle) && ! interrupted) {
clock_gettime(CLOCK_REALTIME, &ts);
f64 dtime = (f64) (ts.tv_sec - ts_old.tv_sec) + (f64) (ts.tv_nsec - ts_old.tv_nsec) / 1.0e9;
ts_old = ts;
}
}
-bool game(Client *client)
+bool game()
{
window_width = 1250;
window_height = 750;
client_player_add_to_scene();
- game_loop(client);
+ game_loop();
client_map_stop();
#ifndef _GAME_H_
#define _GAME_H_
-#include "client/client.h"
-
-bool game(Client *client);
+bool game();
char *take_screenshot();
void game_on_resize(int width, int height);
{
// initialize background pipeline
- if (! shader_program_create(RESSOURCEPATH "shaders/gui/background", &gui.background_prog, NULL)) {
+ if (! shader_program_create(RESSOURCE_PATH "shaders/gui/background", &gui.background_prog, NULL)) {
fprintf(stderr, "Failed to create GUI background shader program\n");
return false;
}
// initialize image pipeline
- if (! shader_program_create(RESSOURCEPATH "shaders/gui/image", &gui.image_prog, NULL)) {
+ if (! shader_program_create(RESSOURCE_PATH "shaders/gui/image", &gui.image_prog, NULL)) {
fprintf(stderr, "Failed to create GUI image shader program\n");
return false;
}
// initialize font pipeline
- if (! shader_program_create(RESSOURCEPATH "shaders/gui/font", &gui.font_prog, NULL)) {
+ if (! shader_program_create(RESSOURCE_PATH "shaders/gui/font", &gui.font_prog, NULL)) {
fprintf(stderr, "Failed to create GUI font shader program\n");
return false;
}
#include <stdbool.h>
#include <linmath.h/linmath.h>
-#include <dragontype/bintree.h>
-#include <dragontype/list.h>
-#include <dragontype/number.h>
+#include <dragonstd/bintree.h>
+#include <dragonstd/list.h>
#include "client/font.h"
#include "client/texture.h"
+#include "types.h"
typedef enum
{
client_player.pitch -= (f32) delta_y * M_PI / 180.0f / 8.0f;
client_player.yaw = fmod(client_player.yaw + M_PI * 2.0f, M_PI * 2.0f);
- client_player.pitch = fmax(fmin(client_player.pitch, M_PI / 2.0f - 0.01f), -M_PI / 2.0f + 0.01f);
+ client_player.pitch = f32_clamp(client_player.pitch, -M_PI / 2.0f + 0.01f, M_PI / 2.0f - 0.01f);
camera_set_angle(client_player.yaw, client_player.pitch);
#ifndef _INPUT_H_
#define _INPUT_H_
-#include <dragontype/number.h>
+#include "types.h"
void input_tick(f64 dtime);
void input_init();
#include <GL/glew.h>
#include <GL/gl.h>
#include <linmath.h/linmath.h>
-#include <dragontype/array.h>
-#include <dragontype/number.h>
+#include <dragonstd/array.h>
#include "client/mesh.h"
#include "client/texture.h"
#include "client/vertex.h"
+#include "types.h"
typedef struct {
GLfloat x, y, z;
client_config.render_distance
);
- if (! shader_program_create(RESSOURCEPATH "shaders/3d", &scene.prog, shader_defs)) {
+ if (! shader_program_create(RESSOURCE_PATH "shaders/3d", &scene.prog, shader_defs)) {
fprintf(stderr, "Failed to create 3D shader program\n");
return false;
}
#include <stdbool.h>
#include <pthread.h>
#include <linmath.h/linmath.h>
-#include <dragontype/bintree.h>
-#include <dragontype/list.h>
-#include <dragontype/number.h>
+#include <dragonstd/bintree.h>
+#include <dragonstd/list.h>
+#include "types.h"
#include "client/object.h"
extern struct Scene
{
// skybox
- if (! shader_program_create(RESSOURCEPATH "shaders/sky/skybox", &sky.skybox_prog, NULL)) {
+ if (! shader_program_create(RESSOURCE_PATH "shaders/sky/skybox", &sky.skybox_prog, NULL)) {
fprintf(stderr, "Failed to create skybox shader program\n");
return false;
}
sky.skybox_loc_VP = glGetUniformLocation(sky.skybox_prog, "VP");
sky.skybox_loc_daylight = glGetUniformLocation(sky.skybox_prog, "daylight");
- sky.skybox_textures[0] = texture_create_cubemap(RESSOURCEPATH "textures/skybox/day");
- sky.skybox_textures[1] = texture_create_cubemap(RESSOURCEPATH "textures/skybox/night");
+ sky.skybox_textures[0] = texture_create_cubemap(RESSOURCE_PATH "textures/skybox/day");
+ sky.skybox_textures[1] = texture_create_cubemap(RESSOURCE_PATH "textures/skybox/night");
GLint texture_indices[2];
for (GLint i = 0; i < 2; i++)
// sun
- if (! shader_program_create(RESSOURCEPATH "shaders/sky/sun", &sky.sun_prog, NULL)) {
+ if (! shader_program_create(RESSOURCE_PATH "shaders/sky/sun", &sky.sun_prog, NULL)) {
fprintf(stderr, "Failed to create sun shader program\n");
return false;
}
sky.sun_loc_MVP = glGetUniformLocation(sky.sun_prog, "MVP");
- sky.sun_texture = texture_load(RESSOURCEPATH "textures/sun.png", false);
+ sky.sun_texture = texture_load(RESSOURCE_PATH "textures/sun.png", false);
sky.sun_mesh = mesh_create();
sky.sun_mesh->textures = &sky.sun_texture->id;
// clouds
- if (! shader_program_create(RESSOURCEPATH "shaders/sky/clouds", &sky.clouds_prog, NULL)) {
+ if (! shader_program_create(RESSOURCE_PATH "shaders/sky/clouds", &sky.clouds_prog, NULL)) {
fprintf(stderr, "Failed to create clouds shader program\n");
return false;
}
#define STB_IMAGE_IMPLEMENTATION
#include <stb/stb_image.h>
#include <stdbool.h>
-#include <dragontype/list.h>
+#include <dragonstd/list.h>
#include "client/client_config.h"
#include "client/texture.h"
#include "util.h"
f64 get_daylight()
{
- return clamp(cos(get_sun_angle()) * 2.0 + 0.5, 0.0, 1.0);
+ return f64_clamp(cos(get_sun_angle()) * 2.0 + 0.5, 0.0, 1.0);
}
void split_time_of_day(int *hours, int *minutes)
#define _DAY_H_
#include <stdbool.h>
-#include <dragontype/number.h>
+#include "types.h"
#define MINUTES_PER_HOUR 60
#define HOURS_PER_DAY 24
#define MINUTES_PER_DAY (HOURS_PER_DAY * MINUTES_PER_HOUR)
"
echo "$COMMON
-run ::1 4000 < $DEBUG_DIR/name
+run \"[::1]:4000\" < $DEBUG_DIR/name
" > $DEBUG_DIR/client_script
echo "$COMMON
set print thread-events off
-run 4000
+run \"[::1]:4000\"
" > $DEBUG_DIR/server_script
kitty --detach -e bash -c "
#ifndef _ENVIRONMENT_H_
#define _ENVIRONMENT_H_
-#include <dragontype/number.h>
+#include "types.h"
f64 get_humidity(v3s32 pos);
f64 get_temperature(v3s32 pos);
#include <stdbool.h>
#include <unistd.h>
#include <math.h>
-#include <endian.h/endian.h>
#include <string.h>
#include "map.h"
#include "util.h"
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&block->mtx, &attr);
- ITERATE_MAPBLOCK block->data[x][y][z] = map_node_create(NODE_UNKNOWN, NULL, 0);
+ ITERATE_MAPBLOCK block->data[x][y][z] = map_node_create(NODE_UNKNOWN, (Blob) {0, NULL});
return block;
}
free(block);
}
-void map_serialize_block(MapBlock *block, char **dataptr, size_t *sizeptr, size_t *rawsizeptr)
+Blob map_serialize_block(MapBlock *block)
{
- unsigned char *uncompressed = NULL;
- size_t uncompressed_size = 0;
+ SerializedMapBlock block_data;
ITERATE_MAPBLOCK {
- MapNode node = block->data[x][y][z];
- NodeDefinition *def = &node_definitions[node.type];
+ MapNode *node = &block->data[x][y][z];
+ SerializedMapNode *node_data = &block_data.raw.nodes[x][y][z];
- u32 type = htobe32(node.type);
- buffer_write(&uncompressed, &uncompressed_size, &type, sizeof(u32));
+ *node_data = (SerializedMapNode) {
+ .type = node->type,
+ .data = {
+ .siz = 0,
+ .data = NULL,
+ },
+ };
- unsigned char *data_buffer = NULL;
- size_t data_bufsiz = 0;
+ NodeDefinition *def = &node_definitions[node->type];
if (def->serialize)
- def->serialize(&node, &data_buffer, &data_bufsiz);
-
- u16 data_size = htobe16(data_bufsiz);
- buffer_write(&uncompressed, &uncompressed_size, &data_size, sizeof(u16));
- buffer_write(&uncompressed, &uncompressed_size, data_buffer, data_bufsiz);
-
- if (data_buffer)
- free(data_buffer);
+ def->serialize(&node_data->data, node->data);
}
- my_compress(uncompressed, uncompressed_size, dataptr, sizeptr);
- *rawsizeptr = uncompressed_size;
+ Blob buffer = {0, NULL};
+ SerializedMapBlock_write(&buffer, &block_data);
+ SerializedMapBlock_free(&block_data);
- if (uncompressed)
- free(uncompressed);
+ return buffer;
}
-bool map_deserialize_block(MapBlock *block, const char *data, size_t size, size_t rawsize)
+bool map_deserialize_block(MapBlock *block, Blob buffer)
{
- unsigned char decompressed[rawsize];
- size_t decompressed_size = rawsize;
-
- if (! my_decompress(data, size, decompressed, decompressed_size))
- return false;
-
- unsigned char *ptr = decompressed;
-
- ITERATE_MAPBLOCK {
- // node type
- u32 *type_ptr = buffer_read(&ptr, &decompressed_size, sizeof(u32));
-
- if (! type_ptr)
- return false;
+ // it's important to copy Blobs that have been malloc'd before reading from them
+ // because reading from a Blob modifies its data and size pointer,
+ // but does not free anything
+ SerializedMapBlock block_data = {0};
+ bool success = SerializedMapBlock_read(&buffer, &block_data);
- u32 type = be32toh(*type_ptr);
-
- // data size
- u16 *data_size_ptr = buffer_read(&ptr, &decompressed_size, sizeof(u16));
-
- if (! data_size_ptr)
- return false;
-
- u16 data_size = be16toh(*data_size_ptr);
-
- // data
- void *data = buffer_read(&ptr, &decompressed_size, data_size);
-
- if (! data && data_size)
- return false;
-
- // set node
- block->data[x][y][z] = map_node_create(type, data, data_size);
- }
+ if (success) ITERATE_MAPBLOCK
+ block->data[x][y][z] = map_node_create(block_data.raw.nodes[x][y][z].type, block_data.raw.nodes[x][y][z].data);
- return true;
+ SerializedMapBlock_free(&block_data);
+ return success;
}
v3s32 map_node_to_block_pos(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, NULL, 0);
+ return map_node_create(NODE_UNLOADED, (Blob) {0, NULL});
return block->data[offset.x][offset.y][offset.z];
}
}
}
-MapNode map_node_create(Node type, void *data, size_t size)
+MapNode map_node_create(Node type, Blob buffer)
{
if (type >= NODE_UNLOADED)
type = NODE_UNKNOWN;
if (def->create)
def->create(&node);
- if (def->deserialize && size)
- def->deserialize(&node, data, size);
+ if (def->deserialize)
+ def->deserialize(&buffer, node.data);
return node;
}
#include <stdbool.h>
#include <pthread.h>
-#include <dragontype/bintree.h>
-#include <dragontype/number.h>
-#include <dragontype/list.h>
+#include <dragonstd/bintree.h>
+#include <dragonstd/list.h>
+#include "types.h"
#include "node.h"
-#define MAPBLOCK_SIZE 16
#define ITERATE_MAPBLOCK for (u8 x = 0; x < MAPBLOCK_SIZE; x++) for (u8 y = 0; y < MAPBLOCK_SIZE; y++) for (u8 z = 0; z < MAPBLOCK_SIZE; z++)
typedef struct MapNode
MapBlock *map_allocate_block(v3s32 pos);
void map_free_block(MapBlock *block);
-void map_serialize_block(MapBlock *block, char **dataptr, size_t *sizeptr, size_t *rawsizeptr);
-bool map_deserialize_block(MapBlock *block, const char *data, size_t size, size_t rawsize);
+Blob map_serialize_block(MapBlock *block);
+bool map_deserialize_block(MapBlock *block, Blob buffer);
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, bool create, void *arg);
-MapNode map_node_create(Node type, void *data, size_t size);
+MapNode map_node_create(Node type, Blob buffer);
void map_node_delete(MapNode node);
#endif
--- /dev/null
+#! /bin/sh
+LUA_PATH="../deps/dragontype/?.lua;../deps/dragontype/?/init.lua" "../deps/dragontype/typegen.lua"
+
+++ /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 bool handle_packets(Client *client) {
- while (client->state != CS_DISCONNECTED || ! interrupted) {
- struct pollfd pfd = {
- .fd = client->fd,
- .events = POLLIN,
- .revents = 0,
- };
-
- int pstate = poll(&pfd, 1, 0);
-
- if (pstate == -1) {
- perror("poll");
- break;
- }
-
- if (pstate == 0) {
- sched_yield();
- continue;
- }
-
- if (! (pfd.revents & POLLIN))
- return false;
-
- HostCommand command;
- if (! read_u32(client->fd, &command))
- break;
-
- 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("Received %s command, but client is in invalid state: %d\n", handler->name, client->state);
- if (! handler->func(client, good))
- break;
- } else {
- printf("Received invalid command %d\n", command);
- }
- }
-
- return client->state == CS_DISCONNECTED || errno == EINTR;
-}
+++ /dev/null
-#ifndef _NETWORK_H_
-#define _NETWORK_H_
-
-#include <stdbool.h>
-#define PLAYER_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
+#include "types.h"
#include "map.h"
#include "node.h"
#include "util.h"
#include <stdio.h>
-static void serialize_hsl(MapNode *node, unsigned char **buffer, size_t *bufsiz)
-{
- HSLData *node_data = node->data;
- buffer_write(buffer, bufsiz, (f32 []) {node_data->color.x, node_data->color.y, node_data->color.z}, sizeof(f32) * 3);
-}
-
-static void deserialize_hsl(MapNode *node, unsigned char *data, size_t size)
-{
- HSLData *node_data = node->data;
-
- f32 *color = buffer_read(&data, &size, sizeof(f32) * 3);
-
- if (! color)
- return;
-
- *node_data = (HSLData) {.color = {color[0], color[1], color[2]}};
-}
-
NodeDefinition node_definitions[NODE_UNLOADED] = {
// unknown
{
.data_size = sizeof(HSLData),
.create = NULL,
.delete = NULL,
- .serialize = &serialize_hsl,
- .deserialize = &deserialize_hsl,
+ .serialize = (void *) &HSLData_write,
+ .deserialize = (void *) &HSLData_read,
},
// oak leaves
{
.data_size = sizeof(HSLData),
.create = NULL,
.delete = NULL,
- .serialize = &serialize_hsl,
- .deserialize = &deserialize_hsl,
+ .serialize = (void *) &HSLData_write,
+ .deserialize = (void *) &HSLData_read,
},
// pine wood
{
.data_size = sizeof(HSLData),
.create = NULL,
.delete = NULL,
- .serialize = &serialize_hsl,
- .deserialize = &deserialize_hsl,
+ .serialize = (void *) &HSLData_write,
+ .deserialize = (void *) &HSLData_read,
},
// pine leaves
{
.data_size = sizeof(HSLData),
.create = NULL,
.delete = NULL,
- .serialize = &serialize_hsl,
- .deserialize = &deserialize_hsl,
+ .serialize = (void *) &HSLData_write,
+ .deserialize = (void *) &HSLData_read,
},
// palm wood
{
.data_size = sizeof(HSLData),
.create = NULL,
.delete = NULL,
- .serialize = &serialize_hsl,
- .deserialize = &deserialize_hsl,
+ .serialize = (void *) &HSLData_write,
+ .deserialize = (void *) &HSLData_read,
},
// palm leaves
{
.data_size = sizeof(HSLData),
.create = NULL,
.delete = NULL,
- .serialize = &serialize_hsl,
- .deserialize = &deserialize_hsl,
+ .serialize = (void *) &HSLData_write,
+ .deserialize = (void *) &HSLData_read,
},
// sand
{
#define _NODE_H_
#include <stdbool.h>
-#include <dragontype/number.h>
+#include "types.h"
#define NODE_DEFINITION(type) ((type) < NODE_UNLOADED ? &node_definitions[NODE_UNKNOWN] : &node_definitions[(type)]);
size_t data_size;
void (*create)(struct MapNode *node);
void (*delete)(struct MapNode *node);
- void (*serialize)(struct MapNode *node, unsigned char **buffer, size_t *bufsiz);
- void (*deserialize)(struct MapNode *node, unsigned char *data, size_t size);
+ void (*serialize)(Blob *buffer, void *data);
+ void (*deserialize)(Blob *buffer, void *data);
} NodeDefinition;
-typedef struct {
- v3f32 color;
-} HSLData;
-
extern NodeDefinition node_definitions[];
#endif
#define _PERLIN_H_
#include <perlin/perlin.h>
-#include <dragontype/number.h>
+#include "types.h"
typedef enum
{
return false;
}
-static inline f64 min(f64 a, f64 b)
-{
- return a < b ? a : b;
-}
-
-static inline f64 max(f64 a, f64 b)
-{
- return a > b ? a : b;
-}
-
static f64 distance(v2s32 a, v2s32 b)
{
return sqrt(pow(a.x - b.x, 2) + pow(a.y - b.y, 2));
bool is_crater = vulcano_height > 0;
if (! is_crater)
- vulcano_height = min(vulcano_height + 5.0, 0.0);
+ vulcano_height = f64_min(vulcano_height + 5.0, 0.0);
if (vulcano_height < 0)
vulcano_height *= 2.0;
OceanRowData *rdata = row_data;
s32 ocean_floor = calculate_ocean_floor(factor, height);
- return rdata->vulcano ? max(ocean_floor, rdata->vulcano_height) : ocean_floor;
+ return rdata->vulcano ? f64_max(ocean_floor, rdata->vulcano_height) : ocean_floor;
}
Node ocean_get_node_at(v3s32 pos, s32 diff, void *row_data)
#ifndef _BIOMES_H_
#define _BIOMES_H_
-#include <dragontype/number.h>
#include "map.h"
#include "perlin.h"
+#include "types.h"
typedef enum
{
return NULL;
}
- size_t psize = sizeof(u32) * 3;
- u32 *pos = malloc(psize);
- pos[0] = htobe32(block->pos.x);
- pos[1] = htobe32(block->pos.y);
- pos[2] = htobe32(block->pos.z);
+ Blob buffer = {0, NULL};
+ v3s32_write(&buffer, &block->pos);
- sqlite3_bind_blob(stmt, 1, pos, psize, &free);
+ sqlite3_bind_blob(stmt, 1, buffer.data, buffer.siz, &free);
return stmt;
}
// bind v3f64 to sqlite3 statement
static inline void bind_v3f64(sqlite3_stmt *stmt, int idx, v3f64 pos)
{
- size_t psize = sizeof(f64) * 3;
- f64 *blob = malloc(psize);
- blob[0] = pos.x;
- blob[1] = pos.y;
- blob[2] = pos.z;
- sqlite3_bind_blob(stmt, idx, blob, psize, &free);
+ Blob buffer = {0, NULL};
+ v3f64_write(&buffer, &pos);
+
+ sqlite3_bind_blob(stmt, idx, buffer.data, buffer.siz, &free);
}
// public functions
}
const char *init_stmts[3]= {
- "CREATE TABLE IF NOT EXISTS map (pos BLOB PRIMARY KEY, generated INTEGER, size INTEGER, rawsize INTEGER, data BLOB, mgsb_size INTEGER, mgsb_data BLOB);",
+ "CREATE TABLE IF NOT EXISTS map (pos BLOB PRIMARY KEY, generated INTEGER, data BLOB, mgsb BLOB);",
"CREATE TABLE IF NOT EXISTS meta (key TEXT PRIMARY KEY, value INTEGER);",
"CREATE TABLE IF NOT EXISTS players (name TEXT PRIMARY KEY, pos BLOB);"
};
{
sqlite3_stmt *stmt;
- if (! (stmt = prepare_block_statement(block, "loading", "SELECT generated, size, rawsize, data, mgsb_size, mgsb_data FROM map WHERE pos=?")))
+ if (! (stmt = prepare_block_statement(block, "loading", "SELECT generated, data, mgsb FROM map WHERE pos=?")))
return false;
int rc = sqlite3_step(stmt);
MapBlockExtraData *extra = block->extra;
extra->state = sqlite3_column_int(stmt, 0) ? MBS_READY : MBS_CREATED;
- extra->size = sqlite3_column_int64(stmt, 1);
- extra->rawsize = sqlite3_column_int64(stmt, 2);
- extra->data = malloc(extra->size);
- memcpy(extra->data, sqlite3_column_blob(stmt, 3), extra->size);
-
- MapgenStageBuffer decompressed_mgsb;
- my_decompress(sqlite3_column_blob(stmt, 5), sqlite3_column_int64(stmt, 4), &decompressed_mgsb, sizeof(MapgenStageBuffer));
-
- ITERATE_MAPBLOCK extra->mgs_buffer[x][y][z] = be32toh(decompressed_mgsb[x][y][z]);
+ Blob_read( &(Blob) {sqlite3_column_bytes(stmt, 1), (void *) sqlite3_column_blob(stmt, 1)}, &extra->data);
+ MapgenStageBuffer_read(&(Blob) {sqlite3_column_bytes(stmt, 2), (void *) sqlite3_column_blob(stmt, 2)}, &extra->mgsb);
- if (! map_deserialize_block(block, extra->data, extra->size, extra->rawsize))
- printf("Error with deserializing block at (%d, %d, %d)\n", block->pos.x, block->pos.y, block->pos.z);
+ if (! map_deserialize_block(block, extra->data)) {
+ fprintf(stderr, "Failed to load block at (%d, %d, %d)\n", block->pos.x, block->pos.y, block->pos.z);
+ exit(EXIT_FAILURE);
+ }
} else if (rc != SQLITE_DONE) {
print_block_error(block, "loading");
}
{
sqlite3_stmt *stmt;
- if (! (stmt = prepare_block_statement(block, "saving", "REPLACE INTO map (pos, generated, size, rawsize, data, mgsb_size, mgsb_data) VALUES(?1, ?2, ?3, ?4, ?5, ?6, ?7)")))
+ if (! (stmt = prepare_block_statement(block, "saving", "REPLACE INTO map (pos, generated, data, mgsb) VALUES(?1, ?2, ?3, ?4)")))
return;
MapBlockExtraData *extra = block->extra;
- MapgenStageBuffer uncompressed_mgsb;
- ITERATE_MAPBLOCK uncompressed_mgsb[x][y][z] = htobe32(extra->mgs_buffer[x][y][z]);
-
- char *mgsb_data;
- size_t mgsb_size;
+ Blob data = {0, NULL};
+ Blob_write(&data, &extra->data);
- my_compress(&uncompressed_mgsb, sizeof(MapgenStageBuffer), &mgsb_data, &mgsb_size);
+ Blob mgsb = {0, NULL};
+ MapgenStageBuffer_write(&mgsb, &extra->mgsb);
sqlite3_bind_int(stmt, 2, extra->state > MBS_CREATED);
- sqlite3_bind_int64(stmt, 3, extra->size);
- sqlite3_bind_int64(stmt, 4, extra->rawsize);
- sqlite3_bind_blob(stmt, 5, extra->data, extra->size, SQLITE_TRANSIENT);
- sqlite3_bind_int64(stmt, 6, mgsb_size);
- sqlite3_bind_blob(stmt, 7, mgsb_data, mgsb_size, &free);
+ sqlite3_bind_blob(stmt, 3, data.data, data.siz, &free);
+ sqlite3_bind_blob(stmt, 4, mgsb.data, mgsb.siz, &free);
if (sqlite3_step(stmt) != SQLITE_DONE)
print_block_error(block, "saving");
int rc = sqlite3_step(stmt);
bool found = rc == SQLITE_ROW;
- if (found) {
- const f64 *pos = sqlite3_column_blob(stmt, 0);
- *pos_ptr = (v3f64) {pos[0], pos[1], pos[2]};
- } else if (rc != SQLITE_DONE) {
+ if (found)
+ v3f64_read(&(Blob) {sqlite3_column_bytes(stmt, 0), (void *) sqlite3_column_blob(stmt, 0)}, pos_ptr);
+ else if (rc != SQLITE_DONE)
fprintf(stderr, "Database error with loading player %s: %s\n", name, sqlite3_errmsg(database));
- }
sqlite3_finalize(stmt);
return found;
.mgs = mgs,
.changed_blocks = changed_blocks,
};
+
map_set_node(server_map.map, pos, node, true, &arg);
}
}
pthread_mutex_lock(&block->mtx);
- if (extra->mgs_buffer[x][y][z] <= MGS_TERRAIN) {
- block->data[x][y][z] = map_node_create(node, NULL, 0);
- extra->mgs_buffer[x][y][z] = MGS_TERRAIN;
+ if (extra->mgsb.raw.nodes[x][y][z] <= MGS_TERRAIN) {
+ block->data[x][y][z] = map_node_create(node, (Blob) {0, NULL});
+ extra->mgsb.raw.nodes[x][y][z] = MGS_TERRAIN;
}
pthread_mutex_unlock(&block->mtx);
}
#include <stdio.h>
#include <stdlib.h>
-#include <unistd.h>
-#include <errno.h>
-#include <netdb.h>
+#include <dragonnet/addr.h>
#include "server/database.h"
#include "server/server.h"
#include "server/server_map.h"
+#include "server/server_player.h"
#include "signal_handlers.h"
#include "util.h"
-static Server server;
+DragonnetListener *server;
-// include handle_packets implementation
-
-#include "network.c"
-
-// utility functions
-
-// pthread start routine for reciever thread
-static void *reciever_thread(void *arg)
+static bool on_recv(DragonnetPeer *peer, DragonnetTypeId type, unused void *pkt)
{
- Client *client = arg;
-
- if (! handle_packets(client))
- server_disconnect_client(client, DISCO_NO_SEND | DISCO_NO_JOIN, "network error");
-
- return NULL;
+ return ((ServerPlayer *) peer->extra)->auth != (type == DRAGONNET_TYPE_ToServerAuth);
}
-// accept a new connection, initialize client and start reciever thread
-static void accept_client()
+static void on_ToServerAuth(DragonnetPeer *peer, ToServerAuth *pkt)
{
- struct sockaddr_storage client_address = {0};
- socklen_t client_addrlen = sizeof(client_address);
-
- int fd = accept(server.sockfd, (struct sockaddr *) &client_address, &client_addrlen);
-
- if (fd == -1) {
- if (errno != EINTR)
- perror("accept");
- return;
- }
-
- Client *client = malloc(sizeof(Client));
- client->fd = fd;
- pthread_mutex_init(&client->mtx, NULL);
- client->state = CS_CREATED;
- client->address = address_string((struct sockaddr_in6 *) &client_address);
- client->name = client->address;
- client->server = &server;
- client->pos = (v3f64) {0.0f, 0.0f, 0.0f};
- pthread_create(&client->net_thread, NULL, &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);
+ if (server_player_auth(peer->extra, pkt->name))
+ pkt->name = NULL;
}
-// list_clear_func callback used on server shutdown to disconnect all clients properly
-static void list_disconnect_client(void *key, unused void *value, unused void *arg)
+// set a node on the map
+static void on_ToServerSetnode(unused DragonnetPeer *peer, ToServerSetnode *pkt)
{
- server_disconnect_client(key, DISCO_NO_REMOVE | DISCO_NO_MESSAGE, "");
+ map_set_node(server_map.map, pkt->pos, map_node_create(pkt->node, (Blob) {0, NULL}), false, NULL);
}
-// start up the server after socket was created, then accept connections until interrupted, then shutdown server
-static void server_run(int fd)
+// update player's position
+static void on_ToServerPos(DragonnetPeer *peer, ToServerPos *pkt)
{
- server.sockfd = fd;
- 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);
-
- database_init();
- server_map_init(&server);
- server_map_prepare_spawn();
-
- while (! interrupted)
- accept_client();
-
- printf("Shutting down\n");
-
- pthread_rwlock_wrlock(&server.clients_rwlck);
- list_clear_func(&server.clients, &list_disconnect_client, NULL);
- 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);
-
- server_map_deinit();
- database_deinit();
+ ServerPlayer *player = peer->extra;
- exit(EXIT_SUCCESS);
+ pthread_rwlock_wrlock(&player->pos_lock);
+ player->pos = pkt->pos;
+ database_update_player_pos(player->name, player->pos);
+ pthread_rwlock_unlock(&player->pos_lock);
}
-// public functions
-
-// disconnect a client with various options an an optional detail message (flags: DiscoFlag bitmask)
-void server_disconnect_client(Client *client, int flags, const char *detail)
+// tell server map manager client requested the block
+static void on_ToServerRequestBlock(DragonnetPeer *peer, ToServerRequestBlock *pkt)
{
- client->state = CS_DISCONNECTED;
-
- 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));
-
- if (! (flags & DISCO_NO_SEND))
- send_command(client, CC_DISCONNECT);
-
- pthread_mutex_lock(&client->mtx);
- close(client->fd);
- pthread_mutex_unlock(&client->mtx);
-
- if (! (flags & DISCO_NO_JOIN))
- pthread_join(client->net_thread, NULL);
-
- if (client->name != client->address)
- free(client->name);
- free(client->address);
-
- pthread_mutex_destroy(&client->mtx);
- free(client);
+ server_map_requested_block(peer->extra, pkt->pos);
}
// server entry point
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));
+ if (argc < 2) {
+ fprintf(stderr, "Missing address\n");
+ return EXIT_FAILURE;
+ }
- int fd = socket(info->ai_family, info->ai_socktype, 0);
+ if (! (server = dragonnet_listener_new(argv[1]))) {
+ fprintf(stderr, "Failed to listen to connections\n");
+ return EXIT_FAILURE;
+ }
- if (fd == -1)
- syscall_error("socket");
+ char *address = dragonnet_addr_str(server->laddr);
+ printf("Listening on %s\n", address);
+ free(address);
- int flag = 1;
+ server->on_connect = &server_player_add;
+ server->on_disconnect = &server_player_remove;
+ server->on_recv = &on_recv;
+ server->on_recv_type[DRAGONNET_TYPE_ToServerAuth] = (void *) &on_ToServerAuth;
+ server->on_recv_type[DRAGONNET_TYPE_ToServerSetnode] = (void *) &on_ToServerSetnode;
+ server->on_recv_type[DRAGONNET_TYPE_ToServerPos] = (void *) &on_ToServerPos;
+ server->on_recv_type[DRAGONNET_TYPE_ToServerRequestBlock] = (void *) &on_ToServerRequestBlock;
- if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag)) == -1)
- syscall_error("setsockopt");
+ signal_handlers_init();
- if (bind(fd, info->ai_addr, info->ai_addrlen) == -1)
- syscall_error("bind");
+ server_player_init();
+ database_init();
+ server_map_init();
+ server_map_prepare_spawn();
+ dragonnet_listener_run(server);
- if (listen(fd, 3) == -1)
- syscall_error("listen");
+ while (! interrupted)
+ sched_yield();
- char *addrstr = address_string((struct sockaddr_in6 *) info->ai_addr);
- printf("Listening on %s\n", addrstr);
- free(addrstr);
+ printf("Shutting down\n");
- freeaddrinfo(info);
+ dragonnet_listener_close(server);
+ server_map_deinit();
+ database_deinit();
+ server_player_deinit();
- signal_handlers_init();
- server_run(fd);
+ dragonnet_listener_delete(server);
return EXIT_SUCCESS;
}
#ifndef _SERVER_H_
#define _SERVER_H_
-#include <pthread.h>
-#include <netinet/in.h>
-#include <dragontype/number.h>
-#include <dragontype/list.h>
-#include "client/client_commands.h"
-#include "server/server_commands.h"
-#include "network.h"
+#include <dragonnet/listen.h>
-typedef struct
-{
- int sockfd; // TCP socket to accept new connections
- pthread_rwlock_t clients_rwlck; // lock to protect client list
- List clients; // Client * -> NULL map with all connected clients
- pthread_rwlock_t players_rwlck; // lock to protect player list
- List players; // char * -> Client * map with clients that have finished auth
-} Server;
-
-typedef struct Client
-{
- int fd; // TCP socket for connection
- pthread_mutex_t mtx; // mutex to protect socket
- ClientState state; // state of the client (created, auth, active, disconnected)
- char *address; // address string to use as identifier for log messages until auth is completed
- char *name; // player name (must be unique)
- Server *server; // pointer to server object (essentially the same for all clients)
- pthread_t net_thread; // reciever thread ID
- v3f64 pos; // player position
-} Client;
-
-typedef enum
-{
- DISCO_NO_REMOVE = 0x01, // don't remove from client and player list (to save extra work on server shutdown)
- DISCO_NO_SEND = 0x02, // don't notfiy client about the disconnect (if client sent disconnect themselves or the TCP connection died)
- DISCO_NO_MESSAGE = 0x04, // don't log a message about the disconnect (used on server shutdown)
- DISCO_NO_JOIN = 0x08, // don't wait for the reciever thread to finish (if TCP connection death was reported by reciever thread, the thread is already dead)
-} DiscoFlag;
-
-void server_disconnect_client(Client *client, int flags, const char *detail); // disconnect a client with various options an an optional detail message (flags: DiscoFlag bitmask)
+DragonnetListener *server;
#endif
+++ /dev/null
-#include <stdio.h>
-#include <stdlib.h>
-#include "day.h"
-#include "server/database.h"
-#include "server/server.h"
-#include "server/server_config.h"
-#include "server/server_map.h"
-#include "perlin.h"
-#include "util.h"
-
-// command callbacks
-
-// disconnect client without sending a packet (client won't recieve it)
-static bool disconnect_handler(Client *client, bool good)
-{
- if (good)
- server_disconnect_client(client, DISCO_NO_SEND, NULL);
- return true;
-}
-
-// insert client into player list and update state (if checks pass)
-static bool auth_handler(Client *client, bool good)
-{
- char *name = read_string(client->fd, PLAYER_NAME_MAX);
-
- if (! name)
- return false;
-
- if (! good) {
- free(name);
- return true;
- }
-
- pthread_rwlock_wrlock(&client->server->players_rwlck);
- u8 success = list_put(&client->server->players, name, client);
-
- if (success) {
- client->name = name;
- client->state = CS_ACTIVE;
-
- if (! database_load_player(client->name, &client->pos)) {
- client->pos = (v3f64) {0.0, server_map.spawn_height + 0.5, 0.0};
-
- database_create_player(client->name, client->pos);
- }
- } else {
- free(name);
- }
-
- pthread_mutex_lock(&client->mtx);
- bool ret = write_u32(client->fd, CC_AUTH) && write_u8(client->fd, success);
- if (ret && success)
- ret = ret
- && write_u32(client->fd, CC_INFO) && write_u32(client->fd, server_config.simulation_distance) && write_s32(client->fd, seed)
- && write_u32(client->fd, CC_SETPOS) && write_v3f64(client->fd, client->pos)
- && write_u32(client->fd, CC_TIMEOFDAY) && write_u64(client->fd, (u64) get_time_of_day());
- pthread_mutex_unlock(&client->mtx);
-
- pthread_rwlock_unlock(&client->server->players_rwlck);
-
- printf("Authentication %s: %s -> %s\n", success ? "success" : "failure", client->address, name);
-
- return ret;
-}
-
-// set a node on the map
-static bool setnode_handler(Client *client, bool good)
-{
- v3s32 pos;
-
- if (! read_v3s32(client->fd, &pos))
- return false;
-
- Node node;
-
- if (! read_u32(client->fd, &node))
- return false;
-
- if (good)
- map_set_node(server_map.map, pos, map_node_create(node, NULL, 0), false, NULL);
-
- return true;
-}
-
-// update player's position
-static bool pos_handler(Client *client, bool good)
-{
- v3f64 pos;
-
- if (! read_v3f64(client->fd, &pos))
- return false;
-
- if (good) {
- client->pos = pos;
- database_update_player_pos(client->name, client->pos);
- }
-
- return true;
-}
-
-// tell server map manager client requested the block
-static bool request_block_handler(Client *client, bool good)
-{
- v3s32 pos;
-
- if (! read_v3s32(client->fd, &pos))
- return false;
-
- if (good)
- server_map_requested_block(client, pos);
-
- return true;
-}
-
-// declared in network.h
-CommandHandler command_handlers[SERVER_COMMAND_COUNT] = {
- {0},
- {&disconnect_handler, "DISCONNECT", CS_CREATED | CS_ACTIVE},
- {&auth_handler, "AUTH", CS_CREATED},
- {&setnode_handler, "SETNODE", CS_ACTIVE},
- {&pos_handler, "POS", CS_ACTIVE},
- {&request_block_handler, "REQUEST_BLOCK", CS_ACTIVE},
-};
+++ /dev/null
-#ifndef _SERVER_COMMANDS_H_
-#define _SERVER_COMMANDS_H_
-
-// this file must be included after client.h or server.h and before network.h
-
-typedef enum
-{
- SERVER_COMMAND_NULL, // invalid command
- SC_DISCONNECT, // client notifies server about disconnecting
- SC_AUTH, // client wants to authentify [body: name (zero terminated string)]
- SC_SETNODE, // player placed a node [body: pos (v3s32), node (Node)]
- SC_POS, // player moved [body: pos (v3f)]
- SC_REQUEST_BLOCK, // request to send a block [body: pos (v3s32)]
- SERVER_COMMAND_COUNT, // count of available commands
-} ServerCommand;
-
-#ifdef _CLIENT_H_
-typedef ServerCommand RemoteCommand;
-#endif
-
-#ifdef _SERVER_H_
-typedef ServerCommand HostCommand;
-#define HOST_COMMAND_COUNT SERVER_COMMAND_COUNT
-#endif
-
-#endif
#include "util.h"
struct ServerMap server_map;
-static Server *server;
// utility functions
+// return true if a player is close enough to a block to access it
+static bool within_simulation_distance(ServerPlayer *player, v3s32 blkp, u32 dist)
+{
+ pthread_rwlock_rdlock(&player->pos_lock);
+ v3s32 ppos = map_node_to_block_pos((v3s32) {player->pos.x, player->pos.y, player->pos.z}, NULL);
+ pthread_rwlock_unlock(&player->pos_lock);
+
+ return abs(ppos.x - blkp.x) <= (s32) dist
+ && abs(ppos.y - blkp.y) <= (s32) dist
+ && abs(ppos.z - blkp.z) <= (s32) dist;
+}
+
// send a block to a client and reset block request
-static void send_block(Client *client, MapBlock *block)
+static void send_block(ServerPlayer *player, MapBlock *block)
{
- MapBlockExtraData *extra = block->extra;
+ if (! within_simulation_distance(player, block->pos, server_config.simulation_distance))
+ return;
- pthread_mutex_lock(&client->mtx);
- if (client->state == CS_ACTIVE)
- (void) (write_u32(client->fd, CC_BLOCK)
- && write_v3s32(client->fd, block->pos)
- && write_u64(client->fd, extra->size)
- && write_u64(client->fd, extra->rawsize)
- && write(client->fd, extra->data, extra->size) != -1);
- pthread_mutex_unlock(&client->mtx);
+ dragonnet_peer_send_ToClientBlock(player->peer, &(ToClientBlock) {
+ .pos = block->pos,
+ .data = ((MapBlockExtraData *) block->extra)->data,
+ });
}
// send block to near clients
if (extra->state == MBS_GENERATING)
return;
- if (extra->data)
- free(extra->data);
+ Blob_free(&extra->data);
+ extra->data = map_serialize_block(block);
- map_serialize_block(block, &extra->data, &extra->size, &extra->rawsize);
database_save_block(block);
if (extra->state == MBS_CREATED)
return;
- pthread_rwlock_rdlock(&server->players_rwlck);
- ITERATE_LIST(&server->players, pair) {
- Client *client = pair->value;
-
- if (within_simulation_distance(client->pos, block->pos, server_config.simulation_distance))
- send_block(client, block);
- }
- pthread_rwlock_unlock(&server->players_rwlck);
+ server_player_iterate((void *) &send_block, block);
}
// list_clear_func callback for sending changed blocks to near clients
if (! database_load_block(block)) {
extra->state = MBS_CREATED;
- extra->data = NULL;
+ extra->data = (Blob) {0, NULL};
ITERATE_MAPBLOCK {
- block->data[x][y][z] = map_node_create(NODE_AIR, NULL, 0);
- extra->mgs_buffer[x][y][z] = MGS_VOID;
+ block->data[x][y][z] = map_node_create(NODE_AIR, (Blob) {0, NULL});
+ extra->mgsb.raw.nodes[x][y][z] = MGS_VOID;
}
}
}
{
MapBlockExtraData *extra = block->extra;
- if (extra->data)
- free(extra->data);
-
+ Blob_free(&extra->data);
free(extra);
}
else
mgs = MGS_PLAYER;
- MapgenStage *old_mgs = &((MapBlockExtraData *) block->extra)->mgs_buffer[offset.x][offset.y][offset.z];
+ MapgenStage *old_mgs = &((MapBlockExtraData *) block->extra)->mgsb.raw.nodes[offset.x][offset.y][offset.z];
if (mgs >= *old_mgs) {
*old_mgs = mgs;
// generate a hut for new players to spawn in
static void generate_spawn_hut()
{
- f32 wood_color[3] = {0.11f, 1.0f, 0.29f};
+ Blob wood_color = {0, NULL};
+ HSLData_write(&wood_color, &(HSLData) {{0.11f, 1.0f, 0.29f}});
+
List changed_blocks = list_create(NULL);
for (s32 x = -4; x <= +4; x++) {
for (s32 y = 0; y <= 3; y++) {
for (s32 z = -3; z <= +2; z++) {
- mapgen_set_node((v3s32) {x, server_map.spawn_height + y, z}, map_node_create(NODE_AIR, NULL, 0), MGS_PLAYER, &changed_blocks);
+ mapgen_set_node((v3s32) {x, server_map.spawn_height + y, z}, map_node_create(NODE_AIR, (Blob) {0, NULL}), MGS_PLAYER, &changed_blocks);
}
}
}
for (s32 x = -5; x <= +5; x++) {
for (s32 z = -4; z <= +3; z++) {
- mapgen_set_node((v3s32) {x, server_map.spawn_height - 1, z}, map_node_create(NODE_OAK_WOOD, wood_color, sizeof wood_color), MGS_PLAYER, &changed_blocks);
- mapgen_set_node((v3s32) {x, server_map.spawn_height + 4, z}, map_node_create(NODE_OAK_WOOD, wood_color, sizeof wood_color), MGS_PLAYER, &changed_blocks);
+ mapgen_set_node((v3s32) {x, server_map.spawn_height - 1, z}, map_node_create(NODE_OAK_WOOD, wood_color), MGS_PLAYER, &changed_blocks);
+ mapgen_set_node((v3s32) {x, server_map.spawn_height + 4, z}, map_node_create(NODE_OAK_WOOD, wood_color), MGS_PLAYER, &changed_blocks);
}
}
for (s32 y = 0; y <= 3; y++) {
for (s32 x = -5; x <= +5; x++) {
- mapgen_set_node((v3s32) {x, server_map.spawn_height + y, -4}, map_node_create(((y == 1 || y == 2) && ((x >= -3 && x <= -1) || (x >= +1 && x <= +2))) ? NODE_AIR : NODE_OAK_WOOD, wood_color, sizeof(f32) * 3), MGS_PLAYER, &changed_blocks);
- mapgen_set_node((v3s32) {x, server_map.spawn_height + y, +3}, map_node_create(((y == 1 || y == 2) && ((x >= -3 && x <= -2) || (x >= +1 && x <= +3))) ? NODE_AIR : NODE_OAK_WOOD, wood_color, sizeof(f32) * 3), MGS_PLAYER, &changed_blocks);
+ mapgen_set_node((v3s32) {x, server_map.spawn_height + y, -4}, map_node_create(((y == 1 || y == 2) && ((x >= -3 && x <= -1) || (x >= +1 && x <= +2))) ? NODE_AIR : NODE_OAK_WOOD, wood_color), MGS_PLAYER, &changed_blocks);
+ mapgen_set_node((v3s32) {x, server_map.spawn_height + y, +3}, map_node_create(((y == 1 || y == 2) && ((x >= -3 && x <= -2) || (x >= +1 && x <= +3))) ? NODE_AIR : NODE_OAK_WOOD, wood_color), MGS_PLAYER, &changed_blocks);
}
}
for (s32 y = 0; y <= 3; y++) {
for (s32 z = -3; z <= +2; z++) {
- mapgen_set_node((v3s32) {-5, server_map.spawn_height + y, z}, map_node_create(NODE_OAK_WOOD, wood_color, sizeof(f32) * 3), MGS_PLAYER, &changed_blocks);
- mapgen_set_node((v3s32) {+5, server_map.spawn_height + y, z}, map_node_create(((y != 3) && (z == -1 || z == +0)) ? NODE_AIR : NODE_OAK_WOOD, wood_color, sizeof(f32) * 3), MGS_PLAYER, &changed_blocks);
+ mapgen_set_node((v3s32) {-5, server_map.spawn_height + y, z}, map_node_create(NODE_OAK_WOOD, wood_color), MGS_PLAYER, &changed_blocks);
+ mapgen_set_node((v3s32) {+5, server_map.spawn_height + y, z}, map_node_create(((y != 3) && (z == -1 || z == +0)) ? NODE_AIR : NODE_OAK_WOOD, wood_color), MGS_PLAYER, &changed_blocks);
}
}
if (node_definitions[node].solid)
break;
- mapgen_set_node(pos, map_node_create(node == NODE_LAVA ? NODE_VULCANO_STONE : NODE_OAK_WOOD, wood_color, sizeof(f32) * 3), MGS_PLAYER, &changed_blocks);
+ mapgen_set_node(pos, map_node_create(node == NODE_LAVA ? NODE_VULCANO_STONE : NODE_OAK_WOOD, wood_color), MGS_PLAYER, &changed_blocks);
}
}
// public functions
// ServerMap singleton constructor
-void server_map_init(Server *srv)
+void server_map_init()
{
- server = srv;
-
server_map.map = map_create((MapCallbacks) {
.create_block = &on_create_block,
.delete_block = &on_delete_block,
}
// handle block request from client (thread safe)
-void server_map_requested_block(Client *client, v3s32 pos)
+void server_map_requested_block(ServerPlayer *player, v3s32 pos)
{
- if (within_simulation_distance(client->pos, pos, server_config.simulation_distance)) {
+ if (within_simulation_distance(player, pos, server_config.simulation_distance)) {
MapBlock *block = map_get_block(server_map.map, pos, true);
pthread_mutex_lock(&block->mtx);
break;
case MBS_READY:
- send_block(client, block);
+ send_block(player, block);
};
pthread_mutex_unlock(&block->mtx);
}
#include <stddef.h>
#include <pthread.h>
#include "map.h"
-#include "server/server.h"
+#include "server/server_player.h"
+#include "types.h"
typedef enum
{
MGS_PLAYER, // player-placed nodes or things placed after map generation
} MapgenStage;
-typedef MapgenStage MapgenStageBuffer[MAPBLOCK_SIZE][MAPBLOCK_SIZE][MAPBLOCK_SIZE];
-
typedef struct {
MapgenStage mgs;
List *changed_blocks;
typedef struct
{
- char *data; // cached serialized data
- size_t size; // size of data
- size_t rawsize; // size of decompressed data
+ Blob data; // the big cum
MapBlockState state; // generation state of the block
pthread_t mapgen_thread; // thread that is generating block
- MapgenStageBuffer mgs_buffer; // buffer to make sure mapgen only overrides things it should
+ MapgenStageBuffer mgsb; // buffer to make sure mapgen only overrides things it should
} MapBlockExtraData;
extern struct ServerMap {
s32 spawn_height; // height to spawn players at
} server_map; // ServerMap singleton
-void server_map_init(Server *server); // ServerMap singleton constructor
-void server_map_deinit(); // ServerMap singleton destructor
-void server_map_requested_block(Client *client, v3s32 pos); // handle block request from client (thread safe)
-void server_map_prepare_spawn(); // prepare spawn region
+void server_map_init(); // ServerMap singleton constructor
+void server_map_deinit(); // ServerMap singleton destructor
+void server_map_requested_block(ServerPlayer *player, v3s32 pos); // handle block request from client (thread safe)
+void server_map_prepare_spawn(); // prepare spawn region
#endif
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <dragonstd/list.h>
+#include "server/database.h"
+#include "server/server_config.h"
+#include "server/server_map.h"
+#include "server/server_player.h"
+#include "perlin.h"
+#include "day.h"
+#include "util.h"
+
+static bool shutting_down = false;
+static pthread_rwlock_t shutting_down_lock;
+
+static List players;
+static pthread_rwlock_t players_lock;
+
+static List names;
+static pthread_rwlock_t names_lock;
+
+static u64 next_id = 1;
+
+static bool list_compare_u64(u64 *p1, u64 *p2)
+{
+ return *p1 == *p2;
+}
+
+static bool get_lock(pthread_rwlock_t *lock, bool write)
+{
+ pthread_rwlock_rdlock(&shutting_down_lock);
+ if (shutting_down) {
+ pthread_rwlock_unlock(&shutting_down_lock);
+ return false;
+ }
+
+ if (write)
+ pthread_rwlock_wrlock(lock);
+ else
+ pthread_rwlock_rdlock(lock);
+
+ pthread_rwlock_unlock(&shutting_down_lock);
+ return true;
+}
+
+void server_player_init()
+{
+ pthread_rwlock_init(&shutting_down_lock, NULL);
+
+ players = list_create((void *) &list_compare_u64);
+ pthread_rwlock_init(&players_lock, NULL);
+
+ names = list_create(&list_compare_string);
+ pthread_rwlock_init(&names_lock, NULL);
+}
+
+// list_clear_func callback used on server shutdown to disconnect all clients properly
+static void list_disconnect_player(void *key, unused void *value, unused void *arg)
+{
+ ServerPlayer *player = key;
+
+ pthread_t recv_thread = player->peer->recv_thread;
+ server_player_disconnect(player);
+ pthread_join(recv_thread, NULL);
+}
+
+void server_player_deinit()
+{
+ pthread_rwlock_wrlock(&shutting_down_lock);
+ shutting_down = true;
+
+ pthread_rwlock_wrlock(&players_lock);
+ pthread_rwlock_wrlock(&names_lock);
+ pthread_rwlock_unlock(&shutting_down_lock);
+
+ list_clear_func(&players, &list_disconnect_player, NULL);
+ list_clear(&names);
+
+ pthread_rwlock_destroy(&players_lock);
+ pthread_rwlock_destroy(&names_lock);
+ pthread_rwlock_destroy(&shutting_down_lock);
+}
+
+void server_player_add(DragonnetPeer *peer)
+{
+ ServerPlayer *player = malloc(sizeof *player);
+
+ player->id = next_id++;
+ player->peer = peer;
+ pthread_rwlock_init(&player->ref, NULL);
+ player->auth = false;
+ player->name = dragonnet_addr_str(peer->raddr);
+ pthread_rwlock_init(&player->auth_lock, NULL);
+ player->pos = (v3f64) {0.0f, 0.0f, 0.0f};
+ pthread_rwlock_init(&player->pos_lock, NULL);
+
+ printf("Connected %s\n", player->name);
+
+ // accept thread is joined before shutdown, we are guaranteed to obtain the lock
+ pthread_rwlock_wrlock(&players_lock);
+
+ list_put(&players, &player->id, player);
+ peer->extra = player;
+
+ pthread_rwlock_unlock(&players_lock);
+}
+
+void server_player_remove(DragonnetPeer *peer)
+{
+ ServerPlayer *player = peer->extra;
+
+ // only (this) recv thread will modify the auth or name fields, no rdlocks needed
+
+ if (get_lock(&players_lock, true)) {
+ list_delete(&players, &player->id);
+ pthread_rwlock_unlock(&players_lock);
+
+ printf("Disconnected %s\n", player->name);
+ }
+
+ if (player->auth && get_lock(&names_lock, true)) {
+ list_delete(&names, player->name);
+ pthread_rwlock_unlock(&names_lock);
+ }
+
+ pthread_rwlock_wrlock(&player->ref);
+
+ free(player->name);
+
+ pthread_rwlock_destroy(&player->ref);
+ pthread_rwlock_destroy(&player->auth_lock);
+ pthread_rwlock_destroy(&player->pos_lock);
+
+ free(player);
+}
+
+u64 server_player_find(char *name)
+{
+ if (! get_lock(&names_lock, false))
+ return 0;
+
+ u64 *id = list_get(&names, name);
+ return id ? *id : 0;
+}
+
+ServerPlayer *server_player_grab(u64 id)
+{
+ if (! id)
+ return NULL;
+
+ if (! get_lock(&players_lock, false))
+ return NULL;
+
+ ServerPlayer *player = list_get(&players, &id);
+ if (player)
+ pthread_rwlock_rdlock(&player->ref);
+
+ pthread_rwlock_unlock(&players_lock);
+
+ return player;
+}
+
+void server_player_drop(ServerPlayer *player)
+{
+ pthread_rwlock_unlock(&player->ref);
+}
+
+bool server_player_auth(ServerPlayer *player, char *name)
+{
+ if (! get_lock(&names_lock, true))
+ return false;
+
+ pthread_rwlock_wrlock(&player->auth_lock);
+ pthread_rwlock_wrlock(&player->pos_lock);
+
+ bool success = list_put(&names, name, &player->id);
+
+ dragonnet_peer_send_ToClientAuth(player->peer, &(ToClientAuth) {
+ .success = success,
+ });
+
+ if (success) {
+ printf("Authentication %s: %s -> %s\n", success ? "success" : "failure", player->name, name);
+
+ free(player->name);
+ player->name = name;
+ player->auth = true;
+
+ if (! database_load_player(player->name, &player->pos)) {
+ player->pos = (v3f64) {0.0, server_map.spawn_height + 0.5, 0.0};
+ database_create_player(player->name, player->pos);
+ }
+
+ dragonnet_peer_send_ToClientInfo(player->peer, &(ToClientInfo) {
+ .seed = seed,
+ .simulation_distance = server_config.simulation_distance,
+ });
+
+ dragonnet_peer_send_ToClientTimeOfDay(player->peer, &(ToClientTimeOfDay) {
+ .time_of_day = get_time_of_day(),
+ });
+
+ server_player_send_pos(player);
+ }
+
+ pthread_rwlock_unlock(&player->pos_lock);
+ pthread_rwlock_unlock(&player->auth_lock);
+ pthread_rwlock_unlock(&names_lock);
+
+ return success;
+}
+
+void server_player_disconnect(ServerPlayer *player)
+{
+ dragonnet_peer_shutdown(player->peer);
+}
+
+void server_player_send_pos(ServerPlayer *player)
+{
+ dragonnet_peer_send_ToClientPos(player->peer, & (ToClientPos) {
+ .pos = player->pos,
+ });
+}
+
+void server_player_iterate(void (cb)(ServerPlayer *, void *), void *arg)
+{
+ if (! get_lock(&players_lock, false))
+ return;
+
+ ITERATE_LIST(&players, pair) {
+ ServerPlayer *player = pair->value;
+
+ pthread_rwlock_rdlock(&player->auth_lock);
+ if (player->auth)
+ cb(player, arg);
+ pthread_rwlock_unlock(&player->auth_lock);
+ }
+
+ pthread_rwlock_unlock(&players_lock);
+}
+
+/*
+229779
+373875
+374193
+110738
+390402
+357272
+390480
+
+(these are only the wholesome ones)
+*/
--- /dev/null
+#ifndef _SERVER_PLAYER_H_
+#define _SERVER_PLAYER_H_
+
+#include <pthread.h>
+#include <stdbool.h>
+#include <dragonnet/peer.h>
+#include "types.h"
+
+typedef struct
+{
+ u64 id; // unique identifier
+ DragonnetPeer *peer;
+ pthread_rwlock_t ref; // programming socks make you 100% cuter
+
+ bool auth;
+ char *name; // player name
+ pthread_rwlock_t auth_lock; // why
+
+ v3f64 pos; // player position
+ pthread_rwlock_t pos_lock; // i want to commit die
+} ServerPlayer;
+
+void server_player_init();
+void server_player_deinit();
+
+void server_player_add(DragonnetPeer *peer);
+void server_player_remove(DragonnetPeer *peer);
+
+u64 server_player_find(char *name);
+
+ServerPlayer *server_player_grab(u64 id);
+void server_player_drop(ServerPlayer *player);
+
+bool server_player_auth(ServerPlayer *player, char *name);
+void server_player_disconnect(ServerPlayer *player);
+void server_player_send_pos(ServerPlayer *player);
+void server_player_iterate(void (cb)(ServerPlayer *, void *), void *arg);
+
+#endif
s32 dir = (noise3d(tree_pos.x, tree_pos.y, tree_pos.z, 0, seed + SO_PINETREE_BRANCH_DIR) * 0.5 + 0.5) * 4.0;
for (v3s32 branch_pos = tree_pos; branch_length > 0; branch_length--, branch_pos = v3s32_add(branch_pos, dirs[dir]))
- mapgen_set_node(branch_pos, map_node_create(NODE_PINE_WOOD, NULL, 0), MGS_TREES, changed_blocks);
+ mapgen_set_node(branch_pos, map_node_create(NODE_PINE_WOOD, (Blob) {0, NULL}), MGS_TREES, changed_blocks);
- mapgen_set_node(tree_pos, map_node_create(NODE_PINE_WOOD, NULL, 0), MGS_TREES, changed_blocks);
+ mapgen_set_node(tree_pos, map_node_create(NODE_PINE_WOOD, (Blob) {0, NULL}), MGS_TREES, changed_blocks);
}
}
#ifndef _TREES_H_
#define _TREES_H_
-#include <dragontype/list.h>
-#include <dragontype/number.h>
+#include <dragonstd/list.h>
#include "perlin.h"
+#include "types.h"
#define NUM_TREES 3
#include "perlin.h"
#include "util.h"
-#define CREATE_NODE map_node_create(node, use_color ? (f32[]) {VOXELCTXSTATE(ctx).h / 360.0, VOXELCTXSTATE(ctx).s, VOXELCTXSTATE(ctx).l} : NULL, use_color ? sizeof(f32) * 3 : 0)
-
static VoxelctxState *create_state(VoxelctxState *old)
{
VoxelctxState *state = malloc(sizeof(VoxelctxState));
v[i] = floor(VOXELCTXSTATE(ctx).pos[i] + f + 0.5f);
}
- mapgen_set_node(v3s32_add(ctx->pos, (v3s32) {v[0], v[2], v[1]}), CREATE_NODE, ctx->mgs, ctx->changed_blocks);
+ Blob buffer = {0, NULL};
+
+ if (use_color)
+ HSLData_write(&buffer, &(HSLData) {{
+ VOXELCTXSTATE(ctx).h / 360.0,
+ VOXELCTXSTATE(ctx).s,
+ VOXELCTXSTATE(ctx).l,
+ }});
+
+ mapgen_set_node(
+ v3s32_add(ctx->pos, (v3s32) {v[0], v[2], v[1]}),
+ map_node_create(node, buffer),
+ ctx->mgs,
+ ctx->changed_blocks
+ );
}
}
#define VOXELCTXSTATE(ctx) (*((VoxelctxState *) (ctx)->statestack.first->key))
#include <linmath.h/linmath.h>
-#include <dragontype/list.h>
-#include <dragontype/number.h>
+#include <dragonstd/list.h>
+#include "types.h"
#include "server/server_map.h"
typedef struct
--- /dev/null
+#define MAPBLOCK_SIZE 16
+
+HSLData
+ v3f32 color
+
+SerializedMapNode
+ u32 type
+ Blob data
+
+SerializedMapBlockRaw
+ SerializedMapNode[MAPBLOCK_SIZE][MAPBLOCK_SIZE][MAPBLOCK_SIZE] nodes
+
+SerializedMapBlock
+ compressed SerializedMapBlockRaw raw
+
+MapgenStageBufferRaw
+ u32[MAPBLOCK_SIZE][MAPBLOCK_SIZE][MAPBLOCK_SIZE] nodes
+
+MapgenStageBuffer
+ compressed MapgenStageBufferRaw raw
+
+pkt ToServerAuth
+ String name
+
+pkt ToServerSetnode
+ v3s32 pos
+ u32 node
+
+pkt ToServerPos
+ v3f64 pos
+
+pkt ToServerRequestBlock
+ v3s32 pos
+
+pkt ToClientAuth
+ u8 success
+
+pkt ToClientBlock
+ v3s32 pos
+ Blob data
+
+pkt ToClientInfo
+ u32 simulation_distance
+ s32 seed
+
+pkt ToClientPos
+ v3f64 pos
+
+pkt ToClientTimeOfDay
+ u64 time_of_day
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-#include <zlib.h>
+#include <stdarg.h>
#include <dragonport/asprintf.h>
-#include "map.h"
-#include "util.h"
-
-const char *program_name;
-
-// print system call related error message and exit
-void syscall_error(const char *err)
-{
- perror(err);
- exit(EXIT_FAILURE);
-}
-
-// print general error message and exit
-void internal_error(const char *err)
-{
- fprintf(stderr, "%s: %s\n", program_name, err);
- exit(EXIT_FAILURE);
-}
-
-// read from fd until \0 or EOF terminator
-// store result including terminator into allocated buffer until bufsiz+1 is hit, return NULL on read error
-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);
-}
-
-// convert IPv6 address to human readable, return allocated buffer
-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;
-}
-
-// compress data using ZLib and store result(buffer allocated by malloc) in compressed
-void my_compress(const void *uncompressed, size_t uncompressed_size, char **compressed, size_t *compressed_size)
-{
- char compressed_buffer[uncompressed_size];
-
- z_stream stream;
- stream.zalloc = Z_NULL;
- stream.zfree = Z_NULL;
- stream.opaque = Z_NULL;
-
- stream.avail_in = stream.avail_out = uncompressed_size;
- stream.next_in = (Bytef *) uncompressed;
- stream.next_out = (Bytef *) compressed_buffer;
-
- deflateInit(&stream, Z_BEST_COMPRESSION);
- deflate(&stream, Z_FINISH);
- deflateEnd(&stream);
-
- *compressed_size = stream.total_out;
- *compressed = malloc(*compressed_size);
- memcpy(*compressed, compressed_buffer, *compressed_size);
-}
-
-// decompress data and put result into decompressed, return false if decompressed size does not match expected_decompressed_size
-bool my_decompress(const char *compressed, size_t compressed_size, void *decompressed, size_t expected_decompressed_size)
-{
- z_stream stream;
- stream.zalloc = Z_NULL;
- stream.zfree = Z_NULL;
- stream.opaque = Z_NULL;
-
- stream.avail_in = compressed_size;
- stream.next_in = (Bytef *) compressed;
- stream.avail_out = expected_decompressed_size;
- stream.next_out = (Bytef *) decompressed;
-
- inflateInit(&stream);
- inflate(&stream, Z_NO_FLUSH);
- inflateEnd(&stream);
-
- return (size_t) stream.total_out == expected_decompressed_size;
-}
-
-// return true if a player is close enough to a block to access it
-bool within_simulation_distance(v3f64 player_pos, v3s32 block_pos, u32 simulation_distance)
-{
- v3s32 player_block_pos = map_node_to_block_pos((v3s32) {player_pos.x, player_pos.y, player_pos.z}, NULL);
- return abs(player_block_pos.x - block_pos.x) <= (s32) simulation_distance && abs(player_block_pos.y - block_pos.y) <= (s32) simulation_distance && abs(player_block_pos.z - block_pos.z) <= (s32) simulation_distance;
-}
-
-f64 clamp(f64 v, f64 min, f64 max)
-{
- return v < min ? min : v > max ? max : v;
-}
char *format_string(const char *format, ...)
{
va_end(args);
return ptr;
}
-
-void *buffer_read(unsigned char **buffer, size_t *bufsiz, size_t size)
-{
- if (size == 0)
- return NULL;
-
- if (*bufsiz < size)
- return NULL;
-
- void *old_buffer = *buffer;
-
- *bufsiz -= size;
- *buffer += size;
-
- return old_buffer;
-}
-
-void buffer_write(unsigned char **buffer, size_t *bufsiz, void *data, size_t size)
-{
- if (size == 0)
- return;
-
- size_t old_bufsiz = *bufsiz;
-
- *bufsiz += size;
- *buffer = realloc(*buffer, *bufsiz);
-
- memcpy(*buffer + old_bufsiz, data, size);
-}
#ifndef _UTIL_H_
#define _UTIL_H_
-#include <stdbool.h>
-#ifdef WIN32
-#include <winsock2.h>
-#include <ws2tcpip.h>
-#else
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <arpa/inet.h>
-#include <netinet/in.h>
-#endif
-#include <dragontype/number.h>
-
#define ever (;;) // infinite for loop with style
#define INBRACES(str) (str) ? "(" : "", (str) ? (str) : "", (str) ? ")" : "" // wrapper for printf to optionally add a message in braces if message is not NULL
#define CMPBOUNDS(x) ((x) == 0 ? 0 : (x) > 0 ? 1 : -1) // resolves to 1 if x > 0, 0 if x == 0 and -1 if x < 0
#define unused __attribute__ ((unused))
#define U32(x) (((u32) 1 << 31) + (x))
-extern const char *program_name; // this has to be set to program name on startup
-
-void syscall_error(const char *err); // print system call related error message and exit
-void internal_error(const char *err); // print general error message and exit
-char *read_string(int fd, size_t bufsiz); // read from fd until \0 or EOF terminator
-char *address_string(struct sockaddr_in6 *addr); // convert IPv6 address to human readable, return allocated buffer
-void my_compress(const void *uncompressed, size_t uncompressed_size, char **compressed, size_t *compressed_size); // compress data using ZLib and store result(buffer allocated by malloc) in compressed
-bool my_decompress(const char *compressed, size_t compressed_size, void *decompressed, size_t expected_decompressed_size); // decompress data and put result into decompressed, return false if decompressed size does not match expected_decompressed_size
-bool within_simulation_distance(v3f64 player_pos, v3s32 block_pos, u32 simulation_distance); // return true if a player is close enough to a block to access it
-f64 clamp(f64 v, f64 min, f64 max);
char *format_string(const char *format, ...);
-void *buffer_read(unsigned char **buffer, size_t *bufsiz, size_t size);
-void buffer_write(unsigned char **buffer, size_t *bufsiz, void *data, size_t size);
#endif