Use dragonnet

master
Elias Fleckenstein 2022-02-12 23:17:32 +01:00
parent d8982996ab
commit 7edcc13168
No known key found for this signature in database
GPG Key ID: 06927A5199D6C9B2
68 changed files with 874 additions and 1228 deletions

18
.gitignore vendored
View File

@ -1,3 +1,4 @@
# CMake
CMakeLists.txt.user
CMakeCache.txt
CMakeFiles
@ -9,11 +10,20 @@ install_manifest.txt
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

15
.gitmodules vendored
View File

@ -7,12 +7,21 @@
[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

1
deps/dragonnet vendored Submodule

@ -0,0 +1 @@
Subproject commit b2fd5e955c0f910fdd1abf89626cfe324efbfe90

1
deps/dragonstd vendored Submodule

@ -0,0 +1 @@
Subproject commit 39b52e8cfa889d55008f2edd0933d6b421556c34

2
deps/dragontype vendored

@ -1 +1 @@
Subproject commit b3c245ba9b111462f2fff6178b08b486cd553f1b
Subproject commit 61292ea8a973ba03f93ebf4acde705071e15ccaf

1
deps/linenoise vendored Submodule

@ -0,0 +1 @@
Subproject commit 97d2850af13c339369093b78abe5265845d78220

View File

@ -1,2 +1,2 @@
#! /bin/bash
./DragonblocksServer 4000 & echo "singleplayer" | ./Dragonblocks localhost 4000; killall DragonblocksServer
./DragonblocksServer "[::1]:4000" & echo "singleplayer" | ./Dragonblocks "[::1]:4000"; killall DragonblocksServer

View File

@ -4,7 +4,7 @@ cp -r * .build/
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

View File

@ -1,31 +1,42 @@
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")
@ -37,20 +48,22 @@ if ("${CMAKE_SYSTEM_NAME}" STREQUAL "OpenBSD")
include_directories("/usr/X11R6/include")
endif()
add_compile_options(-Wall -Wextra -Werror)
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"
"${DEPS_DIR}/dragonport/asprintf.c"
"${DEPS_DIR}/perlin/perlin.c"
)
# Common sources
set(COMMON_SOURCES
${DEPS_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"
config.c
day.c
environment.c
@ -58,15 +71,18 @@ set(COMMON_SOURCES
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
@ -100,15 +116,17 @@ target_include_directories(Dragonblocks PUBLIC
${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
)
@ -117,13 +135,27 @@ target_link_libraries(DragonblocksServer
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)

View File

@ -4,7 +4,7 @@
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <linmath.h/linmath.h>
#include <dragontype/number.h>
#include "types.h"
extern struct Camera
{

View File

@ -2,159 +2,111 @@
#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);
while (client_auth.state == AUTH_INIT)
;
client.state = CS_DISCONNECTED;
printf("Disconnected %s%s%s\n", INBRACES(detail));
close(client.fd);
return (client_auth.state == AUTH_WAIT) == (type == DRAGONNET_TYPE_ToClientAuth);
}
static void on_disconnect(unused DragonnetPeer *peer)
{
interrupted = true;
while (! finished)
;
}
static void on_ToClientAuth(unused DragonnetPeer *peer, ToClientAuth *pkt)
{
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");
}
pthread_mutex_unlock(&client.mtx);
}
void client_send_position(v3f64 pos)
static void on_ToClientBlock(unused DragonnetPeer *peer, ToClientBlock *pkt)
{
pthread_mutex_lock(&client.mtx);
(void) (write_u32(client.fd, SC_POS) && write_v3f64(client.fd, pos));
pthread_mutex_unlock(&client.mtx);
MapBlock *block = map_get_block(client_map.map, pkt->pos, true);
map_deserialize_block(block, pkt->data);
client_map_block_received(block);
}
#include "network.c"
static void *reciever_thread(unused void *arg)
static void on_ToClientInfo(unused DragonnetPeer *peer, ToClientInfo *pkt)
{
handle_packets(&client);
if (errno != EINTR)
client_disconnect(false, "network error");
return NULL;
client_map_set_simulation_distance(pkt->simulation_distance);
seed = pkt->seed;
}
static bool client_name_prompt()
static void on_ToClientPos(unused DragonnetPeer *peer, ToClientPos *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_player_set_position(pkt->pos);
}
static bool client_authenticate()
static void on_ToClientTimeOfDay(unused DragonnetPeer *peer, ToClientTimeOfDay *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;
}
static bool client_start(int fd)
{
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 < 2) {
fprintf(stderr, "Missing address\n");
return EXIT_FAILURE;
}
if (argc < 3)
internal_error("missing address or port");
if (! (client = dragonnet_connect(argv[1]))) {
fprintf(stderr, "Failed to connect to server\n");
return EXIT_FAILURE;
}
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 (gai_state != 0)
internal_error(gai_strerror(gai_state));
int fd = socket(info->ai_family, info->ai_socktype, info->ai_protocol);
if (fd == -1)
syscall_error("socket");
if (connect(fd, info->ai_addr, info->ai_addrlen) == -1)
syscall_error("connect");
char *addrstr = address_string((struct sockaddr_in6 *) info->ai_addr);
printf("Connected to %s\n", addrstr);
free(addrstr);
freeaddrinfo(info);
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;
signal_handlers_init();
client_map_init();
client_player_init();
dragonnet_peer_run(client);
return client_start(fd) ? EXIT_SUCCESS : EXIT_FAILURE;
if (! client_auth_init())
return EXIT_FAILURE;
if (! game())
return EXIT_FAILURE;
dragonnet_peer_shutdown(client);
client_auth_deinit();
client_player_deinit();
client_map_deinit();
pthread_t recv_thread = client->recv_thread;
finished = true;
pthread_join(recv_thread, NULL);
return EXIT_SUCCESS;
}

View File

@ -1,29 +1,8 @@
#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

53
src/client/client_auth.c Normal file
View File

@ -0,0 +1,53 @@
#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);
}

21
src/client/client_auth.h Normal file
View File

@ -0,0 +1,21 @@
#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

View File

@ -1,129 +0,0 @@
#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},
};

View File

@ -1,25 +0,0 @@
#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

View File

@ -9,7 +9,6 @@
#define MAX_BLOCK_REQUESTS 4
struct ClientMap client_map;
Client *client;
// meshgen functions
@ -48,9 +47,9 @@ static void *meshgen_thread(unused void *arg)
// 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
@ -156,10 +155,8 @@ static bool on_get_block(MapBlock *block, bool create)
// 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,

View File

@ -3,7 +3,7 @@
#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

View File

@ -52,7 +52,7 @@ static void render_grass(v3s32 pos, unused MapNode *node, Vertex3D *vertex, unus
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});
}
@ -71,7 +71,7 @@ static void render_hsl(unused v3s32 pos, MapNode *node, Vertex3D *vertex, unused
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,
@ -85,28 +85,28 @@ ClientNodeDefinition client_node_definitions[NODE_UNLOADED] = {
},
// 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,
@ -114,7 +114,7 @@ ClientNodeDefinition client_node_definitions[NODE_UNLOADED] = {
// 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},
},
@ -124,7 +124,7 @@ ClientNodeDefinition client_node_definitions[NODE_UNLOADED] = {
},
// 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,
@ -132,7 +132,7 @@ ClientNodeDefinition client_node_definitions[NODE_UNLOADED] = {
// 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},
},
@ -142,7 +142,7 @@ ClientNodeDefinition client_node_definitions[NODE_UNLOADED] = {
},
// 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,
@ -150,7 +150,7 @@ ClientNodeDefinition client_node_definitions[NODE_UNLOADED] = {
// 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},
},
@ -160,35 +160,35 @@ ClientNodeDefinition client_node_definitions[NODE_UNLOADED] = {
},
// 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,

View File

@ -15,7 +15,9 @@ struct ClientPlayer client_player;
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);
@ -100,7 +102,7 @@ void client_player_add_to_scene()
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++) {
@ -194,7 +196,7 @@ void client_player_tick(f64 dtime)
} \
} \
} \
a ## _physics_done: (void) 0;\
a ## _physics_done: (void) 0; \
}
PHYSICS(x, y, z)

View File

@ -2,9 +2,9 @@
#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
{

View File

@ -1,5 +1,5 @@
#include <stdlib.h>
#include <dragontype/array.h>
#include <dragonstd/array.h>
#include "client/facecache.h"
static struct

View File

@ -2,7 +2,7 @@
#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);

View File

@ -3,10 +3,10 @@
#include FT_FREETYPE_H
#include "client/client.h"
#include "client/font.h"
#include "client/texture.h"
#define NUM_CHARS 128
typedef struct
{
Texture *texture;
@ -66,7 +66,7 @@ bool font_init()
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;
}

View File

@ -4,6 +4,7 @@
#include <stdbool.h>
#include <stddef.h>
#include "client/mesh.h"
#include "types.h"
typedef struct
{

View File

@ -3,7 +3,7 @@
#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);

View File

@ -14,6 +14,7 @@
#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"
@ -33,7 +34,7 @@ static void crosshair_init()
.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},
});
@ -58,7 +59,7 @@ static void render(f64 dtime)
gui_render();
}
static void game_loop(Client *client)
static void game_loop()
{
f64 fps_update_timer = 1.0f;
int frames = 0;
@ -66,7 +67,7 @@ static void game_loop(Client *client)
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;
@ -93,7 +94,7 @@ static void game_loop(Client *client)
}
}
bool game(Client *client)
bool game()
{
window_width = 1250;
window_height = 750;
@ -145,7 +146,7 @@ bool game(Client *client)
client_player_add_to_scene();
game_loop(client);
game_loop();
client_map_stop();

View File

@ -1,9 +1,7 @@
#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);

View File

@ -127,7 +127,7 @@ bool gui_init()
{
// 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;
}
@ -147,7 +147,7 @@ bool gui_init()
// 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;
}
@ -166,7 +166,7 @@ bool gui_init()
// 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;
}

View File

@ -3,11 +3,11 @@
#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
{

View File

@ -49,7 +49,7 @@ void input_on_cursor_pos(double current_x, double current_y)
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);

View File

@ -1,7 +1,7 @@
#ifndef _INPUT_H_
#define _INPUT_H_
#include <dragontype/number.h>
#include "types.h"
void input_tick(f64 dtime);
void input_init();

View File

@ -6,11 +6,11 @@
#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;

View File

@ -32,7 +32,7 @@ bool scene_init()
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;
}

View File

@ -4,9 +4,9 @@
#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

View File

@ -106,7 +106,7 @@ bool sky_init()
{
// 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;
}
@ -114,8 +114,8 @@ bool sky_init()
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++)
@ -142,14 +142,14 @@ bool sky_init()
// 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;
@ -162,7 +162,7 @@ bool sky_init()
// 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;
}

View File

@ -1,7 +1,7 @@
#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"

View File

@ -30,7 +30,7 @@ f64 get_sun_angle()
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)

View File

@ -2,7 +2,7 @@
#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)

View File

@ -21,12 +21,12 @@ end
"
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 "

View File

@ -1,7 +1,7 @@
#ifndef _ENVIRONMENT_H_
#define _ENVIRONMENT_H_
#include <dragontype/number.h>
#include "types.h"
f64 get_humidity(v3s32 pos);
f64 get_temperature(v3s32 pos);

View File

@ -2,7 +2,6 @@
#include <stdbool.h>
#include <unistd.h>
#include <math.h>
#include <endian.h/endian.h>
#include <string.h>
#include "map.h"
#include "util.h"
@ -131,7 +130,7 @@ MapBlock *map_allocate_block(v3s32 pos)
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;
}
@ -144,77 +143,48 @@ void map_free_block(MapBlock *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;
// 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);
if (! my_decompress(data, size, decompressed, decompressed_size))
return false;
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);
unsigned char *ptr = decompressed;
ITERATE_MAPBLOCK {
// node type
u32 *type_ptr = buffer_read(&ptr, &decompressed_size, sizeof(u32));
if (! type_ptr)
return false;
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);
}
return true;
SerializedMapBlock_free(&block_data);
return success;
}
v3s32 map_node_to_block_pos(v3s32 pos, v3u8 *offset)
@ -230,7 +200,7 @@ MapNode map_get_node(Map *map, v3s32 pos)
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];
}
@ -251,7 +221,7 @@ 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)
{
if (type >= NODE_UNLOADED)
type = NODE_UNKNOWN;
@ -265,8 +235,8 @@ MapNode map_node_create(Node type, void *data, size_t size)
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;
}

View File

@ -3,12 +3,11 @@
#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
@ -61,15 +60,15 @@ MapBlock *map_get_block(Map *map, v3s32 pos, bool create);
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

3
src/mktypes.sh Executable file
View File

@ -0,0 +1,3 @@
#! /bin/sh
LUA_PATH="../deps/dragontype/?.lua;../deps/dragontype/?/init.lua" "../deps/dragontype/typegen.lua"

View File

@ -1,55 +0,0 @@
#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;
}

View File

@ -1,27 +0,0 @@
#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

View File

@ -1,26 +1,9 @@
#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
{
@ -82,8 +65,8 @@ NodeDefinition node_definitions[NODE_UNLOADED] = {
.data_size = sizeof(HSLData),
.create = NULL,
.delete = NULL,
.serialize = &serialize_hsl,
.deserialize = &deserialize_hsl,
.serialize = (void *) &HSLData_write,
.deserialize = (void *) &HSLData_read,
},
// oak leaves
{
@ -91,8 +74,8 @@ NodeDefinition node_definitions[NODE_UNLOADED] = {
.data_size = sizeof(HSLData),
.create = NULL,
.delete = NULL,
.serialize = &serialize_hsl,
.deserialize = &deserialize_hsl,
.serialize = (void *) &HSLData_write,
.deserialize = (void *) &HSLData_read,
},
// pine wood
{
@ -100,8 +83,8 @@ NodeDefinition node_definitions[NODE_UNLOADED] = {
.data_size = sizeof(HSLData),
.create = NULL,
.delete = NULL,
.serialize = &serialize_hsl,
.deserialize = &deserialize_hsl,
.serialize = (void *) &HSLData_write,
.deserialize = (void *) &HSLData_read,
},
// pine leaves
{
@ -109,8 +92,8 @@ NodeDefinition node_definitions[NODE_UNLOADED] = {
.data_size = sizeof(HSLData),
.create = NULL,
.delete = NULL,
.serialize = &serialize_hsl,
.deserialize = &deserialize_hsl,
.serialize = (void *) &HSLData_write,
.deserialize = (void *) &HSLData_read,
},
// palm wood
{
@ -118,8 +101,8 @@ NodeDefinition node_definitions[NODE_UNLOADED] = {
.data_size = sizeof(HSLData),
.create = NULL,
.delete = NULL,
.serialize = &serialize_hsl,
.deserialize = &deserialize_hsl,
.serialize = (void *) &HSLData_write,
.deserialize = (void *) &HSLData_read,
},
// palm leaves
{
@ -127,8 +110,8 @@ NodeDefinition node_definitions[NODE_UNLOADED] = {
.data_size = sizeof(HSLData),
.create = NULL,
.delete = NULL,
.serialize = &serialize_hsl,
.deserialize = &deserialize_hsl,
.serialize = (void *) &HSLData_write,
.deserialize = (void *) &HSLData_read,
},
// sand
{

View File

@ -2,7 +2,7 @@
#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)]);
@ -35,14 +35,10 @@ typedef struct
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

View File

@ -2,7 +2,7 @@
#define _PERLIN_H_
#include <perlin/perlin.h>
#include <dragontype/number.h>
#include "types.h"
typedef enum
{

View File

@ -122,16 +122,6 @@ static bool find_near_vulcano(v2s32 pos, v2s32 *result)
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));
@ -183,7 +173,7 @@ static void preprocess_row_ocean(v2s32 pos, unused f64 factor, void *row_data, v
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;
@ -202,7 +192,7 @@ static s32 height_ocean(unused v2s32 pos, f64 factor, f32 height, void *row_data
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)

View File

@ -1,9 +1,9 @@
#ifndef _BIOMES_H_
#define _BIOMES_H_
#include <dragontype/number.h>
#include "map.h"
#include "perlin.h"
#include "types.h"
typedef enum
{

View File

@ -37,13 +37,10 @@ static sqlite3_stmt *prepare_block_statement(MapBlock *block, const char *action
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;
}
@ -51,12 +48,10 @@ static sqlite3_stmt *prepare_block_statement(MapBlock *block, const char *action
// 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
@ -72,7 +67,7 @@ void database_init()
}
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);"
};
@ -115,7 +110,7 @@ bool database_load_block(MapBlock *block)
{
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);
@ -125,18 +120,13 @@ bool database_load_block(MapBlock *block)
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);
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);
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]);
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");
}
@ -150,25 +140,20 @@ void database_save_block(MapBlock *block)
{
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]);
Blob data = {0, NULL};
Blob_write(&data, &extra->data);
char *mgsb_data;
size_t mgsb_size;
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");
@ -234,12 +219,10 @@ bool database_load_player(char *name, v3f64 *pos_ptr)
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;

View File

@ -15,6 +15,7 @@ void mapgen_set_node(v3s32 pos, MapNode node, MapgenStage mgs, List *changed_blo
.mgs = mgs,
.changed_blocks = changed_blocks,
};
map_set_node(server_map.map, pos, node, true, &arg);
}
@ -84,9 +85,9 @@ void mapgen_generate_block(MapBlock *block, List *changed_blocks)
}
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);
}

View File

@ -1,193 +1,93 @@
#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);
ServerPlayer *player = peer->extra;
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();
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) {
fprintf(stderr, "Missing address\n");
return EXIT_FAILURE;
}
if (argc < 2)
internal_error("missing port");
if (! (server = dragonnet_listener_new(argv[1]))) {
fprintf(stderr, "Failed to listen to connections\n");
return EXIT_FAILURE;
}
struct addrinfo hints = {
.ai_family = AF_INET6,
.ai_socktype = SOCK_STREAM,
.ai_protocol = 0,
.ai_flags = AI_NUMERICSERV | AI_PASSIVE,
};
char *address = dragonnet_addr_str(server->laddr);
printf("Listening on %s\n", address);
free(address);
struct addrinfo *info = NULL;
int gai_state = getaddrinfo(NULL, argv[1], &hints, &info);
if (gai_state != 0)
internal_error(gai_strerror(gai_state));
int fd = socket(info->ai_family, info->ai_socktype, 0);
if (fd == -1)
syscall_error("socket");
int flag = 1;
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag)) == -1)
syscall_error("setsockopt");
if (bind(fd, info->ai_addr, info->ai_addrlen) == -1)
syscall_error("bind");
if (listen(fd, 3) == -1)
syscall_error("listen");
char *addrstr = address_string((struct sockaddr_in6 *) info->ai_addr);
printf("Listening on %s\n", addrstr);
free(addrstr);
freeaddrinfo(info);
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;
signal_handlers_init();
server_run(fd);
server_player_init();
database_init();
server_map_init();
server_map_prepare_spawn();
dragonnet_listener_run(server);
while (! interrupted)
sched_yield();
printf("Shutting down\n");
dragonnet_listener_close(server);
server_map_deinit();
database_deinit();
server_player_deinit();
dragonnet_listener_delete(server);
return EXIT_SUCCESS;
}

View File

@ -1,43 +1,8 @@
#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

View File

@ -1,123 +0,0 @@
#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},
};

View File

@ -1,26 +0,0 @@
#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

View File

@ -11,23 +11,31 @@
#include "util.h"
struct ServerMap server_map;
static Server *server;
// utility functions
// send a block to a client and reset block request
static void send_block(Client *client, MapBlock *block)
// 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)
{
MapBlockExtraData *extra = block->extra;
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);
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);
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(ServerPlayer *player, MapBlock *block)
{
if (! within_simulation_distance(player, block->pos, server_config.simulation_distance))
return;
dragonnet_peer_send_ToClientBlock(player->peer, &(ToClientBlock) {
.pos = block->pos,
.data = ((MapBlockExtraData *) block->extra)->data,
});
}
// send block to near clients
@ -39,23 +47,15 @@ static void send_block_to_near(MapBlock *block)
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
@ -128,11 +128,11 @@ static void on_create_block(MapBlock *block)
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;
}
}
}
@ -143,9 +143,7 @@ static void on_delete_block(MapBlock *block)
{
MapBlockExtraData *extra = block->extra;
if (extra->data)
free(extra->data);
Blob_free(&extra->data);
free(extra);
}
@ -174,7 +172,7 @@ static bool on_set_node(MapBlock *block, v3u8 offset, unused MapNode *node, void
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;
@ -215,35 +213,37 @@ static void join_mapgen_threads()
// 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);
}
}
@ -271,7 +271,7 @@ static void generate_spawn_hut()
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);
}
}
@ -281,10 +281,8 @@ static void generate_spawn_hut()
// 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,
@ -308,9 +306,9 @@ void server_map_deinit()
}
// 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);
@ -325,7 +323,7 @@ void server_map_requested_block(Client *client, v3s32 pos)
break;
case MBS_READY:
send_block(client, block);
send_block(player, block);
};
pthread_mutex_unlock(&block->mtx);
}

View File

@ -4,7 +4,8 @@
#include <stddef.h>
#include <pthread.h>
#include "map.h"
#include "server/server.h"
#include "server/server_player.h"
#include "types.h"
typedef enum
{
@ -22,8 +23,6 @@ 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;
@ -31,12 +30,10 @@ typedef struct {
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 {
@ -48,9 +45,9 @@ 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

252
src/server/server_player.c Normal file
View File

@ -0,0 +1,252 @@
#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)
*/

View File

@ -0,0 +1,39 @@
#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

View File

@ -130,9 +130,9 @@ static void pine_tree(v3s32 pos, List *changed_blocks)
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);
}
}

View File

@ -1,9 +1,9 @@
#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

View File

@ -5,8 +5,6 @@
#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));
@ -229,7 +227,21 @@ void voxelctx_cube(Voxelctx *ctx, Node node, bool use_color)
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
);
}
}

View File

@ -4,8 +4,8 @@
#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

50
src/types.def Normal file
View File

@ -0,0 +1,50 @@
#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

View File

@ -1,117 +1,5 @@
#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, ...)
{
@ -122,32 +10,3 @@ 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);
}

View File

@ -1,18 +1,6 @@
#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
@ -20,18 +8,6 @@
#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