sudo apt install build-essential make zlib1g-dev
```
-The development versions of OpenGL, GLFW3, GLEW are required to build the client.
+The development versions of OpenGL, GLFW3, GLEW and Freetype are required to build the client.
```bash
-sudo apt install libgl1-mesa-dev libglfw3-dev libglew-dev
+sudo apt install libgl1-mesa-dev libglfw3-dev libglew-dev libfreetype-dev
```
For building the server the SQLite3 development library is required.
## Dependencies
-The client depends on GLFW3, OpenGL and GLEW.
+The client depends on GLFW3, OpenGL, GLEW and Freetype.
```bash
-sudo apt install libgl1-mesa-dri libglfw3 libglew2.1
+sudo apt install libgl1-mesa-dri libglfw3 libglew2.1 libfreetype6
```
The server depends on SQLite3.
--- /dev/null
+#version 330 core
+
+in vec2 fragmentTextureCoords;
+in vec3 fragmentColor;
+
+out vec4 outColor;
+
+uniform sampler2D texture0;
+
+vec3 hsv2rgb(vec3 c)
+{
+ vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
+ vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
+ return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
+}
+
+void main()
+{
+ outColor = texture(texture0, fragmentTextureCoords) * vec4(hsv2rgb(vec3(fragmentColor)), 1.0);
+ if (outColor.a == 0.0)
+ discard;
+}
--- /dev/null
+#version 330 core
+
+layout(location = 0) in vec3 vertexPosition;
+layout(location = 1) in vec2 vertexTextureCoords;
+layout(location = 2) in vec3 vertexColor;
+
+out vec2 fragmentTextureCoords;
+out vec3 fragmentColor;
+
+uniform mat4 model;
+uniform mat4 view;
+uniform mat4 projection;
+
+void main()
+{
+ gl_Position = projection * view * model * vec4(vertexPosition, 1.0);
+ fragmentTextureCoords = vertexTextureCoords;
+ fragmentColor = vertexColor;
+}
+
+++ /dev/null
-#version 330 core
-
-in vec2 fragmentTextureCoords;
-in vec3 fragmentColor;
-
-out vec4 outColor;
-
-uniform sampler2D texture0;
-
-vec3 hsv2rgb(vec3 c)
-{
- vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
- vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
- return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
-}
-
-void main()
-{
- outColor = texture(texture0, fragmentTextureCoords) * vec4(hsv2rgb(vec3(fragmentColor)), 1.0);
- if (outColor.a == 0.0)
- discard;
-}
--- /dev/null
+#version 330 core
+
+in vec2 fragmentTextureCoords;
+
+out vec4 outColor;
+
+uniform sampler2D texture0;
+uniform vec3 textColor;
+
+void main()
+{
+ outColor = vec4(1.0, 1.0, 1.0, texture(texture0, fragmentTextureCoords).r) * vec4(textColor, 1.0);
+ if (outColor.a == 0.0)
+ discard;
+}
--- /dev/null
+#version 330 core
+
+layout(location = 0) in vec2 vertexPosition;
+layout(location = 1) in vec2 vertexTextureCoords;
+
+out vec2 fragmentTextureCoords;
+
+uniform mat4 projection;
+uniform mat4 model;
+
+void main()
+{
+ gl_Position = projection * model * vec4(vertexPosition, 0.0, 1.0);
+ fragmentTextureCoords = vertexTextureCoords;
+}
--- /dev/null
+#version 330 core
+
+in vec2 fragmentTextureCoords;
+
+out vec4 outColor;
+
+uniform sampler2D texture0;
+
+void main()
+{
+ outColor = texture(texture0, fragmentTextureCoords);
+ if (outColor.a == 0.0)
+ discard;
+}
--- /dev/null
+#version 330 core
+
+layout(location = 0) in vec2 vertexPosition;
+layout(location = 1) in vec2 vertexTextureCoords;
+
+out vec2 fragmentTextureCoords;
+
+uniform mat4 model;
+uniform mat4 projection;
+
+void main()
+{
+ gl_Position = projection * model * vec4(vertexPosition, 0.0, 1.0);
+ fragmentTextureCoords = vertexTextureCoords;
+}
+
+++ /dev/null
-#version 330 core
-
-layout(location = 0) in vec3 vertexPosition;
-layout(location = 1) in vec2 vertexTextureCoords;
-layout(location = 2) in vec3 vertexColor;
-
-out vec2 fragmentTextureCoords;
-out vec3 fragmentColor;
-
-uniform mat4 model;
-uniform mat4 view;
-uniform mat4 projection;
-
-void main()
-{
- gl_Position = projection * view * model * vec4(vertexPosition, 1.0);
- fragmentTextureCoords = vertexTextureCoords;
- fragmentColor = vertexColor;
-}
-
COMMON = array.o bintree.o list.o map.o signal.o util.o types.o node.o queue.o
-SERVER = $(COMMON) server.o servercommands.o servermap.o perlin.o facecache.o mapgen.o mapdb.o
-CLIENT = $(COMMON) camera.o client.o clientcommands.o clientmap.o clientnode.o clientplayer.o cube.o hud.o input.o mesh.o scene.o shaders.o blockmesh.o texture.o
+
+SERVER = $(COMMON) \
+ server/facecache.o \
+ server/mapdb.o \
+ server/mapgen.o \
+ server/perlin.o \
+ server/server.o \
+ server/server_commands.o \
+ server/server_map.o \
+
+CLIENT = $(COMMON) \
+ client/blockmesh.o \
+ client/camera.o \
+ client/client.o \
+ client/client_commands.o \
+ client/client_map.o \
+ client/client_node.o \
+ client/client_player.o \
+ client/cube.o \
+ client/font.o \
+ client/game.o \
+ client/hud.o \
+ client/input.o \
+ client/mesh.o \
+ client/object.o \
+ client/scene.o \
+ client/shader.o \
+ client/texture.o \
+ client/vertex.o \
+ client/window.o \
+
LIBRARIES = -lpthread -lm -lz
FLAGS = -g -fmax-errors=4
FLAGS = -O3 -DRELEASE
endif
+CFLAGS = -Wall -Wextra -Wpedantic -Werror -iquote . -isystem ../deps
+
all: Dragonblocks DragonblocksServer
Dragonblocks: $(CLIENT)
- cc $(FLAGS) -o Dragonblocks $(CLIENT) $(LIBRARIES) -lGL -lGLEW -lglfw
+ cc $(FLAGS) -o Dragonblocks $(CLIENT) $(LIBRARIES) -lGL -lGLEW -lglfw -lfreetype
DragonblocksServer: $(SERVER)
cc $(FLAGS) -o DragonblocksServer $(SERVER) $(LIBRARIES) -lsqlite3
%.o: %.c
- cc $(FLAGS) -Wall -Wextra -Wpedantic -Werror -isystem ../deps -c -o $@ $<
+ cc $(FLAGS) $(CFLAGS) -c -o $@ $<
+
+server/%.o: server/%.c
+ cc $(FLAGS) $(CFLAGS) -c -o $@ $<
+
+client/%.o: client/%.c
+ cc $(FLAGS) $(CFLAGS) -isystem /usr/include/freetype2 -c -o $@ $<
clean:
- rm -rf *.o
+ rm -rf *.o client/*.o server/*.o
clobber: clean
rm -rf Dragonblocks DragonblocksServer
+++ /dev/null
-#include "blockmesh.h"
-#include "clientnode.h"
-#include "cube.h"
-
-static v3s8 fdir[6] = {
- {+0, +0, -1},
- {+0, +0, +1},
- {-1, +0, +0},
- {+1, +0, +0},
- {+0, -1, +0},
- {+0, +1, +0},
-};
-
-static VertexBuffer make_vertices(MapBlock *block, Map *map)
-{
- VertexBuffer buffer = vertexbuffer_create();
-
- ITERATE_MAPBLOCK {
- MapNode *node = &block->data[x][y][z];
-
- if (node_definitions[node->type].visible) {
- v3f offset = {x + 8.0f, y + 8.0f, z + 8.0f};
-
- ClientNodeDefintion *client_def = &client_node_definitions[node->type];
- vertexbuffer_set_texture(&buffer, client_def->texture);
-
- for (int f = 0; f < 6; f++) {
- v3s8 npos = {
- x + fdir[f].x,
- y + fdir[f].y,
- z + fdir[f].z,
- };
-
- Node neighbor;
-
- if (npos.x >= 0 && npos.x < 16 && npos.y >= 0 && npos.y < 16 && npos.z >= 0 && npos.z < 16)
- neighbor = block->data[npos.x][npos.y][npos.z].type;
- else
- neighbor = map_get_node(map, (v3s32) {npos.x + block->pos.x * 16, npos.y + block->pos.y * 16, npos.z + block->pos.z * 16}).type;
-
- if (neighbor != NODE_UNLOADED && ! node_definitions[neighbor].visible) {
- for (int v = 0; v < 6; v++) {
- Vertex vertex = cube_vertices[f][v];
- vertex.x += offset.x;
- vertex.y += offset.y;
- vertex.z += offset.z;
-
- if (client_def->render)
- client_def->render(node, &vertex);
-
- vertexbuffer_add_vertex(&buffer, &vertex);
- }
- }
- }
- }
- }
-
- return buffer;
-}
-
-#undef GNODDEF
-#undef VALIDPOS
-
-void make_block_mesh(MapBlock *block, Map *map, Scene *scene)
-{
- MeshObject *old = block->extra;
- block->extra = meshobject_create(make_vertices(block, map), scene, (v3f) {block->pos.x * 16.0f - 8.0f, block->pos.y * 16.0f - 8.0f, block->pos.z * 16.0f - 8.0f});
- if (old)
- old->remove = true;
-}
+++ /dev/null
-#ifndef _BLOCKMESH_H_
-#define _BLOCKMESH_H_
-
-#include "map.h"
-#include "scene.h"
-
-void make_block_mesh(MapBlock *block, Map *map, Scene *scene);
-
-#endif
+++ /dev/null
-#include <math.h>
-#include "client.h"
-#include "camera.h"
-
-static struct
-{
- mat4x4 view, projection;
- GLFWwindow *window;
- ShaderProgram *prog;
-
- vec3 eye, front, right, up;
-} camera;
-
-static vec3 world_up = {0.0f, 1.0f, 0.0f};
-
-struct movement_dirs movement_dirs;
-
-static void update_camera()
-{
- vec3 center;
- vec3_add(center, camera.eye, camera.front);
-
- mat4x4_look_at(camera.view, camera.eye, center, camera.up);
-}
-
-void camera_enable()
-{
- glUniformMatrix4fv(camera.prog->loc_view, 1, GL_FALSE, camera.view[0]);
- glUniformMatrix4fv(camera.prog->loc_projection, 1, GL_FALSE, camera.projection[0]);
-}
-
-void set_camera_position(v3f pos)
-{
- camera.eye[0] = pos.x;
- camera.eye[1] = pos.y;
- camera.eye[2] = pos.z;
-
- update_camera();
-}
-
-void set_camera_angle(f32 yaw, f32 pitch)
-{
- camera.front[0] = movement_dirs.front[0] = cos(yaw) * cos(pitch);
- camera.front[1] = sin(pitch);
- camera.front[2] = movement_dirs.front[2] = sin(yaw) * cos(pitch);
-
- vec3_norm(camera.front, camera.front);
- vec3_norm(movement_dirs.front, movement_dirs.front);
-
- vec3_mul_cross(camera.right, camera.front, world_up);
- movement_dirs.right[0] = camera.right[0];
- movement_dirs.right[2] = camera.right[2];
-
- vec3_norm(camera.right, camera.right);
- vec3_norm(movement_dirs.right, movement_dirs.right);
-
- vec3_mul_cross(camera.up, camera.right, camera.front);
- vec3_norm(camera.up, camera.up);
-
- update_camera();
-}
-
-void camera_on_resize(int width, int height)
-{
- mat4x4_perspective(camera.projection, 86.1f / 180.0f * M_PI, (float) width / (float) height, 0.01f, 1000.0f);
-}
-
-void init_camera(GLFWwindow *window, ShaderProgram *prog)
-{
- camera.window = window;
- camera.prog = prog;
-}
+++ /dev/null
-#ifndef _CAMERA_H_
-#define _CAMERA_H_
-
-#include <GL/glew.h>
-#include <GLFW/glfw3.h>
-#include <linmath.h/linmath.h>
-#include "shaders.h"
-#include "types.h"
-
-void init_camera(GLFWwindow *window, ShaderProgram *prog);
-void set_camera_position(v3f pos);
-void set_camera_angle(f32 yaw, f32 pitch);
-void camera_on_resize(int width, int height);
-void camera_enable();
-
-extern struct movement_dirs
-{
- vec3 front;
- vec3 right;
-} movement_dirs;
-
-#endif
+++ /dev/null
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <math.h>
-#include <arpa/inet.h>
-#include <unistd.h>
-#include <errno.h>
-#include <netdb.h>
-#include <GL/glew.h>
-#include <GL/gl.h>
-#include <GLFW/glfw3.h>
-#include "camera.h"
-#include "client.h"
-#include "clientmap.h"
-#include "clientnode.h"
-#include "cube.h"
-#include "hud.h"
-#include "input.h"
-#include "signal.h"
-#include "shaders.h"
-#include "util.h"
-
-Client client;
-
-void client_disconnect(bool send, const char *detail)
-{
- pthread_mutex_lock(&client.mtx);
- if (client.state != CS_DISCONNECTED) {
- if (send)
- write_u32(client.fd, SC_DISCONNECT);
-
- client.state = CS_DISCONNECTED;
- printf("Disconnected %s%s%s\n", INBRACES(detail));
- close(client.fd);
- }
- pthread_mutex_unlock(&client.mtx);
-}
-
-#include "network.c"
-
-static void *reciever_thread(__attribute__((unused)) void *unused)
-{
- handle_packets(&client);
-
- if (errno != EINTR)
- client_disconnect(false, "network error");
-
- return NULL;
-}
-
-static void framebuffer_size_callback(__attribute__((unused)) GLFWwindow* window, int width, int height)
-{
- glViewport(0, 0, width, height);
-
- camera_on_resize(width, height);
- hud_on_resize(width, height);
- input_on_resize(width, height);
-}
-
-static void cursor_pos_callback(__attribute__((unused)) GLFWwindow* window, double current_x, double current_y)
-{
- input_on_cursor_pos(current_x, current_y);
-}
-
-static void window_pos_callback(__attribute__((unused)) GLFWwindow* window, int x, int y)
-{
- input_on_window_pos(x, y);
-}
-
-static void client_loop()
-{
- if(! glfwInit()) {
- printf("Failed to initialize GLFW\n");
- return;
- }
-
- glfwWindowHint(GLFW_SAMPLES, 8);
- glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
- glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
- glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
-
- int width, height;
- width = 1250;
- height = 750;
-
- GLFWwindow *window = glfwCreateWindow(width, height, "Dragonblocks", NULL, NULL);
-
- if (! window) {
- fprintf(stderr, "Failed to create window\n");
- glfwTerminate();
- return;
- }
-
- glfwMakeContextCurrent(window);
- if (glewInit() != GLEW_OK) {
- fprintf(stderr, "Failed to initialize GLEW\n");
- return;
- }
-
- glfwSetInputMode(window, GLFW_STICKY_KEYS, GL_TRUE);
-
- ShaderProgram *prog = create_shader_program(RESSOURCEPATH "shaders");
- if (! prog) {
- fprintf(stderr, "Failed to create shader program\n");
- return;
- }
-
- init_client_node_definitions();
- clientmap_start_meshgen();
-
- glUseProgram(prog->id);
-
- init_camera(window, prog);
-
- set_camera_position((v3f) {0.0f, 0.0f, 0.0f});
- set_camera_angle(0.0f, 0.0f);
-
- camera_on_resize(width, height);
-
- hud_init(prog);
- hud_on_resize(width, height);
-
- hud_add(RESSOURCEPATH "textures/crosshair.png", (v3f) {0.0f, 0.0f, 0.0f}, (v2f) {1.0f, 1.0f}, HUD_SCALE_TEXTURE);
-
- init_input(&client, window);
- input_on_resize(width, height);
-
- clientplayer_add_to_scene(&client.player);
-
- glfwSetFramebufferSizeCallback(window, &framebuffer_size_callback);
- glfwSetCursorPosCallback(window, &cursor_pos_callback);
- glfwSetWindowPosCallback(window, &window_pos_callback);
-
- struct timespec ts, ts_old;
- clock_gettime(CLOCK_REALTIME, &ts_old);
-
- while (! glfwWindowShouldClose(window) && client.state != CS_DISCONNECTED && ! interrupted) {
- clock_gettime(CLOCK_REALTIME, &ts);
- f64 dtime = (f64) (ts.tv_sec - ts_old.tv_sec) + (f64) (ts.tv_nsec - ts_old.tv_nsec) / 1000000000.0;
- ts_old = ts;
-
- glEnable(GL_DEPTH_TEST);
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
- glClearColor(0.52941176470588f, 0.8078431372549f, 0.92156862745098f, 1.0f);
-
- process_input();
- clientplayer_tick(&client.player, dtime);
-
- camera_enable();
- scene_render(client.scene, prog);
-
- hud_render();
-
- glfwSwapBuffers(window);
- glfwPollEvents();
- }
-
- delete_shader_program(prog);
- hud_deinit();
-}
-
-static bool client_name_prompt()
-{
- printf("Enter name: ");
- fflush(stdout);
- char name[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;
-}
-
-static bool client_authenticate()
-{
- 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 void client_start(int fd)
-{
- client.fd = fd;
- pthread_mutex_init(&client.mtx, NULL);
- client.state = CS_CREATED;
- client.name = NULL;
- client.map = map_create();
- client.scene = scene_create();
-
- clientplayer_init(&client);
- clientmap_init(&client);
-
- pthread_t recv_thread;
- pthread_create(&recv_thread, NULL, &reciever_thread, NULL);
-
- if (client_authenticate())
- client_loop();
-
- if (client.state != CS_DISCONNECTED)
- client_disconnect(true, NULL);
-
- if (client.name)
- free(client.name);
-
- clientmap_deinit();
-
- map_delete(client.map);
- scene_delete(client.scene);
-
- pthread_mutex_destroy(&client.mtx);
-}
-
-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 (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);
-
- init_signal_handlers();
- client_start(fd);
-
- return EXIT_SUCCESS;
-}
+++ /dev/null
-#ifndef _CLIENT_H_
-#define _CLIENT_H_
-
-#include <stdbool.h>
-#include <pthread.h>
-#include "servercommands.h"
-#include "clientcommands.h"
-#include "clientplayer.h"
-#include "map.h"
-#include "network.h"
-#include "scene.h"
-
-#ifdef RELEASE
- #define RESSOURCEPATH ""
-#else
- #define RESSOURCEPATH "../"
-#endif
-
-typedef struct Client
-{
- int fd;
- pthread_mutex_t mtx;
- ClientState state;
- char *name;
- Map *map;
- Scene *scene;
- ClientPlayer player;
-} Client;
-
-void client_disconnect(bool send, const char *detail);
-
-#endif
--- /dev/null
+#include "client/blockmesh.h"
+#include "client/client_node.h"
+#include "client/cube.h"
+
+static v3s8 fdir[6] = {
+ {+0, +0, -1},
+ {+0, +0, +1},
+ {-1, +0, +0},
+ {+1, +0, +0},
+ {+0, -1, +0},
+ {+0, +1, +0},
+};
+
+static void make_vertices(Object *object, MapBlock *block, Map *map)
+{
+ ITERATE_MAPBLOCK {
+ MapNode *node = &block->data[x][y][z];
+
+ if (node_definitions[node->type].visible) {
+ v3f offset = {x + 8.0f, y + 8.0f, z + 8.0f};
+
+ ClientNodeDefintion *client_def = &client_node_definitions[node->type];
+ object_set_texture(object, client_def->texture);
+
+ for (int f = 0; f < 6; f++) {
+ v3s8 npos = {
+ x + fdir[f].x,
+ y + fdir[f].y,
+ z + fdir[f].z,
+ };
+
+ Node neighbor;
+
+ if (npos.x >= 0 && npos.x < 16 && npos.y >= 0 && npos.y < 16 && npos.z >= 0 && npos.z < 16)
+ neighbor = block->data[npos.x][npos.y][npos.z].type;
+ else
+ neighbor = map_get_node(map, (v3s32) {npos.x + block->pos.x * 16, npos.y + block->pos.y * 16, npos.z + block->pos.z * 16}).type;
+
+ if (neighbor != NODE_UNLOADED && ! node_definitions[neighbor].visible) {
+ for (int v = 0; v < 6; v++) {
+ Vertex3D vertex = cube_vertices[f][v];
+ vertex.position.x += offset.x;
+ vertex.position.y += offset.y;
+ vertex.position.z += offset.z;
+
+ if (client_def->render)
+ client_def->render(node, &vertex);
+
+ object_add_vertex(object, &vertex);
+ }
+ }
+ }
+ }
+ }
+}
+
+void blockmesh_make(MapBlock *block, Map *map)
+{
+ Object *obj = object_create();
+ obj->pos = (v3f) {block->pos.x * 16.0f - 8.0f, block->pos.y * 16.0f - 8.0f, block->pos.z * 16.0f - 8.0f};
+
+ make_vertices(obj, block, map);
+
+ if (! object_add_to_scene(obj)) {
+ object_delete(obj);
+ obj = NULL;
+ }
+
+ if (block->extra)
+ ((Object *) block->extra)->remove = true;
+
+ block->extra = obj;
+}
--- /dev/null
+#ifndef _BLOCKMESH_H_
+#define _BLOCKMESH_H_
+
+#include "client/scene.h"
+#include "map.h"
+
+void blockmesh_make(MapBlock *block, Map *map);
+
+#endif
--- /dev/null
+#include <math.h>
+#include "client/client.h"
+#include "client/camera.h"
+
+static struct
+{
+ mat4x4 view;
+ vec3 eye, front, right, up;
+} camera;
+
+static vec3 world_up = {0.0f, 1.0f, 0.0f};
+
+struct camera_movement_dirs camera_movement_dirs;
+
+static void camera_update()
+{
+ vec3 center;
+ vec3_add(center, camera.eye, camera.front);
+
+ mat4x4_look_at(camera.view, camera.eye, center, camera.up);
+}
+
+void camera_enable(GLint loc_view)
+{
+ glUniformMatrix4fv(loc_view, 1, GL_FALSE, camera.view[0]);
+}
+
+void camera_set_position(v3f pos)
+{
+ camera.eye[0] = pos.x;
+ camera.eye[1] = pos.y;
+ camera.eye[2] = pos.z;
+
+ camera_update();
+}
+
+void camera_set_angle(f32 yaw, f32 pitch)
+{
+ camera.front[0] = camera_movement_dirs.front[0] = cos(yaw) * cos(pitch);
+ camera.front[1] = sin(pitch);
+ camera.front[2] = camera_movement_dirs.front[2] = sin(yaw) * cos(pitch);
+
+ vec3_norm(camera.front, camera.front);
+ vec3_norm(camera_movement_dirs.front, camera_movement_dirs.front);
+
+ vec3_mul_cross(camera.right, camera.front, world_up);
+ camera_movement_dirs.right[0] = camera.right[0];
+ camera_movement_dirs.right[2] = camera.right[2];
+
+ vec3_norm(camera.right, camera.right);
+ vec3_norm(camera_movement_dirs.right, camera_movement_dirs.right);
+
+ vec3_mul_cross(camera.up, camera.right, camera.front);
+ vec3_norm(camera.up, camera.up);
+
+ camera_update();
+}
--- /dev/null
+#ifndef _CAMERA_H_
+#define _CAMERA_H_
+
+#include <GL/glew.h>
+#include <GLFW/glfw3.h>
+#include <linmath.h/linmath.h>
+#include "types.h"
+
+void camera_set_position(v3f pos);
+void camera_set_angle(f32 yaw, f32 pitch);
+void camera_on_resize(int width, int height);
+void camera_enable(GLint loc_view);
+
+extern struct camera_movement_dirs
+{
+ vec3 front;
+ vec3 right;
+} camera_movement_dirs;
+
+#endif
--- /dev/null
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <errno.h>
+#include <netdb.h>
+#include "client/client.h"
+#include "client/client_map.h"
+#include "client/client_player.h"
+#include "client/game.h"
+#include "client/input.h"
+#include "signal.h"
+#include "util.h"
+
+Client client;
+
+void client_disconnect(bool send, const char *detail)
+{
+ pthread_mutex_lock(&client.mtx);
+ if (client.state != CS_DISCONNECTED) {
+ if (send)
+ write_u32(client.fd, SC_DISCONNECT);
+
+ client.state = CS_DISCONNECTED;
+ printf("Disconnected %s%s%s\n", INBRACES(detail));
+ close(client.fd);
+ }
+ pthread_mutex_unlock(&client.mtx);
+}
+
+void client_send_position(v3f pos)
+{
+ pthread_mutex_lock(&client.mtx);
+ (void) (write_u32(client.fd, SC_POS) && write_v3f32(client.fd, pos));
+ pthread_mutex_unlock(&client.mtx);
+}
+
+#include "network.c"
+
+static void *reciever_thread(__attribute__((unused)) void *unused)
+{
+ handle_packets(&client);
+
+ if (errno != EINTR)
+ client_disconnect(false, "network error");
+
+ return NULL;
+}
+
+static bool client_name_prompt()
+{
+ 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;
+}
+
+static bool client_authenticate()
+{
+ 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 void client_start(int fd)
+{
+ client.fd = fd;
+ pthread_mutex_init(&client.mtx, NULL);
+ client.state = CS_CREATED;
+ client.name = NULL;
+ client.map = map_create();
+
+ client_player_init(client.map);
+ client_map_init(client.map);
+
+ pthread_t recv_thread;
+ pthread_create(&recv_thread, NULL, &reciever_thread, NULL);
+
+ if (client_authenticate())
+ game(&client);
+
+ if (client.state != CS_DISCONNECTED)
+ client_disconnect(true, NULL);
+
+ if (client.name)
+ free(client.name);
+
+ client_map_deinit();
+
+ map_delete(client.map);
+
+ pthread_mutex_destroy(&client.mtx);
+}
+
+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 (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);
+
+ signal_handlers_init();
+ client_start(fd);
+
+ return EXIT_SUCCESS;
+}
--- /dev/null
+#ifndef _CLIENT_H_
+#define _CLIENT_H_
+
+#include <stdbool.h>
+#include <pthread.h>
+#include "client/client_commands.h"
+#include "client/scene.h"
+#include "server/server_commands.h"
+#include "map.h"
+#include "network.h"
+#include "types.h"
+
+#ifdef RELEASE
+ #define RESSOURCEPATH ""
+#else
+ #define RESSOURCEPATH "../"
+#endif
+
+typedef struct Client
+{
+ int fd;
+ pthread_mutex_t mtx;
+ ClientState state;
+ char *name;
+ Map *map;
+} Client;
+
+void client_disconnect(bool send, const char *detail);
+void client_send_position(v3f pos);
+
+#endif
--- /dev/null
+#include <stdio.h>
+#include <unistd.h>
+#include "client/client.h"
+#include "client/client_map.h"
+#include "types.h"
+
+static bool disconnect_handler(__attribute__((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;
+
+ MapBlockHeader header;
+
+ if (! read_u32(client->fd, &header))
+ return false;
+
+ char data[header];
+ if (! read_full(client->fd, data, header))
+ return false;
+
+ MapBlock *block;
+
+ if (good)
+ block = map_get_block(client->map, pos, true);
+ else
+ block = map_allocate_block(pos);
+
+ if (block->state != MBS_CREATED)
+ map_clear_meta(block);
+
+ bool ret = map_deserialize_block(block, data, header);
+
+ if (good)
+ client_map_block_changed(block);
+ else
+ map_free_block(block);
+
+ return ret;
+}
+
+CommandHandler command_handlers[CLIENT_COMMAND_COUNT] = {
+ {0},
+ {&disconnect_handler, "DISCONNECT", CS_CREATED | CS_AUTH | CS_ACTIVE},
+ {&auth_handler, "AUTH", CS_AUTH},
+ {&block_handler, "BLOCK", CS_ACTIVE},
+};
--- /dev/null
+#ifndef _CLIENT_COMMANDS_H_
+#define _CLIENT_COMMANDS_H_
+
+typedef enum
+{
+ CLIENT_COMMAND_NULL,
+ CC_DISCONNECT,
+ CC_AUTH,
+ CC_BLOCK,
+ CLIENT_COMMAND_COUNT
+} ClientCommand;
+
+#ifdef _SERVER_H_
+typedef ClientCommand RemoteCommand;
+#endif
+
+#ifdef _CLIENT_H_
+typedef ClientCommand HostCommand;
+#define HOST_COMMAND_COUNT CLIENT_COMMAND_COUNT
+#endif
+
+#endif
--- /dev/null
+#include <stdlib.h>
+#include <pthread.h>
+#include "client/blockmesh.h"
+#include "client/client_map.h"
+#include "queue.h"
+
+static struct
+{
+ Map *map;
+ Queue *queue;
+ pthread_t thread;
+ bool cancel;
+} client_map;
+
+static void set_block_ready(void *block)
+{
+ ((MapBlock *) block)->state = MBS_READY;
+}
+
+static void *meshgen_thread(__attribute__((unused)) void *unused)
+{
+ while (! client_map.cancel) {
+ MapBlock *block;
+ if ((block = queue_dequeue_callback(client_map.queue, &set_block_ready)))
+ blockmesh_make(block, client_map.map);
+ else
+ sched_yield();
+ }
+
+ return NULL;
+}
+
+void client_map_init(Map *map)
+{
+ client_map.map = map;
+ client_map.queue = queue_create();
+}
+
+void client_map_start_meshgen()
+{
+ pthread_create(&client_map.thread, NULL, &meshgen_thread, NULL);
+}
+
+void client_map_deinit()
+{
+ client_map.cancel = true;
+ queue_delete(client_map.queue);
+ if (client_map.thread)
+ pthread_join(client_map.thread, NULL);
+}
+
+static void schedule_update_block(MapBlock *block)
+{
+ if (! block)
+ return;
+
+ pthread_mutex_lock(&block->mtx);
+ if (block->state != MBS_PROCESSING) {
+ block->state = MBS_PROCESSING;
+ queue_enqueue(client_map.queue, block);
+ }
+ pthread_mutex_unlock(&block->mtx);
+}
+
+void client_map_block_changed(MapBlock *block)
+{
+ schedule_update_block(block);
+
+ schedule_update_block(map_get_block(client_map.map, (v3s32) {block->pos.x + 1, block->pos.y + 0, block->pos.z + 0}, false));
+ schedule_update_block(map_get_block(client_map.map, (v3s32) {block->pos.x + 0, block->pos.y + 1, block->pos.z + 0}, false));
+ schedule_update_block(map_get_block(client_map.map, (v3s32) {block->pos.x + 0, block->pos.y + 0, block->pos.z + 1}, false));
+ schedule_update_block(map_get_block(client_map.map, (v3s32) {block->pos.x - 1, block->pos.y - 0, block->pos.z - 0}, false));
+ schedule_update_block(map_get_block(client_map.map, (v3s32) {block->pos.x - 0, block->pos.y - 1, block->pos.z - 0}, false));
+ schedule_update_block(map_get_block(client_map.map, (v3s32) {block->pos.x - 0, block->pos.y - 0, block->pos.z - 1}, false));
+}
--- /dev/null
+#ifndef _CLIENT_MAP_H_
+#define _CLIENT_MAP_H_
+
+#include "map.h"
+
+void client_map_init(Map *map);
+void client_map_start_meshgen();
+void client_map_deinit();
+void client_map_block_changed(MapBlock *block);
+
+#endif
--- /dev/null
+#include "client/client.h"
+#include "client/client_node.h"
+#include "node.h"
+
+static void render_state_biome(MapNode *node, Vertex3D *vertex)
+{
+ vertex->color.h = node->state.biome.x;
+ vertex->color.s = node->state.biome.y;
+ vertex->color.v = node->state.biome.z;
+}
+
+ClientNodeDefintion client_node_definitions[NODE_UNLOADED] = {
+ // invalid
+ {
+ .texture_path = RESSOURCEPATH "textures/invalid.png",
+ .texture = NULL,
+ .render = NULL,
+ },
+ // air
+ {
+ .texture_path = NULL,
+ .texture = NULL,
+ .render = NULL,
+ },
+ // grass
+ {
+ .texture_path = RESSOURCEPATH "textures/grass.png",
+ .texture = NULL,
+ .render = &render_state_biome,
+ },
+ // dirt
+ {
+ .texture_path = RESSOURCEPATH "textures/dirt.png",
+ .texture = NULL,
+ .render = NULL,
+ },
+ // stone
+ {
+ .texture_path = RESSOURCEPATH "textures/stone.png",
+ .texture = NULL,
+ .render = NULL,
+ },
+ // snow
+ {
+ .texture_path = RESSOURCEPATH "textures/snow.png",
+ .texture = NULL,
+ .render = NULL,
+ },
+};
+
+void client_node_init()
+{
+ for (Node node = NODE_INVALID; node < NODE_UNLOADED; node++) {
+ ClientNodeDefintion *def = &client_node_definitions[node];
+ if (def->texture_path)
+ def->texture = texture_get(def->texture_path);
+ }
+}
--- /dev/null
+#ifndef _CLIENT_NODE_H_
+#define _CLIENT_NODE_H_
+
+#include "client/object.h"
+#include "client/texture.h"
+#include "map.h"
+
+typedef struct
+{
+ char *texture_path;
+ Texture *texture;
+ void (*render)(MapNode *node, Vertex3D *vertex);
+} ClientNodeDefintion;
+
+extern ClientNodeDefintion client_node_definitions[];
+void client_node_init();
+
+#endif
--- /dev/null
+#include "client/camera.h"
+#include "client/client.h"
+#include "client/client_player.h"
+#include "client/cube.h"
+#include "client/texture.h"
+
+struct ClientPlayer client_player;
+
+static void update_pos()
+{
+ camera_set_position((v3f) {client_player.pos.x, client_player.pos.y + client_player.eye_height, client_player.pos.z});
+ client_send_position(client_player.pos);
+
+ client_player.obj->pos = client_player.pos;
+ object_transform(client_player.obj);
+}
+
+void client_player_init(Map *map)
+{
+ client_player.map = map;
+ client_player.pos = (v3f) {0.0f, 200.0f, 0.0f};
+ client_player.velocity = (v3f) {0.0f, 0.0f, 0.0f};
+ client_player.box = (aabb3f) {{-0.3f, 0.0f, -0.3f}, {0.3f, 1.75f, 0.3f}};
+ client_player.yaw = client_player.pitch = 0.0f;
+ client_player.eye_height = 1.5f;
+}
+
+void client_player_add_to_scene()
+{
+ client_player.obj = object_create();
+ client_player.obj->scale = (v3f) {0.6f, 1.75f, 0.6f};
+ client_player.obj->visible = false;
+
+ object_set_texture(client_player.obj, texture_get(RESSOURCEPATH "textures/player.png"));
+
+ for (int f = 0; f < 6; f++) {
+ for (int v = 0; v < 6; v++) {
+ Vertex3D vertex = cube_vertices[f][v];
+ vertex.position.y += 0.5;
+ object_add_vertex(client_player.obj, &vertex);
+ }
+ }
+
+ update_pos();
+}
+
+static aabb3f get_box()
+{
+ return (aabb3f) {
+ {client_player.box.min.x + client_player.pos.x, client_player.box.min.y + client_player.pos.y, client_player.box.min.z + client_player.pos.z},
+ {client_player.box.max.x + client_player.pos.x, client_player.box.max.y + client_player.pos.y, client_player.box.max.z + client_player.pos.z},
+ };
+}
+
+static aabb3s32 round_box(aabb3f box)
+{
+ return (aabb3s32) {
+ {floor(box.min.x + 0.5f), floor(box.min.y + 0.5f), floor(box.min.z + 0.5f)},
+ {ceil(box.max.x - 0.5f), ceil(box.max.y - 0.5f), ceil(box.max.z - 0.5f)},
+ };
+}
+
+static bool is_solid(s32 x, s32 y, s32 z)
+{
+ Node node = map_get_node(client_player.map, (v3s32) {x, y, z}).type;
+ return node == NODE_UNLOADED || node_definitions[node].solid;
+}
+
+static bool can_jump()
+{
+ aabb3f fbox = get_box();
+ fbox.min.y -= 0.5f;
+
+ aabb3s32 box = round_box(fbox);
+
+ if (fbox.min.y - (f32) box.min.y > 0.01f)
+ return false;
+
+ for (s32 x = box.min.x; x <= box.max.x; x++)
+ for (s32 z = box.min.z; z <= box.max.z; z++)
+ if (is_solid(x, box.min.y, z))
+ return true;
+
+ return false;
+}
+
+void client_player_jump()
+{
+ if (can_jump())
+ client_player.velocity.y += 10.0f;
+}
+
+void client_player_tick(f64 dtime)
+{
+ v3f old_pos = client_player.pos;
+ v3f old_velocity = client_player.velocity;
+
+ client_player.velocity.y -= 32.0f * dtime;
+
+#define GETS(vec, comp) *(s32 *) ((char *) &vec + offsetof(v3s32, comp))
+#define GETF(vec, comp) *(f32 *) ((char *) &vec + offsetof(v3f32, comp))
+#define PHYSICS(a, b, c) { \
+ f32 v = (GETF(client_player.velocity, a) + GETF(old_velocity, a)) / 2.0f; \
+ if (v == 0.0f) \
+ goto a ## _physics_done; \
+ aabb3s32 box = round_box(get_box()); \
+ v3f old_pos = client_player.pos; \
+ GETF(client_player.pos, a) += v * dtime; \
+ s32 dir; \
+ f32 offset; \
+ if (v > 0.0f) { \
+ dir = +1; \
+ offset = GETF(client_player.box.max, a); \
+ GETS(box.min, a) = ceil(GETF(old_pos, a) + offset + 0.5f); \
+ GETS(box.max, a) = floor(GETF(client_player.pos, a) + offset + 0.5f); \
+ } else { \
+ dir = -1; \
+ offset = GETF(client_player.box.min, a); \
+ GETS(box.min, a) = floor(GETF(old_pos, a) + offset - 0.5f); \
+ GETS(box.max, a) = ceil(GETF(client_player.pos, a) + offset - 0.5f); \
+ } \
+ GETS(box.max, a) += dir; \
+ for (s32 a = GETS(box.min, a); a != GETS(box.max, a); a += dir) { \
+ for (s32 b = GETS(box.min, b); b <= GETS(box.max, b); b++) { \
+ for (s32 c = GETS(box.min, c); c <= GETS(box.max, c); c++) { \
+ if (is_solid(x, y, z)) { \
+ GETF(client_player.pos, a) = (f32) a - offset - 0.5f * (f32) dir; \
+ GETF(client_player.velocity, a) = 0.0f; \
+ goto a ## _physics_done; \
+ } \
+ } \
+ } \
+ } \
+ a ## _physics_done: (void) 0;\
+ }
+
+ PHYSICS(x, y, z)
+ PHYSICS(y, x, z)
+ PHYSICS(z, x, y)
+
+#undef GETS
+#undef GETF
+#undef PHYSICS
+
+ if (old_pos.x != client_player.pos.x || old_pos.y != client_player.pos.y || old_pos.z != client_player.pos.z)
+ update_pos();
+}
--- /dev/null
+#ifndef _CLIENT_PLAYER_H_
+#define _CLIENT_PLAYER_H_
+
+#include "client/client.h"
+#include "client/object.h"
+#include "types.h"
+
+extern struct ClientPlayer
+{
+ v3f pos;
+ v3f velocity;
+ aabb3f box;
+ f32 yaw, pitch;
+ f32 eye_height;
+ Object *obj;
+ Map *map;
+} client_player;
+
+void client_player_init(Map *map);
+void client_player_add_to_scene();
+void client_player_jump();
+void client_player_tick(f64 dtime);
+
+#endif
--- /dev/null
+#include "client/cube.h"
+
+Vertex3D cube_vertices[6][6] = {
+ {
+ {{-0.5, -0.5, -0.5}, {+0.0, +0.0}, {+1.0, +0.0, +1.0}},
+ {{+0.5, -0.5, -0.5}, {+1.0, +0.0}, {+1.0, +0.0, +1.0}},
+ {{+0.5, +0.5, -0.5}, {+1.0, +1.0}, {+1.0, +0.0, +1.0}},
+ {{+0.5, +0.5, -0.5}, {+1.0, +1.0}, {+1.0, +0.0, +1.0}},
+ {{-0.5, +0.5, -0.5}, {+0.0, +1.0}, {+1.0, +0.0, +1.0}},
+ {{-0.5, -0.5, -0.5}, {+0.0, +0.0}, {+1.0, +0.0, +1.0}},
+ },
+ {
+ {{-0.5, -0.5, +0.5}, {+0.0, +0.0}, {+1.0, +0.0, +1.0}},
+ {{+0.5, +0.5, +0.5}, {+1.0, +1.0}, {+1.0, +0.0, +1.0}},
+ {{+0.5, -0.5, +0.5}, {+1.0, +0.0}, {+1.0, +0.0, +1.0}},
+ {{+0.5, +0.5, +0.5}, {+1.0, +1.0}, {+1.0, +0.0, +1.0}},
+ {{-0.5, -0.5, +0.5}, {+0.0, +0.0}, {+1.0, +0.0, +1.0}},
+ {{-0.5, +0.5, +0.5}, {+0.0, +1.0}, {+1.0, +0.0, +1.0}},
+ },
+ {
+ {{-0.5, +0.5, +0.5}, {+1.0, +1.0}, {+1.0, +0.0, +1.0}},
+ {{-0.5, -0.5, -0.5}, {+0.0, +0.0}, {+1.0, +0.0, +1.0}},
+ {{-0.5, +0.5, -0.5}, {+0.0, +1.0}, {+1.0, +0.0, +1.0}},
+ {{-0.5, -0.5, -0.5}, {+0.0, +0.0}, {+1.0, +0.0, +1.0}},
+ {{-0.5, +0.5, +0.5}, {+1.0, +1.0}, {+1.0, +0.0, +1.0}},
+ {{-0.5, -0.5, +0.5}, {+1.0, +0.0}, {+1.0, +0.0, +1.0}},
+ },
+ {
+ {{+0.5, +0.5, +0.5}, {+1.0, +1.0}, {+1.0, +0.0, +1.0}},
+ {{+0.5, +0.5, -0.5}, {+0.0, +1.0}, {+1.0, +0.0, +1.0}},
+ {{+0.5, -0.5, -0.5}, {+0.0, +0.0}, {+1.0, +0.0, +1.0}},
+ {{+0.5, -0.5, -0.5}, {+0.0, +0.0}, {+1.0, +0.0, +1.0}},
+ {{+0.5, -0.5, +0.5}, {+1.0, +0.0}, {+1.0, +0.0, +1.0}},
+ {{+0.5, +0.5, +0.5}, {+1.0, +1.0}, {+1.0, +0.0, +1.0}},
+ },
+ {
+ {{-0.5, -0.5, -0.5}, {+0.0, +1.0}, {+1.0, +0.0, +1.0}},
+ {{+0.5, -0.5, -0.5}, {+1.0, +1.0}, {+1.0, +0.0, +1.0}},
+ {{+0.5, -0.5, +0.5}, {+1.0, +0.0}, {+1.0, +0.0, +1.0}},
+ {{+0.5, -0.5, +0.5}, {+1.0, +0.0}, {+1.0, +0.0, +1.0}},
+ {{-0.5, -0.5, +0.5}, {+0.0, +0.0}, {+1.0, +0.0, +1.0}},
+ {{-0.5, -0.5, -0.5}, {+0.0, +1.0}, {+1.0, +0.0, +1.0}},
+ },
+ {
+ {{-0.5, +0.5, -0.5}, {+0.0, +1.0}, {+1.0, +0.0, +1.0}},
+ {{+0.5, +0.5, -0.5}, {+1.0, +1.0}, {+1.0, +0.0, +1.0}},
+ {{+0.5, +0.5, +0.5}, {+1.0, +0.0}, {+1.0, +0.0, +1.0}},
+ {{+0.5, +0.5, +0.5}, {+1.0, +0.0}, {+1.0, +0.0, +1.0}},
+ {{-0.5, +0.5, +0.5}, {+0.0, +0.0}, {+1.0, +0.0, +1.0}},
+ {{-0.5, +0.5, -0.5}, {+0.0, +1.0}, {+1.0, +0.0, +1.0}},
+ },
+};
+
--- /dev/null
+#ifndef _CUBE_H_
+#define _CUBE_H_
+
+#include "client/object.h"
+
+extern Vertex3D cube_vertices[6][6];
+
+#endif
--- /dev/null
+#include <string.h>
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include "client/client.h"
+#include "client/font.h"
+#include "client/hud.h"
+
+#define NUM_CHARS 128
+
+
+typedef struct
+{
+ Texture *texture;
+ v2s32 bearing;
+ u32 advance;
+} Character;
+
+static struct
+{
+ FT_Library library;
+ FT_Face face;
+ Character chars[NUM_CHARS];
+ GLfloat height;
+} font;
+
+typedef struct
+{
+ GLfloat x, y;
+} __attribute__((packed)) VertexFontPosition;
+
+typedef struct
+{
+ GLfloat s, t;
+} __attribute__((packed)) VertexFontTextureCoordinates;
+
+typedef struct
+{
+ VertexFontPosition position;
+ VertexFontTextureCoordinates textureCoordinates;
+} __attribute__((packed)) VertexFont;
+
+static VertexAttribute vertex_attributes[2] = {
+ // position
+ {
+ .type = GL_FLOAT,
+ .length = 2,
+ .size = sizeof(VertexFontPosition),
+ },
+ // textureCoordinates
+ {
+ .type = GL_FLOAT,
+ .length = 2,
+ .size = sizeof(VertexFontTextureCoordinates),
+ },
+};
+
+static VertexLayout vertex_layout = {
+ .attributes = vertex_attributes,
+ .count = 2,
+ .size = sizeof(VertexFont),
+};
+
+bool font_init()
+{
+ if (FT_Init_FreeType(&font.library)) {
+ fprintf(stderr, "Failed to initialize Freetype\n");
+ return false;
+ }
+
+ if (FT_New_Face(font.library, RESSOURCEPATH "fonts/Ubuntu.ttf", 0, &font.face)) {
+ fprintf(stderr, "Failed to load Ubuntu.ttf\n");
+ return false;
+ }
+
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+ FT_Set_Pixel_Sizes(font.face, 0, 24);
+
+ for (unsigned char c = 0; c < NUM_CHARS; c++) {
+ if (FT_Load_Char(font.face, c, FT_LOAD_RENDER)) {
+ fprintf(stderr, "Failed to load glyph %c\n", c);
+
+ font.chars[c] = (Character) {
+ .texture = NULL,
+ .bearing = {0, 0},
+ .advance = 0,
+ };
+ } else {
+ font.chars[c] = (Character) {
+ .texture = texture_create(font.face->glyph->bitmap.buffer, font.face->glyph->bitmap.width, font.face->glyph->bitmap.rows, GL_RED),
+ .bearing = {font.face->glyph->bitmap_left, font.face->glyph->bitmap_top},
+ .advance = font.face->glyph->advance.x,
+ };
+ }
+ }
+
+ font.height = font.chars['|'].texture->height;
+
+ FT_Done_Face(font.face);
+ FT_Done_FreeType(font.library);
+
+ return true;
+}
+
+void font_deinit()
+{
+ for (unsigned char c = 0; c < NUM_CHARS; c++) {
+ if (font.chars[c].texture)
+ texture_delete(font.chars[c].texture);
+ }
+}
+
+Font *font_create(char *text)
+{
+ Font *fnt = malloc(sizeof(fnt));
+
+ size_t len = strlen(text);
+
+ fnt->meshes = malloc(sizeof(Mesh *) * len);
+ fnt->meshes_count = len;
+
+ GLfloat offset = 0.0f;
+
+ for (size_t i = 0; i < len; i++) {
+ unsigned char c = text[i];
+
+ if (c >= NUM_CHARS || ! font.chars[c].texture)
+ c = '?';
+
+ Character *ch = &font.chars[c];
+
+ GLfloat width = ch->texture->width;
+ GLfloat height = ch->texture->height;
+
+ GLfloat x = ch->bearing.x + offset;
+ GLfloat y = font.height - ch->bearing.y;
+
+ VertexFont vertices[6] = {
+ {{x, y }, {0.0f, 0.0f}},
+ {{x, y + height}, {0.0f, 1.0f}},
+ {{x + width, y + height}, {1.0f, 1.0f}},
+ {{x, y }, {0.0f, 0.0f}},
+ {{x + width, y + height}, {1.0f, 1.0f}},
+ {{x + width, y }, {1.0f, 0.0f}},
+ };
+
+ Mesh *mesh = fnt->meshes[i] = mesh_create();
+ mesh->texture = ch->texture->id;
+ mesh->layout = &vertex_layout;
+ mesh->vertices = vertices;
+ mesh->vertices_count = 6;
+ mesh_configure(mesh);
+
+ offset += ch->advance >> 6;
+ }
+
+ return fnt;
+}
+
+void font_delete(Font *fnt)
+{
+ for (size_t i = 0; i < fnt->meshes_count; i++)
+ mesh_delete(fnt->meshes[i]);
+
+ free(fnt);
+}
+
+void font_render(Font *fnt)
+{
+ for (size_t i = 0; i < fnt->meshes_count; i++)
+ mesh_render(fnt->meshes[i]);
+}
--- /dev/null
+#ifndef _FONT_H_
+#define _FONT_H_
+
+#include <stdbool.h>
+#include <stddef.h>
+#include "client/mesh.h"
+
+typedef struct
+{
+ Mesh **meshes;
+ size_t meshes_count;
+} Font;
+
+bool font_init();
+void font_deinit();
+Font *font_create(char *text);
+void font_delete(Font *fnt);
+void font_render(Font *fnt);
+
+#endif
--- /dev/null
+#include <stdio.h>
+#include <unistd.h>
+#include <GL/glew.h>
+#include <GL/gl.h>
+#include <GLFW/glfw3.h>
+#include "client/camera.h"
+#include "client/client.h"
+#include "client/client_map.h"
+#include "client/client_node.h"
+#include "client/client_player.h"
+#include "client/hud.h"
+#include "client/input.h"
+#include "client/font.h"
+#include "client/window.h"
+#include "signal.h"
+
+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) {
+ clock_gettime(CLOCK_REALTIME, &ts);
+ f64 dtime = (f64) (ts.tv_sec - ts_old.tv_sec) + (f64) (ts.tv_nsec - ts_old.tv_nsec) / 1000000000.0;
+ ts_old = ts;
+
+ glEnable(GL_DEPTH_TEST);
+ glEnable(GL_BLEND);
+ glEnable(GL_MULTISAMPLE);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ glClearColor(0.52941176470588f, 0.8078431372549f, 0.92156862745098f, 1.0f);
+
+ input_tick();
+ client_player_tick(dtime);
+
+ scene_render();
+ hud_render();
+ // font_render();
+
+ glfwSwapBuffers(window.handle);
+ glfwPollEvents();
+ }
+}
+
+void game(Client *client)
+{
+ int width, height;
+ width = 1250;
+ height = 750;
+
+ if (! window_init(width, height))
+ return;
+
+ if (! font_init())
+ return;
+
+ if (! scene_init())
+ return;
+
+ scene_on_resize(width, height);
+
+ client_node_init();
+ client_map_start_meshgen();
+
+ camera_set_position((v3f) {0.0f, 0.0f, 0.0f});
+ camera_set_angle(0.0f, 0.0f);
+
+ hud_init();
+ hud_on_resize(width, height);
+
+ hud_add((HUDElementDefinition) {
+ .type = HUD_IMAGE,
+ .pos = {0.0f, 0.0f, 0.0f},
+ .offset = {0, 0},
+ .type_def = {
+ .image = {
+ .texture = texture_get(RESSOURCEPATH "textures/crosshair.png"),
+ .scale = {1.0f, 1.0f},
+ .scale_type = HUD_SCALE_TEXTURE,
+ },
+ },
+ });
+
+ hud_add((HUDElementDefinition) {
+ .type = HUD_TEXT,
+ .pos = {-1.0f, -1.0f, 0.0f},
+ .offset = {5, 0},
+ .type_def = {
+ .text = {
+ .text = "Dragonblocks Alpha",
+ .color = {1.0f, 1.0f, 1.0f},
+ },
+ },
+ });
+
+ input_init();
+
+ client_player_add_to_scene();
+
+ game_loop(client);
+
+ font_deinit();
+ hud_deinit();
+ scene_deinit();
+}
--- /dev/null
+#ifndef _GAME_H_
+#define _GAME_H_
+
+#include "client/client.h"
+
+void game(Client *client);
+
+#endif
--- /dev/null
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <GL/glew.h>
+#include <GL/gl.h>
+#include "client/client.h"
+#include "client/cube.h"
+#include "client/hud.h"
+#include "client/mesh.h"
+#include "client/shader.h"
+#include "client/vertex.h"
+
+static struct
+{
+ List elements;
+
+ GLuint image_prog;
+ GLint image_loc_model;
+ GLint image_loc_projection;
+ Mesh *image_mesh;
+
+ GLuint font_prog;
+ GLint font_loc_model;
+ GLint font_loc_projection;
+ GLint font_loc_text_color;
+
+ mat4x4 projection;
+ int width, height;
+} hud;
+
+typedef struct
+{
+ GLfloat x, y;
+} __attribute__((packed)) VertexImagePosition;
+
+typedef struct
+{
+ GLfloat s, t;
+} __attribute__((packed)) VertexImageTextureCoordinates;
+
+typedef struct
+{
+ VertexImagePosition position;
+ VertexImageTextureCoordinates textureCoordinates;
+} __attribute__((packed)) VertexImage;
+
+static VertexAttribute image_vertex_attributes[2] = {
+ // position
+ {
+ .type = GL_FLOAT,
+ .length = 2,
+ .size = sizeof(VertexImagePosition),
+ },
+ // textureCoordinates
+ {
+ .type = GL_FLOAT,
+ .length = 2,
+ .size = sizeof(VertexImageTextureCoordinates),
+ },
+};
+
+static VertexLayout image_vertex_layout = {
+ .attributes = image_vertex_attributes,
+ .count = 2,
+ .size = sizeof(VertexImage),
+};
+
+static VertexImage image_vertices[6] = {
+ {{-0.5, -0.5}, {+0.0, +0.0}},
+ {{+0.5, -0.5}, {+1.0, +0.0}},
+ {{+0.5, +0.5}, {+1.0, +1.0}},
+ {{+0.5, +0.5}, {+1.0, +1.0}},
+ {{-0.5, +0.5}, {+0.0, +1.0}},
+ {{-0.5, -0.5}, {+0.0, +0.0}},
+};
+
+bool hud_init()
+{
+ if (! shader_program_create(RESSOURCEPATH "shaders/hud/image", &hud.image_prog)) {
+ fprintf(stderr, "Failed to create HUD image shader program\n");
+ return false;
+ }
+
+ hud.image_loc_model = glGetUniformLocation(hud.image_prog, "model");
+ hud.image_loc_projection = glGetUniformLocation(hud.image_prog, "projection");
+
+ if (! shader_program_create(RESSOURCEPATH "shaders/hud/font", &hud.font_prog)) {
+ fprintf(stderr, "Failed to create HUD font shader program\n");
+ return false;
+ }
+
+ hud.font_loc_model = glGetUniformLocation(hud.font_prog, "model");
+ hud.font_loc_projection = glGetUniformLocation(hud.font_prog, "projection");
+ hud.font_loc_text_color = glGetUniformLocation(hud.font_prog, "textColor");
+
+
+ hud.elements = list_create(NULL);
+
+ hud.image_mesh = mesh_create();
+ hud.image_mesh->vertices = image_vertices;
+ hud.image_mesh->vertices_count = 6;
+ hud.image_mesh->layout = &image_vertex_layout;
+
+ return true;
+}
+
+static void free_element(void *key, __attribute__((unused)) void *value, __attribute__((unused)) void *arg)
+{
+ HUDElement *element = key;
+
+ if (element->def.type == HUD_TEXT)
+ font_delete(element->type_data.text);
+
+ free(element);
+}
+
+void hud_deinit()
+{
+ glDeleteProgram(hud.image_prog);
+ glDeleteProgram(hud.font_prog);
+ mesh_delete(hud.image_mesh);
+ list_clear_func(&hud.elements, &free_element, NULL);
+}
+
+static void element_transform(HUDElement *element)
+{
+ v3f pos = {
+ (f32) element->def.offset.x + (1.0f + element->def.pos.x) / 2.0f * (f32) hud.width,
+ (f32) element->def.offset.y + (1.0f + element->def.pos.y) / 2.0f * (f32) hud.height,
+ element->def.pos.z,
+ };
+
+ mat4x4_translate(element->transform, pos.x, pos.y, pos.z);
+
+ if (element->def.type == HUD_IMAGE) {
+ v2f scale = element->def.type_def.image.scale;
+
+ switch (element->def.type_def.image.scale_type) {
+ case HUD_SCALE_TEXTURE:
+ scale.x *= element->def.type_def.image.texture->width;
+ scale.y *= element->def.type_def.image.texture->height;
+
+ break;
+
+ case HUD_SCALE_SCREEN:
+ scale.x *= hud.width * 2.0f;
+ scale.y *= hud.height * 2.0f;
+
+ break;
+
+ default:
+ break;
+ }
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wpedantic"
+ mat4x4_scale_aniso(element->transform, element->transform, scale.x, scale.y, 1.0f);
+ }
+#pragma GCC diagnostic pop
+}
+
+void hud_on_resize(int width, int height)
+{
+ hud.width = width;
+ hud.height = height;
+
+ mat4x4_ortho(hud.projection, 0, width, height, 0, -1.0f, 1.0f);
+ glProgramUniformMatrix4fv(hud.image_prog, hud.image_loc_projection, 1, GL_FALSE, hud.projection[0]);
+ glProgramUniformMatrix4fv(hud.font_prog, hud.font_loc_projection, 1, GL_FALSE, hud.projection[0]);
+
+ for (ListPair *pair = hud.elements.first; pair != NULL; pair = pair->next)
+ element_transform(pair->key);
+}
+
+void hud_render()
+{
+ glActiveTexture(GL_TEXTURE0);
+
+ for (ListPair *pair = hud.elements.first; pair != NULL; pair = pair->next) {
+ HUDElement *element = pair->key;
+
+ if (element->visible) {
+ switch (element->def.type) {
+ case HUD_IMAGE:
+ glUseProgram(hud.image_prog);
+ glUniformMatrix4fv(hud.image_loc_model, 1, GL_FALSE, element->transform[0]);
+ hud.image_mesh->texture = element->def.type_def.image.texture->id;
+ mesh_render(hud.image_mesh);
+
+ break;
+
+ case HUD_TEXT:
+ glUseProgram(hud.font_prog);
+ glUniformMatrix4fv(hud.font_loc_model, 1, GL_FALSE, element->transform[0]);
+ glUniform3f(hud.font_loc_text_color, element->def.type_def.text.color.x, element->def.type_def.text.color.y, element->def.type_def.text.color.z);
+ font_render(element->type_data.text);
+
+ break;
+ };
+ }
+ }
+}
+
+HUDElement *hud_add(HUDElementDefinition def)
+{
+ HUDElement *element = malloc(sizeof(HUDElement));
+ element->def = def;
+ element->visible = true;
+
+ element_transform(element);
+
+ if (element->def.type == HUD_TEXT)
+ element->type_data.text = font_create(element->def.type_def.text.text);
+
+ list_set(&hud.elements, element, NULL);
+
+ return element;
+}
--- /dev/null
+#ifndef _HUD_H_
+#define _HUD_H_
+
+#include <stdbool.h>
+#include <linmath.h/linmath.h>
+#include "client/font.h"
+#include "client/texture.h"
+#include "types.h"
+
+typedef enum
+{
+ HUD_SCALE_TEXTURE,
+ HUD_SCALE_SCREEN,
+ HUD_SCALE_NONE,
+} HUDImageScaleType;
+
+typedef enum
+{
+ HUD_IMAGE,
+ HUD_TEXT,
+} HUDElementType;
+
+typedef struct
+{
+ HUDElementType type;
+ v3f pos;
+ v2s32 offset;
+ union
+ {
+ struct {
+ Texture *texture;
+ v2f scale;
+ HUDImageScaleType scale_type;
+ } image;
+ struct {
+ char *text;
+ v3f color;
+ } text;
+ } type_def;
+} HUDElementDefinition;
+
+typedef struct
+{
+ HUDElementDefinition def;
+ bool visible;
+ mat4x4 transform;
+ union
+ {
+ Font *text;
+ } type_data;
+} HUDElement;
+
+bool hud_init();
+void hud_deinit();
+void hud_on_resize(int width, int height);
+void hud_render();
+HUDElement *hud_add(HUDElementDefinition def);
+
+#endif
--- /dev/null
+#include <math.h>
+#include "client/camera.h"
+#include "client/client.h"
+#include "client/client_player.h"
+#include "client/hud.h"
+#include "client/input.h"
+#include "client/window.h"
+
+typedef struct
+{
+ int key;
+ bool was_pressed;
+ bool fired;
+} KeyListener;
+
+static struct
+{
+ HUDElement *pause_menu_hud;
+ bool paused;
+ KeyListener pause_listener;
+ KeyListener fullscreen_listener;
+} input;
+
+void input_on_cursor_pos(double current_x, double current_y)
+{
+ if (input.paused)
+ return;
+
+ static double last_x, last_y = 0.0;
+
+ double delta_x = current_x - last_x;
+ double delta_y = current_y - last_y;
+ last_x = current_x;
+ last_y = current_y;
+
+ client_player.yaw += (f32) delta_x * M_PI / 180.0f / 8.0f;
+ client_player.pitch -= (f32) delta_y * M_PI / 180.0f / 8.0f;
+
+ client_player.pitch = fmax(fmin(client_player.pitch, M_PI / 2.0f - 0.01f), -M_PI / 2.0f + 0.01f);
+
+ camera_set_angle(client_player.yaw, client_player.pitch);
+}
+
+static bool move(int forward, int backward, vec3 dir)
+{
+ f32 sign;
+ f32 speed = 4.317f;
+
+ if (glfwGetKey(window.handle, forward) == GLFW_PRESS)
+ sign = +1.0f;
+ else if (glfwGetKey(window.handle, backward) == GLFW_PRESS)
+ sign = -1.0f;
+ else
+ return false;
+
+ client_player.velocity.x += dir[0] * speed * sign;
+ client_player.velocity.z += dir[2] * speed * sign;
+
+ return true;
+}
+
+static void enter_game()
+{
+ glfwSetInputMode(window.handle, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
+ input.pause_menu_hud->visible = false;
+}
+
+static void do_key_listener(KeyListener *listener)
+{
+ bool is_pressed = glfwGetKey(window.handle, listener->key) == GLFW_PRESS;
+ listener->fired = listener->was_pressed && ! is_pressed;
+ listener->was_pressed = is_pressed;
+}
+
+static KeyListener create_key_listener(int key)
+{
+ return (KeyListener) {
+ .key = key,
+ .was_pressed = false,
+ .fired = false,
+ };
+}
+
+void input_tick()
+{
+ do_key_listener(&input.pause_listener);
+ do_key_listener(&input.fullscreen_listener);
+
+ if (input.pause_listener.fired) {
+ input.paused = ! input.paused;
+
+ if (input.paused) {
+ glfwSetInputMode(window.handle, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
+ input.pause_menu_hud->visible = true;
+ } else {
+ enter_game();
+ }
+ }
+
+ if (input.fullscreen_listener.fired) {
+ if (window.fullscreen)
+ window_exit_fullscreen();
+ else
+ window_enter_fullscreen();
+ }
+
+ client_player.velocity.x = 0.0f;
+ client_player.velocity.z = 0.0f;
+
+ if (! input.paused) {
+ move(GLFW_KEY_W, GLFW_KEY_S, camera_movement_dirs.front);
+ move(GLFW_KEY_D, GLFW_KEY_A, camera_movement_dirs.right);
+
+ if (glfwGetKey(window.handle, GLFW_KEY_SPACE) == GLFW_PRESS)
+ client_player_jump();
+ }
+}
+
+void input_init()
+{
+ input.paused = false;
+
+ input.pause_listener = create_key_listener(GLFW_KEY_ESCAPE);
+ input.fullscreen_listener = create_key_listener(GLFW_KEY_F11);
+
+ input.pause_menu_hud = hud_add((HUDElementDefinition) {
+ .type = HUD_IMAGE,
+ .pos = {-1.0f, -1.0f, 0.5f},
+ .offset = {0, 0},
+ .type_def = {
+ .image = {
+ .texture = texture_get(RESSOURCEPATH "textures/pause_layer.png"),
+ .scale = {1.0f, 1.0f},
+ .scale_type = HUD_SCALE_SCREEN
+ },
+ },
+ });
+
+ glfwSetInputMode(window.handle, GLFW_STICKY_KEYS, GL_TRUE);
+
+ enter_game();
+}
--- /dev/null
+#ifndef _INPUT_H_
+#define _INPUT_H_
+
+void input_tick();
+void input_init();
+void input_on_cursor_pos(double current_x, double current_y);
+
+#endif
--- /dev/null
+#include <stdlib.h>
+#include <stddef.h>
+#include "client/mesh.h"
+
+Mesh *mesh_create()
+{
+ Mesh *mesh = malloc(sizeof(Mesh));
+ mesh->VAO = mesh->VBO = 0;
+ mesh->free_vertices = false;
+
+ return mesh;
+}
+
+void mesh_delete(Mesh *mesh)
+{
+ if (mesh->vertices && mesh->free_vertices)
+ free(mesh->vertices);
+
+ if (mesh->VAO)
+ glDeleteVertexArrays(1, &mesh->VAO);
+
+ if (mesh->VBO)
+ glDeleteBuffers(1, &mesh->VAO);
+
+ free(mesh);
+}
+
+void mesh_configure(Mesh *mesh)
+{
+ glGenVertexArrays(1, &mesh->VAO);
+ glGenBuffers(1, &mesh->VBO);
+
+ glBindVertexArray(mesh->VAO);
+ glBindBuffer(GL_ARRAY_BUFFER, mesh->VBO);
+
+ glBufferData(GL_ARRAY_BUFFER, mesh->vertices_count * mesh->layout->size, mesh->vertices, GL_STATIC_DRAW);
+
+ vertex_layout_configure(mesh->layout);
+
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glBindVertexArray(0);
+
+ if (mesh->free_vertices)
+ free(mesh->vertices);
+
+ mesh->vertices = NULL;
+}
+
+void mesh_render(Mesh *mesh)
+{
+ if (mesh->vertices)
+ mesh_configure(mesh);
+
+ glBindTexture(GL_TEXTURE_2D, mesh->texture);
+ glBindVertexArray(mesh->VAO);
+ glDrawArrays(GL_TRIANGLES, 0, mesh->vertices_count);
+
+}
--- /dev/null
+#ifndef _MESH_H_
+#define _MESH_H_
+
+#include <GL/glew.h>
+#include <GL/gl.h>
+#include <stdbool.h>
+#include "client/vertex.h"
+
+typedef struct
+{
+ GLuint VAO, VBO;
+ GLuint texture;
+ GLvoid *vertices;
+ GLuint vertices_count;
+ bool free_vertices;
+ VertexLayout *layout;
+} Mesh;
+
+Mesh *mesh_create();
+void mesh_delete(Mesh *mesh);
+void mesh_configure(Mesh *mesh);
+void mesh_render(Mesh *mesh);
+
+#endif
--- /dev/null
+#include <stdlib.h>
+#include "client/object.h"
+#include "client/scene.h"
+
+static VertexAttribute vertex_attributes[3] = {
+ // position
+ {
+ .type = GL_FLOAT,
+ .length = 3,
+ .size = sizeof(Vertex3DPosition),
+ },
+ // textureCoordinates
+ {
+ .type = GL_FLOAT,
+ .length = 2,
+ .size = sizeof(Vertex3DTextureCoordinates),
+
+ },
+ // color
+ {
+ .type = GL_FLOAT,
+ .length = 3,
+ .size = sizeof(Vertex3DColor),
+ },
+};
+
+static VertexLayout vertex_layout = {
+ .attributes = vertex_attributes,
+ .count = 3,
+ .size = sizeof(Vertex3D),
+};
+
+Object *object_create()
+{
+ Object *obj = malloc(sizeof(Object));
+ obj->pos = (v3f) {0.0f, 0.0f, 0.0f};
+ obj->rot = (v3f) {0.0f, 0.0f, 0.0f};
+ obj->scale = (v3f) {1.0f, 1.0f, 1.0f};
+ obj->angle = 0.0f;
+ obj->remove = false;
+ obj->meshes = NULL;
+ obj->meshes_count = 0;
+ obj->visible = true;
+ obj->wireframe = false;
+ obj->current_face = NULL;
+ obj->faces = array_create(sizeof(ObjectFace));
+
+ return obj;
+}
+
+void object_delete(Object *obj)
+{
+ for (size_t i = 0; i < obj->meshes_count; i++)
+ mesh_delete(obj->meshes[i]);
+
+ free(obj);
+}
+
+void object_set_texture(Object *obj, Texture *texture)
+{
+ if (obj->current_face && obj->current_face->texture == texture->id)
+ return;
+
+ ObjectFace face = {
+ .texture = texture->id,
+ .vertices = array_create(sizeof(Vertex3D)),
+ };
+
+ array_append(&obj->faces, &face);
+ obj->current_face = &((ObjectFace *) obj->faces.ptr)[obj->faces.siz - 1];
+}
+
+void object_add_vertex(Object *obj, Vertex3D *vertex)
+{
+ array_append(&obj->current_face->vertices, vertex);
+}
+
+static int qsort_compare_faces(const void *f1, const void *f2)
+{
+ return ((ObjectFace *) f1)->texture - ((ObjectFace *) f2)->texture;
+}
+
+static void add_mesh(Array *meshes, Array *vertices, GLuint texture)
+{
+ if (vertices->siz > 0) {
+ Mesh *mesh = mesh_create();
+ mesh->vertices = vertices->ptr;
+ mesh->vertices_count = vertices->siz;
+ mesh->free_vertices = true;
+ mesh->texture = texture;
+ mesh->layout = &vertex_layout;
+
+ array_append(meshes, &mesh);
+ }
+
+ *vertices = array_create(sizeof(Vertex3D));
+}
+
+bool object_add_to_scene(Object *obj)
+{
+ if (obj->faces.siz == 0)
+ return false;
+
+ object_transform(obj);
+
+ qsort(obj->faces.ptr, obj->faces.siz, sizeof(ObjectFace), &qsort_compare_faces);
+
+ Array meshes = array_create(sizeof(Mesh *));
+ Array vertices = array_create(sizeof(Vertex3D));
+ GLuint texture = 0;
+
+ for (size_t f = 0; f < obj->faces.siz; f++) {
+ ObjectFace *face = &((ObjectFace *) obj->faces.ptr)[f];
+
+ if (face->texture != texture) {
+ add_mesh(&meshes, &vertices, texture);
+ texture = face->texture;
+ }
+
+ for (size_t v = 0; v < face->vertices.siz; v++)
+ array_append(&vertices, &((Vertex3D *) face->vertices.ptr)[v]);
+ free(face->vertices.ptr);
+ }
+ add_mesh(&meshes, &vertices, texture);
+ free(obj->faces.ptr);
+
+ array_copy(&meshes, (void *) &obj->meshes, &obj->meshes_count);
+ free(meshes.ptr);
+
+ scene_add_object(obj);
+
+ return true;
+}
+
+void object_transform(Object *obj)
+{
+ mat4x4_translate(obj->transform, obj->pos.x, obj->pos.y, obj->pos.z);
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wpedantic"
+ mat4x4_rotate(obj->transform, obj->transform, obj->rot.x, obj->rot.y, obj->rot.z, obj->angle);
+ mat4x4_scale_aniso(obj->transform, obj->transform, obj->scale.x, obj->scale.y, obj->scale.z);
+#pragma GCC diagnostic pop
+}
+
+void object_render(Object *obj, GLint loc_model)
+{
+ if (! obj->visible)
+ return;
+
+ glUniformMatrix4fv(loc_model, 1, GL_FALSE, obj->transform[0]);
+
+ if (obj->wireframe)
+ glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+
+ for (size_t i = 0; i < obj->meshes_count; i++)
+ mesh_render(obj->meshes[i]);
+
+ if (obj->wireframe)
+ glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+}
--- /dev/null
+#ifndef _OBJECT_H_
+#define _OBJECT_H_
+
+#include <stddef.h>
+#include <stdbool.h>
+#include <GL/glew.h>
+#include <GL/gl.h>
+#include <linmath.h/linmath.h>
+#include "client/mesh.h"
+#include "client/texture.h"
+#include "client/vertex.h"
+#include "array.h"
+#include "types.h"
+
+typedef struct {
+ GLfloat x, y, z;
+} __attribute__((packed)) Vertex3DPosition;
+
+typedef struct {
+ GLfloat s, t;
+} __attribute__((packed)) Vertex3DTextureCoordinates;
+
+typedef struct {
+ GLfloat h, s, v;
+} __attribute__((packed)) Vertex3DColor;
+
+typedef struct
+{
+ Vertex3DPosition position;
+ Vertex3DTextureCoordinates textureCoordinates;
+ Vertex3DColor color;
+} __attribute__((packed)) Vertex3D;
+
+typedef struct
+{
+ GLuint texture;
+ Array vertices;
+} ObjectFace;
+
+typedef struct
+{
+ v3f pos, rot, scale;
+ f32 angle;
+ bool remove;
+ Mesh **meshes;
+ size_t meshes_count;
+ mat4x4 transform;
+ bool visible;
+ bool wireframe;
+ ObjectFace *current_face;
+ Array faces;
+} Object;
+
+Object *object_create();
+void object_delete(Object *obj);
+void object_set_texture(Object *obj, Texture *texture);
+void object_add_vertex(Object *obj, Vertex3D *vertex);
+bool object_add_to_scene(Object *obj);
+void object_transform(Object *obj);
+void object_render(Object *obj, GLint loc_model);
+
+#endif
--- /dev/null
+#include <stdlib.h>
+#include <stdio.h>
+#include <pthread.h>
+#include <linmath.h/linmath.h>
+#include "client/camera.h"
+#include "client/client.h"
+#include "client/scene.h"
+#include "client/shader.h"
+#include "list.h"
+
+static struct
+{
+ List objects;
+ pthread_mutex_t mtx;
+ GLuint prog;
+ GLint loc_model;
+ GLint loc_view;
+ GLint loc_projection;
+ mat4x4 projection;
+ f32 fov;
+ f32 render_distance;
+} scene;
+
+bool scene_init()
+{
+ scene.objects = list_create(NULL),
+ pthread_mutex_init(&scene.mtx, NULL);
+
+ if (! shader_program_create(RESSOURCEPATH "shaders/3d", &scene.prog)) {
+ fprintf(stderr, "Failed to create 3D shader program\n");
+ return false;
+ }
+
+ scene.loc_model = glGetUniformLocation(scene.prog, "model");
+ scene.loc_view = glGetUniformLocation(scene.prog, "view");
+ scene.loc_projection = glGetUniformLocation(scene.prog, "projection");
+
+ scene.fov = 86.1f;
+ scene.render_distance = 1000.0f;
+
+ return true;
+}
+
+static void list_delete_object(void *key, __attribute__((unused)) void *value, __attribute__((unused)) void *unused)
+{
+ object_delete(key);
+}
+
+void scene_deinit()
+{
+ list_clear_func(&scene.objects, &list_delete_object, NULL);
+ pthread_mutex_destroy(&scene.mtx);
+ glDeleteProgram(scene.prog);
+}
+
+void scene_add_object(Object *obj)
+{
+ pthread_mutex_lock(&scene.mtx);
+ list_put(&scene.objects, obj, NULL);
+ pthread_mutex_unlock(&scene.mtx);
+}
+
+void scene_render()
+{
+ glUseProgram(scene.prog);
+ camera_enable(scene.loc_view);
+ glUniformMatrix4fv(scene.loc_projection, 1, GL_FALSE, scene.projection[0]);
+
+ glActiveTexture(GL_TEXTURE0);
+
+ for (ListPair **pairptr = &scene.objects.first; *pairptr != NULL; ) {
+ ListPair *pair = *pairptr;
+ Object *obj = pair->key;
+ if (obj->remove) {
+ pthread_mutex_lock(&scene.mtx);
+ *pairptr = pair->next;
+ pthread_mutex_unlock(&scene.mtx);
+ free(pair);
+ object_delete(obj);
+ } else {
+ object_render(obj, scene.loc_model);
+ pairptr = &pair->next;
+ }
+ }
+}
+
+void scene_on_resize(int width, int height)
+{
+ mat4x4_perspective(scene.projection, scene.fov / 180.0f * M_PI, (float) width / (float) height, 0.01f, scene.render_distance);
+}
--- /dev/null
+#ifndef _SCENE_H_
+#define _SCENE_H_
+
+#include "client/object.h"
+
+bool scene_init();
+void scene_deinit();
+void scene_add_object(Object *obj);
+void scene_render();
+void scene_on_resize(int width, int height);
+
+#endif
--- /dev/null
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include "client/shader.h"
+
+static GLuint compile_and_attach_shader(GLenum type, const char *path, const char *name, GLuint program)
+{
+ char full_path[strlen(path) + 1 + strlen(name) + 1 + 4 + 1];
+ sprintf(full_path, "%s/%s.glsl", path, name);
+
+ FILE *file = fopen(full_path, "r");
+ if (! file) {
+ perror("fopen");
+ return 0;
+ }
+
+ if (fseek(file, 0, SEEK_END) == -1) {
+ perror("fseek");
+ fclose(file);
+ return 0;
+ }
+
+ long size = ftell(file);
+
+ if (size == 1) {
+ perror("ftell");
+ fclose(file);
+ return 0;
+ }
+
+ if (fseek(file, 0, SEEK_SET) == -1) {
+ perror("fseek");
+ fclose(file);
+ return 0;
+ }
+
+ char code[size];
+
+ if (fread(code, 1, size, file) != (size_t) size) {
+ perror("fread");
+ fclose(file);
+ return 0;
+ }
+
+ fclose(file);
+
+ GLuint id = glCreateShader(type);
+
+ char const *codeptr = code;
+ const int isize = (int) size;
+ glShaderSource(id, 1, &codeptr, &isize);
+
+ glCompileShader(id);
+
+ GLint success;
+ glGetShaderiv(id, GL_COMPILE_STATUS, &success);
+ if (! success) {
+ char errbuf[BUFSIZ];
+ glGetShaderInfoLog(id, BUFSIZ, NULL, errbuf);
+ fprintf(stderr, "Failed to compile %s shader: %s\n", name, errbuf);
+ glDeleteShader(id);
+ return 0;
+ }
+
+ glAttachShader(program, id);
+
+ return id;
+}
+
+
+bool shader_program_create(const char *path, GLuint *idptr)
+{
+ GLuint id = glCreateProgram();
+
+ GLuint vert, frag;
+
+ if (! (vert = compile_and_attach_shader(GL_VERTEX_SHADER, path, "vertex", id))) {
+ glDeleteProgram(id);
+ return false;
+ }
+
+ if (! (frag = compile_and_attach_shader(GL_FRAGMENT_SHADER, path, "fragment", id))) {
+ glDeleteShader(vert);
+ glDeleteProgram(id);
+ return false;
+ }
+
+ glLinkProgram(id);
+ glDeleteShader(vert);
+ glDeleteShader(frag);
+
+ GLint success;
+ glGetProgramiv(id, GL_LINK_STATUS, &success);
+ if (! success) {
+ char errbuf[BUFSIZ];
+ glGetProgramInfoLog(id, BUFSIZ, NULL, errbuf);
+ fprintf(stderr, "Failed to link shader program: %s\n", errbuf);
+ glDeleteProgram(id);
+ return false;
+ }
+
+ *idptr = id;
+ return true;
+}
--- /dev/null
+#ifndef _SHADER_H_
+#define _SHADER_H_
+
+#include <stdbool.h>
+#include <GL/glew.h>
+#include <GL/gl.h>
+
+bool shader_program_create(const char *path, GLuint *idptr);
+
+#endif
--- /dev/null
+#define STB_IMAGE_IMPLEMENTATION
+#include <stb/stb_image.h>
+#include <stdbool.h>
+#include "client/texture.h"
+#include "list.h"
+
+static List textures;
+
+__attribute((constructor(101))) static void textures_init()
+{
+ stbi_set_flip_vertically_on_load(true);
+
+ textures = list_create(&list_compare_string);
+}
+
+static void list_delete_texture(__attribute__((unused)) void *key, void *value, __attribute__((unused)) void *unused)
+{
+ texture_delete(value);
+}
+
+__attribute((destructor)) static void textures_deinit()
+{
+ list_clear_func(&textures, &list_delete_texture, NULL);
+}
+
+Texture *texture_create(unsigned char *data, int width, int height, GLenum format)
+{
+ Texture *texture = malloc(sizeof(Texture));
+ texture->width = width;
+ texture->height = height;
+
+ glGenTextures(1, &texture->id);
+
+ glBindTexture(GL_TEXTURE_2D, texture->id);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ glTexImage2D(GL_TEXTURE_2D, 0, format, texture->width, texture->height, 0, format, GL_UNSIGNED_BYTE, data);
+ glGenerateMipmap(GL_TEXTURE_2D);
+
+ glBindTexture(GL_TEXTURE_2D, 0);
+
+ return texture;
+}
+
+void texture_delete(Texture *texture)
+{
+ glDeleteTextures(1, &texture->id);
+ free(texture);
+}
+
+static void *create_image_texture(void *key)
+{
+ int width, height, channels;
+
+ unsigned char *data = stbi_load(key, &width, &height, &channels, 0);
+ if (! data) {
+ printf("Failed to load texture %s\n", (char *) key);
+ return 0;
+ }
+
+ Texture *texture = texture_create(data, width, height, GL_RGBA);
+
+ stbi_image_free(data);
+
+ return texture;
+}
+
+Texture *texture_get(char *path)
+{
+ return list_get_cached(&textures, path, &create_image_texture);
+}
--- /dev/null
+#ifndef _TEXTURE_H_
+#define _TEXTURE_H_
+
+#include <GL/glew.h>
+#include <GL/gl.h>
+
+typedef struct
+{
+ GLuint id;
+ int width, height;
+} Texture;
+
+Texture *texture_create(unsigned char *data, int width, int height, GLenum format);
+void texture_delete(Texture *texture);
+Texture *texture_get(char *path);
+
+#endif
--- /dev/null
+#include "client/vertex.h"
+
+void vertex_layout_configure(VertexLayout *layout)
+{
+ size_t offset = 0;
+
+ for (GLuint i = 0; i < layout->count; i++) {
+ VertexAttribute *attrib = &layout->attributes[i];
+
+ glVertexAttribPointer(i, attrib->length, attrib->type, GL_FALSE, layout->size, (GLvoid *) offset);
+ glEnableVertexAttribArray(i);
+
+ offset += attrib->size;
+ }
+}
--- /dev/null
+#ifndef _VERTEX_H_
+#define _VERTEX_H_
+
+#include <GL/glew.h>
+#include <GL/gl.h>
+
+typedef struct
+{
+ GLenum type;
+ GLsizei length;
+ GLsizei size;
+} VertexAttribute;
+
+typedef struct
+{
+ VertexAttribute *attributes;
+ GLuint count;
+ GLsizei size;
+} VertexLayout;
+
+void vertex_layout_configure(VertexLayout *layout);
+
+#endif
--- /dev/null
+#include <stdio.h>
+#include <GL/glew.h>
+#include <GL/gl.h>
+#include "client/hud.h"
+#include "client/input.h"
+#include "client/scene.h"
+#include "client/window.h"
+
+struct Window window;
+
+static void framebuffer_size_callback(__attribute__((unused)) GLFWwindow *handle, int width, int height)
+{
+ glViewport(0, 0, width, height);
+
+ if (! window.fullscreen) {
+ window.small_bounds.width = width;
+ window.small_bounds.height = height;
+ }
+
+ scene_on_resize(width, height);
+ hud_on_resize(width, height);
+}
+
+static void cursor_pos_callback(__attribute__((unused)) GLFWwindow *handle, double current_x, double current_y)
+{
+ input_on_cursor_pos(current_x, current_y);
+}
+
+static void window_pos_callback(__attribute__((unused)) GLFWwindow *handle, int x, int y)
+{
+ if (! window.fullscreen) {
+ window.small_bounds.x = x;
+ window.small_bounds.y = y;
+ }
+}
+
+void window_enter_fullscreen()
+{
+ window.fullscreen = true;
+ GLFWmonitor *monitor = glfwGetPrimaryMonitor();
+ const GLFWvidmode *vidmode = glfwGetVideoMode(monitor);
+ glfwSetWindowMonitor(window.handle, monitor, 0, 0, vidmode->width, vidmode->height, 0);
+}
+
+void window_exit_fullscreen()
+{
+ window.fullscreen = false;
+ glfwSetWindowMonitor(window.handle, NULL, window.small_bounds.x, window.small_bounds.y, window.small_bounds.width, window.small_bounds.height, 0);
+}
+
+bool window_init(int width, int height)
+{
+ if(! glfwInit()) {
+ fprintf(stderr, "Failed to initialize GLFW\n");
+ return false;
+ }
+
+ glfwWindowHint(GLFW_SAMPLES, 8);
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
+ glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
+
+ window.handle = glfwCreateWindow(width, height, "Dragonblocks", NULL, NULL);
+
+ window.small_bounds.width = width;
+ window.small_bounds.height = height;
+
+ if (! window.handle) {
+ fprintf(stderr, "Failed to create window\n");
+ glfwTerminate();
+ return false;
+ }
+
+ glfwMakeContextCurrent(window.handle);
+
+ if (glewInit() != GLEW_OK) {
+ fprintf(stderr, "Failed to initialize GLEW\n");
+ return false;
+ }
+
+ glfwSetFramebufferSizeCallback(window.handle, &framebuffer_size_callback);
+ glfwSetCursorPosCallback(window.handle, &cursor_pos_callback);
+ glfwSetWindowPosCallback(window.handle, &window_pos_callback);
+
+ return true;
+}
--- /dev/null
+#ifndef _WINDOW_H_
+#define _WINDOW_H_
+
+#include <GLFW/glfw3.h>
+
+extern struct Window
+{
+ GLFWwindow *handle;
+ bool fullscreen;
+ struct
+ {
+ int x, y;
+ int width, height;
+ } small_bounds;
+} window;
+
+bool window_init(int width, int height);
+void window_enter_fullscreen();
+void window_exit_fullscreen();
+
+#endif
+++ /dev/null
-#include <stdio.h>
-#include <unistd.h>
-#include "client.h"
-#include "clientmap.h"
-#include "types.h"
-
-static bool disconnect_handler(__attribute__((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;
-
- MapBlockHeader header;
-
- if (! read_u32(client->fd, &header))
- return false;
-
- char data[header];
- if (! read_full(client->fd, data, header))
- return false;
-
- MapBlock *block;
-
- if (good)
- block = map_get_block(client->map, pos, true);
- else
- block = map_allocate_block(pos);
-
- if (block->state != MBS_CREATED)
- map_clear_meta(block);
-
- bool ret = map_deserialize_block(block, data, header);
-
- if (good)
- clientmap_block_changed(block);
- else
- map_free_block(block);
-
- return ret;
-}
-
-CommandHandler command_handlers[CLIENT_COMMAND_COUNT] = {
- {0},
- {&disconnect_handler, "DISCONNECT", CS_CREATED | CS_AUTH | CS_ACTIVE},
- {&auth_handler, "AUTH", CS_AUTH},
- {&block_handler, "BLOCK", CS_ACTIVE},
-};
+++ /dev/null
-#ifndef _CLIENT_COMMAND_H_
-#define _CLIENT_COMMAND_H_
-
-typedef enum
-{
- CLIENT_COMMAND_NULL,
- CC_DISCONNECT,
- CC_AUTH,
- CC_BLOCK,
- CLIENT_COMMAND_COUNT
-} ClientCommand;
-
-#ifdef _SERVER_H_
-typedef ClientCommand RemoteCommand;
-#endif
-
-#ifdef _CLIENT_H_
-typedef ClientCommand HostCommand;
-#define HOST_COMMAND_COUNT CLIENT_COMMAND_COUNT
-#endif
-
-#endif
+++ /dev/null
-#include <stdlib.h>
-#include "clientmap.h"
-#include "blockmesh.h"
-#include "queue.h"
-
-static struct
-{
- Queue *queue;
- pthread_t thread;
- bool cancel;
-} meshgen;
-
-static Client *client = NULL;
-
-static void set_block_ready(void *block)
-{
- ((MapBlock *) block)->state = MBS_READY;
-}
-
-static void *meshgen_thread(__attribute__((unused)) void *unused)
-{
- while (! meshgen.cancel) {
- MapBlock *block;
- if ((block = dequeue_callback(meshgen.queue, &set_block_ready)))
- make_block_mesh(block, client->map, client->scene);
- else
- sched_yield();
- }
-
- return NULL;
-}
-
-void clientmap_init(Client *cli)
-{
- client = cli;
- meshgen.queue = create_queue();
-}
-
-void clientmap_start_meshgen()
-{
- pthread_create(&meshgen.thread, NULL, &meshgen_thread, NULL);
-}
-
-void clientmap_deinit()
-{
- meshgen.cancel = true;
- delete_queue(meshgen.queue);
- if (meshgen.thread)
- pthread_join(meshgen.thread, NULL);
-}
-
-static void schedule_update_block(MapBlock *block)
-{
- if (! block)
- return;
-
- pthread_mutex_lock(&block->mtx);
- if (block->state != MBS_PROCESSING) {
- block->state = MBS_PROCESSING;
- enqueue(meshgen.queue, block);
- }
- pthread_mutex_unlock(&block->mtx);
-}
-
-void clientmap_block_changed(MapBlock *block)
-{
- schedule_update_block(block);
-
- schedule_update_block(map_get_block(client->map, (v3s32) {block->pos.x + 1, block->pos.y + 0, block->pos.z + 0}, false));
- schedule_update_block(map_get_block(client->map, (v3s32) {block->pos.x + 0, block->pos.y + 1, block->pos.z + 0}, false));
- schedule_update_block(map_get_block(client->map, (v3s32) {block->pos.x + 0, block->pos.y + 0, block->pos.z + 1}, false));
- schedule_update_block(map_get_block(client->map, (v3s32) {block->pos.x - 1, block->pos.y - 0, block->pos.z - 0}, false));
- schedule_update_block(map_get_block(client->map, (v3s32) {block->pos.x - 0, block->pos.y - 1, block->pos.z - 0}, false));
- schedule_update_block(map_get_block(client->map, (v3s32) {block->pos.x - 0, block->pos.y - 0, block->pos.z - 1}, false));
-}
+++ /dev/null
-#ifndef _CLIENTMAP_H_
-#define _CLIENTMAP_H_
-
-#include "client.h"
-
-void clientmap_init(Client *cli);
-void clientmap_start_meshgen();
-void clientmap_deinit();
-
-void clientmap_block_changed(MapBlock *block);
-
-#endif
+++ /dev/null
-#include "client.h"
-#include "clientnode.h"
-#include "node.h"
-
-static void render_state_biome(MapNode *node, Vertex *vertex)
-{
- vertex->r = node->state.biome.x;
- vertex->g = node->state.biome.y;
- vertex->b = node->state.biome.z;
-}
-
-ClientNodeDefintion client_node_definitions[NODE_UNLOADED] = {
- // invalid
- {
- .texture_path = RESSOURCEPATH "textures/invalid.png",
- .texture = NULL,
- .render = NULL,
- },
- // air
- {
- .texture_path = NULL,
- .texture = NULL,
- .render = NULL,
- },
- // grass
- {
- .texture_path = RESSOURCEPATH "textures/grass.png",
- .texture = NULL,
- .render = &render_state_biome,
- },
- // dirt
- {
- .texture_path = RESSOURCEPATH "textures/dirt.png",
- .texture = NULL,
- .render = NULL,
- },
- // stone
- {
- .texture_path = RESSOURCEPATH "textures/stone.png",
- .texture = NULL,
- .render = NULL,
- },
- // snow
- {
- .texture_path = RESSOURCEPATH "textures/snow.png",
- .texture = NULL,
- .render = NULL,
- },
-};
-
-void init_client_node_definitions()
-{
- for (Node node = NODE_INVALID; node < NODE_UNLOADED; node++) {
- ClientNodeDefintion *def = &client_node_definitions[node];
- if (def->texture_path)
- def->texture = get_texture(def->texture_path);
- }
-}
+++ /dev/null
-#ifndef _CLIENTNODE_H_
-#define _CLIENTNODE_H_
-
-#include "map.h"
-#include "mesh.h"
-#include "texture.h"
-
-typedef struct
-{
- char *texture_path;
- Texture *texture;
- void (*render)(MapNode *node, Vertex *vertex);
-} ClientNodeDefintion;
-
-extern ClientNodeDefintion client_node_definitions[];
-void init_client_node_definitions();
-
-#endif
+++ /dev/null
-#include "camera.h"
-#include "client.h"
-#include "clientplayer.h"
-#include "cube.h"
-#include "texture.h"
-
-static void update_pos(ClientPlayer *player)
-{
- set_camera_position((v3f) {player->pos.x, player->pos.y + player->eye_height, player->pos.z});
-
- pthread_mutex_lock(&player->client->mtx);
- (void) (write_u32(player->client->fd, SC_POS) && write_v3f32(player->client->fd, player->pos));
- pthread_mutex_unlock(&player->client->mtx);
-
- player->obj->pos = player->pos;
- meshobject_transform(player->obj);
-}
-
-void clientplayer_init(Client *client)
-{
- client->player.client = client;
- client->player.pos = (v3f) {0.0f, 200.0f, 0.0f};
- client->player.velocity = (v3f) {0.0f, 0.0f, 0.0f};
- client->player.box = (aabb3f) {{-0.3f, 0.0f, -0.3f}, {0.3f, 1.75f, 0.3f}};
- client->player.yaw = client->player.pitch = 0.0f;
- client->player.eye_height = 1.5f;
-}
-
-void clientplayer_add_to_scene(ClientPlayer *player)
-{
- VertexBuffer buffer = vertexbuffer_create();
- vertexbuffer_set_texture(&buffer, get_texture(RESSOURCEPATH "textures/player.png"));
-
- for (int f = 0; f < 6; f++) {
- for (int v = 0; v < 6; v++) {
- Vertex vertex = cube_vertices[f][v];
- vertex.y += 0.5;
- vertexbuffer_add_vertex(&buffer, &vertex);
- }
- }
-
- player->obj = meshobject_create(buffer, player->client->scene, (v3f) {0.0f, 0.0f, 0.0f});
- player->obj->scale = (v3f) {0.6f, 1.75f, 0.6f};
- player->obj->visible = false;
-
- update_pos(player);
-}
-
-static aabb3f get_box(ClientPlayer *player)
-{
- return (aabb3f) {
- {player->box.min.x + player->pos.x, player->box.min.y + player->pos.y, player->box.min.z + player->pos.z},
- {player->box.max.x + player->pos.x, player->box.max.y + player->pos.y, player->box.max.z + player->pos.z},
- };
-}
-
-static aabb3s32 round_box(aabb3f box)
-{
- return (aabb3s32) {
- {floor(box.min.x + 0.5f), floor(box.min.y + 0.5f), floor(box.min.z + 0.5f)},
- {ceil(box.max.x - 0.5f), ceil(box.max.y - 0.5f), ceil(box.max.z - 0.5f)},
- };
-}
-
-static bool is_solid(Map *map, s32 x, s32 y, s32 z)
-{
- Node node = map_get_node(map, (v3s32) {x, y, z}).type;
- return node == NODE_UNLOADED || node_definitions[node].solid;
-}
-
-static bool can_jump(ClientPlayer *player)
-{
- aabb3f fbox = get_box(player);
- fbox.min.y -= 0.5f;
-
- aabb3s32 box = round_box(fbox);
-
- if (fbox.min.y - (f32) box.min.y > 0.01f)
- return false;
-
- for (s32 x = box.min.x; x <= box.max.x; x++)
- for (s32 z = box.min.z; z <= box.max.z; z++)
- if (is_solid(player->client->map, x, box.min.y, z))
- return true;
-
- return false;
-}
-
-void clientplayer_jump(ClientPlayer *player)
-{
- if (can_jump(player))
- player->velocity.y += 10.0f;
-}
-
-void clientplayer_tick(ClientPlayer *player, f64 dtime)
-{
- v3f old_pos = player->pos;
- v3f old_velocity = player->velocity;
-
- player->velocity.y -= 32.0f * dtime;
-
-#define GETS(vec, comp) *(s32 *) ((char *) &vec + offsetof(v3s32, comp))
-#define GETF(vec, comp) *(f32 *) ((char *) &vec + offsetof(v3f32, comp))
-#define PHYSICS(a, b, c) { \
- f32 v = (GETF(player->velocity, a) + GETF(old_velocity, a)) / 2.0f; \
- if (v == 0.0f) \
- goto a ## _physics_done; \
- aabb3s32 box = round_box(get_box(player)); \
- v3f old_pos = player->pos; \
- GETF(player->pos, a) += v * dtime; \
- s32 dir; \
- f32 offset; \
- if (v > 0.0f) { \
- dir = +1; \
- offset = GETF(player->box.max, a); \
- GETS(box.min, a) = ceil(GETF(old_pos, a) + offset + 0.5f); \
- GETS(box.max, a) = floor(GETF(player->pos, a) + offset + 0.5f); \
- } else { \
- dir = -1; \
- offset = GETF(player->box.min, a); \
- GETS(box.min, a) = floor(GETF(old_pos, a) + offset - 0.5f); \
- GETS(box.max, a) = ceil(GETF(player->pos, a) + offset - 0.5f); \
- } \
- GETS(box.max, a) += dir; \
- for (s32 a = GETS(box.min, a); a != GETS(box.max, a); a += dir) { \
- for (s32 b = GETS(box.min, b); b <= GETS(box.max, b); b++) { \
- for (s32 c = GETS(box.min, c); c <= GETS(box.max, c); c++) { \
- if (is_solid(player->client->map, x, y, z)) { \
- GETF(player->pos, a) = (f32) a - offset - 0.5f * (f32) dir; \
- GETF(player->velocity, a) = 0.0f; \
- goto a ## _physics_done; \
- } \
- } \
- } \
- } \
- a ## _physics_done: (void) 0;\
- }
-
- PHYSICS(x, y, z)
- PHYSICS(y, x, z)
- PHYSICS(z, x, y)
-
-#undef GETS
-#undef GETF
-#undef PHYSICS
-
- if (old_pos.x != player->pos.x || old_pos.y != player->pos.y || old_pos.z != player->pos.z)
- update_pos(player);
-}
+++ /dev/null
-#ifndef _CLIENTPLAYER_H_
-#define _CLIENTPLAYER_H_
-
-#include "mesh.h"
-#include "types.h"
-
-typedef struct
-{
- struct Client *client;
- v3f pos;
- v3f velocity;
- aabb3f box;
- f32 yaw, pitch;
- f32 eye_height;
- MeshObject *obj;
-} ClientPlayer;
-
-void clientplayer_init(struct Client *client);
-void clientplayer_add_to_scene(ClientPlayer *player);
-void clientplayer_jump(ClientPlayer *player);
-void clientplayer_tick(ClientPlayer *player, f64 dtime);
-
-#endif
+++ /dev/null
-#include "cube.h"
-
-Vertex cube_vertices[6][6] = {
- {
- {-0.5, -0.5, -0.5, +0.0, +0.0, +1.0, +0.0, +1.0},
- {+0.5, -0.5, -0.5, +1.0, +0.0, +1.0, +0.0, +1.0},
- {+0.5, +0.5, -0.5, +1.0, +1.0, +1.0, +0.0, +1.0},
- {+0.5, +0.5, -0.5, +1.0, +1.0, +1.0, +0.0, +1.0},
- {-0.5, +0.5, -0.5, +0.0, +1.0, +1.0, +0.0, +1.0},
- {-0.5, -0.5, -0.5, +0.0, +0.0, +1.0, +0.0, +1.0},
- },
- {
- {-0.5, -0.5, +0.5, +0.0, +0.0, +1.0, +0.0, +1.0},
- {+0.5, +0.5, +0.5, +1.0, +1.0, +1.0, +0.0, +1.0},
- {+0.5, -0.5, +0.5, +1.0, +0.0, +1.0, +0.0, +1.0},
- {+0.5, +0.5, +0.5, +1.0, +1.0, +1.0, +0.0, +1.0},
- {-0.5, -0.5, +0.5, +0.0, +0.0, +1.0, +0.0, +1.0},
- {-0.5, +0.5, +0.5, +0.0, +1.0, +1.0, +0.0, +1.0},
- },
- {
- {-0.5, +0.5, +0.5, +1.0, +1.0, +1.0, +0.0, +1.0},
- {-0.5, -0.5, -0.5, +0.0, +0.0, +1.0, +0.0, +1.0},
- {-0.5, +0.5, -0.5, +0.0, +1.0, +1.0, +0.0, +1.0},
- {-0.5, -0.5, -0.5, +0.0, +0.0, +1.0, +0.0, +1.0},
- {-0.5, +0.5, +0.5, +1.0, +1.0, +1.0, +0.0, +1.0},
- {-0.5, -0.5, +0.5, +1.0, +0.0, +1.0, +0.0, +1.0},
- },
- {
- {+0.5, +0.5, +0.5, +1.0, +1.0, +1.0, +0.0, +1.0},
- {+0.5, +0.5, -0.5, +0.0, +1.0, +1.0, +0.0, +1.0},
- {+0.5, -0.5, -0.5, +0.0, +0.0, +1.0, +0.0, +1.0},
- {+0.5, -0.5, -0.5, +0.0, +0.0, +1.0, +0.0, +1.0},
- {+0.5, -0.5, +0.5, +1.0, +0.0, +1.0, +0.0, +1.0},
- {+0.5, +0.5, +0.5, +1.0, +1.0, +1.0, +0.0, +1.0},
- },
- {
- {-0.5, -0.5, -0.5, +0.0, +1.0, +1.0, +0.0, +1.0},
- {+0.5, -0.5, -0.5, +1.0, +1.0, +1.0, +0.0, +1.0},
- {+0.5, -0.5, +0.5, +1.0, +0.0, +1.0, +0.0, +1.0},
- {+0.5, -0.5, +0.5, +1.0, +0.0, +1.0, +0.0, +1.0},
- {-0.5, -0.5, +0.5, +0.0, +0.0, +1.0, +0.0, +1.0},
- {-0.5, -0.5, -0.5, +0.0, +1.0, +1.0, +0.0, +1.0},
- },
- {
- {-0.5, +0.5, -0.5, +0.0, +1.0, +1.0, +0.0, +1.0},
- {+0.5, +0.5, -0.5, +1.0, +1.0, +1.0, +0.0, +1.0},
- {+0.5, +0.5, +0.5, +1.0, +0.0, +1.0, +0.0, +1.0},
- {+0.5, +0.5, +0.5, +1.0, +0.0, +1.0, +0.0, +1.0},
- {-0.5, +0.5, +0.5, +0.0, +0.0, +1.0, +0.0, +1.0},
- {-0.5, +0.5, -0.5, +0.0, +1.0, +1.0, +0.0, +1.0},
- },
-};
-
+++ /dev/null
-#ifndef _CUBE_H_
-#define _CUBE_H_
-
-#include "mesh.h"
-
-extern Vertex cube_vertices[6][6];
-
-#endif
+++ /dev/null
-#include <stdlib.h>
-#include "array.h"
-#include "facecache.h"
-
-static struct
-{
- Array positions;
- u32 size;
- pthread_mutex_t mtx;
-} facecache;
-
-__attribute((constructor)) static void init_face_cache()
-{
- facecache.size = 0;
- facecache.positions = array_create(sizeof(v3s32));
- v3s32 pos = {0, 0, 0};
- array_append(&facecache.positions, &pos);
- pthread_mutex_init(&facecache.mtx, NULL);
-}
-
-__attribute((destructor)) void deinit_face_cache()
-{
- if (facecache.positions.ptr)
- free(facecache.positions.ptr);
- pthread_mutex_destroy(&facecache.mtx);
-}
-
-static void calculate_face_cache(s32 size)
-{
-#define ADDPOS(a, b, c, va, vb, vc) \
- { \
- v3s32 pos; \
- *(s32 *) ((char *) &pos + offsetof(v3s32, a)) = va; \
- *(s32 *) ((char *) &pos + offsetof(v3s32, b)) = vb; \
- *(s32 *) ((char *) &pos + offsetof(v3s32, c)) = vc; \
- array_append(&facecache.positions, &pos); \
- }
-#define SQUARES(a, b, c) \
- for (s32 va = -size + 1; va < size; va++) { \
- for (s32 vb = -size + 1; vb < size; vb++) { \
- ADDPOS(a, b, c, va, vb, size) \
- ADDPOS(a, b, c, va, vb, -size) \
- } \
- }
- SQUARES(x, z, y)
- SQUARES(x, y, z)
- SQUARES(z, y, x)
-#undef SQUARES
-#define EDGES(a, b, c) \
- for (s32 va = -size + 1; va < size; va++) { \
- ADDPOS(a, b, c, va, size, size) \
- ADDPOS(a, b, c, va, size, -size) \
- ADDPOS(a, b, c, va, -size, size) \
- ADDPOS(a, b, c, va, -size, -size) \
- }
- EDGES(x, y, z)
- EDGES(z, x, y)
- EDGES(y, x, z)
-#undef EDGES
- ADDPOS(x, y, z, size, size, size)
- ADDPOS(x, y, z, size, size, -size)
- ADDPOS(x, y, z, size, -size, size)
- ADDPOS(x, y, z, size, -size, -size)
- ADDPOS(x, y, z, -size, size, size)
- ADDPOS(x, y, z, -size, size, -size)
- ADDPOS(x, y, z, -size, -size, size)
- ADDPOS(x, y, z, -size, -size, -size)
-#undef ADDPOS
-}
-
-v3s32 get_face(size_t i, v3s32 *base)
-{
- pthread_mutex_lock(&facecache.mtx);
- while (facecache.positions.siz <= i)
- calculate_face_cache(++facecache.size);
- v3s32 pos = ((v3s32 *) facecache.positions.ptr)[i];
- pthread_mutex_unlock(&facecache.mtx);
- if (base) {
- pos.x += base->x;
- pos.y += base->y;
- pos.z += base->z;
- }
- return pos;
-}
-
-size_t get_face_count(u32 size)
-{
- size_t len = 1 + size * 2;
- return 1 + len * len * len;
-}
+++ /dev/null
-#ifndef _FACECACHE_H_
-#define _FACECACHE_H_
-
-#include <pthread.h>
-#include "types.h"
-
-v3s32 get_face(size_t i, v3s32 *base);
-size_t get_face_count(u32 size);
-
-#endif
+++ /dev/null
-#include <string.h>
-#include <stdlib.h>
-#include <GL/glew.h>
-#include <GL/gl.h>
-#include "client.h"
-#include "cube.h"
-#include "hud.h"
-
-static struct
-{
- MeshObject *obj;
- List elements;
- ShaderProgram *prog;
- mat4x4 view, projection;
- int width, height;
-} hud;
-
-void hud_init(ShaderProgram *prog)
-{
- hud.prog = prog;
- hud.elements = list_create(NULL);
-
- Texture *texture = get_texture(RESSOURCEPATH "textures/invalid.png");
- VertexBuffer buffer = vertexbuffer_create();
- vertexbuffer_set_texture(&buffer, texture);
-
- for (int v = 0; v < 6; v++) {
- Vertex vertex = cube_vertices[0][v];
- vertex.z = 0.0f;
- vertexbuffer_add_vertex(&buffer, &vertex);
- }
-
- hud.obj = meshobject_create(buffer, NULL, (v3f) {0.0f, 0.0f, 0.0f});
-
- mat4x4_identity(hud.view);
-}
-
-static void free_element(void *key, __attribute__((unused)) void *value, __attribute__((unused)) void *arg)
-{
- free(key);
-}
-
-void hud_deinit()
-{
- meshobject_delete(hud.obj);
- list_clear_func(&hud.elements, &free_element, NULL);
-}
-
-static void element_transform(HUDElement *element)
-{
- mat4x4_translate(element->transform, (1.0f + element->pos.x) / 2.0f * (f32) hud.width, (1.0f + element->pos.y) / 2.0f * (f32) hud.height, element->pos.z);
-
- v2f scale = element->scale;
-
- switch (element->scale_type) {
- case HUD_SCALE_TEXTURE:
- scale.x *= element->texture->width;
- scale.y *= element->texture->height;
-
- break;
-
- case HUD_SCALE_SCREEN:
- scale.x *= hud.width * 2.0f;
- scale.y *= hud.height * 2.0f;
-
- break;
-
- default:
- break;
- }
-
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wpedantic"
- mat4x4_scale_aniso(element->transform, element->transform, scale.x, scale.y, 1.0f);
-#pragma GCC diagnostic pop
-}
-
-void hud_on_resize(int width, int height)
-{
- hud.width = width;
- hud.height = height;
- mat4x4_ortho(hud.projection, 0, width, height, 0, -1.0f, 1.0f);
-
- for (ListPair *pair = hud.elements.first; pair != NULL; pair = pair->next)
- element_transform(pair->key);
-}
-
-void hud_render()
-{
- glUniformMatrix4fv(hud.prog->loc_view, 1, GL_FALSE, hud.view[0]);
- glUniformMatrix4fv(hud.prog->loc_projection, 1, GL_FALSE, hud.projection[0]);
-
- for (ListPair *pair = hud.elements.first; pair != NULL; pair = pair->next) {
- HUDElement *element = pair->key;
-
- if (element->visible) {
- memcpy(hud.obj->transform, element->transform, sizeof(mat4x4));
- hud.obj->meshes[0]->texture = element->texture->id;
- meshobject_render(hud.obj, hud.prog);
- }
- }
-}
-
-HUDElement *hud_add(char *texture, v3f pos, v2f scale, HUDScaleType scale_type)
-{
- HUDElement *element = malloc(sizeof(HUDElement));
- element->texture = get_texture(texture);
- element->visible = true;
- element->pos = pos;
- element->scale = scale;
- element->scale_type = scale_type;
-
- element_transform(element);
-
- list_set(&hud.elements, element, NULL);
- return element;
-}
+++ /dev/null
-#ifndef _HUD_H_
-#define _HUD_H_
-
-#include <stdbool.h>
-#include <linmath.h/linmath.h>
-#include "shaders.h"
-#include "texture.h"
-#include "types.h"
-
-typedef enum
-{
- HUD_SCALE_TEXTURE,
- HUD_SCALE_SCREEN,
- HUD_SCALE_NONE,
-} HUDScaleType;
-
-typedef struct
-{
- Texture *texture;
- bool visible;
- v3f pos;
- v2f scale;
- HUDScaleType scale_type;
- mat4x4 transform;
-} HUDElement;
-
-void hud_init(ShaderProgram *prog);
-void hud_deinit();
-void hud_on_resize(int width, int height);
-void hud_render();
-HUDElement *hud_add(char *texture, v3f pos, v2f scale, HUDScaleType scale_type);
-
-#endif
+++ /dev/null
-#include <math.h>
-#include "camera.h"
-#include "client.h"
-#include "hud.h"
-#include "input.h"
-
-typedef struct
-{
- int key;
- bool was_pressed;
- bool fired;
-} KeyListener;
-
-static struct
-{
- GLFWwindow *window;
- Client *client;
- HUDElement *pause_menu_hud;
- bool paused;
- bool fullscreen;
- KeyListener pause_listener;
- KeyListener fullscreen_listener;
- int small_x, small_y, small_width, small_height;
-} input;
-
-void input_on_cursor_pos(double current_x, double current_y)
-{
- if (input.paused)
- return;
-
- static double last_x, last_y = 0.0;
-
- double delta_x = current_x - last_x;
- double delta_y = current_y - last_y;
- last_x = current_x;
- last_y = current_y;
-
- input.client->player.yaw += (f32) delta_x * M_PI / 180.0f / 8.0f;
- input.client->player.pitch -= (f32) delta_y * M_PI / 180.0f / 8.0f;
-
- input.client->player.pitch = fmax(fmin(input.client->player.pitch, M_PI / 2.0f - 0.01f), -M_PI / 2.0f + 0.01f);
-
- set_camera_angle(input.client->player.yaw, input.client->player.pitch);
-}
-
-void input_on_resize(int width, int height)
-{
- if (! input.fullscreen) {
- input.small_width = width;
- input.small_height = height;
- }
-}
-
-void input_on_window_pos(int x, int y)
-{
- if (! input.fullscreen) {
- input.small_x = x;
- input.small_y = y;
- }
-}
-
-static bool move(int forward, int backward, vec3 dir)
-{
- f32 sign;
- f32 speed = 4.317f;
-
- if (glfwGetKey(input.window, forward) == GLFW_PRESS)
- sign = +1.0f;
- else if (glfwGetKey(input.window, backward) == GLFW_PRESS)
- sign = -1.0f;
- else
- return false;
-
- input.client->player.velocity.x += dir[0] * speed * sign;
- input.client->player.velocity.z += dir[2] * speed * sign;
-
- return true;
-}
-
-static void enter_game()
-{
- glfwSetInputMode(input.window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
- input.pause_menu_hud->visible = false;
-}
-
-static void do_key_listener(KeyListener *listener)
-{
- bool is_pressed = glfwGetKey(input.window, listener->key) == GLFW_PRESS;
- listener->fired = listener->was_pressed && ! is_pressed;
- listener->was_pressed = is_pressed;
-}
-
-static KeyListener create_key_listener(int key)
-{
- return (KeyListener) {
- .key = key,
- .was_pressed = false,
- .fired = false,
- };
-}
-
-void process_input()
-{
- do_key_listener(&input.pause_listener);
- do_key_listener(&input.fullscreen_listener);
-
- if (input.pause_listener.fired) {
- input.paused = ! input.paused;
-
- if (input.paused) {
- glfwSetInputMode(input.window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
- input.pause_menu_hud->visible = true;
- } else {
- enter_game();
- }
- }
-
- if (input.fullscreen_listener.fired) {
- input.fullscreen = ! input.fullscreen;
-
- if (input.fullscreen) {
- GLFWmonitor *monitor = glfwGetPrimaryMonitor();
- const GLFWvidmode *vidmode = glfwGetVideoMode(monitor);
- glfwSetWindowMonitor(input.window, monitor, 0, 0, vidmode->width, vidmode->height, 0);
- } else {
- glfwSetWindowMonitor(input.window, NULL, input.small_x, input.small_y, input.small_width, input.small_height, 0);
- }
- }
-
- input.client->player.velocity.x = 0.0f;
- input.client->player.velocity.z = 0.0f;
-
- if (! input.paused) {
- move(GLFW_KEY_W, GLFW_KEY_S, movement_dirs.front);
- move(GLFW_KEY_D, GLFW_KEY_A, movement_dirs.right);
-
- if (glfwGetKey(input.window, GLFW_KEY_SPACE) == GLFW_PRESS)
- clientplayer_jump(&input.client->player);
- }
-}
-
-void init_input(Client *client, GLFWwindow *window)
-{
- input.client = client;
- input.window = window;
-
- input.paused = false;
-
- input.pause_listener = create_key_listener(GLFW_KEY_ESCAPE);
- input.fullscreen_listener = create_key_listener(GLFW_KEY_F11);
-
- input.pause_menu_hud = hud_add(RESSOURCEPATH "textures/pause_layer.png", (v3f) {-1.0f, -1.0f, 0.5f}, (v2f) {1.0f, 1.0f}, HUD_SCALE_SCREEN);
-
- enter_game();
-}
-
-
+++ /dev/null
-#ifndef _INPUT_H_
-#define _INPUT_H_
-
-#include <GLFW/glfw3.h>
-
-void process_input();
-void init_input(Client *client, GLFWwindow *window);
-void input_on_cursor_pos(double current_x, double current_y);
-void input_on_resize(int width, int height);
-void input_on_window_pos(int x, int y);
-
-#endif
node.type = type;
memset(&node.state, 0, sizeof(NodeState));
- if (node_definitions[node.type].create)
+ if (node.type != NODE_UNLOADED && node_definitions[node.type].create)
node_definitions[node.type].create(&node);
return node;
+++ /dev/null
-#include <stdio.h>
-#include <endian.h>
-#include <stdlib.h>
-#include "mapdb.h"
-#include "servermap.h"
-
-static void print_error(sqlite3 *db, MapBlock *block, const char *action)
-{
- printf("Database error with %s block at %d %d %d: %s\n", action, block->pos.x, block->pos.y, block->pos.z, sqlite3_errmsg(db));
-}
-
-sqlite3 *open_mapdb(const char *path)
-{
- sqlite3 *db;
- char *err;
-
- if (sqlite3_open_v2(path, &db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX, NULL) != SQLITE_OK) {
- printf("Failed to open database: %s\n", sqlite3_errmsg(db));
- } else if (sqlite3_exec(db, "CREATE TABLE IF NOT EXISTS blocks (pos BLOB PRIMARY KEY, data BLOB NOT NULL);", NULL, NULL, &err) != SQLITE_OK) {
- printf("Failed to initialize database: %s\n", err);
- sqlite3_free(err);
- }
-
- return db;
-}
-
-static sqlite3_stmt *prepare_statement(sqlite3 *db, MapBlock *block, const char *action, const char *sql)
-{
- sqlite3_stmt *stmt;
-
- if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) != SQLITE_OK) {
- print_error(db, block, action);
- return NULL;
- }
-
- size_t psize = sizeof(s32) * 3;
- s32 *pos = malloc(psize);
- pos[0] = htobe32(block->pos.x);
- pos[1] = htobe32(block->pos.y);
- pos[2] = htobe32(block->pos.z);
-
- sqlite3_bind_blob(stmt, 1, pos, psize, &free);
-
- return stmt;
-}
-
-bool load_block(sqlite3 *db, MapBlock *block)
-{
- sqlite3_stmt *stmt;
-
- if (! (stmt = prepare_statement(db, block, "loading", "SELECT data FROM blocks WHERE pos=?")))
- return false;
-
- int rc = sqlite3_step(stmt);
- bool found = rc == SQLITE_ROW;
-
- if (found) {
- const char *data = sqlite3_column_blob(stmt, 0);
- map_deserialize_block(block, data + sizeof(MapBlockHeader), be32toh(*(MapBlockHeader *) data));
- } else if (rc != SQLITE_DONE) {
- print_error(db, block, "loading");
- }
-
- sqlite3_finalize(stmt);
- return found;
-}
-
-void save_block(sqlite3 *db, MapBlock *block)
-{
- sqlite3_stmt *stmt;
-
- if (! (stmt = prepare_statement(db, block, "saving", "REPLACE INTO blocks (pos, data) VALUES(?1, ?2)")))
- return;
-
- MapBlockExtraData *extra = block->extra;
-
- sqlite3_bind_blob(stmt, 2, extra->data, extra->size, SQLITE_TRANSIENT);
-
- if (sqlite3_step(stmt) != SQLITE_DONE)
- print_error(db, block, "saving");
-
- sqlite3_finalize(stmt);
-}
+++ /dev/null
-#ifndef _MAPDB_H_
-#define _MAPDB_H_
-
-#include <sqlite3.h>
-#include <stdbool.h>
-#include "map.h"
-
-sqlite3 *open_mapdb(const char *path);
-bool load_block(sqlite3 *db, MapBlock *block);
-void save_block(sqlite3 *db, MapBlock *block);
-
-#endif
+++ /dev/null
-#include <math.h>
-#include "mapgen.h"
-#include "perlin.h"
-
-void mapgen_generate_block(MapBlock *block)
-{
- for (u8 x = 0; x < 16; x++) {
- u32 ux = x + block->pos.x * 16 + ((u32) 1 << 31);
- for (u8 z = 0; z < 16; z++) {
- u32 uz = z + block->pos.z * 16 + ((u32) 1 << 31);
- s32 height = smooth2d(ux / 32.0, uz / 32.0, 0, 0) * 16.0 + 128.0;
- bool is_mountain = false;
-
- double mountain_factor = (smooth2d(ux / 1000.0, uz / 1000.0, 0, 1) - 0.3) * 5.0;
-
- if (mountain_factor > 0.0) {
- height = pow(height * pow(((smooth2d(ux / 50.0, uz / 50.0, 2, 2) + 1.0) * 256.0 + 128.0), mountain_factor), 1.0 / (mountain_factor + 1.0));
- is_mountain = true;
- }
-
- for (u8 y = 0; y < 16; y++) {
- s32 ay = block->pos.y * 16 + y;
- s32 diff = ay - height;
-
- Node node = NODE_AIR;
-
- if (diff < -5)
- node = NODE_STONE;
- else if (diff < -1)
- node = is_mountain ? NODE_STONE : NODE_DIRT;
- else if (diff < 0)
- node = is_mountain ? NODE_STONE : NODE_GRASS;
- else if (diff < 1)
- node = (is_mountain && ay > 256) ? NODE_SNOW : NODE_AIR;
-
- block->data[x][y][z] = map_node_create(node);
- block->metadata[x][y][z] = list_create(&list_compare_string);
-
- if (node == NODE_GRASS) {
- double min, max;
- min = 0.15;
- max = 0.45;
- block->data[x][y][z].state.biome.x = (smooth2d(ux / 128.0, uz / 128.0, 0, 3) * 0.5 + 0.5) * (max - min) + min;
- block->data[x][y][z].state.biome.y = 1.0;
- }
- }
- }
- }
-}
+++ /dev/null
-#ifndef _MAPGEN_H_
-#define _MAPGEN_H_
-
-#include "map.h"
-
-void mapgen_generate_block(MapBlock *block);
-
-#endif
+++ /dev/null
-#include <assert.h>
-#include <stdlib.h>
-#include <stddef.h>
-#include "mesh.h"
-#include "scene.h"
-
-VertexBuffer vertexbuffer_create()
-{
- return (VertexBuffer) {
- .faces = array_create(sizeof(Face)),
- };
-}
-
-void vertexbuffer_set_texture(VertexBuffer *buffer, Texture *texture)
-{
- if (buffer->current && buffer->current->texture == texture->id)
- return;
- Face face = {
- .texture = texture->id,
- .vertices = array_create(sizeof(Vertex)),
- };
- array_append(&buffer->faces, &face);
- buffer->current = &((Face *) buffer->faces.ptr)[buffer->faces.siz - 1];
-}
-
-void vertexbuffer_add_vertex(VertexBuffer *buffer, Vertex *vertex)
-{
- array_append(&buffer->current->vertices, vertex);
-}
-
-static int qsort_compare_faces(const void *f1, const void *f2)
-{
- return ((Face *) f1)->texture - ((Face *) f2)->texture;
-}
-
-static void add_mesh(Array *meshes, Array *vertices, GLuint texture)
-{
- if (vertices->siz > 0) {
- Mesh *mesh = malloc(sizeof(Mesh));
- mesh->VAO = mesh->VBO = 0;
- // the reason the vertices are not copied and then free'd like anything else is that the vertices will be deleted after the first render anyway
- mesh->vertices = vertices->ptr;
- mesh->vertices_count = vertices->siz;
- mesh->texture = texture;
-
- array_append(meshes, &mesh);
- }
-
- *vertices = array_create(sizeof(Vertex));
-}
-
-MeshObject *meshobject_create(VertexBuffer buffer, struct Scene *scene, v3f pos)
-{
- if (buffer.faces.siz == 0)
- return NULL;
-
- MeshObject *obj = malloc(sizeof(MeshObject));
- obj->remove = false;
-
- obj->pos = pos;
- obj->rot = (v3f) {0.0f, 0.0f, 0.0f};
- obj->scale = (v3f) {1.0f, 1.0f, 1.0f};
- obj->angle = 0.0f;
- obj->visible = true;
- obj->wireframe = false;
- meshobject_transform(obj);
-
- qsort(buffer.faces.ptr, buffer.faces.siz, sizeof(Face), &qsort_compare_faces);
-
- Array meshes = array_create(sizeof(Mesh *));
- Array vertices = array_create(sizeof(Vertex));
- GLuint texture = 0;
-
- for (size_t f = 0; f < buffer.faces.siz; f++) {
- Face *face = &((Face *) buffer.faces.ptr)[f];
-
- if (face->texture != texture) {
- add_mesh(&meshes, &vertices, texture);
- texture = face->texture;
- }
-
- for (size_t v = 0; v < face->vertices.siz; v++)
- array_append(&vertices, &((Vertex *) face->vertices.ptr)[v]);
- free(face->vertices.ptr);
- }
- add_mesh(&meshes, &vertices, texture);
- free(buffer.faces.ptr);
-
- array_copy(&meshes, (void *) &obj->meshes, &obj->meshes_count);
- free(meshes.ptr);
-
- if (scene)
- scene_add_object(scene, obj);
-
- return obj;
-}
-
-
-void meshobject_transform(MeshObject *obj)
-{
- mat4x4_translate(obj->transform, obj->pos.x, obj->pos.y, obj->pos.z);
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wpedantic"
- mat4x4_rotate(obj->transform, obj->transform, obj->rot.x, obj->rot.y, obj->rot.z, obj->angle);
- mat4x4_scale_aniso(obj->transform, obj->transform, obj->scale.x, obj->scale.y, obj->scale.z);
-#pragma GCC diagnostic pop
-}
-
-
-void meshobject_delete(MeshObject *obj)
-{
- for (size_t i = 0; i < obj->meshes_count; i++) {
- Mesh *mesh = obj->meshes[i];
-
- if (mesh->vertices)
- free(mesh->vertices);
-
- if (mesh->VAO)
- glDeleteVertexArrays(1, &mesh->VAO);
-
- if (mesh->VBO)
- glDeleteBuffers(1, &mesh->VAO);
-
- free(mesh);
- }
-
- free(obj);
-}
-
-static void mesh_configure(Mesh *mesh)
-{
- glGenVertexArrays(1, &mesh->VAO);
- glGenBuffers(1, &mesh->VBO);
-
- glBindVertexArray(mesh->VAO);
- glBindBuffer(GL_ARRAY_BUFFER, mesh->VBO);
-
- glBufferData(GL_ARRAY_BUFFER, mesh->vertices_count * sizeof(Vertex), mesh->vertices, GL_STATIC_DRAW);
-
- glVertexAttribPointer(0, 3, GL_FLOAT, false, sizeof(Vertex), (GLvoid *) offsetof(Vertex, x));
- glEnableVertexAttribArray(0);
-
- glVertexAttribPointer(1, 2, GL_FLOAT, false, sizeof(Vertex), (GLvoid *) offsetof(Vertex, s));
- glEnableVertexAttribArray(1);
-
- glVertexAttribPointer(2, 3, GL_FLOAT, false, sizeof(Vertex), (GLvoid *) offsetof(Vertex, r));
- glEnableVertexAttribArray(2);
-
- glBindBuffer(GL_ARRAY_BUFFER, 0);
- glBindVertexArray(0);
-
- free(mesh->vertices);
- mesh->vertices = NULL;
-}
-
-void meshobject_render(MeshObject *obj, ShaderProgram *prog)
-{
- if (! obj->visible)
- return;
-
- glUniformMatrix4fv(prog->loc_model, 1, GL_FALSE, obj->transform[0]);
-
- glActiveTexture(GL_TEXTURE0);
-
- if (obj->wireframe)
- glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
-
- for (size_t i = 0; i < obj->meshes_count; i++) {
- Mesh *mesh = obj->meshes[i];
-
- if (mesh->vertices)
- mesh_configure(mesh);
-
- glBindTexture(GL_TEXTURE_2D, mesh->texture);
- glBindVertexArray(mesh->VAO);
-
- glDrawArrays(GL_TRIANGLES, 0, mesh->vertices_count);
- }
-
- glBindTexture(GL_TEXTURE_2D, 0);
- glBindVertexArray(0);
-
- if (obj->wireframe)
- glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
-}
+++ /dev/null
-#ifndef _MESH_H_
-#define _MESH_H_
-
-#include <GL/glew.h>
-#include <GL/gl.h>
-#include <linmath.h/linmath.h>
-#include <stdbool.h>
-#include "array.h"
-#include "shaders.h"
-#include "texture.h"
-#include "types.h"
-
-typedef struct
-{
- GLfloat x, y, z;
- GLfloat s, t;
- GLfloat r, g, b;
-} __attribute__((packed)) Vertex;
-
-typedef struct
-{
- GLuint texture;
- Array vertices;
-} Face;
-
-typedef struct
-{
- Face *current;
- Array faces;
-} VertexBuffer;
-
-typedef struct
-{
- GLuint VAO, VBO;
- GLuint texture;
- Vertex *vertices;
- GLuint vertices_count;
-} Mesh;
-
-typedef struct
-{
- v3f pos, rot, scale;
- f32 angle;
- bool visible;
- mat4x4 transform;
- bool remove;
- Mesh **meshes;
- size_t meshes_count;
- bool wireframe;
-} MeshObject;
-
-struct Scene;
-
-VertexBuffer vertexbuffer_create();
-void vertexbuffer_set_texture(VertexBuffer *buffer, Texture *texture);
-void vertexbuffer_add_vertex(VertexBuffer *buffer, Vertex *vertex);
-
-MeshObject *meshobject_create(VertexBuffer buffer, struct Scene *scene, v3f pos);
-void meshobject_delete(MeshObject *obj);
-void meshobject_transform(MeshObject *obj);
-void meshobject_render(MeshObject *obj, ShaderProgram *prog);
-
-#endif
#define _NETWORK_H_
#include <stdbool.h>
-#define NAME_MAX 64
+#define PLAYER_NAME_MAX 64
typedef enum
{
+++ /dev/null
-#include <perlin/perlin.c>
-
+++ /dev/null
-#ifndef _PERLIN_H_
-#define _PERLIN_H_
-
-#include <perlin/perlin.h>
-
-#endif
#include <stdlib.h>
#include "queue.h"
-Queue *create_queue()
+Queue *queue_create()
{
Queue *queue = malloc(sizeof(Queue));
queue->list = list_create(NULL);
return queue;
}
-void delete_queue(Queue *queue)
+void queue_delete(Queue *queue)
{
pthread_mutex_destroy(&queue->mtx);
list_clear(&queue->list);
free(queue);
}
-void enqueue(Queue *queue, void *elem)
+void queue_enqueue(Queue *queue, void *elem)
{
pthread_mutex_lock(&queue->mtx);
list_put(&queue->list, elem, NULL);
void *dequeue(Queue *queue)
{
- return dequeue_callback(queue, NULL);
+ return queue_dequeue_callback(queue, NULL);
}
-void *dequeue_callback(Queue *queue, void (*callback)(void *elem))
+void *queue_dequeue_callback(Queue *queue, void (*callback)(void *elem))
{
pthread_mutex_lock(&queue->mtx);
void *elem = NULL;
pthread_mutex_t mtx;
} Queue;
-Queue *create_queue();
-void delete_queue(Queue *queue);
-void enqueue(Queue *queue, void *elem);
-void *dequeue(Queue *queue);
-void *dequeue_callback(Queue *queue, void (*callback)(void *elem));
+Queue *queue_create();
+void queue_delete(Queue *queue);
+void queue_enqueue(Queue *queue, void *elem);
+void *queue_dequeue(Queue *queue);
+void *queue_dequeue_callback(Queue *queue, void (*callback)(void *elem));
#endif
+++ /dev/null
-#include <stdlib.h>
-#include "scene.h"
-
-Scene *scene_create()
-{
- Scene *scene = malloc(sizeof(Scene));
- scene->objects = list_create(NULL),
- pthread_mutex_init(&scene->mtx, NULL);
- return scene;
-}
-
-static void list_delete_mesh(void *key, __attribute__((unused)) void *value, __attribute__((unused)) void *unused)
-{
- meshobject_delete(key);
-}
-
-void scene_delete(Scene *scene)
-{
- list_clear_func(&scene->objects, &list_delete_mesh, NULL);
- pthread_mutex_destroy(&scene->mtx);
- free(scene);
-}
-
-void scene_add_object(Scene *scene, MeshObject *obj)
-{
- pthread_mutex_lock(&scene->mtx);
- list_put(&scene->objects, obj, NULL);
- pthread_mutex_unlock(&scene->mtx);
-}
-
-void scene_render(Scene *scene, ShaderProgram *prog)
-{
- for (ListPair **pairptr = &scene->objects.first; *pairptr != NULL; ) {
- ListPair *pair = *pairptr;
- MeshObject *obj = pair->key;
- if (obj->remove) {
- pthread_mutex_lock(&scene->mtx);
- *pairptr = pair->next;
- pthread_mutex_unlock(&scene->mtx);
- free(pair);
- meshobject_delete(obj);
- } else {
- meshobject_render(obj, prog);
- pairptr = &pair->next;
- }
- }
-}
+++ /dev/null
-#ifndef _SCENE_H_
-#define _SCENE_H_
-
-#include <pthread.h>
-#include "list.h"
-#include "mesh.h"
-#include "shaders.h"
-
-typedef struct Scene
-{
- List objects;
- pthread_mutex_t mtx;
-} Scene;
-
-Scene *scene_create();
-void scene_delete(Scene *scene);
-
-void scene_add_object(Scene *scene, MeshObject *obj);
-void scene_render(Scene *scene, ShaderProgram *prog);
-
-#endif
+++ /dev/null
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <errno.h>
-#include <netdb.h>
-#include "server.h"
-#include "servermap.h"
-#include "signal.h"
-#include "util.h"
-
-Server server;
-
-void server_disconnect_client(Client *client, int flags, const char *detail)
-{
- 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);
-}
-
-#include "network.c"
-
-static void *server_reciever_thread(void *cli)
-{
- Client *client = cli;
-
- if (! handle_packets(client))
- server_disconnect_client(client, DISCO_NO_SEND | DISCO_NO_JOIN, "network error");
-
- return NULL;
-}
-
-static void server_accept_client()
-{
- 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 = (v3f) {0.0f, 0.0f, 0.0f};
- pthread_create(&client->net_thread, NULL, &server_reciever_thread, client);
- client->sent_blocks = list_create(NULL);
-
- pthread_rwlock_wrlock(&server.clients_rwlck);
- list_put(&server.clients, client, NULL);
- pthread_rwlock_unlock(&server.clients_rwlck);
-
- printf("Connected %s\n", client->address);
-}
-
-static void list_disconnect_client(void *key, __attribute__((unused)) void *value, __attribute__((unused)) void *unused)
-{
- server_disconnect_client(key, DISCO_NO_REMOVE | DISCO_NO_MESSAGE, "");
-}
-
-void server_start(int fd)
-{
- server.sockfd = fd;
- server.map = map_create(NULL);
- 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);
-
- servermap_init(&server);
-
- while (! interrupted)
- server_accept_client();
-
- printf("Shutting down\n");
-
- servermap_deinit();
-
- 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);
-
- map_delete(server.map);
-
- exit(EXIT_SUCCESS);
-}
-
-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));
-
- 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);
-
- init_signal_handlers();
- server_start(fd);
-
- return EXIT_SUCCESS;
-}
+++ /dev/null
-#ifndef _SERVER_H_
-#define _SERVER_H_
-
-#include <pthread.h>
-#include <netinet/in.h>
-#include "clientcommands.h"
-#include "servercommands.h"
-#include "list.h"
-#include "map.h"
-#include "network.h"
-
-typedef struct
-{
- int sockfd;
- pthread_rwlock_t clients_rwlck;
- List clients;
- pthread_rwlock_t players_rwlck;
- List players;
- Map *map;
-} Server;
-
-typedef struct Client
-{
- int fd;
- pthread_mutex_t mtx;
- ClientState state;
- char *address;
- char *name;
- Server *server;
- pthread_t net_thread;
- List sent_blocks;
- v3f pos;
-} Client;
-
-typedef enum
-{
- DISCO_NO_REMOVE = 0x01,
- DISCO_NO_SEND = 0x02,
- DISCO_NO_MESSAGE = 0x04,
- DISCO_NO_JOIN = 0x08,
-} DiscoFlag;
-
-void server_disconnect_client(Client *client, int flags, const char *detail);
-void server_shutdown();
-
-#endif
--- /dev/null
+#include <stdlib.h>
+#include "server/facecache.h"
+#include "array.h"
+
+static struct
+{
+ Array positions;
+ u32 size;
+ pthread_mutex_t mtx;
+} facecache;
+
+__attribute((constructor)) static void face_cache_init()
+{
+ facecache.size = 0;
+ facecache.positions = array_create(sizeof(v3s32));
+ v3s32 pos = {0, 0, 0};
+ array_append(&facecache.positions, &pos);
+ pthread_mutex_init(&facecache.mtx, NULL);
+}
+
+__attribute((destructor)) void face_cache_deinit()
+{
+ if (facecache.positions.ptr)
+ free(facecache.positions.ptr);
+ pthread_mutex_destroy(&facecache.mtx);
+}
+
+static void face_cache_calculate(s32 size)
+{
+#define ADDPOS(a, b, c, va, vb, vc) \
+ { \
+ v3s32 pos; \
+ *(s32 *) ((char *) &pos + offsetof(v3s32, a)) = va; \
+ *(s32 *) ((char *) &pos + offsetof(v3s32, b)) = vb; \
+ *(s32 *) ((char *) &pos + offsetof(v3s32, c)) = vc; \
+ array_append(&facecache.positions, &pos); \
+ }
+#define SQUARES(a, b, c) \
+ for (s32 va = -size + 1; va < size; va++) { \
+ for (s32 vb = -size + 1; vb < size; vb++) { \
+ ADDPOS(a, b, c, va, vb, size) \
+ ADDPOS(a, b, c, va, vb, -size) \
+ } \
+ }
+ SQUARES(x, z, y)
+ SQUARES(x, y, z)
+ SQUARES(z, y, x)
+#undef SQUARES
+#define EDGES(a, b, c) \
+ for (s32 va = -size + 1; va < size; va++) { \
+ ADDPOS(a, b, c, va, size, size) \
+ ADDPOS(a, b, c, va, size, -size) \
+ ADDPOS(a, b, c, va, -size, size) \
+ ADDPOS(a, b, c, va, -size, -size) \
+ }
+ EDGES(x, y, z)
+ EDGES(z, x, y)
+ EDGES(y, x, z)
+#undef EDGES
+ ADDPOS(x, y, z, size, size, size)
+ ADDPOS(x, y, z, size, size, -size)
+ ADDPOS(x, y, z, size, -size, size)
+ ADDPOS(x, y, z, size, -size, -size)
+ ADDPOS(x, y, z, -size, size, size)
+ ADDPOS(x, y, z, -size, size, -size)
+ ADDPOS(x, y, z, -size, -size, size)
+ ADDPOS(x, y, z, -size, -size, -size)
+#undef ADDPOS
+}
+
+v3s32 facecache_face(size_t i, v3s32 *base)
+{
+ pthread_mutex_lock(&facecache.mtx);
+ while (facecache.positions.siz <= i)
+ face_cache_calculate(++facecache.size);
+ v3s32 pos = ((v3s32 *) facecache.positions.ptr)[i];
+ pthread_mutex_unlock(&facecache.mtx);
+ if (base) {
+ pos.x += base->x;
+ pos.y += base->y;
+ pos.z += base->z;
+ }
+ return pos;
+}
+
+size_t facecache_count(u32 size)
+{
+ size_t len = 1 + size * 2;
+ return len * len * len;
+}
--- /dev/null
+#ifndef _FACECACHE_H_
+#define _FACECACHE_H_
+
+#include <pthread.h>
+#include "types.h"
+
+v3s32 facecache_face(size_t i, v3s32 *base);
+size_t facecache_count(u32 size);
+
+#endif
--- /dev/null
+#include <stdio.h>
+#include <endian.h>
+#include <stdlib.h>
+#include "server/mapdb.h"
+#include "server/server_map.h"
+
+static void print_error(sqlite3 *db, MapBlock *block, const char *action)
+{
+ printf("Database error with %s block at %d %d %d: %s\n", action, block->pos.x, block->pos.y, block->pos.z, sqlite3_errmsg(db));
+}
+
+sqlite3 *mapdb_open(const char *path)
+{
+ sqlite3 *db;
+ char *err;
+
+ if (sqlite3_open_v2(path, &db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX, NULL) != SQLITE_OK) {
+ printf("Failed to open database: %s\n", sqlite3_errmsg(db));
+ } else if (sqlite3_exec(db, "CREATE TABLE IF NOT EXISTS blocks (pos BLOB PRIMARY KEY, data BLOB NOT NULL);", NULL, NULL, &err) != SQLITE_OK) {
+ printf("Failed to initialize database: %s\n", err);
+ sqlite3_free(err);
+ }
+
+ return db;
+}
+
+static sqlite3_stmt *prepare_statement(sqlite3 *db, MapBlock *block, const char *action, const char *sql)
+{
+ sqlite3_stmt *stmt;
+
+ if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) != SQLITE_OK) {
+ print_error(db, block, action);
+ return NULL;
+ }
+
+ size_t psize = sizeof(s32) * 3;
+ s32 *pos = malloc(psize);
+ pos[0] = htobe32(block->pos.x);
+ pos[1] = htobe32(block->pos.y);
+ pos[2] = htobe32(block->pos.z);
+
+ sqlite3_bind_blob(stmt, 1, pos, psize, &free);
+
+ return stmt;
+}
+
+bool mapdb_load_block(sqlite3 *db, MapBlock *block)
+{
+ sqlite3_stmt *stmt;
+
+ if (! (stmt = prepare_statement(db, block, "loading", "SELECT data FROM blocks WHERE pos=?")))
+ return false;
+
+ int rc = sqlite3_step(stmt);
+ bool found = rc == SQLITE_ROW;
+
+ if (found) {
+ const char *data = sqlite3_column_blob(stmt, 0);
+ map_deserialize_block(block, data + sizeof(MapBlockHeader), be32toh(*(MapBlockHeader *) data));
+ } else if (rc != SQLITE_DONE) {
+ print_error(db, block, "loading");
+ }
+
+ sqlite3_finalize(stmt);
+ return found;
+}
+
+void mapdb_save_block(sqlite3 *db, MapBlock *block)
+{
+ sqlite3_stmt *stmt;
+
+ if (! (stmt = prepare_statement(db, block, "saving", "REPLACE INTO blocks (pos, data) VALUES(?1, ?2)")))
+ return;
+
+ MapBlockExtraData *extra = block->extra;
+
+ sqlite3_bind_blob(stmt, 2, extra->data, extra->size, SQLITE_TRANSIENT);
+
+ if (sqlite3_step(stmt) != SQLITE_DONE)
+ print_error(db, block, "saving");
+
+ sqlite3_finalize(stmt);
+}
--- /dev/null
+#ifndef _MAPDB_H_
+#define _MAPDB_H_
+
+#include <sqlite3.h>
+#include <stdbool.h>
+#include "map.h"
+
+sqlite3 *mapdb_open(const char *path);
+bool mapdb_load_block(sqlite3 *db, MapBlock *block);
+void mapdb_save_block(sqlite3 *db, MapBlock *block);
+
+#endif
--- /dev/null
+#include <math.h>
+#include "server/mapgen.h"
+#include "server/perlin.h"
+
+void mapgen_generate_block(MapBlock *block)
+{
+ for (u8 x = 0; x < 16; x++) {
+ u32 ux = x + block->pos.x * 16 + ((u32) 1 << 31);
+ for (u8 z = 0; z < 16; z++) {
+ u32 uz = z + block->pos.z * 16 + ((u32) 1 << 31);
+ s32 height = smooth2d(ux / 32.0, uz / 32.0, 0, 0) * 16.0 + 128.0;
+ bool is_mountain = false;
+
+ double mountain_factor = (smooth2d(ux / 1000.0, uz / 1000.0, 0, 1) - 0.3) * 5.0;
+
+ if (mountain_factor > 0.0) {
+ height = pow(height * pow(((smooth2d(ux / 50.0, uz / 50.0, 2, 2) + 1.0) * 256.0 + 128.0), mountain_factor), 1.0 / (mountain_factor + 1.0));
+ is_mountain = true;
+ }
+
+ for (u8 y = 0; y < 16; y++) {
+ s32 ay = block->pos.y * 16 + y;
+ s32 diff = ay - height;
+
+ Node node = NODE_AIR;
+
+ if (diff < -5)
+ node = NODE_STONE;
+ else if (diff < -1)
+ node = is_mountain ? NODE_STONE : NODE_DIRT;
+ else if (diff < 0)
+ node = is_mountain ? NODE_STONE : NODE_GRASS;
+ else if (diff < 1)
+ node = (is_mountain && ay > 256) ? NODE_SNOW : NODE_AIR;
+
+ block->data[x][y][z] = map_node_create(node);
+ block->metadata[x][y][z] = list_create(&list_compare_string);
+
+ if (node == NODE_GRASS) {
+ double min, max;
+ min = 0.15;
+ max = 0.45;
+ block->data[x][y][z].state.biome.x = (smooth2d(ux / 128.0, uz / 128.0, 0, 3) * 0.5 + 0.5) * (max - min) + min;
+ block->data[x][y][z].state.biome.y = 1.0;
+ }
+ }
+ }
+ }
+}
--- /dev/null
+#ifndef _MAPGEN_H_
+#define _MAPGEN_H_
+
+#include "map.h"
+
+void mapgen_generate_block(MapBlock *block);
+
+#endif
--- /dev/null
+#include <perlin/perlin.c>
+
--- /dev/null
+#ifndef _PERLIN_H_
+#define _PERLIN_H_
+
+#include <perlin/perlin.h>
+
+#endif
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <netdb.h>
+#include "server/server.h"
+#include "server/server_map.h"
+#include "signal.h"
+#include "util.h"
+
+Server server;
+
+void server_disconnect_client(Client *client, int flags, const char *detail)
+{
+ 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);
+}
+
+#include "network.c"
+
+static void *server_reciever_thread(void *cli)
+{
+ Client *client = cli;
+
+ if (! handle_packets(client))
+ server_disconnect_client(client, DISCO_NO_SEND | DISCO_NO_JOIN, "network error");
+
+ return NULL;
+}
+
+static void server_accept_client()
+{
+ 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 = (v3f) {0.0f, 0.0f, 0.0f};
+ pthread_create(&client->net_thread, NULL, &server_reciever_thread, client);
+ client->sent_blocks = list_create(NULL);
+
+ pthread_rwlock_wrlock(&server.clients_rwlck);
+ list_put(&server.clients, client, NULL);
+ pthread_rwlock_unlock(&server.clients_rwlck);
+
+ printf("Connected %s\n", client->address);
+}
+
+static void list_disconnect_client(void *key, __attribute__((unused)) void *value, __attribute__((unused)) void *unused)
+{
+ server_disconnect_client(key, DISCO_NO_REMOVE | DISCO_NO_MESSAGE, "");
+}
+
+void server_start(int fd)
+{
+ server.sockfd = fd;
+ server.map = map_create(NULL);
+ 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);
+
+ server_map_init(&server);
+
+ while (! interrupted)
+ server_accept_client();
+
+ printf("Shutting down\n");
+
+ server_map_deinit();
+
+ 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);
+
+ map_delete(server.map);
+
+ exit(EXIT_SUCCESS);
+}
+
+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));
+
+ 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);
+
+ signal_handlers_init();
+ server_start(fd);
+
+ return EXIT_SUCCESS;
+}
--- /dev/null
+#ifndef _SERVER_H_
+#define _SERVER_H_
+
+#include <pthread.h>
+#include <netinet/in.h>
+#include "client/client_commands.h"
+#include "server/server_commands.h"
+#include "list.h"
+#include "map.h"
+#include "network.h"
+
+typedef struct
+{
+ int sockfd;
+ pthread_rwlock_t clients_rwlck;
+ List clients;
+ pthread_rwlock_t players_rwlck;
+ List players;
+ Map *map;
+} Server;
+
+typedef struct Client
+{
+ int fd;
+ pthread_mutex_t mtx;
+ ClientState state;
+ char *address;
+ char *name;
+ Server *server;
+ pthread_t net_thread;
+ List sent_blocks;
+ v3f pos;
+} Client;
+
+typedef enum
+{
+ DISCO_NO_REMOVE = 0x01,
+ DISCO_NO_SEND = 0x02,
+ DISCO_NO_MESSAGE = 0x04,
+ DISCO_NO_JOIN = 0x08,
+} DiscoFlag;
+
+void server_disconnect_client(Client *client, int flags, const char *detail);
+void server_shutdown();
+
+#endif
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include "server/server.h"
+#include "server/server_map.h"
+#include "util.h"
+
+static bool disconnect_handler(Client *client, bool good)
+{
+ if (good)
+ server_disconnect_client(client, DISCO_NO_SEND, NULL);
+ return true;
+}
+
+static bool auth_handler(Client *client, bool good)
+{
+ char *name = read_string(client->fd, 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;
+ } else {
+ free(name);
+ }
+
+ pthread_mutex_lock(&client->mtx);
+ bool ret = write_u32(client->fd, CC_AUTH) && write_u8(client->fd, success);
+ pthread_mutex_unlock(&client->mtx);
+
+ pthread_rwlock_unlock(&client->server->players_rwlck);
+
+ printf("Authentication %s: %s -> %s\n", success ? "success" : "failure", client->address, name);
+
+ return ret;
+}
+
+static bool setnode_handler(Client *client, bool good)
+{
+ v3s32 pos;
+
+ if (! read_v3s32(client->fd, &pos))
+ return false;
+
+ MapNode node;
+
+ if (! map_deserialize_node(client->fd, &node))
+ return false;
+
+ if (good)
+ map_set_node(client->server->map, pos, node);
+
+ return true;
+}
+
+static bool pos_handler(Client *client, bool good)
+{
+ v3f pos;
+
+ if (! read_v3f32(client->fd, &pos))
+ return false;
+
+ if (good)
+ client->pos = pos;
+
+ return true;
+}
+
+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},
+};
--- /dev/null
+#ifndef _SERVER_COMMANDS_H_
+#define _SCOMMANDS_H_
+
+typedef enum
+{
+ SERVER_COMMAND_NULL,
+ SC_DISCONNECT,
+ SC_AUTH,
+ SC_SETNODE,
+ SC_POS,
+ SERVER_COMMAND_COUNT,
+} ServerCommand;
+
+#ifdef _CLIENT_H_
+typedef ServerCommand RemoteCommand;
+#endif
+
+#ifdef _SERVER_H_
+typedef ServerCommand HostCommand;
+#define HOST_COMMAND_COUNT SERVER_COMMAND_COUNT
+#endif
+
+#endif
--- /dev/null
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdio.h>
+#include "server/facecache.h"
+#include "server/mapdb.h"
+#include "server/mapgen.h"
+#include "server/server_map.h"
+#include "map.h"
+
+static struct {
+ Server *server;
+ size_t max_blocks;
+ pthread_t thread;
+ sqlite3 *db;
+ bool cancel;
+} server_map;
+
+static void initialize_block(MapBlock *block)
+{
+ if (! mapdb_load_block(server_map.db, block))
+ mapgen_generate_block(block);
+ block->state = MBS_MODIFIED;
+}
+
+static void reset_client_block(void *key, __attribute__((unused)) void *value, void *block)
+{
+ Client *client = list_get(&server_map.server->players, key);
+ if (client)
+ list_delete(&client->sent_blocks, block);
+ free(key);
+}
+
+static void list_delete_extra_data(void *key, __attribute__((unused)) void *value, __attribute__((unused)) void *unused)
+{
+ free(key);
+}
+
+static void free_extra_data(void *ext)
+{
+ MapBlockExtraData *extra = ext;
+
+ if (extra) {
+ list_clear_func(&extra->clients, &list_delete_extra_data, NULL);
+ free(extra->data);
+ free(extra);
+ }
+}
+
+static void reset_block(MapBlock *block)
+{
+ MapBlockExtraData *extra = block->extra;
+
+ if (extra) {
+ free(extra->data);
+ } else {
+ extra = malloc(sizeof(MapBlockExtraData));
+ extra->clients = list_create(&list_compare_string);
+ }
+
+ map_serialize_block(block, &extra->data, &extra->size);
+
+ block->extra = extra;
+ block->free_extra = &free_extra_data;
+
+ mapdb_save_block(server_map.db, block);
+
+ list_clear_func(&extra->clients, &reset_client_block, block);
+ list_clear(&extra->clients);
+
+ block->state = MBS_READY;
+}
+
+static void send_block(Client *client, MapBlock *block)
+{
+ MapBlockExtraData *extra = block->extra;
+
+ list_put(&client->sent_blocks, block, block);
+ list_put(&extra->clients, strdup(client->name), NULL);
+
+ pthread_mutex_lock(&client->mtx);
+ if (client->state == CS_ACTIVE)
+ (void) (write_u32(client->fd, CC_BLOCK) && write_v3s32(client->fd, block->pos) && write(client->fd, extra->data, extra->size) != -1);
+ pthread_mutex_unlock(&client->mtx);
+}
+
+static void reset_modified_blocks(Client *client)
+{
+ for (ListPair **pairptr = &client->sent_blocks.first; *pairptr != NULL; pairptr = &(*pairptr)->next) {
+
+ MapBlock *block = (*pairptr)->key;
+
+ pthread_mutex_lock(&block->mtx);
+ if (block->state == MBS_MODIFIED) {
+ reset_block(block);
+
+ ListPair *next = (*pairptr)->next;
+ free(*pairptr);
+ *pairptr = next;
+ }
+ pthread_mutex_unlock(&block->mtx);
+ }
+}
+
+static void send_blocks(Client *client)
+{
+ v3s32 pos = map_node_to_block_pos((v3s32) {client->pos.x, client->pos.y, client->pos.z}, NULL);
+
+ for (size_t i = 0; i < server_map.max_blocks; i++) {
+ MapBlock *block = map_get_block(client->server->map, facecache_face(i, &pos), true);
+
+ pthread_mutex_lock(&block->mtx);
+ switch (block->state) {
+ case MBS_CREATED:
+
+ initialize_block(block);
+
+ __attribute__ ((fallthrough)); case MBS_MODIFIED:
+
+ reset_block(block);
+
+ send:
+ send_block(client, block);
+
+ pthread_mutex_unlock(&block->mtx);
+ return;
+
+ case MBS_READY:
+
+ if (! list_get(&client->sent_blocks, block))
+ goto send;
+ else
+ break;
+
+ default:
+
+ break;
+ }
+ pthread_mutex_unlock(&block->mtx);
+ }
+}
+
+static void map_step()
+{
+ pthread_rwlock_rdlock(&server_map.server->players_rwlck);
+ ITERATE_LIST(&server_map.server->players, pair) reset_modified_blocks(pair->value);
+ ITERATE_LIST(&server_map.server->players, pair) send_blocks(pair->value);
+ pthread_rwlock_unlock(&server_map.server->players_rwlck);
+}
+
+static void *map_thread(__attribute__((unused)) void *unused)
+{
+ server_map.db = mapdb_open("map.sqlite");
+
+ while (! server_map.cancel)
+ map_step();
+
+ return NULL;
+}
+
+void server_map_init(Server *server)
+{
+ server_map.server = server;
+ server_map.max_blocks = facecache_count(10);
+
+ pthread_create(&server_map.thread, NULL, &map_thread, NULL);
+}
+
+void server_map_deinit()
+{
+ server_map.cancel = true;
+ pthread_join(server_map.thread, NULL);
+ sqlite3_close(server_map.db);
+}
--- /dev/null
+#ifndef _SERVER_MAP_H_
+#define _SERVER_MAP_H_
+
+#include "server/server.h"
+
+typedef struct
+{
+ List clients;
+ char *data;
+ size_t size;
+} MapBlockExtraData;
+
+void server_map_init(Server *server);
+void server_map_deinit();
+
+#endif
+++ /dev/null
-#include <stdio.h>
-#include <stdlib.h>
-#include "server.h"
-#include "servermap.h"
-#include "util.h"
-
-static bool disconnect_handler(Client *client, bool good)
-{
- if (good)
- server_disconnect_client(client, DISCO_NO_SEND, NULL);
- return true;
-}
-
-static bool auth_handler(Client *client, bool good)
-{
- char *name = read_string(client->fd, NAME_MAX);
-
- if (! name)
- return false;
-
- if (! good) {
- free(name);
- return true;
- }
-
- 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;
- } else {
- free(name);
- }
-
- pthread_mutex_lock(&client->mtx);
- bool ret = write_u32(client->fd, CC_AUTH) && write_u8(client->fd, success);
- pthread_mutex_unlock(&client->mtx);
-
- pthread_rwlock_unlock(&client->server->players_rwlck);
-
- printf("Authentication %s: %s -> %s\n", success ? "success" : "failure", client->address, name);
-
- return ret;
-}
-
-static bool setnode_handler(Client *client, bool good)
-{
- v3s32 pos;
-
- if (! read_v3s32(client->fd, &pos))
- return false;
-
- MapNode node;
-
- if (! map_deserialize_node(client->fd, &node))
- return false;
-
- if (good)
- map_set_node(client->server->map, pos, node);
-
- return true;
-}
-
-static bool pos_handler(Client *client, bool good)
-{
- v3f pos;
-
- if (! read_v3f32(client->fd, &pos))
- return false;
-
- if (good)
- client->pos = pos;
-
- return true;
-}
-
-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},
-};
+++ /dev/null
-#ifndef _SERVER_COMMAND_H_
-#define _SERVER_COMMAND_H_
-
-typedef enum
-{
- SERVER_COMMAND_NULL,
- SC_DISCONNECT,
- SC_AUTH,
- SC_SETNODE,
- SC_POS,
- SERVER_COMMAND_COUNT,
-} ServerCommand;
-
-#ifdef _CLIENT_H_
-typedef ServerCommand RemoteCommand;
-#endif
-
-#ifdef _SERVER_H_
-typedef ServerCommand HostCommand;
-#define HOST_COMMAND_COUNT SERVER_COMMAND_COUNT
-#endif
-
-#endif
+++ /dev/null
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <stdio.h>
-#include "facecache.h"
-#include "map.h"
-#include "mapdb.h"
-#include "mapgen.h"
-#include "servermap.h"
-
-static struct {
- size_t max_blocks;
- pthread_t thread;
- sqlite3 *db;
- bool cancel;
-} servermap;
-
-static Server *server = NULL;
-
-static void initialize_block(MapBlock *block)
-{
- if (! load_block(servermap.db, block))
- mapgen_generate_block(block);
- block->state = MBS_MODIFIED;
-}
-
-static void reset_client_block(void *key, __attribute__((unused)) void *value, void *block)
-{
- Client *client = list_get(&server->players, key);
- if (client)
- list_delete(&client->sent_blocks, block);
- free(key);
-}
-
-static void list_delete_extra_data(void *key, __attribute__((unused)) void *value, __attribute__((unused)) void *unused)
-{
- free(key);
-}
-
-static void free_extra_data(void *ext)
-{
- MapBlockExtraData *extra = ext;
-
- if (extra) {
- list_clear_func(&extra->clients, &list_delete_extra_data, NULL);
- free(extra->data);
- free(extra);
- }
-}
-
-static void reset_block(MapBlock *block)
-{
- MapBlockExtraData *extra = block->extra;
-
- if (extra) {
- free(extra->data);
- } else {
- extra = malloc(sizeof(MapBlockExtraData));
- extra->clients = list_create(&list_compare_string);
- }
-
- map_serialize_block(block, &extra->data, &extra->size);
-
- block->extra = extra;
- block->free_extra = &free_extra_data;
-
- save_block(servermap.db, block);
-
- list_clear_func(&extra->clients, &reset_client_block, block);
- list_clear(&extra->clients);
-
- block->state = MBS_READY;
-}
-
-static void send_block(Client *client, MapBlock *block)
-{
- MapBlockExtraData *extra = block->extra;
-
- list_put(&client->sent_blocks, block, block);
- list_put(&extra->clients, strdup(client->name), NULL);
-
- pthread_mutex_lock(&client->mtx);
- if (client->state == CS_ACTIVE)
- (void) (write_u32(client->fd, CC_BLOCK) && write_v3s32(client->fd, block->pos) && write(client->fd, extra->data, extra->size) != -1);
- pthread_mutex_unlock(&client->mtx);
-}
-
-static void reset_modified_blocks(Client *client)
-{
- for (ListPair **pairptr = &client->sent_blocks.first; *pairptr != NULL; pairptr = &(*pairptr)->next) {
-
- MapBlock *block = (*pairptr)->key;
-
- pthread_mutex_lock(&block->mtx);
- if (block->state == MBS_MODIFIED) {
- reset_block(block);
-
- ListPair *next = (*pairptr)->next;
- free(*pairptr);
- *pairptr = next;
- }
- pthread_mutex_unlock(&block->mtx);
- }
-}
-
-static void send_blocks(Client *client)
-{
- v3s32 pos = map_node_to_block_pos((v3s32) {client->pos.x, client->pos.y, client->pos.z}, NULL);
-
- for (size_t i = 0; i < servermap.max_blocks; i++) {
- MapBlock *block = map_get_block(client->server->map, get_face(i, &pos), true);
-
- pthread_mutex_lock(&block->mtx);
- switch (block->state) {
- case MBS_CREATED:
-
- initialize_block(block);
-
- __attribute__ ((fallthrough)); case MBS_MODIFIED:
-
- reset_block(block);
-
- send:
- send_block(client, block);
-
- pthread_mutex_unlock(&block->mtx);
- return;
-
- case MBS_READY:
-
- if (! list_get(&client->sent_blocks, block))
- goto send;
- else
- break;
-
- default:
-
- break;
- }
- pthread_mutex_unlock(&block->mtx);
- }
-}
-
-static void map_step()
-{
- pthread_rwlock_rdlock(&server->players_rwlck);
- ITERATE_LIST(&server->players, pair) reset_modified_blocks(pair->value);
- ITERATE_LIST(&server->players, pair) send_blocks(pair->value);
- pthread_rwlock_unlock(&server->players_rwlck);
-}
-
-static void *map_thread(__attribute__((unused)) void *unused)
-{
- servermap.db = open_mapdb("map.sqlite");
-
- while (! servermap.cancel)
- map_step();
-
- return NULL;
-}
-
-void servermap_init(Server *srv)
-{
- server = srv;
- servermap.max_blocks = get_face_count(10);
-
- pthread_create(&servermap.thread, NULL, &map_thread, NULL);
-}
-
-void servermap_deinit()
-{
- servermap.cancel = true;
- pthread_join(servermap.thread, NULL);
- sqlite3_close(servermap.db);
-}
+++ /dev/null
-#ifndef _SERVERMAP_H_
-#define _SERVERMAP_H_
-
-#include "server.h"
-
-typedef struct
-{
- List clients;
- char *data;
- size_t size;
-} MapBlockExtraData;
-
-void servermap_init(Server *srv);
-void servermap_deinit();
-
-#endif
+++ /dev/null
-#include <string.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include "shaders.h"
-
-static GLuint compile_and_attach_shader(GLenum type, const char *path, const char *name, GLuint program)
-{
- char full_path[strlen(path) + 1 + strlen(name) + 1 + 4 + 1];
- sprintf(full_path, "%s/%s.glsl", path, name);
-
- FILE *file = fopen(full_path, "r");
- if (! file) {
- perror("fopen");
- return 0;
- }
-
- if (fseek(file, 0, SEEK_END) == -1) {
- perror("fseek");
- fclose(file);
- return 0;
- }
-
- long size = ftell(file);
-
- if (size == 1) {
- perror("ftell");
- fclose(file);
- return 0;
- }
-
- if (fseek(file, 0, SEEK_SET) == -1) {
- perror("fseek");
- fclose(file);
- return 0;
- }
-
- char code[size];
-
- if (fread(code, 1, size, file) != (size_t) size) {
- perror("fread");
- fclose(file);
- return 0;
- }
-
- fclose(file);
-
- GLuint id = glCreateShader(type);
-
- char const *codeptr = code;
- const int isize = (int) size;
- glShaderSource(id, 1, &codeptr, &isize);
-
- glCompileShader(id);
-
- GLint success;
- glGetShaderiv(id, GL_COMPILE_STATUS, &success);
- if (! success) {
- char errbuf[BUFSIZ];
- glGetShaderInfoLog(id, BUFSIZ, NULL, errbuf);
- fprintf(stderr, "Failed to compile %s shader: %s\n", name, errbuf);
- glDeleteShader(id);
- return 0;
- }
-
- glAttachShader(program, id);
-
- return id;
-}
-
-
-ShaderProgram *create_shader_program(const char *path)
-{
- GLuint id = glCreateProgram();
-
- GLuint vert, frag;
-
- if (! (vert = compile_and_attach_shader(GL_VERTEX_SHADER, path, "vertex", id))) {
- glDeleteProgram(id);
- return NULL;
- }
-
- if (! (frag = compile_and_attach_shader(GL_FRAGMENT_SHADER, path, "fragment", id))) {
- glDeleteShader(vert);
- glDeleteProgram(id);
- return NULL;
- }
-
- glLinkProgram(id);
- glDeleteShader(vert);
- glDeleteShader(frag);
-
- GLint success;
- glGetProgramiv(id, GL_LINK_STATUS, &success);
- if (! success) {
- char errbuf[BUFSIZ];
- glGetProgramInfoLog(id, BUFSIZ, NULL, errbuf);
- fprintf(stderr, "Failed to link shader program: %s\n", errbuf);
- glDeleteProgram(id);
- return NULL;
- }
-
- ShaderProgram *prog = malloc(sizeof(ShaderProgram));
- prog->id = id;
- prog->loc_model = glGetUniformLocation(id, "model");
- prog->loc_view = glGetUniformLocation(id, "view");
- prog->loc_projection = glGetUniformLocation(id, "projection");
-
- return prog;
-}
-
-void delete_shader_program(ShaderProgram *prog)
-{
- glDeleteProgram(prog->id);
- free(prog);
-}
-
+++ /dev/null
-#ifndef _SHADERS_H_
-#define _SHADERS_H_
-
-#include <GL/glew.h>
-#include <GL/gl.h>
-
-typedef struct
-{
- GLuint id;
- GLint loc_model;
- GLint loc_view;
- GLint loc_projection;
-} ShaderProgram;
-
-ShaderProgram *create_shader_program(const char *path);
-void delete_shader_program(ShaderProgram *prog);
-
-#endif
static struct sigaction sigact_interrupt = {0};
static struct sigaction sigact_silent = {0};
-void init_signal_handlers()
+void signal_handlers_init()
{
sigact_interrupt.sa_handler = &interrupt_handler;
sigaction(SIGINT, &sigact_interrupt, NULL);
#include <stdbool.h>
extern bool interrupted;
-void init_signal_handlers();
+void signal_handlers_init();
#endif
+++ /dev/null
-#define STB_IMAGE_IMPLEMENTATION
-#include <stb/stb_image.h>
-#include <stdbool.h>
-#include "list.h"
-#include "texture.h"
-
-static List textures;
-
-__attribute((constructor(101))) static void init_textures()
-{
- stbi_set_flip_vertically_on_load(true);
-
- textures = list_create(&list_compare_string);
-}
-
-static void list_delete_texture(__attribute__((unused)) void *key, void *value, __attribute__((unused)) void *unused)
-{
- Texture *texture = value;
- glDeleteTextures(1, &texture->id);
- free(texture);
-}
-
-__attribute((destructor)) static void delete_textures()
-{
- list_clear_func(&textures, &list_delete_texture, NULL);
-}
-
-static void *create_texture(void *key)
-{
- Texture *texture = malloc(sizeof(Texture));
- int channels;
-
- unsigned char *data = stbi_load(key, &texture->width, &texture->height, &channels, 0);
- if (! data) {
- printf("Failed to load texture %s\n", (char *) key);
- return 0;
- }
-
- glGenTextures(1, &texture->id);
-
- glBindTexture(GL_TEXTURE_2D, texture->id);
-
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texture->width, texture->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
- glGenerateMipmap(GL_TEXTURE_2D);
-
- stbi_image_free(data);
-
- glBindTexture(GL_TEXTURE_2D, 0);
-
- return texture;
-}
-
-Texture *get_texture(char *path)
-{
- return list_get_cached(&textures, path, &create_texture);
-}
+++ /dev/null
-#ifndef _TEXTURE_H_
-#define _TEXTURE_H_
-
-#include <GL/glew.h>
-#include <GL/gl.h>
-
-typedef struct
-{
- GLuint id;
- int width, height;
-} Texture;
-
-Texture *get_texture(char *path);
-
-#endif