]> git.lizzy.rs Git - dragonblocks_alpha.git/commitdiff
Trees break if they have no connection to ground
authorElias Fleckenstein <eliasfleckenstein@web.de>
Fri, 22 Apr 2022 09:24:03 +0000 (11:24 +0200)
committerElias Fleckenstein <eliasfleckenstein@web.de>
Fri, 22 Apr 2022 09:24:03 +0000 (11:24 +0200)
45 files changed:
deps/dragonnet
deps/dragonstd
models/player.txt
singleplayer.sh
src/CMakeLists.txt
src/client/client.c
src/client/client_auth.c
src/client/client_node.c
src/client/client_node.h
src/client/client_player.c
src/client/client_terrain.c
src/client/facedir.c [deleted file]
src/client/facedir.h [deleted file]
src/client/raycast.c
src/client/terrain_gfx.c
src/client/texture.c
src/debug.sh
src/facedir.c [new file with mode: 0644]
src/facedir.h [new file with mode: 0644]
src/node.c
src/node.h
src/physics.c
src/server/biomes.c
src/server/database.c
src/server/schematic.c
src/server/schematic.h
src/server/server_item.c
src/server/server_node.c [new file with mode: 0644]
src/server/server_node.h [new file with mode: 0644]
src/server/server_terrain.c
src/server/server_terrain.h
src/server/terrain_gen.c
src/server/tree.c [new file with mode: 0644]
src/server/tree.h [new file with mode: 0644]
src/server/tree_physics.c [new file with mode: 0644]
src/server/tree_physics.h [new file with mode: 0644]
src/server/trees.c [deleted file]
src/server/trees.h [deleted file]
src/server/voxel_depth_search.c
src/server/voxel_depth_search.h
src/server/voxel_procedural.c
src/server/voxel_procedural.h
src/terrain.c
src/terrain.h
src/types.def

index 431e3bd29073d9c10ab3a853116860ba83ed1c5e..9ab2148e1b8d03dab816bbba6209794663b31576 160000 (submodule)
@@ -1 +1 @@
-Subproject commit 431e3bd29073d9c10ab3a853116860ba83ed1c5e
+Subproject commit 9ab2148e1b8d03dab816bbba6209794663b31576
index 8878e826fa3e7b1231e652fc13d11c7a61629d13..a3a50433ffe0674291d4c5e9d2247cca7f691faa 160000 (submodule)
@@ -1 +1 @@
-Subproject commit 8878e826fa3e7b1231e652fc13d11c7a61629d13
+Subproject commit a3a50433ffe0674291d4c5e9d2247cca7f691faa
index e67a0e9093d0dc9c00addc60259f88b3bc865f67..ec997b8b98f67e2d4e9419b23b1c8c018db6f39b 100644 (file)
@@ -3,13 +3,13 @@ name neck pos 0 1.35 0
        name head pos 0 0.225 0 scale 0.45 0.45 0.45 cube head
                name eyes pos 0 0 +0.5
 name chest pos 0 1.0125 0 scale 0.48 0.675 0.225 cube chest
-name arm_left pos -0.36 1.35 0 scale -1 1 1 clockwise
+name arm_left pos +0.36 1.35 0
        pos 0 -0.3375 0 scale 0.24 0.675 0.225 cube arm
        name hand pos 0 -0.585 0 scale 0.0625 0.0625 0.0625 rot 90 0 0
-name arm_right pos +0.36 1.35 0
+name arm_right pos -0.36 1.35 0 scale -1 1 1 clockwise
        pos 0 -0.3375 0 scale 0.24 0.675 0.225 cube arm
        name hand pos 0 -0.585 0 scale 0.0625 0.0625 0.0625 rot 90 0 0
-name leg_left pos -0.12 0.675 0 scale -1 1 1 clockwise
+name leg_left pos +0.12 0.675 0
        pos 0 -0.3375 0 scale 0.24 0.675 0.225 cube leg
-name leg_right pos +0.12 0.675 0
+name leg_right pos -0.12 0.675 0 scale -1 1 1 clockwise
        pos 0 -0.3375 0 scale 0.24 0.675 0.225 cube leg
index b1961d067b7e044989286ace0227ac87af117208..0daa513c474054ac3620d036afedc83899bacfdc 100755 (executable)
@@ -1,4 +1,4 @@
 #!/bin/bash
 ./dragonblocks_server "[::1]:4000" &
 echo "singleplayer" | ./dragonblocks "[::1]:4000"
-pkill -P $$
+pkill -P $$ -9
index ba84911f2ee4bd814546d698feb3e581a9b62161..bb6123108aca7e5ac4cc4c3a442ca900691090e0 100644 (file)
@@ -78,6 +78,7 @@ set(COMMON_SOURCES
        config.c
        day.c
        environment.c
+       facedir.c
        interrupt.c
        item.c
        node.c
@@ -103,7 +104,6 @@ add_executable(dragonblocks
        client/cube.c
        client/debug_menu.c
        client/facecache.c
-       client/facedir.c
        client/font.c
        client/frustum.c
        client/game.c
@@ -144,10 +144,12 @@ add_executable(dragonblocks_server
        server/server.c
        server/server_config.c
        server/server_item.c
+       server/server_node.c
        server/server_player.c
        server/server_terrain.c
        server/terrain_gen.c
-       server/trees.c
+       server/tree.c
+       server/tree_physics.c
        server/voxel_depth_search.c
        server/voxel_procedural.c
 )
@@ -159,7 +161,7 @@ target_link_libraries(dragonblocks_server
 # Version
 
 add_custom_target(version
-       COMMAND ${CMAKE_COMMAND} -DBINARY_DIR=${CMAKE_CURRENT_BINARY_DIR}  -P ${CMAKE_CURRENT_SOURCE_DIR}/version.cmake
+       COMMAND ${CMAKE_COMMAND} -DBINARY_DIR=${CMAKE_CURRENT_BINARY_DIR} -P ${CMAKE_CURRENT_SOURCE_DIR}/version.cmake
        WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
 )
 
index 40f58dcfeda681650007e364ac43931126a5ab90..a4e811d3f611d8cffbee6bc3dc2c89b0a8d599a3 100644 (file)
@@ -8,6 +8,7 @@
 #include "client/client.h"
 #include "client/client_auth.h"
 #include "client/client_inventory.h"
+#include "client/client_node.h"
 #include "client/client_player.h"
 #include "client/client_terrain.h"
 #include "client/debug_menu.h"
@@ -89,7 +90,7 @@ static void on_ToClientChunk(__attribute__((unused)) DragonnetPeer *peer, ToClie
 {
        TerrainChunk *chunk = terrain_get_chunk(client_terrain, pkt->pos, true);
 
-       terrain_deserialize_chunk(chunk, pkt->data);
+       terrain_deserialize_chunk(client_terrain, chunk, pkt->data, &client_node_deserialize);
        ((TerrainChunkMeta *) chunk->extra)->empty = (pkt->data.siz == 0);
        client_terrain_chunk_received(chunk);
 }
index 4782d32a5f7f7043e17c963a62399b3ccba0aaf9..c0fa81af2c549e7a797d89240091da1692b97d5c 100644 (file)
@@ -8,6 +8,7 @@
 
 struct ClientAuth client_auth;
 
+#include <string.h>
 static void auth_loop()
 {
        while (!interrupt.set) switch (client_auth.state) {
@@ -15,8 +16,11 @@ static void auth_loop()
                        if (client_auth.name)
                                linenoiseFree(client_auth.name);
 
+                       /*
                        if (!(client_auth.name = linenoise("Enter name: ")))
                                return;
+                       */
+                       client_auth.name = strdup("singleplayer");
 
                        printf("[access] authenticating as %s...\n", client_auth.name);
                        client_auth.state = AUTH_WAIT;
index 5285c53985b982625c364392d595935811f69a6e..1142a85b0d59cb5511276d219a9576327d40678f 100644 (file)
@@ -1,3 +1,4 @@
+#include <stdlib.h>
 #include "client/client.h"
 #include "client/client_node.h"
 #include "color.h"
@@ -38,7 +39,7 @@ static void render_color(NodeArgsRender *args)
        args->vertex.color = ((ColorData *) args->node->data)->color;
 }
 
-ClientNodeDef client_node_def[NODE_UNLOADED] = {
+ClientNodeDef client_node_def[COUNT_NODE] = {
        // unknown
        {
                .tiles = TILES_SIMPLE(RESSOURCE_PATH "textures/unknown.png"),
@@ -215,7 +216,7 @@ ClientNodeDef client_node_def[NODE_UNLOADED] = {
 
 void client_node_init()
 {
-       for (NodeType node = 0; node < NODE_UNLOADED; node++) {
+       for (NodeType node = 0; node < COUNT_NODE; node++) {
                ClientNodeDef *def = &client_node_def[node];
 
                if (def->visibility != VISIBILITY_NONE) {
@@ -235,3 +236,27 @@ void client_node_init()
                }
        }
 }
+
+void client_node_delete(TerrainNode *node)
+{
+       switch (node->type) {
+               NODES_TREE
+                       free(node->data);
+                       break;
+
+               default:
+                       break;
+       }
+}
+
+void client_node_deserialize(TerrainNode *node, Blob buffer)
+{
+       switch (node->type) {
+               NODES_TREE
+                       ColorData_read(&buffer, node->data = malloc(sizeof(ColorData)));
+                       break;
+
+               default:
+                       break;
+       }
+}
index 6f23c872b10d2250d5a74662f4cd32962a52caec..8068eca4c6f28d18ed774c2b0915eca4e9605df8 100644 (file)
@@ -36,4 +36,7 @@ typedef struct {
 extern ClientNodeDef client_node_def[];
 void client_node_init();
 
+void client_node_delete(TerrainNode *node);
+void client_node_deserialize(TerrainNode *node, Blob buffer);
+
 #endif // _CLIENT_NODE_H_
index a92b4c1dba65cfafe6a60b760662e4f54721cdd3..ffdc6b2215ae1b60803a7732d087c1c4a0178e04 100644 (file)
@@ -144,7 +144,7 @@ static void local_on_add(ClientEntity *entity)
 
        if (player_entity) {
                fprintf(stderr, "[error] attempt to re-add localplayer entity\n");
-               exit(EXIT_FAILURE);
+               abort();
        }
 
        on_add(entity);
index 2e33b87532e4fd0917f9a8deaf60b1be8bb39652..9dde1094dfa7ff9093b68459813baddb0298d4bc 100644 (file)
@@ -6,13 +6,14 @@
 #include <stdlib.h>
 #include <pthread.h>
 #include "client/client.h"
-#include "client/facedir.h"
 #include "client/facecache.h"
 #include "client/client_config.h"
+#include "client/client_node.h"
 #include "client/client_player.h"
 #include "client/client_terrain.h"
 #include "client/debug_menu.h"
 #include "client/terrain_gfx.h"
+#include "facedir.h"
 
 #define MAX_REQUESTS 4
 
@@ -77,7 +78,7 @@ static void sync_step()
                return;
        }
 
-       v3s32 center = terrain_node_to_chunk_pos((v3s32) {player_pos.x, player_pos.y, player_pos.z}, NULL);
+       v3s32 center = terrain_chunkp((v3s32) {player_pos.x, player_pos.y, player_pos.z});
 
        u64 last_tick = tick++;
 
@@ -196,11 +197,10 @@ static bool on_get_chunk(TerrainChunk *chunk, bool create)
 void client_terrain_init()
 {
        client_terrain = terrain_create();
-       client_terrain->callbacks.create_chunk   = &on_create_chunk;
-       client_terrain->callbacks.delete_chunk   = &on_delete_chunk;
-       client_terrain->callbacks.get_chunk      = &on_get_chunk;
-       client_terrain->callbacks.set_node       = NULL;
-       client_terrain->callbacks.after_set_node = NULL;
+       client_terrain->callbacks.create_chunk = &on_create_chunk;
+       client_terrain->callbacks.delete_chunk = &on_delete_chunk;
+       client_terrain->callbacks.get_chunk    = &on_get_chunk;
+       client_terrain->callbacks.delete_node  = &client_node_delete;
 
        cancel = false;
        queue_ini(&meshgen_tasks);
diff --git a/src/client/facedir.c b/src/client/facedir.c
deleted file mode 100644 (file)
index 4eaf32c..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-#include "client/facedir.h"
-
-v3s32 facedir[6] = {
-       {+0, +0, -1},
-       {+0, +0, +1},
-       {-1, +0, +0},
-       {+1, +0, +0},
-       {+0, -1, +0},
-       {+0, +1, +0},
-};
diff --git a/src/client/facedir.h b/src/client/facedir.h
deleted file mode 100644 (file)
index b676f0e..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-#ifndef _FACEDIR_H_
-#define _FACEDIR_H_
-
-#include "types.h"
-
-extern v3s32 facedir[];
-
-#endif // _FACEDIR_H_
index d836bdefe03208fcce7362a64daca188b93c34ca..99cffdc65e8712475a8f8dc3d31f6bd29f77a3c7 100644 (file)
@@ -11,7 +11,7 @@ bool raycast(v3f64 pos, v3f64 dir, f64 len, v3s32 *node_pos, NodeType *node)
                *node = terrain_get_node(client_terrain,
                        *node_pos = (v3s32) {floor(pos.x + 0.5), floor(pos.y + 0.5), floor(pos.z + 0.5)}).type;
 
-               if (*node == NODE_UNLOADED)
+               if (*node == COUNT_NODE)
                        return false;
 
                if (client_node_def[*node].pointable)
index 0883d631fbca61838cc91b50d1bb48bb1b4bf7a2..bfe8757e29151126d2fa7911b48e9b379ad8a959 100644 (file)
@@ -6,12 +6,12 @@
 #include "client/client_node.h"
 #include "client/client_terrain.h"
 #include "client/cube.h"
-#include "client/facedir.h"
 #include "client/frustum.h"
 #include "client/gl_debug.h"
 #include "client/light.h"
 #include "client/shader.h"
 #include "client/terrain_gfx.h"
+#include "facedir.h"
 
 typedef struct {
        bool visible;
@@ -60,7 +60,7 @@ static inline bool show_face(NodeType self, NodeType nbr)
                        return nbr != self;
 
                case VISIBILITY_SOLID:
-                       return nbr != NODE_UNLOADED && client_node_def[nbr].visibility != VISIBILITY_SOLID;
+                       return nbr != COUNT_NODE && client_node_def[nbr].visibility != VISIBILITY_SOLID;
 
                default: // impossible
                        break;
@@ -99,7 +99,7 @@ static inline void render_node(ChunkRenderData *data, v3s32 offset)
                        data->tried_nbrs[args.f] = true;
                }
 
-               NodeType nbr_node = NODE_UNLOADED;
+               NodeType nbr_node = COUNT_NODE;
                if (nbr_chunk)
                        nbr_node = nbr_chunk->data
                                [(nbr_offset.x + CHUNK_SIZE) % CHUNK_SIZE]
index 525b2aaba41dcc70d5740c5236afa60ea88d1bdf..54063fe3995370ee02cb92da96c29cff9a9ed199 100644 (file)
@@ -64,7 +64,7 @@ Texture *texture_load(char *path, bool mipmap)
                &texture->width, &texture->height, &texture->channels, 0);
        if (!data) {
                fprintf(stderr, "[error] failed to load texture %s\n", path);
-               exit(EXIT_FAILURE);
+               abort();
        }
 
        texture_upload(texture, data, GL_RGBA, mipmap);
@@ -123,7 +123,7 @@ Texture *texture_load_cubemap(char *path, bool bilinear_filter)
                if (!(faces[i].data = stbi_load(filename,
                                &faces[i].width, &faces[i].height, &faces[i].channels, 0))) {
                        fprintf(stderr, "[error] failed to load texture %s\n", filename);
-                       exit(EXIT_FAILURE);
+                       abort();
                }
 
                size = least_common_multiple(size, faces[i].width);
index 5dc2bac9be813791ab24ce609a80e28f042ce1b3..0dcf103ccbcf6b4d6748e898055486609b3413ff 100755 (executable)
@@ -1,6 +1,6 @@
 #!/bin/bash
 
-if ! make -j$(nproc); then
+if ! make -j$(nproc) dragonblocks_server; then
        exit 1
 fi
 
@@ -19,10 +19,10 @@ define hook-stop
         quit
     end
 end
-break gl_error
 "
 
 echo "$COMMON
+break gl_error
 run \"[::1]:4000\" < $DEBUG_DIR/name
 " > $DEBUG_DIR/client_script
 
diff --git a/src/facedir.c b/src/facedir.c
new file mode 100644 (file)
index 0000000..e1125a9
--- /dev/null
@@ -0,0 +1,10 @@
+#include "facedir.h"
+
+v3s32 facedir[6] = {
+       {+0, +0, -1},
+       {+0, +0, +1},
+       {-1, +0, +0},
+       {+1, +0, +0},
+       {+0, -1, +0},
+       {+0, +1, +0},
+};
diff --git a/src/facedir.h b/src/facedir.h
new file mode 100644 (file)
index 0000000..b676f0e
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef _FACEDIR_H_
+#define _FACEDIR_H_
+
+#include "types.h"
+
+extern v3s32 facedir[];
+
+#endif // _FACEDIR_H_
index 54b62941b2f9b8e5bbe4edf12850631f4876f013..e79352b5f2fc5c3a3d47178dd0ed39628a1d15c9 100644 (file)
 #include "terrain.h"
 #include "types.h"
 
-NodeDef node_def[NODE_UNLOADED] = {
+NodeDef node_def[COUNT_NODE] = {
        // unknown
        {
                .solid = true,
-               .data_size = 0,
                .dig_class = DIG_NONE,
-               .callbacks = {NULL},
        },
        // air
        {
                .solid = false,
-               .data_size = 0,
                .dig_class = DIG_NONE,
-               .callbacks = {NULL},
        },
        // grass
        {
                .solid = true,
-               .data_size = 0,
                .dig_class = DIG_DIRT,
-               .callbacks = {NULL},
        },
        // dirt
        {
                .solid = true,
-               .data_size = 0,
                .dig_class = DIG_DIRT,
-               .callbacks = {NULL},
        },
        // stone
        {
                .solid = true,
-               .data_size = 0,
                .dig_class = DIG_STONE,
-               .callbacks = {NULL},
        },
        // snow
        {
                .solid = true,
-               .data_size = 0,
                .dig_class = DIG_DIRT,
-               .callbacks = {NULL},
        },
        // oak wood
        {
                .solid = true,
-               .data_size = sizeof(ColorData),
                .dig_class = DIG_WOOD,
-               .callbacks = {
-                       .create = NULL,
-                       .delete = NULL,
-                       .serialize = (void *) &ColorData_write,
-                       .deserialize = (void *) &ColorData_read,
-               },
        },
        // oak leaves
        {
                .solid = true,
-               .data_size = sizeof(ColorData),
                .dig_class = DIG_LEAVES,
-               .callbacks = {
-                       .create = NULL,
-                       .delete = NULL,
-                       .serialize = (void *) &ColorData_write,
-                       .deserialize = (void *) &ColorData_read,
-               },
        },
        // pine wood
        {
                .solid = true,
-               .data_size = sizeof(ColorData),
                .dig_class = DIG_WOOD,
-               .callbacks = {
-                       .create = NULL,
-                       .delete = NULL,
-                       .serialize = (void *) &ColorData_write,
-                       .deserialize = (void *) &ColorData_read,
-               },
        },
        // pine leaves
        {
                .solid = true,
-               .data_size = sizeof(ColorData),
                .dig_class = DIG_LEAVES,
-               .callbacks = {
-                       .create = NULL,
-                       .delete = NULL,
-                       .serialize = (void *) &ColorData_write,
-                       .deserialize = (void *) &ColorData_read,
-               },
        },
        // palm wood
        {
                .solid = true,
-               .data_size = sizeof(ColorData),
                .dig_class = DIG_WOOD,
-               .callbacks = {
-                       .create = NULL,
-                       .delete = NULL,
-                       .serialize = (void *) &ColorData_write,
-                       .deserialize = (void *) &ColorData_read,
-               },
        },
        // palm leaves
        {
                .solid = true,
-               .data_size = sizeof(ColorData),
                .dig_class = DIG_LEAVES,
-               .callbacks = {
-                       .create = NULL,
-                       .delete = NULL,
-                       .serialize = (void *) &ColorData_write,
-                       .deserialize = (void *) &ColorData_read,
-               },
        },
        // sand
        {
                .solid = true,
-               .data_size = 0,
                .dig_class = DIG_DIRT,
-               .callbacks = {NULL},
        },
        // water
        {
                .solid = false,
-               .data_size = 0,
                .dig_class = DIG_NONE,
-               .callbacks = {NULL},
        },
        // lava
        {
                .solid = false,
-               .data_size = 0,
                .dig_class = DIG_NONE,
-               .callbacks = {NULL},
        },
        // vulcanostone
        {
                .solid = true,
-               .data_size = 0,
                .dig_class = DIG_STONE,
-               .callbacks = {NULL},
        },
 };
index cdb1a3b8d2e54df36913e4065844896616a3794d..ef2171361cdf942552c8c116aaca5cceb7a29b41 100644 (file)
@@ -5,6 +5,8 @@
 #include <stddef.h>
 #include "types.h"
 
+#define NODES_TREE case NODE_OAK_WOOD: case NODE_OAK_LEAVES: case NODE_PINE_WOOD: case NODE_PINE_LEAVES: case NODE_PALM_WOOD: case NODE_PALM_LEAVES:
+
 typedef enum {
        NODE_UNKNOWN,       // Used for unknown nodes received from server (caused by outdated clients)
        NODE_AIR,
@@ -22,21 +24,14 @@ typedef enum {
        NODE_WATER,
        NODE_LAVA,
        NODE_VULCANO_STONE,
-       NODE_UNLOADED,      // Used for nodes in unloaded chunks
+       COUNT_NODE,      // Used for nodes in unloaded chunks
 } NodeType;
 
 struct TerrainNode;
 
 typedef struct {
        bool solid;
-       size_t data_size;
        unsigned long dig_class;
-       struct {
-               void (*create)(struct TerrainNode *node);
-               void (*delete)(struct TerrainNode *node);
-               void (*serialize)(Blob *buffer, void *data);
-               void (*deserialize)(Blob *buffer, void *data);
-       } callbacks;
 } NodeDef;
 
 extern NodeDef node_def[];
index 4074d8b58daf1cfbcea58b6dccc50eed7c0c7375..5fab1145018db58e922e776e70729ffe527e9be5 100644 (file)
@@ -20,7 +20,7 @@ static aabb3s32 round_box(aabb3f64 box)
 static bool is_solid(Terrain *terrain, s32 x, s32 y, s32 z)
 {
        NodeType node = terrain_get_node(terrain, (v3s32) {x, y, z}).type;
-       return node == NODE_UNLOADED || node_def[node].solid;
+       return node == COUNT_NODE || node_def[node].solid;
 }
 
 bool physics_ground(Terrain *terrain, bool collide, aabb3f32 box, v3f64 *pos, v3f64 *vel)
index da21675b6ba9d2708cb44530c71ecb1a78d760ac..e9253dcafcc24b219aa5e30ebac528934c40aae7 100644 (file)
@@ -232,14 +232,16 @@ static bool is_boulder(s32 diff, v3s32 pos)
                smooth3d(U32(pos.x) / 16.0, U32(pos.y) / 12.0, U32(pos.z) / 16.0, 0, seed + OFFSET_BOULDER) > 0.8;
 }
 
-static DepthSearchNodeType boulder_get_node_type(v3s32 pos)
+static void boulder_search_callback(DepthSearchNode *node)
 {
-       s32 diff = pos.y - terrain_gen_get_base_height((v2s32) {pos.x, pos.z});
+       s32 diff = node->pos.y - terrain_gen_get_base_height((v2s32) {node->pos.x, node->pos.z});
 
        if (diff <= 0)
-               return DEPTH_SEARCH_TARGET;
-
-       return is_boulder(diff, pos) ? DEPTH_SEARCH_PATH : DEPTH_SEARCH_BLOCK;
+               node->type = DEPTH_SEARCH_TARGET;
+       else if (is_boulder(diff, node->pos))
+               node->type = DEPTH_SEARCH_PATH;
+       else
+               node->type = DEPTH_SEARCH_BLOCK;
 }
 
 static NodeType generate_hills(BiomeArgsGenerate *args)
@@ -247,7 +249,7 @@ static NodeType generate_hills(BiomeArgsGenerate *args)
        HillsChunkData *chunk_data = args->chunk_data;
 
        if (is_boulder(args->diff, args->pos) && (args->diff <= 0 || voxel_depth_search(args->pos,
-                       &boulder_get_node_type,
+                       (void *) &boulder_search_callback, NULL,
                        &chunk_data->boulder_success[args->offset.x][args->offset.y][args->offset.z],
                        &chunk_data->boulder_visit)))
                return NODE_STONE;
index 3d06edb9ab38ffdf714739dffe22b81f177319f9..aa029c9d06722a2fdf00c797247d3064b603193d 100644 (file)
@@ -6,6 +6,7 @@
 #include <time.h>
 #include "day.h"
 #include "server/database.h"
+#include "server/server_node.h"
 #include "server/server_terrain.h"
 #include "perlin.h"
 
@@ -138,12 +139,13 @@ bool database_load_chunk(TerrainChunk *chunk)
                TerrainChunkMeta *meta = chunk->extra;
 
                meta->state = sqlite3_column_int(stmt, 0) ? CHUNK_READY : CHUNK_CREATED;
-               Blob_read(                 &(Blob) {sqlite3_column_bytes(stmt, 1), (void *) sqlite3_column_blob(stmt, 1)}, &meta->data);
-               TerrainGenStageBuffer_read(&(Blob) {sqlite3_column_bytes(stmt, 2), (void *) sqlite3_column_blob(stmt, 2)}, &meta->tgsb);
+               Blob data = {sqlite3_column_bytes(stmt, 1), (void *) sqlite3_column_blob(stmt, 1)};
+               Blob tgsb = {sqlite3_column_bytes(stmt, 2), (void *) sqlite3_column_blob(stmt, 2)};
 
-               if (!terrain_deserialize_chunk(chunk, meta->data)) {
+               TerrainGenStageBuffer_read(&tgsb, &meta->tgsb);
+               if (!terrain_deserialize_chunk(server_terrain, chunk, data, &server_node_deserialize)) {
                        fprintf(stderr, "[error] failed deserializing chunk at (%d, %d, %d)\n", chunk->pos.x, chunk->pos.y, chunk->pos.z);
-                       exit(EXIT_FAILURE);
+                       abort();
                }
        } else if (rc != SQLITE_DONE) {
                print_chunk_error(chunk, "loading");
@@ -163,8 +165,7 @@ void database_save_chunk(TerrainChunk *chunk)
 
        TerrainChunkMeta *meta = chunk->extra;
 
-       Blob data = {0, NULL};
-       Blob_write(&data, &meta->data);
+       Blob data = terrain_serialize_chunk(server_terrain, chunk, &server_node_serialize);
 
        Blob tgsb = {0, NULL};
        TerrainGenStageBuffer_write(&tgsb, &meta->tgsb);
index 0f18ebb7876294920977353b79aad41e5512f7cc..9cd92702dd8acb0541aea8bfe31bf2938fa6f838 100644 (file)
@@ -1,6 +1,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include "server/schematic.h"
+#include "server/server_node.h"
 #include "terrain.h"
 
 void schematic_load(List *schematic, const char *path, SchematicMapping *mappings, size_t num_mappings)
@@ -26,7 +27,6 @@ void schematic_load(List *schematic, const char *path, SchematicMapping *mapping
                        continue;
 
                SchematicNode *node = malloc(sizeof *node);
-               node->data = (Blob) {0, NULL};
 
                v3s32 color;
                if (sscanf(line, "%d %d %d %2x%2x%2x",
@@ -52,14 +52,12 @@ void schematic_load(List *schematic, const char *path, SchematicMapping *mapping
                        continue;
                }
 
-               node->type = mapping->type;
-
-               if (mapping->use_color)
-                       ColorData_write(&node->data, &(ColorData) {{
+               node->node = mapping->use_color
+                       ? server_node_create_color(mapping->type, (v3f32) {
                                (f32) color.x / 0xFF,
                                (f32) color.y / 0xFF,
                                (f32) color.z / 0xFF,
-                       }});
+                       }) : server_node_create(mapping->type);
 
                list_apd(schematic, node);
        }
@@ -77,14 +75,14 @@ void schematic_place(List *schematic, v3s32 pos, TerrainGenStage tgs, List *chan
 
                server_terrain_gen_node(
                        v3s32_add(pos, node->pos),
-                       terrain_node_create(node->type, node->data),
+                       server_node_copy(node->node),
                        tgs, changed_chunks);
        }
 }
 
 static void delete_schematic_node(SchematicNode *node)
 {
-       Blob_free(&node->data);
+       server_node_delete(&node->node);
        free(node);
 }
 
index d4e1a2e4559fd26d475c184cb0be1578b49a6b2d..213645ee4294fb67104858fb4a2e65b7d3856662 100644 (file)
@@ -16,8 +16,7 @@ typedef struct {
 
 typedef struct {
        v3s32 pos;
-       NodeType type;
-       Blob data;
+       TerrainNode node;
 } SchematicNode;
 
 void schematic_load(List *schematic, const char *path, SchematicMapping *mappings, size_t num_mappings);
index e411d7efb2325f0a4160d1ff22de671a41aacda2..a8065915c3850e6391db2b8437085d2415c10285 100644 (file)
@@ -1,23 +1,38 @@
 #include "node.h"
 #include "server/server_item.h"
+#include "server/server_node.h"
 #include "server/server_terrain.h"
+#include "server/tree_physics.h"
 
 static void use_dig(__attribute__((unused)) ServerPlayer *player, ItemStack *stack, bool pointed, v3s32 pos)
 {
        if (!pointed)
                return;
 
-       NodeType node = terrain_get_node(server_terrain, pos).type;
-
-       if (node == NODE_UNLOADED)
+       v3s32 off;
+       TerrainChunk *chunk = terrain_get_chunk_nodep(server_terrain, pos, &off, false);
+       if (!chunk)
                return;
+       TerrainChunkMeta *meta = chunk->extra;
+       terrain_lock_chunk(chunk);
+
+       TerrainNode *node = &chunk->data[off.x][off.y][off.z];
 
-       if (!(node_def[node].dig_class & item_def[stack->type].dig_class))
+       if (!(node_def[node->type].dig_class & item_def[stack->type].dig_class)) {
+               pthread_mutex_unlock(&chunk->mtx);
                return;
+       }
+
+       *node = server_node_create(NODE_AIR);
+       meta->tgsb.raw.nodes[off.x][off.y][off.z] = STAGE_PLAYER;
+
+       pthread_mutex_unlock(&chunk->mtx);
+
+       server_terrain_lock_and_send_chunk(chunk);
 
-       terrain_set_node(server_terrain, pos,
-               terrain_node_create(NODE_AIR, (Blob) {0, NULL}),
-               false, NULL);
+       // destroy trees if they have no connection to ground
+       // todo: run in seperate thread to not block client connection
+       tree_physics_check(pos);
 }
 
 ServerItemDef server_item_def[COUNT_ITEM] = {
diff --git a/src/server/server_node.c b/src/server/server_node.c
new file mode 100644 (file)
index 0000000..9918275
--- /dev/null
@@ -0,0 +1,90 @@
+#include <stdlib.h>
+#include "server/server_node.h"
+
+TerrainNode server_node_create(NodeType type)
+{
+       switch (type) {
+               NODES_TREE
+                       return server_node_create_tree(type, (TreeData) {{0.5f, 0.5f, 0.5f}, 0, {0, 0, 0}});
+
+               default:
+                       return (TerrainNode) {type, NULL};
+       }
+}
+
+TerrainNode server_node_create_color(NodeType type, v3f32 color)
+{
+       switch (type) {
+               NODES_TREE
+                       return server_node_create_tree(type, (TreeData) {color, 0, {0, 0, 0}});
+
+               default:
+                       return server_node_create(type);
+       }
+}
+
+TerrainNode server_node_create_tree(NodeType type, TreeData data)
+{
+       TerrainNode node = {type, malloc(sizeof data)};
+       *((TreeData *) node.data) = data;
+       return node;
+}
+
+TerrainNode server_node_copy(TerrainNode node)
+{
+       switch (node.type) {
+               NODES_TREE
+                       return server_node_create_tree(node.type, *((TreeData *) node.data));
+
+               default:
+                       return server_node_create(node.type);
+       }
+}
+
+void server_node_delete(TerrainNode *node)
+{
+       switch (node->type) {
+               NODES_TREE
+                       free(node->data);
+                       break;
+
+               default:
+                       break;
+       }
+}
+
+void server_node_deserialize(TerrainNode *node, Blob buffer)
+{
+       switch (node->type) {
+               NODES_TREE
+                       TreeData_read(&buffer, node->data = malloc(sizeof(TreeData)));
+                       break;
+
+               default:
+                       break;
+       }
+}
+
+void server_node_serialize(TerrainNode *node, Blob *buffer)
+{
+       switch (node->type) {
+               NODES_TREE
+                       TreeData_write(buffer, node->data);
+                       break;
+
+               default:
+                       break;
+       }
+}
+
+void server_node_serialize_client(TerrainNode *node, Blob *buffer)
+{
+       switch (node->type) {
+               NODES_TREE
+                       ColorData_write(buffer, &(ColorData) {((TreeData *) node->data)->color});
+                       break;
+
+               default:
+                       break;
+       }
+}
diff --git a/src/server/server_node.h b/src/server/server_node.h
new file mode 100644 (file)
index 0000000..045ffcc
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef _SERVER_NODE_H_
+#define _SERVER_NODE_H_
+
+#include "terrain.h"
+#include "types.h"
+
+TerrainNode server_node_create(NodeType type);
+TerrainNode server_node_create_color(NodeType type, v3f32 color);
+TerrainNode server_node_create_tree(NodeType type, TreeData data);
+TerrainNode server_node_copy(TerrainNode node);
+void server_node_delete(TerrainNode *node);
+void server_node_deserialize(TerrainNode *node, Blob buffer);
+void server_node_serialize(TerrainNode *node, Blob *buffer);
+void server_node_serialize_client(TerrainNode *node, Blob *buffer);
+
+#endif
index 71dce28f0ac6955057cb96d3ad65e0cd4849cb5b..61ce30e706888798aaf672a24be884aaee4066ee 100644 (file)
@@ -12,6 +12,7 @@
 #include "server/database.h"
 #include "server/schematic.h"
 #include "server/server_config.h"
+#include "server/server_node.h"
 #include "server/server_terrain.h"
 #include "server/terrain_gen.h"
 #include "terrain.h"
@@ -32,7 +33,7 @@ static pthread_mutex_t mtx_num_gen_chunks; // lock to protect the above
 static bool within_load_distance(ServerPlayer *player, v3s32 cpos, u32 dist)
 {
        pthread_rwlock_rdlock(&player->lock_pos);
-       v3s32 ppos = terrain_node_to_chunk_pos((v3s32) {player->pos.x, player->pos.y, player->pos.z}, NULL);
+       v3s32 ppos = terrain_chunkp((v3s32) {player->pos.x, player->pos.y, player->pos.z});
        pthread_rwlock_unlock(&player->lock_pos);
 
        return abs(ppos.x - cpos.x) <= (s32) dist
@@ -41,7 +42,7 @@ static bool within_load_distance(ServerPlayer *player, v3s32 cpos, u32 dist)
 }
 
 // send a chunk to a client and reset chunk request
-static void send_chunk(ServerPlayer *player, TerrainChunk *chunk)
+static void send_chunk_to_client(ServerPlayer *player, TerrainChunk *chunk)
 {
        if (!within_load_distance(player, chunk->pos, server_config.load_distance))
                return;
@@ -55,34 +56,6 @@ static void send_chunk(ServerPlayer *player, TerrainChunk *chunk)
        pthread_rwlock_unlock(&player->lock_peer);
 }
 
-// send chunk to near clients
-// chunk mutex has to be locked
-static void send_chunk_to_near(TerrainChunk *chunk)
-{
-       TerrainChunkMeta *meta = chunk->extra;
-
-       if (meta->state == CHUNK_GENERATING)
-               return;
-
-       Blob_free(&meta->data);
-       meta->data = terrain_serialize_chunk(chunk);
-
-       database_save_chunk(chunk);
-
-       if (meta->state == CHUNK_CREATED)
-               return;
-
-       server_player_iterate(&send_chunk, chunk);
-}
-
-// Iterator for sending changed chunks to near clients
-static void iterator_send_chunk_to_near(TerrainChunk *chunk)
-{
-       pthread_mutex_lock(&chunk->mtx);
-       send_chunk_to_near(chunk);
-       pthread_mutex_unlock(&chunk->mtx);
-}
-
 // me when the
 static void terrain_gen_step()
 {
@@ -100,11 +73,11 @@ static void terrain_gen_step()
 
        terrain_gen_chunk(chunk, &changed_chunks);
 
-       pthread_mutex_lock(&chunk->mtx);
+       pthread_mutex_lock(&meta->mtx);
        meta->state = CHUNK_READY;
-       pthread_mutex_unlock(&chunk->mtx);
+       pthread_mutex_unlock(&meta->mtx);
 
-       list_clr(&changed_chunks, &iterator_send_chunk_to_near, NULL, NULL);
+       server_terrain_lock_and_send_chunks(&changed_chunks);
 
        pthread_mutex_lock(&mtx_num_gen_chunks);
        num_gen_chunks--;
@@ -123,7 +96,7 @@ static void *terrain_gen_thread()
                terrain_gen_step();
 
        return NULL;
-}
+       }
 
 // enqueue chunk
 static void generate_chunk(TerrainChunk *chunk)
@@ -136,25 +109,26 @@ static void generate_chunk(TerrainChunk *chunk)
        pthread_mutex_unlock(&mtx_num_gen_chunks);
 
        TerrainChunkMeta *meta = chunk->extra;
+
        meta->state = CHUNK_GENERATING;
        queue_enq(&terrain_gen_tasks, chunk);
 }
 
-// terrain callbacks
-// note: all these functions require the chunk mutex to be locked, which is always the case when a terrain callback is invoked
-
 // callback for initializing a newly created chunk
 // load chunk from database or initialize state, tgstage buffer and data
 static void on_create_chunk(TerrainChunk *chunk)
 {
        TerrainChunkMeta *meta = chunk->extra = malloc(sizeof *meta);
+       pthread_mutex_init(&meta->mtx, NULL);
 
-       if (!database_load_chunk(chunk)) {
+       if (database_load_chunk(chunk)) {
+               meta->data = terrain_serialize_chunk(server_terrain, chunk, &server_node_serialize_client);
+       } else {
                meta->state = CHUNK_CREATED;
                meta->data = (Blob) {0, NULL};
 
                CHUNK_ITERATE {
-                       chunk->data[x][y][z] = terrain_node_create(NODE_AIR, (Blob) {0, NULL});
+                       chunk->data[x][y][z] = server_node_create(NODE_AIR);
                        meta->tgsb.raw.nodes[x][y][z] = STAGE_VOID;
                }
        }
@@ -165,6 +139,7 @@ static void on_create_chunk(TerrainChunk *chunk)
 static void on_delete_chunk(TerrainChunk *chunk)
 {
        TerrainChunkMeta *meta = chunk->extra;
+       pthread_mutex_destroy(&meta->mtx);
 
        Blob_free(&meta->data);
        free(meta);
@@ -174,42 +149,16 @@ static void on_delete_chunk(TerrainChunk *chunk)
 // hold back chunks that are not fully generated except when the create flag is set to true
 static bool on_get_chunk(TerrainChunk *chunk, bool create)
 {
-       TerrainChunkMeta *meta = chunk->extra;
-
-       if (meta->state < CHUNK_READY && !create)
-               return false;
-
-       return true;
-}
-
-// callback for deciding whether a set_node call succeeds or not
-// reject set_node calls that try to override nodes placed by later terraingen stages, else update tgs buffer - also make sure chunk is inserted into changed_chunks list
-static bool on_set_node(TerrainChunk *chunk, v3u8 offset, __attribute__((unused)) TerrainNode *node, void *_arg)
-{
-       TerrainSetNodeArg *arg = _arg;
-
-       TerrainGenStage new_tgs = arg ? arg->tgs : STAGE_PLAYER;
-       TerrainGenStage *tgs = &((TerrainChunkMeta *) chunk->extra)->
-               tgsb.raw.nodes[offset.x][offset.y][offset.z];
-
-       if (new_tgs >= *tgs) {
-               *tgs = new_tgs;
-
-               if (arg)
-                       list_add(arg->changed_chunks, chunk, chunk, &cmp_ref, NULL);
-
+       if (create)
                return true;
-       }
 
-       return false;
-}
+       TerrainChunkMeta *meta = chunk->extra;
+       pthread_mutex_lock(&meta->mtx);
 
-// callback for when chunk content changes
-// send chunk to near clients if not part of terrain generation
-static void on_after_set_node(TerrainChunk *chunk, __attribute__((unused)) v3u8 offset, void *arg)
-{
-       if (!arg)
-               send_chunk_to_near(chunk);
+       bool ret = meta->state == CHUNK_READY;
+
+       pthread_mutex_unlock(&meta->mtx);
+       return ret;
 }
 
 // generate a hut for new players to spawn in
@@ -248,8 +197,10 @@ static void generate_spawn_hut()
                {+4, +1},
        };
 
-       Blob wood_color = {0, NULL};
-       ColorData_write(&wood_color, &(ColorData) {{(f32) 0x7d / 0xff, (f32) 0x54 / 0xff, (f32) 0x35 / 0xff}});
+       v3f32 wood_color = {
+               (f32) 0x7d / 0xff,
+               (f32) 0x54 / 0xff,
+               (f32) 0x35 / 0xff};
 
        for (int i = 0; i < 6; i++) {
                for (s32 y = spawn_height - 1;; y--) {
@@ -266,17 +217,14 @@ static void generate_spawn_hut()
                        if (node_def[node].solid)
                                break;
 
-                       server_terrain_gen_node(pos,
-                               terrain_node_create(node == NODE_LAVA
-                                               ? NODE_VULCANO_STONE
-                                               : NODE_OAK_WOOD,
-                                       wood_color),
+                       server_terrain_gen_node(pos, node == NODE_LAVA
+                                       ? server_node_create(NODE_VULCANO_STONE)
+                                       : server_node_create_color(NODE_OAK_WOOD, wood_color),
                                STAGE_PLAYER, &changed_chunks);
                }
        }
 
-       Blob_free(&wood_color);
-       list_clr(&changed_chunks, &iterator_send_chunk_to_near, NULL, NULL);
+       server_terrain_lock_and_send_chunks(&changed_chunks);
 }
 
 // public functions
@@ -288,8 +236,7 @@ void server_terrain_init()
        server_terrain->callbacks.create_chunk   = &on_create_chunk;
        server_terrain->callbacks.delete_chunk   = &on_delete_chunk;
        server_terrain->callbacks.get_chunk      = &on_get_chunk;
-       server_terrain->callbacks.set_node       = &on_set_node;
-       server_terrain->callbacks.after_set_node = &on_after_set_node;
+       server_terrain->callbacks.delete_node    = &server_node_delete;
 
        cancel = false;
        queue_ini(&terrain_gen_tasks);
@@ -322,10 +269,9 @@ void server_terrain_requested_chunk(ServerPlayer *player, v3s32 pos)
 {
        if (within_load_distance(player, pos, server_config.load_distance)) {
                TerrainChunk *chunk = terrain_get_chunk(server_terrain, pos, true);
-
-               pthread_mutex_lock(&chunk->mtx);
-
                TerrainChunkMeta *meta = chunk->extra;
+
+               pthread_mutex_lock(&meta->mtx);
                switch (meta->state) {
                        case CHUNK_CREATED:
                                generate_chunk(chunk);
@@ -335,10 +281,11 @@ void server_terrain_requested_chunk(ServerPlayer *player, v3s32 pos)
                                break;
 
                        case CHUNK_READY:
-                               send_chunk(player, chunk);
+                               send_chunk_to_client(player, chunk);
+                               break;
                };
 
-               pthread_mutex_unlock(&chunk->mtx);
+               pthread_mutex_unlock(&meta->mtx);
        }
 }
 
@@ -374,11 +321,12 @@ void server_terrain_prepare_spawn()
                                        return;
 
                                TerrainChunk *chunk = terrain_get_chunk(server_terrain, (v3s32) {x, y, z}, true);
+                               TerrainChunkMeta *meta = chunk->extra;
 
-                               pthread_mutex_lock(&chunk->mtx);
-                               if (((TerrainChunkMeta *) chunk->extra)->state == CHUNK_CREATED)
+                               pthread_mutex_lock(&meta->mtx);
+                               if (meta->state == CHUNK_CREATED)
                                        generate_chunk(chunk);
-                               pthread_mutex_unlock(&chunk->mtx);
+                               pthread_mutex_unlock(&meta->mtx);
 
                                update_percentage();
                        }
@@ -411,14 +359,31 @@ void server_terrain_prepare_spawn()
        }
 }
 
-void server_terrain_gen_node(v3s32 pos, TerrainNode node, TerrainGenStage tgs, List *changed_chunks)
+void server_terrain_gen_node(v3s32 pos, TerrainNode node, TerrainGenStage new_tgs, List *changed_chunks)
 {
-       TerrainSetNodeArg arg = {
-               .tgs = tgs,
-               .changed_chunks = changed_chunks,
-       };
+       v3s32 offset;
+       TerrainChunk *chunk = terrain_get_chunk_nodep(server_terrain, pos, &offset, true);
+       TerrainChunkMeta *meta = chunk->extra;
 
-       terrain_set_node(server_terrain, pos, node, true, &arg);
+       terrain_lock_chunk(chunk);
+
+       u32 *tgs = &meta->tgsb.raw.nodes[offset.x][offset.y][offset.z];
+
+       if (new_tgs < *tgs) {
+               pthread_mutex_unlock(&chunk->mtx);
+               server_node_delete(&node);
+               return;
+       }
+
+       *tgs = new_tgs;
+       chunk->data[offset.x][offset.y][offset.z] = node;
+
+       if (changed_chunks)
+               list_add(changed_chunks, chunk, chunk, &cmp_ref, NULL);
+       else
+               server_terrain_send_chunk(chunk);
+
+       pthread_mutex_unlock(&chunk->mtx);
 }
 
 s32 server_terrain_spawn_height()
@@ -426,3 +391,40 @@ s32 server_terrain_spawn_height()
        // wow, so useful!
        return spawn_height;
 }
+
+// send chunk to near clients
+// meta mutex has to be locked
+void server_terrain_send_chunk(TerrainChunk *chunk)
+{
+       TerrainChunkMeta *meta = chunk->extra;
+
+       if (meta->state == CHUNK_GENERATING)
+               return;
+
+       terrain_lock_chunk(chunk);
+
+       Blob_free(&meta->data);
+       meta->data = terrain_serialize_chunk(server_terrain, chunk, &server_node_serialize_client);
+       database_save_chunk(chunk);
+
+       pthread_mutex_unlock(&chunk->mtx);
+
+       if (meta->state == CHUNK_CREATED)
+               return;
+
+       server_player_iterate(&send_chunk_to_client, chunk);
+}
+
+void server_terrain_lock_and_send_chunk(TerrainChunk *chunk)
+{
+       TerrainChunkMeta *meta = chunk->extra;
+
+       pthread_mutex_lock(&meta->mtx);
+       server_terrain_send_chunk(chunk);
+       pthread_mutex_unlock(&meta->mtx);
+}
+
+void server_terrain_lock_and_send_chunks(List *changed_chunks)
+{
+       list_clr(changed_chunks, &server_terrain_lock_and_send_chunk, NULL, NULL);
+}
index 88fa7a858d8d5088431e651a41ba3554beaf7100..62c0a11c183a02adcbf832b4a3097f49cad50ad0 100644 (file)
@@ -1,6 +1,7 @@
 #ifndef _SERVER_TERRAIN_H_
 #define _SERVER_TERRAIN_H_
 
+#include <dragonstd/list.h>
 #include <pthread.h>
 #include "server/server_player.h"
 #include "terrain.h"
@@ -15,8 +16,7 @@ typedef enum {
 typedef enum {
        STAGE_VOID,     // initial air, can be overridden by anything
        STAGE_TERRAIN,  // basic terrain, can be overridden by anything except the void
-       STAGE_BOULDERS, // boulders, replace terrain
-       STAGE_TREES,    // trees replace boulders
+       STAGE_TREES,    // trees replace terrain
        STAGE_PLAYER,   // player-placed nodes or things placed after terrain generation
 } TerrainGenStage;
 
@@ -26,19 +26,56 @@ typedef struct {
 } TerrainSetNodeArg;
 
 typedef struct {
+       pthread_mutex_t mtx;        // UwU please hit me senpai
        Blob data;                  // the big cum
        TerrainChunkState state;    // generation state of the chunk
        pthread_t gen_thread;       // thread that is generating chunk
        TerrainGenStageBuffer tgsb; // buffer to make sure terraingen only overrides things it should
 } TerrainChunkMeta; // OMG META VERSE WEB 3.0 VIRTUAL REALITY
 
-extern Terrain *server_terrain; // terrain object, data is stored here
+/*
+       Locking conventions:
+       - chunk mutex protects chunk->data and meta->tgsb
+       - meta mutex protects everything else in meta
+       - if both meta and chunk mutex are locked, meta must be locked first
+       - you may not lock multiple meta mutexes at once
+       - if multiple chunk mutexes are being locked at once, EDEADLK must be handled
+       - when locking a single chunk mtx, check return value and crash on failure (terrain_lock_chunk())
 
-void server_terrain_init();                                                                           // called on server startup
-void server_terrain_deinit();                                                                         // called on server shutdown
-void server_terrain_requested_chunk(ServerPlayer *player, v3s32 pos);                                 // handle chunk request from client (thread safe)
-void server_terrain_prepare_spawn();                                                                  // prepare spawn region
-void server_terrain_gen_node(v3s32 pos, TerrainNode node, TerrainGenStage tgs, List *changed_chunks); // set node with terraingen stage
-s32 server_terrain_spawn_height();                                                                    // get the spawn height because idk
+       After changing the data in a chunk:
+       1. release chunk mtx
+       2.
+               - if meta mutex is currently locked: use server_terrain_send_chunk
+               - if meta mutex is not locked: use server_terrain_lock_and_send_chunk
+
+       If an operation affects multiple nodes (potentially in multiple chunks):
+               - create a list changed_chunks
+               - do job as normal, release individual chunk mutexes immediately after modifying their data
+               - use server_terrain_lock_and_send_chunks to clear the list
+
+       Note: Unless changed_chunks is given to server_terrain_gen_node, it sends chunks automatically
+*/
+
+// terrain object, data is stored here
+extern Terrain *server_terrain;
+
+// called on server startup
+void server_terrain_init();
+// called on server shutdown
+void server_terrain_deinit();
+// handle chunk request from client (thread safe)
+void server_terrain_requested_chunk(ServerPlayer *player, v3s32 pos);
+// prepare spawn region
+void server_terrain_prepare_spawn();
+// set node with terraingen stage
+void server_terrain_gen_node(v3s32 pos, TerrainNode node, TerrainGenStage new_tgs, List *changed_chunks);
+// get the spawn height because idk
+s32 server_terrain_spawn_height();
+// when bit chunkus changes
+void server_terrain_send_chunk(TerrainChunk *chunk);
+// lock and send
+void server_terrain_lock_and_send_chunk(TerrainChunk *chunk);
+// lock and send multiple chunks at once
+void server_terrain_lock_and_send_chunks(List *list);
 
 #endif // _SERVER_TERRAIN_H_
index 39e64b3be694bcdf164015aab27b6c67516cdc6d..b5ed20911b503d755a93a77c3697a66891a9da28 100644 (file)
@@ -3,9 +3,10 @@
 #include "environment.h"
 #include "perlin.h"
 #include "server/biomes.h"
+#include "server/server_node.h"
 #include "server/server_terrain.h"
 #include "server/terrain_gen.h"
-#include "server/trees.h"
+#include "server/tree.h"
 
 s32 terrain_gen_get_base_height(v2s32 pos)
 {
@@ -105,9 +106,9 @@ void terrain_gen_chunk(TerrainChunk *chunk, List *changed_chunks)
                                        }
                                }
 
-                               pthread_mutex_lock(&chunk->mtx);
+                               terrain_lock_chunk(chunk);
                                if (meta->tgsb.raw.nodes[x][y][z] <= STAGE_TERRAIN) {
-                                       chunk->data[x][y][z] = terrain_node_create(node, (Blob) {0, NULL});
+                                       chunk->data[x][y][z] = server_node_create(node);
                                        meta->tgsb.raw.nodes[x][y][z] = STAGE_TERRAIN;
                                }
                                pthread_mutex_unlock(&chunk->mtx);
diff --git a/src/server/tree.c b/src/server/tree.c
new file mode 100644 (file)
index 0000000..effe1dc
--- /dev/null
@@ -0,0 +1,254 @@
+#include <stdlib.h>
+#include "server/biomes.h"
+#include "server/server_node.h"
+#include "server/server_terrain.h"
+#include "server/tree.h"
+#include "server/voxel_procedural.h"
+
+typedef struct {
+       NodeType type;
+       v3s32 root;
+} ProceduralTreeArg;
+
+static TerrainNode create_tree_node(__attribute__((unused)) v3s32 pos, v3f32 color, ProceduralTreeArg *arg)
+{
+       return server_node_create_tree(arg->type, (TreeData) {
+               .color = color,
+               .has_root = 1,
+               .root = arg->root,
+       });
+}
+
+// oak
+
+static bool oak_condition(TreeArgsCondition *args)
+{
+       return args->biome == BIOME_HILLS;
+}
+
+static void oak_tree_leaf(VoxelProcedural *proc, v3s32 root)
+{
+       if (!voxel_procedural_is_alive(proc))
+               return;
+
+       voxel_procedural_push(proc);
+               voxel_procedural_cube(proc, (void *) &create_tree_node,
+                       &(ProceduralTreeArg) {NODE_OAK_LEAVES, root});
+       voxel_procedural_pop(proc);
+
+       voxel_procedural_push(proc);
+               voxel_procedural_x(proc, 0.5f);
+               voxel_procedural_sx(proc, 0.9f);
+               voxel_procedural_sy(proc, 0.9f);
+               voxel_procedural_sz(proc, 0.8f);
+               voxel_procedural_ry(proc, 25.0f);
+               voxel_procedural_x(proc, 0.4f);
+               oak_tree_leaf(proc, root);
+       voxel_procedural_pop(proc);
+}
+
+static void oak_tree_top(VoxelProcedural *proc, v3s32 root)
+{
+       if (!voxel_procedural_is_alive(proc))
+               return;
+
+       voxel_procedural_push(proc);
+       for (int i = 0; i < 8; i++) {
+               voxel_procedural_rz(proc, 360.0f / 8.0f);
+               voxel_procedural_push(proc);
+                       voxel_procedural_life(proc, 8);
+                       voxel_procedural_sy(proc, 2.0f);
+                       voxel_procedural_z(proc, voxel_procedural_random(proc, 0.0f, 5.0f));
+                       voxel_procedural_s(proc, 5.0f);
+                       voxel_procedural_light(proc, -0.4f);
+                       voxel_procedural_sat(proc, 0.5f);
+                       voxel_procedural_hue(proc, voxel_procedural_random(proc, 60.0f, 20.0f));
+                       voxel_procedural_ry(proc, -45.0f);
+                       oak_tree_leaf(proc, root);
+               voxel_procedural_pop(proc);
+       }
+       voxel_procedural_pop(proc);
+}
+
+static void oak_tree_part(VoxelProcedural *proc, v3s32 root, f32 n)
+{
+       if (!voxel_procedural_is_alive(proc))
+               return;
+
+       voxel_procedural_push(proc);
+       for (int i = 1; i <= n; i++) {
+               voxel_procedural_z(proc, 1.0f);
+               voxel_procedural_rz(proc, voxel_procedural_random(proc, 30.0f, 10.0f));
+               voxel_procedural_rx(proc, voxel_procedural_random(proc, 0.0f, 10.0f));
+
+               voxel_procedural_push(proc);
+                       voxel_procedural_s(proc, 4.0f);
+                       voxel_procedural_x(proc, 0.1f);
+                       voxel_procedural_light(proc, voxel_procedural_random(proc, 0.0f, 0.1f));
+                       voxel_procedural_cylinder(proc, (void *) &create_tree_node,
+                               &(ProceduralTreeArg) {NODE_OAK_WOOD, root});
+               voxel_procedural_pop(proc);
+
+               if (i == (int) (n - 2.0f)) {
+                       voxel_procedural_push(proc);
+                               oak_tree_top(proc, root);
+                       voxel_procedural_pop(proc);
+               }
+       }
+       voxel_procedural_pop(proc);
+}
+
+static void oak_tree(v3s32 root, List *changed_chunks)
+{
+       VoxelProcedural *proc = voxel_procedural_create(changed_chunks, STAGE_TREES, root);
+
+       voxel_procedural_hue(proc, 40.0f);
+       voxel_procedural_light(proc, -0.5f);
+       voxel_procedural_sat(proc, 0.5f);
+
+       f32 n = voxel_procedural_random(proc, 40.0f, 10.0f);
+
+       voxel_procedural_push(proc);
+       for (int i = 1; i <= 3; i++) {
+               voxel_procedural_rz(proc, voxel_procedural_random(proc, 120.0f, 45.0f));
+               voxel_procedural_push(proc);
+                       voxel_procedural_y(proc, 0.5f);
+                       voxel_procedural_light(proc, voxel_procedural_random(proc, -0.3f, 0.05f));
+                       oak_tree_part(proc, root, n);
+               voxel_procedural_pop(proc);
+       }
+       voxel_procedural_pop(proc);
+
+       voxel_procedural_delete(proc);
+}
+
+// pine
+
+static bool pine_condition(TreeArgsCondition *args)
+{
+       return args->biome == BIOME_MOUNTAIN;
+}
+
+static void pine_tree(v3s32 pos, List *changed_chunks)
+{
+       s32 tree_top = (noise2d(pos.x, pos.z, 0, seed + OFFSET_PINETREE_HEIGHT) * 0.5 + 0.5) * (35.0 - 20.0) + 20.0 + pos.y;
+       for (v3s32 tree_pos = pos; tree_pos.y < tree_top; tree_pos.y++) {
+               f64 branch_length = noise3d(tree_pos.x, tree_pos.y, tree_pos.z, 0, seed + OFFSET_PINETREE_BRANCH) * 3.0;
+
+               v3s32 dirs[4] = {
+                       {+0, +0, +1},
+                       {+1, +0, +0},
+                       {+0, +0, -1},
+                       {-1, +0, +0},
+               };
+
+               s32 dir = (noise3d(tree_pos.x, tree_pos.y, tree_pos.z, 0, seed + OFFSET_PINETREE_BRANCH_DIR) * 0.5 + 0.5) * 4.0;
+
+               for (v3s32 branch_pos = tree_pos; branch_length > 0; branch_length--, branch_pos = v3s32_add(branch_pos, dirs[dir]))
+                       server_terrain_gen_node(branch_pos, server_node_create(NODE_PINE_WOOD),
+                               STAGE_TREES, changed_chunks);
+
+               server_terrain_gen_node(tree_pos, server_node_create(NODE_PINE_WOOD),
+                       STAGE_TREES, changed_chunks);
+       }
+}
+
+// palm
+
+static bool palm_condition(TreeArgsCondition *args)
+{
+       return args->biome == BIOME_OCEAN
+               && ocean_get_node_at((v3s32) {args->pos.x, args->pos.y - 0, args->pos.z}, 1, args->row_data) == NODE_AIR
+               && ocean_get_node_at((v3s32) {args->pos.x, args->pos.y - 1, args->pos.z}, 0, args->row_data) == NODE_SAND;
+}
+
+static void palm_branch(VoxelProcedural *proc, v3s32 root)
+{
+       if (!voxel_procedural_is_alive(proc))
+               return;
+
+       voxel_procedural_cube(proc, (void *) &create_tree_node,
+               &(ProceduralTreeArg) {NODE_PALM_LEAVES, root});
+
+       voxel_procedural_push(proc);
+               voxel_procedural_z(proc, 0.5f);
+               voxel_procedural_s(proc, 0.8f);
+               voxel_procedural_rx(proc, voxel_procedural_random(proc, 20.0f, 4.0f));
+               voxel_procedural_z(proc, 0.5f);
+               palm_branch(proc, root);
+       voxel_procedural_pop(proc);
+}
+
+static void palm_tree(v3s32 root, List *changed_chunks)
+{
+       VoxelProcedural *proc = voxel_procedural_create(changed_chunks, STAGE_TREES, (v3s32) {root.x, root.y - 1, root.z});
+
+       f32 s = voxel_procedural_random(proc, 8.0f, 2.0f);
+
+       voxel_procedural_push(proc);
+       for (int i = 1; i <= s; i++) {
+               voxel_procedural_z(proc, 1.0f);
+               voxel_procedural_push(proc);
+                       voxel_procedural_s(proc, 1.0f);
+                       voxel_procedural_light(proc, voxel_procedural_random(proc, -0.8f, 0.1f));
+                       voxel_procedural_sat(proc, 0.5f);
+                       voxel_procedural_cube(proc, (void *) &create_tree_node,
+                               &(ProceduralTreeArg) {NODE_PALM_WOOD, root});
+               voxel_procedural_pop(proc);
+       }
+       voxel_procedural_pop(proc);
+
+       voxel_procedural_z(proc, s);
+       voxel_procedural_sat(proc, 1.0f),
+       voxel_procedural_light(proc, -0.5f);
+       voxel_procedural_hue(proc, voxel_procedural_random(proc, 50.0f, 30.0f));
+
+       voxel_procedural_push(proc);
+       for (int i = 0; i < 6; i++) {
+               voxel_procedural_rz(proc, 360.0f / 6.0f);
+               voxel_procedural_rz(proc, voxel_procedural_random(proc, 0.0f, 10.0f));
+               voxel_procedural_push(proc);
+                       voxel_procedural_light(proc, voxel_procedural_random(proc, 0.0f, 0.3f));
+                       voxel_procedural_rx(proc, 90.0f);
+                       voxel_procedural_s(proc, 2.0f);
+                       palm_branch(proc, root);
+               voxel_procedural_pop(proc);
+       }
+       voxel_procedural_pop(proc);
+
+       voxel_procedural_delete(proc);
+}
+
+TreeDef tree_def[NUM_TREES] = {
+       // oak
+       {
+               .spread = 64.0f,
+               .probability = 0.0005f,
+               .area_probability = 0.3f,
+               .offset = OFFSET_OAKTREE,
+               .area_offset = OFFSET_OAKTREE_AREA,
+               .condition = &oak_condition,
+               .generate = &oak_tree,
+       },
+       // pine
+       {
+               .spread = 256.0f,
+               .probability = 0.01f,
+               .area_probability = 0.1f,
+               .offset = OFFSET_PINETREE,
+               .area_offset = OFFSET_PINETREE_AREA,
+               .condition = &pine_condition,
+               .generate = &pine_tree,
+       },
+       // palm
+       {
+               .spread = 16.0f,
+               .probability = 0.005f,
+               .area_probability = 0.5f,
+               .offset = OFFSET_PALMTREE,
+               .area_offset = OFFSET_PALMTREE_AREA,
+               .condition = &palm_condition,
+               .generate = &palm_tree,
+       },
+};
+
diff --git a/src/server/tree.h b/src/server/tree.h
new file mode 100644 (file)
index 0000000..2eb08aa
--- /dev/null
@@ -0,0 +1,35 @@
+#ifndef _TREE_H_
+#define _TREE_H_
+
+#include <dragonstd/list.h>
+#include <stdbool.h>
+#include "perlin.h"
+#include "terrain.h"
+#include "types.h"
+
+#define NUM_TREES 3
+
+typedef struct {
+       v3s32 pos;
+       f64 humidity;
+       f64 temperature;
+       Biome biome;
+       f64 factor;
+       TerrainChunk *chunk;
+       void *row_data;
+       void *chunk_data;
+} TreeArgsCondition;
+
+typedef struct {
+       f32 spread;
+       f32 probability;
+       f32 area_probability;
+       SeedOffset offset;
+       SeedOffset area_offset;
+       bool (*condition)(TreeArgsCondition *args);
+       void (*generate)(v3s32 pos, List *changed_chunks);
+} TreeDef;
+
+extern TreeDef tree_def[];
+
+#endif // _TREE_H_
diff --git a/src/server/tree_physics.c b/src/server/tree_physics.c
new file mode 100644 (file)
index 0000000..03c6726
--- /dev/null
@@ -0,0 +1,352 @@
+#include <dragonstd/array.h>
+#include <dragonstd/list.h>
+#include <dragonstd/tree.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include "facedir.h"
+#include "server/server_node.h"
+#include "server/server_terrain.h"
+#include "server/tree_physics.h"
+#include "server/voxel_depth_search.h"
+
+typedef struct {
+       v3s32 root;
+       bool deadlock;
+       Tree chunks;
+} CheckTreeArg;
+
+typedef struct {
+       TerrainChunk *chunk;
+       TerrainNode *node;
+       u32 *tgs;
+} CheckTreeSearchNodeMeta;
+
+static inline bool is_tree_with_root(TerrainNode *node)
+{
+       switch (node->type) {
+               NODES_TREE
+                       return ((TreeData *) node->data)->has_root;
+
+               default:
+                       return false;
+       }
+}
+
+static int cmp_chunk(const TerrainChunk *chunk, const v3s32 *pos)
+{
+       return v3s32_cmp(&chunk->pos, pos);
+}
+
+static void unlock_chunk(TerrainChunk *chunk)
+{
+       pthread_mutex_unlock(&chunk->mtx);
+}
+
+static void init_search_node(DepthSearchNode *search_node, CheckTreeArg *arg)
+{
+       // first, get chunk position and offset
+       v3s32 chunkp = terrain_chunkp(search_node->pos);
+
+       // check for chunk in cache
+       TerrainChunk *chunk = tree_get(&arg->chunks, &chunkp, &cmp_chunk, NULL);
+
+       // if not found in cache, get it from server_terrain and lock it
+       if (!chunk) {
+               chunk = terrain_get_chunk(server_terrain, chunkp, false);
+
+               // check if chunk is unloaded
+               if (!chunk) {
+                       // if chunk is unloaded, don't remove the tree, it might have a connection to ground
+                       search_node->type = DEPTH_SEARCH_TARGET;
+
+                       // done
+                       return;
+               }
+
+               // try to obtain the chunk mutex
+               int lock_err = pthread_mutex_lock(&chunk->mtx);
+
+               // a deadlock might occur because of the order the chunks are locked
+               if (lock_err == EDEADLK) {
+                       // notify caller deadlock has occured
+                       arg->deadlock = true;
+
+                       // finish search directly
+                       search_node->type = DEPTH_SEARCH_TARGET;
+
+                       // done
+                       return;
+               } else if (lock_err != 0) {
+                       // a different error has occured while trying to obtain the lock
+                       // this should never happen
+
+                       // print error message
+                       fprintf(stderr, "[error] failed to lock terrain chunk mutex\n");
+
+                       // exit program
+                       abort();
+               }
+
+               // insert chunk into cache
+               tree_add(&arg->chunks, &chunk->pos, chunk, &cmp_chunk, NULL);
+       }
+
+       // get node offset
+       v3s32 offset = terrain_offset(search_node->pos);
+
+       // type coersion for easier access
+       TerrainChunkMeta *meta = chunk->extra;
+
+       // pointer to node and generation stage
+       TerrainNode *node = &chunk->data[offset.x][offset.y][offset.z];
+       u32 *tgs = &meta->tgsb.raw.nodes[offset.x][offset.y][offset.z];
+
+       // type coersion for easier access
+       TreeData *data = node->data;
+
+       // have we found terrain?
+       if (*tgs == STAGE_TERRAIN && node->type != NODE_AIR) {
+               // if we've reached the target, set search node type accordingly
+               search_node->type = DEPTH_SEARCH_TARGET;
+       } else if (is_tree_with_root(node) && v3s32_equals(arg->root, data->root)) {
+               // if node is part of our tree, continue search
+               search_node->type = DEPTH_SEARCH_PATH;
+
+               // allocate meta storage
+               CheckTreeSearchNodeMeta *search_meta = search_node->extra = malloc(sizeof *search_meta);
+
+               // store chunk, node and stage pointer for later
+               search_meta->chunk = chunk;
+               search_meta->node = node;
+               search_meta->tgs = tgs;
+       } else {
+               // otherwise, this is a roadblock
+               search_node->type = DEPTH_SEARCH_BLOCK;
+       }
+}
+
+static void free_search_node(DepthSearchNode *node)
+{
+       if (node->extra)
+               free(node->extra);
+
+       free(node);
+}
+
+static void destroy_search_node(DepthSearchNode *node, List *changed_chunks)
+{
+       if (node->type == DEPTH_SEARCH_PATH && !(*node->success)) {
+               // this is a tree/leaves node without connection to ground
+
+               CheckTreeSearchNodeMeta *meta = node->extra;
+
+               // overwrite node and generation stage
+               *meta->node = server_node_create(NODE_AIR);
+               *meta->tgs  = STAGE_PLAYER;
+
+               // flag chunk as changed
+               list_add(changed_chunks, meta->chunk, meta->chunk, &cmp_ref, NULL);
+       }
+
+       free_search_node(node);
+}
+
+/*
+       Check whether all positions (that are part of the same tree) still are connected to the ground.
+       Destroy any tree parts without ground connection.
+
+       The advantage of grouping them together is that they can use the same search cache.
+*/
+static bool check_tree(v3s32 root, Array *positions, Array *chunks)
+{
+       CheckTreeArg arg;
+       // inform depth search callbacks about root of tree (to only match nodes that belong to it)
+       arg.root = root;
+       // output parameter to prevent deadlocks
+       arg.deadlock = false;
+       // cache chunks, to accelerate lookup and prevent locking them twice
+       tree_ini(&arg.chunks);
+
+       // add the chunks the starting points are in to the chunk cache
+       for (size_t i = 0; i < chunks->siz; i++) {
+               TerrainChunk *chunk = ((TerrainChunk **) chunks->ptr)[i];
+               tree_add(&arg.chunks, &chunk->pos, chunk, &cmp_chunk, NULL);
+       }
+
+       // nodes that have been visited
+       // serves as search cache and contains all tree nodes, to remove them if no ground found
+       Tree visit;
+       tree_ini(&visit);
+
+       // success means ground has been found
+
+       // true if ground has been found for all positions
+       bool success_all = true;
+       // individual buffer for each start position (required by depth search algo)
+       bool success_buf[positions->siz];
+
+       // iterate over start positions
+       for (size_t i = 0; i < positions->siz; i++) {
+               success_buf[i] = false;
+
+               // call depth search algorithm to collect positions and find ground
+               if (!voxel_depth_search(((v3s32 *) positions->ptr)[i], (void *) &init_search_node, &arg,
+                               &success_buf[i], &visit))
+                       success_all = false;
+
+               // immediately stop if there was a deadlock
+               if (arg.deadlock)
+                       break;
+       }
+
+       if (success_all || arg.deadlock) {
+               // ground has been found for all parts (or a deadlock was detected)
+
+               // if ground has been found for all, there is no need to pass more complex callback
+               tree_clr(&visit, &free_search_node, NULL, NULL, 0);
+
+               // unlock grabbed chunks
+               tree_clr(&arg.chunks, &unlock_chunk, NULL, NULL, 0);
+
+               // return false if there was a deadlock - caller will reinitiate search
+               return !arg.deadlock;
+       }
+
+       // keep track of changed chunks
+       List changed_chunks;
+       list_ini(&changed_chunks);
+
+       // some or all positions have no connection to ground, pass callback to destroy nodes without
+       tree_clr(&visit, &destroy_search_node, &changed_chunks, NULL, 0);
+
+       // now, unlock all the chunks (before sending some of them)
+       tree_clr(&arg.chunks, &unlock_chunk, NULL, NULL, 0);
+
+       // send changed chunks
+       server_terrain_lock_and_send_chunks(&changed_chunks);
+
+       // done
+       return true;
+}
+
+/*
+       To be called after a node has been removed.
+       For all neigbors that are part of a tree, check whether the tree now still has connection to
+               the ground, destroy tree (partly) otherwise.
+
+       - select neighbor nodes that are leaves or wood
+       - in every iteration, select only the nodes that belong to the same tree (have the same root)
+       - in every iteration, keep the mutexes of the chunks the selected nodes belong to locked
+       - skip nodes that don't match the currently selected root, process them in a later iteration
+*/
+void tree_physics_check(v3s32 center)
+{
+       // remember directions that have been processed
+       bool dirs[6] = {false};
+
+       bool skipped;
+       do {
+               skipped = false;
+
+               // the first node that has a root will initialize these variables
+               bool selected_root = false;
+               v3s32 root;
+
+               // remember selected positions and their associated locked chunks
+               Array positions, chunks;
+               array_ini(&positions, sizeof(v3s32), 5);
+               array_ini(&chunks, sizeof(TerrainChunk *), 5);
+
+               // remember indices of positions selected in this iteration
+               bool selected[6] = {false};
+
+               for (int i = 0; i < 6; i++) {
+                       // we already processed this direction
+                       if (dirs[i])
+                               continue;
+
+                       // this may change back to false if we skip
+                       dirs[i] = true;
+
+                       // facedir contains offsets to neighbor nodes
+                       v3s32 pos = v3s32_add(center, facedir[i]);
+
+                       // get chunk
+                       v3s32 offset;
+                       TerrainChunk *chunk = terrain_get_chunk_nodep(server_terrain, pos, &offset, false);
+                       if (!chunk)
+                               continue;
+
+                       // check if chunk is already locked
+                       bool locked_before = array_idx(&chunks, &chunk) != -1;
+
+                       // lock if not locked
+                       if (!locked_before)
+                               terrain_lock_chunk(chunk);
+
+                       // now that chunk is locked, actually get node
+                       TerrainNode *node = &chunk->data[offset.x][offset.y][offset.z];
+
+                       // check whether we're dealing with a tree node that has a root
+                       if (is_tree_with_root(node)) {
+                               // type coersion for easier access
+                               TreeData *data = node->data;
+
+                               // select root and initialize variables
+                               if (!selected_root) {
+                                       selected_root = true;
+                                       root = data->root;
+                               }
+
+                               // check whether root matches
+                               if (v3s32_equals(root, data->root)) {
+                                       // remember position
+                                       array_apd(&positions, &pos);
+
+                                       // remember chunk - unless it's already on the list
+                                       if (!locked_before)
+                                               array_apd(&chunks, &chunk);
+
+                                       // remember index was selected
+                                       selected[i] = true;
+
+                                       // don't run rest of loop body: don't unlock chunk mutex
+                                       continue;
+                               } else {
+                                       // doesn't match selected root: mark as skipped
+                                       skipped = true;
+                                       dirs[i] = false;
+                               }
+                       }
+
+                       // only unlock if it wasn't locked before
+                       if (!locked_before)
+                               pthread_mutex_unlock(&chunk->mtx);
+               }
+
+               if (selected_root) {
+                       // run depth search
+                       if (!check_tree(root, &positions, &chunks)) {
+                               // a return value of false means a deadlock occured (should be very rare)
+                               printf("[verbose] tree_physics detected deadlock (this not an issue, but should not happen frequently)\n");
+
+                               // sleep for 50ms to hopefully resolve the conflict
+                               nanosleep(&(struct timespec) {0, 50e6}, NULL);
+
+                               // invalidate faces that were selected in this iteration
+                               for (int i = 0; i < 6; i++)
+                                       if (selected[i])
+                                               dirs[i] = false;
+                       }
+
+                       // free memory
+                       array_clr(&positions);
+                       array_clr(&chunks);
+               }
+
+       // repeat until all directions have been processed
+       } while (skipped);
+}
diff --git a/src/server/tree_physics.h b/src/server/tree_physics.h
new file mode 100644 (file)
index 0000000..bee088a
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef _TREE_PHYSICS_H_
+#define _TREE_PHYSICS_H_
+
+#include "types.h"
+
+void tree_physics_check(v3s32 pos);
+
+#endif // _TREE_PHYSICS_H_
diff --git a/src/server/trees.c b/src/server/trees.c
deleted file mode 100644 (file)
index d5c04d6..0000000
+++ /dev/null
@@ -1,236 +0,0 @@
-#include <stdlib.h>
-#include "server/biomes.h"
-#include "server/server_terrain.h"
-#include "server/trees.h"
-#include "server/voxel_procedural.h"
-
-// oak
-
-static bool oak_condition(TreeArgsCondition *args)
-{
-       return args->biome == BIOME_HILLS;
-}
-
-static void oak_tree_leaf(VoxelProcedural *proc)
-{
-       if (!voxel_procedural_is_alive(proc))
-               return;
-
-       voxel_procedural_push(proc);
-               voxel_procedural_cube(proc, NODE_OAK_LEAVES, true);
-       voxel_procedural_pop(proc);
-
-       voxel_procedural_push(proc);
-               voxel_procedural_x(proc, 0.5f);
-               voxel_procedural_sx(proc, 0.9f);
-               voxel_procedural_sy(proc, 0.9f);
-               voxel_procedural_sz(proc, 0.8f);
-               voxel_procedural_ry(proc, 25.0f);
-               voxel_procedural_x(proc, 0.4f);
-               oak_tree_leaf(proc);
-       voxel_procedural_pop(proc);
-}
-
-static void oak_tree_top(VoxelProcedural *proc)
-{
-       if (!voxel_procedural_is_alive(proc))
-               return;
-
-       voxel_procedural_push(proc);
-       for (int i = 0; i < 8; i++) {
-               voxel_procedural_rz(proc, 360.0f / 8.0f);
-               voxel_procedural_push(proc);
-                       voxel_procedural_life(proc, 8);
-                       voxel_procedural_sy(proc, 2.0f);
-                       voxel_procedural_z(proc, voxel_procedural_random(proc, 0.0f, 5.0f));
-                       voxel_procedural_s(proc, 5.0f);
-                       voxel_procedural_light(proc, -0.4f);
-                       voxel_procedural_sat(proc, 0.5f);
-                       voxel_procedural_hue(proc, voxel_procedural_random(proc, 60.0f, 20.0f));
-                       voxel_procedural_ry(proc, -45.0f);
-                       oak_tree_leaf(proc);
-               voxel_procedural_pop(proc);
-       }
-       voxel_procedural_pop(proc);
-}
-
-static void oak_tree_part(VoxelProcedural *proc, f32 n)
-{
-       if (!voxel_procedural_is_alive(proc))
-               return;
-
-       voxel_procedural_push(proc);
-       for (int i = 1; i <= n; i++) {
-               voxel_procedural_z(proc, 1.0f);
-               voxel_procedural_rz(proc, voxel_procedural_random(proc, 30.0f, 10.0f));
-               voxel_procedural_rx(proc, voxel_procedural_random(proc, 0.0f, 10.0f));
-
-               voxel_procedural_push(proc);
-                       voxel_procedural_s(proc, 4.0f);
-                       voxel_procedural_x(proc, 0.1f);
-                       voxel_procedural_light(proc, voxel_procedural_random(proc, 0.0f, 0.1f));
-                       voxel_procedural_cylinder(proc, NODE_OAK_WOOD, true);
-               voxel_procedural_pop(proc);
-
-               if (i == (int) (n - 2.0f)) {
-                       voxel_procedural_push(proc);
-                               oak_tree_top(proc);
-                       voxel_procedural_pop(proc);
-               }
-       }
-       voxel_procedural_pop(proc);
-}
-
-static void oak_tree(v3s32 pos, List *changed_chunks)
-{
-       VoxelProcedural *proc = voxel_procedural_create(changed_chunks, STAGE_TREES, pos);
-
-       voxel_procedural_hue(proc, 40.0f);
-       voxel_procedural_light(proc, -0.5f);
-       voxel_procedural_sat(proc, 0.5f);
-
-       f32 n = voxel_procedural_random(proc, 40.0f, 10.0f);
-
-       voxel_procedural_push(proc);
-       for (int i = 1; i <= 3; i++) {
-               voxel_procedural_rz(proc, voxel_procedural_random(proc, 120.0f, 45.0f));
-               voxel_procedural_push(proc);
-                       voxel_procedural_y(proc, 0.5f);
-                       voxel_procedural_light(proc, voxel_procedural_random(proc, -0.3f, 0.05f));
-                       oak_tree_part(proc, n);
-               voxel_procedural_pop(proc);
-       }
-       voxel_procedural_pop(proc);
-
-       voxel_procedural_delete(proc);
-}
-
-// pine
-
-static bool pine_condition(TreeArgsCondition *args)
-{
-       return args->biome == BIOME_MOUNTAIN;
-}
-
-static void pine_tree(v3s32 pos, List *changed_chunks)
-{
-       s32 tree_top = (noise2d(pos.x, pos.z, 0, seed + OFFSET_PINETREE_HEIGHT) * 0.5 + 0.5) * (35.0 - 20.0) + 20.0 + pos.y;
-       for (v3s32 tree_pos = pos; tree_pos.y < tree_top; tree_pos.y++) {
-               f64 branch_length = noise3d(tree_pos.x, tree_pos.y, tree_pos.z, 0, seed + OFFSET_PINETREE_BRANCH) * 3.0;
-
-               v3s32 dirs[4] = {
-                       {+0, +0, +1},
-                       {+1, +0, +0},
-                       {+0, +0, -1},
-                       {-1, +0, +0},
-               };
-
-               s32 dir = (noise3d(tree_pos.x, tree_pos.y, tree_pos.z, 0, seed + OFFSET_PINETREE_BRANCH_DIR) * 0.5 + 0.5) * 4.0;
-
-               for (v3s32 branch_pos = tree_pos; branch_length > 0; branch_length--, branch_pos = v3s32_add(branch_pos, dirs[dir]))
-                       server_terrain_gen_node(branch_pos,
-                               terrain_node_create(NODE_PINE_WOOD, (Blob) {0, NULL}),
-                               STAGE_TREES, changed_chunks);
-
-               server_terrain_gen_node(tree_pos,
-                       terrain_node_create(NODE_PINE_WOOD, (Blob) {0, NULL}),
-                       STAGE_TREES, changed_chunks);
-       }
-}
-
-// palm
-
-static bool palm_condition(TreeArgsCondition *args)
-{
-       return args->biome == BIOME_OCEAN
-               && ocean_get_node_at((v3s32) {args->pos.x, args->pos.y - 0, args->pos.z}, 1, args->row_data) == NODE_AIR
-               && ocean_get_node_at((v3s32) {args->pos.x, args->pos.y - 1, args->pos.z}, 0, args->row_data) == NODE_SAND;
-}
-
-static void palm_branch(VoxelProcedural *proc)
-{
-       if (!voxel_procedural_is_alive(proc))
-               return;
-
-       voxel_procedural_cube(proc, NODE_PALM_LEAVES, true);
-       voxel_procedural_push(proc);
-               voxel_procedural_z(proc, 0.5f);
-               voxel_procedural_s(proc, 0.8f);
-               voxel_procedural_rx(proc, voxel_procedural_random(proc, 20.0f, 4.0f));
-               voxel_procedural_z(proc, 0.5f);
-               palm_branch(proc);
-       voxel_procedural_pop(proc);
-}
-
-static void palm_tree(v3s32 pos, List *changed_chunks)
-{
-       VoxelProcedural *proc = voxel_procedural_create(changed_chunks, STAGE_TREES, (v3s32) {pos.x, pos.y - 1, pos.z});
-
-       f32 s = voxel_procedural_random(proc, 8.0f, 2.0f);
-
-       voxel_procedural_push(proc);
-       for (int i = 1; i <= s; i++) {
-               voxel_procedural_z(proc, 1.0f);
-               voxel_procedural_push(proc);
-                       voxel_procedural_s(proc, 1.0f);
-                       voxel_procedural_light(proc, voxel_procedural_random(proc, -0.8f, 0.1f));
-                       voxel_procedural_sat(proc, 0.5f);
-                       voxel_procedural_cube(proc, NODE_PALM_WOOD, true);
-               voxel_procedural_pop(proc);
-       }
-       voxel_procedural_pop(proc);
-
-       voxel_procedural_z(proc, s);
-       voxel_procedural_sat(proc, 1.0f),
-       voxel_procedural_light(proc, -0.5f);
-       voxel_procedural_hue(proc, voxel_procedural_random(proc, 50.0f, 30.0f));
-
-       voxel_procedural_push(proc);
-       for (int i = 0; i < 6; i++) {
-               voxel_procedural_rz(proc, 360.0f / 6.0f);
-               voxel_procedural_rz(proc, voxel_procedural_random(proc, 0.0f, 10.0f));
-               voxel_procedural_push(proc);
-                       voxel_procedural_light(proc, voxel_procedural_random(proc, 0.0f, 0.3f));
-                       voxel_procedural_rx(proc, 90.0f);
-                       voxel_procedural_s(proc, 2.0f);
-                       palm_branch(proc);
-               voxel_procedural_pop(proc);
-       }
-       voxel_procedural_pop(proc);
-
-       voxel_procedural_delete(proc);
-}
-
-TreeDef tree_def[NUM_TREES] = {
-       // oak
-       {
-               .spread = 64.0f,
-               .probability = 0.0005f,
-               .area_probability = 0.3f,
-               .offset = OFFSET_OAKTREE,
-               .area_offset = OFFSET_OAKTREE_AREA,
-               .condition = &oak_condition,
-               .generate = &oak_tree,
-       },
-       // pine
-       {
-               .spread = 256.0f,
-               .probability = 0.01f,
-               .area_probability = 0.1f,
-               .offset = OFFSET_PINETREE,
-               .area_offset = OFFSET_PINETREE_AREA,
-               .condition = &pine_condition,
-               .generate = &pine_tree,
-       },
-       // palm
-       {
-               .spread = 16.0f,
-               .probability = 0.005f,
-               .area_probability = 0.5f,
-               .offset = OFFSET_PALMTREE,
-               .area_offset = OFFSET_PALMTREE_AREA,
-               .condition = &palm_condition,
-               .generate = &palm_tree,
-       },
-};
-
diff --git a/src/server/trees.h b/src/server/trees.h
deleted file mode 100644 (file)
index e4ee7ef..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-#ifndef _TREES_H_
-#define _TREES_H_
-
-#include <dragonstd/list.h>
-#include <stdbool.h>
-#include "perlin.h"
-#include "terrain.h"
-#include "types.h"
-
-#define NUM_TREES 3
-
-typedef struct {
-       v3s32 pos;
-       f64 humidity;
-       f64 temperature;
-       Biome biome;
-       f64 factor;
-       TerrainChunk *chunk;
-       void *row_data;
-       void *chunk_data;
-} TreeArgsCondition;
-
-typedef struct {
-       f32 spread;
-       f32 probability;
-       f32 area_probability;
-       SeedOffset offset;
-       SeedOffset area_offset;
-       bool (*condition)(TreeArgsCondition *args);
-       void (*generate)(v3s32 pos, List *changed_chunks);
-} TreeDef;
-
-extern TreeDef tree_def[];
-
-#endif // _TREES_H_
index 722aedaf2748ce58ca59bb50131773927aef237b..edf9fb5128d9236049bd42a8f7023a3c0164450b 100644 (file)
@@ -15,7 +15,7 @@ static int cmp_depth_search_node(const DepthSearchNode *node, const v3s32 *pos)
        return v3s32_cmp(&node->pos, pos);
 }
 
-bool voxel_depth_search(v3s32 pos, DepthSearchNodeType (*get_type)(v3s32 pos), bool *success, Tree *visit)
+bool voxel_depth_search(v3s32 pos, void (*callback)(DepthSearchNode *node, void *arg), void *arg, bool *success, Tree *visit)
 {
        TreeNode **tree_node = tree_nfd(visit, &pos, &cmp_depth_search_node);
        if (*tree_node)
@@ -23,14 +23,15 @@ bool voxel_depth_search(v3s32 pos, DepthSearchNodeType (*get_type)(v3s32 pos), b
 
        DepthSearchNode *node = malloc(sizeof *node);
        tree_nmk(visit, tree_node, node);
-       node->type = get_type(pos);
        node->pos = pos;
+       node->extra = NULL;
+       callback(node, arg);
        if ((*(node->success = success) = (node->type == DEPTH_SEARCH_TARGET)))
                return true;
 
        if (node->type == DEPTH_SEARCH_PATH)
                for (int i = 0; i < 6; i++)
-                       if (voxel_depth_search(v3s32_add(pos, dirs[i]), get_type, success, visit))
+                       if (voxel_depth_search(v3s32_add(pos, dirs[i]), callback, arg, success, visit))
                                return true;
 
        return false;
index 27127070d8cc9a85f0204d8cee8510918344f382..b9163a051e6688f9fd87a3feb6b921e8193a554e 100644 (file)
@@ -8,15 +8,16 @@
 typedef enum {
        DEPTH_SEARCH_TARGET, // goal has been reached
        DEPTH_SEARCH_PATH,   // can used this as path
-       DEPTH_SEARCH_BLOCK   // cannot use this as paths
+       DEPTH_SEARCH_BLOCK   // cannot use this as path
 } DepthSearchNodeType;
 
 typedef struct {
        v3s32 pos;
        DepthSearchNodeType type;
        bool *success;
+       void *extra;
 } DepthSearchNode;
 
-bool voxel_depth_search(v3s32 pos, DepthSearchNodeType (*get_type)(v3s32 pos), bool *success, Tree *visit);
+bool voxel_depth_search(v3s32 pos, void (*callback)(DepthSearchNode *node, void *arg), void *arg, bool *success, Tree *visit);
 
 #endif // _VOXEL_DEPTH_SEARCH_
index ff61ea87bdb21244cd48259f2a3d77839d43f0f6..2eb9bf8e2a6d2319c779a44f6a6ad44cb604f2ae 100644 (file)
@@ -171,11 +171,16 @@ bool voxel_procedural_is_alive(VoxelProcedural *proc)
                VOXEL_PROCEDURAL_STATE(proc).scale[2] >= 1.0f;
 }
 
-void voxel_procedural_cube(VoxelProcedural *proc, NodeType node, bool use_color)
+void voxel_procedural_cube(VoxelProcedural *proc, VoxelProceduralNode func, void *arg)
 {
        if (!voxel_procedural_is_alive(proc))
                return;
 
+       v3f32 color = hsl_to_rgb((v3f32) {
+               VOXEL_PROCEDURAL_STATE(proc).h / 360.0,
+               VOXEL_PROCEDURAL_STATE(proc).s,
+               VOXEL_PROCEDURAL_STATE(proc).l});
+
        vec4 base_corners[8] = {
                {0.0f, 0.0f, 0.0f, 0.0f},
                {0.0f, 0.0f, 1.0f, 0.0f},
@@ -221,30 +226,16 @@ void voxel_procedural_cube(VoxelProcedural *proc, NodeType node, bool use_color)
                        v[i] = floor(VOXEL_PROCEDURAL_STATE(proc).pos[i] + f + 0.5f);
                }
 
-               Blob buffer = {0, NULL};
-
-               if (use_color)
-                       ColorData_write(&buffer, &(ColorData) {hsl_to_rgb((v3f32) {
-                               VOXEL_PROCEDURAL_STATE(proc).h / 360.0,
-                               VOXEL_PROCEDURAL_STATE(proc).s,
-                               VOXEL_PROCEDURAL_STATE(proc).l,
-                       })});
-
-               server_terrain_gen_node(
-                       v3s32_add(proc->pos, (v3s32) {v[0], v[2], v[1]}),
-                       terrain_node_create(node, buffer),
-                       proc->tgs,
-                       proc->changed_chunks
-               );
-
-               Blob_free(&buffer);
+               v3s32 pos = v3s32_add(proc->pos, (v3s32) {v[0], v[2], v[1]});
+               server_terrain_gen_node(pos, func(pos, color, arg),
+                       proc->tgs, proc->changed_chunks);
        }
 }
 
 
-void voxel_procedural_cylinder(VoxelProcedural *proc, NodeType node, bool use_color)
+void voxel_procedural_cylinder(VoxelProcedural *proc, VoxelProceduralNode func, void *arg)
 {
-       voxel_procedural_cube(proc, node, use_color);
+       voxel_procedural_cube(proc, func, arg);
 }
 
 /*
index c953add1198672834eecb8a72710158ec01015e5..6de0f1e8f6d88e61d2adc957304ceb4b2f33d834 100644 (file)
@@ -24,6 +24,8 @@ typedef struct {
        List state;
 } VoxelProcedural;
 
+typedef TerrainNode (*VoxelProceduralNode)(v3s32 pos, v3f32 color, void *arg);
+
 VoxelProcedural *voxel_procedural_create(List *changed_chunks, TerrainGenStage tgs, v3s32 pos);
 void voxel_procedural_delete(VoxelProcedural *proc);
 void voxel_procedural_hue(VoxelProcedural *proc, f32 value);
@@ -43,8 +45,8 @@ void voxel_procedural_s(VoxelProcedural *proc, f32 value);
 void voxel_procedural_pop(VoxelProcedural *proc);
 void voxel_procedural_push(VoxelProcedural *proc);
 bool voxel_procedural_is_alive(VoxelProcedural *proc);
-void voxel_procedural_cube(VoxelProcedural *proc, NodeType node, bool use_color);
-void voxel_procedural_cylinder(VoxelProcedural *proc, NodeType node, bool use_color);
+void voxel_procedural_cube(VoxelProcedural *proc,  VoxelProceduralNode func, void *arg);
+void voxel_procedural_cylinder(VoxelProcedural *proc,  VoxelProceduralNode func, void *arg);
 f32 voxel_procedural_random(VoxelProcedural *proc, f32 base, f32 vary);
 
 #endif // _VOXEL_PROCEDURAL_H_
index 3897b448fc33137dda4dc0a47b018bf3b31f9f10..9238b8e038f9b1d506d3b1a2b9f83470a1b6f6a7 100644 (file)
@@ -1,16 +1,49 @@
 #include <math.h>
 #include <stdbool.h>
+#include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 #include "terrain.h"
 
+typedef struct {
+       v2s32 pos;
+       Tree chunks;
+       pthread_rwlock_t lock;
+} TerrainSector;
+
+static TerrainChunk *allocate_chunk(v3s32 pos)
+{
+       TerrainChunk *chunk = malloc(sizeof * chunk);
+       chunk->level = pos.y;
+       chunk->pos = pos;
+       chunk->extra = NULL;
+       pthread_mutexattr_t attr;
+       pthread_mutexattr_init(&attr);
+       pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
+       pthread_mutex_init(&chunk->mtx, &attr);
+
+       CHUNK_ITERATE
+               chunk->data[x][y][z] = (TerrainNode) {NODE_UNKNOWN, NULL};
+
+       return chunk;
+}
+
+static void free_chunk(Terrain *terrain, TerrainChunk *chunk)
+{
+       if (terrain->callbacks.delete_node) CHUNK_ITERATE
+               terrain->callbacks.delete_node(&chunk->data[x][y][z]);
+
+       pthread_mutex_destroy(&chunk->mtx);
+       free(chunk);
+}
+
 static void delete_chunk(TerrainChunk *chunk, Terrain *terrain)
 {
        if (terrain->callbacks.delete_chunk)
                terrain->callbacks.delete_chunk(chunk);
 
-       terrain_free_chunk(chunk);
+       free_chunk(terrain, chunk);
 }
 
 static void delete_sector(TerrainSector *sector, Terrain *terrain)
@@ -20,25 +53,7 @@ static void delete_sector(TerrainSector *sector, Terrain *terrain)
        free(sector);
 }
 
-Terrain *terrain_create()
-{
-       Terrain *terrain = malloc(sizeof *terrain);
-       tree_ini(&terrain->sectors);
-       pthread_rwlock_init(&terrain->lock, NULL);
-       terrain->cache = NULL;
-       pthread_rwlock_init(&terrain->cache_lock, NULL);
-       return terrain;
-}
-
-void terrain_delete(Terrain *terrain)
-{
-       tree_clr(&terrain->sectors, &delete_sector, terrain, NULL, 0);
-       pthread_rwlock_destroy(&terrain->lock);
-       pthread_rwlock_destroy(&terrain->cache_lock);
-       free(terrain);
-}
-
-TerrainSector *terrain_get_sector(Terrain *terrain, v2s32 pos, bool create)
+static TerrainSector *get_sector(Terrain *terrain, v2s32 pos, bool create)
 {
        if (create)
                pthread_rwlock_wrlock(&terrain->lock);
@@ -64,6 +79,24 @@ TerrainSector *terrain_get_sector(Terrain *terrain, v2s32 pos, bool create)
        return sector;
 }
 
+Terrain *terrain_create()
+{
+       Terrain *terrain = malloc(sizeof *terrain);
+       tree_ini(&terrain->sectors);
+       pthread_rwlock_init(&terrain->lock, NULL);
+       terrain->cache = NULL;
+       pthread_rwlock_init(&terrain->cache_lock, NULL);
+       return terrain;
+}
+
+void terrain_delete(Terrain *terrain)
+{
+       tree_clr(&terrain->sectors, &delete_sector, terrain, NULL, 0);
+       pthread_rwlock_destroy(&terrain->lock);
+       pthread_rwlock_destroy(&terrain->cache_lock);
+       free(terrain);
+}
+
 TerrainChunk *terrain_get_chunk(Terrain *terrain, v3s32 pos, bool create)
 {
        TerrainChunk *cache = NULL;
@@ -75,7 +108,7 @@ TerrainChunk *terrain_get_chunk(Terrain *terrain, v3s32 pos, bool create)
        if (cache && v3s32_equals(cache->pos, pos))
                return cache;
 
-       TerrainSector *sector = terrain_get_sector(terrain, (v2s32) {pos.x, pos.z}, create);
+       TerrainSector *sector = get_sector(terrain, (v2s32) {pos.x, pos.z}, create);
        if (!sector)
                return NULL;
 
@@ -90,18 +123,15 @@ TerrainChunk *terrain_get_chunk(Terrain *terrain, v3s32 pos, bool create)
        if (*loc) {
                chunk = (*loc)->dat;
 
-               pthread_mutex_lock(&chunk->mtx);
                if (terrain->callbacks.get_chunk && !terrain->callbacks.get_chunk(chunk, create)) {
-                       pthread_mutex_unlock(&chunk->mtx);
                        chunk = NULL;
                } else {
-                       pthread_mutex_unlock(&chunk->mtx);
                        pthread_rwlock_wrlock(&terrain->cache_lock);
                        terrain->cache = chunk;
                        pthread_rwlock_unlock(&terrain->cache_lock);
                }
        } else if (create) {
-               tree_nmk(&sector->chunks, loc, chunk = terrain_allocate_chunk(pos));
+               tree_nmk(&sector->chunks, loc, chunk = allocate_chunk(pos));
 
                if (terrain->callbacks.create_chunk)
                        terrain->callbacks.create_chunk(chunk);
@@ -112,33 +142,16 @@ TerrainChunk *terrain_get_chunk(Terrain *terrain, v3s32 pos, bool create)
        return chunk;
 }
 
-TerrainChunk *terrain_allocate_chunk(v3s32 pos)
+TerrainChunk *terrain_get_chunk_nodep(Terrain *terrain, v3s32 nodep, v3s32 *offset, bool create)
 {
-       TerrainChunk *chunk = malloc(sizeof * chunk);
-       chunk->level = pos.y;
-       chunk->pos = pos;
-       chunk->extra = NULL;
-       pthread_mutexattr_t attr;
-       pthread_mutexattr_init(&attr);
-       pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
-       pthread_mutex_init(&chunk->mtx, &attr);
-
-       CHUNK_ITERATE
-               chunk->data[x][y][z] = terrain_node_create(NODE_UNKNOWN, (Blob) {0, NULL});
-
+       TerrainChunk *chunk = terrain_get_chunk(terrain, terrain_chunkp(nodep), create);
+       if (!chunk)
+               return NULL;
+       *offset = terrain_offset(nodep);
        return chunk;
 }
 
-void terrain_free_chunk(TerrainChunk *chunk)
-{
-       CHUNK_ITERATE
-               terrain_node_delete(chunk->data[x][y][z]);
-
-       pthread_mutex_destroy(&chunk->mtx);
-       free(chunk);
-}
-
-Blob terrain_serialize_chunk(TerrainChunk *chunk)
+Blob terrain_serialize_chunk(__attribute__((unused)) Terrain *terrain, TerrainChunk *chunk, void (*callback)(TerrainNode *node, Blob *buffer))
 {
        bool empty = true;
 
@@ -152,118 +165,95 @@ Blob terrain_serialize_chunk(TerrainChunk *chunk)
        if (empty)
                return (Blob) {0, NULL};
 
-       SerializedTerrainChunk chunk_data;
+       SerializedTerrainChunk serialized_chunk;
 
        CHUNK_ITERATE {
                TerrainNode *node = &chunk->data[x][y][z];
-               SerializedTerrainNode *node_data = &chunk_data.raw.nodes[x][y][z];
-
-               *node_data = (SerializedTerrainNode) {
-                       .type = node->type,
-                       .data = {
-                               .siz = 0,
-                               .data = NULL,
-                       },
-               };
+               SerializedTerrainNode *serialized = &serialized_chunk.raw.nodes[x][y][z];
 
-               NodeDef *def = &node_def[node->type];
+               serialized->type = node->type;
+               serialized->data = (Blob) {0, NULL};
 
-               if (def->callbacks.serialize)
-                       def->callbacks.serialize(&node_data->data, node->data);
+               if (callback)
+                       callback(node, &serialized->data);
        }
 
        Blob buffer = {0, NULL};
-       SerializedTerrainChunk_write(&buffer, &chunk_data);
-       SerializedTerrainChunk_free(&chunk_data);
-
+       SerializedTerrainChunk_write(&buffer, &serialized_chunk);
+       SerializedTerrainChunk_free(&serialized_chunk);
        return buffer;
 }
 
-bool terrain_deserialize_chunk(TerrainChunk *chunk, Blob buffer)
+bool terrain_deserialize_chunk(Terrain *terrain, TerrainChunk *chunk, Blob buffer, void (*callback)(TerrainNode *node, Blob buffer))
 {
        if (buffer.siz == 0) {
-               CHUNK_ITERATE
-                       chunk->data[x][y][z] = terrain_node_create(NODE_AIR, (Blob) {0, NULL});
+               CHUNK_ITERATE {
+                       if (terrain->callbacks.delete_node)
+                               terrain->callbacks.delete_node(&chunk->data[x][y][z]);
 
+                       chunk->data[x][y][z] = (TerrainNode) {NODE_AIR, NULL};
+               }
                return true;
        }
 
        // it's important to copy Blobs that have been malloc'd before reading from them
        // because reading from a Blob modifies its data and size pointer,
        // but does not free anything
-       SerializedTerrainChunk chunk_data = {0};
-       bool success = SerializedTerrainChunk_read(&buffer, &chunk_data);
+       SerializedTerrainChunk serialized_chunk = {0};
+       bool success = SerializedTerrainChunk_read(&buffer, &serialized_chunk);
 
-       if (success) CHUNK_ITERATE
-               chunk->data[x][y][z] = terrain_node_create(chunk_data.raw.nodes[x][y][z].type, chunk_data.raw.nodes[x][y][z].data);
+       if (success) CHUNK_ITERATE {
+               if (terrain->callbacks.delete_node)
+                       terrain->callbacks.delete_node(&chunk->data[x][y][z]);
 
-       SerializedTerrainChunk_free(&chunk_data);
-       return success;
-}
+               TerrainNode *node = &chunk->data[x][y][z];
+               SerializedTerrainNode *serialized = &serialized_chunk.raw.nodes[x][y][z];
 
-v3s32 terrain_node_to_chunk_pos(v3s32 pos, v3u8 *offset)
-{
-       if (offset)
-               *offset = (v3u8) {(u32) pos.x % CHUNK_SIZE, (u32) pos.y % CHUNK_SIZE, (u32) pos.z % CHUNK_SIZE};
-       return (v3s32) {floor((double) pos.x / (double) CHUNK_SIZE), floor((double) pos.y / (double) CHUNK_SIZE), floor((double) pos.z / (double) CHUNK_SIZE)};
-}
+               node->type = serialized->type;
 
-TerrainNode terrain_get_node(Terrain *terrain, v3s32 pos)
-{
-       v3u8 offset;
-       v3s32 chunkpos = terrain_node_to_chunk_pos(pos, &offset);
-       TerrainChunk *chunk = terrain_get_chunk(terrain, chunkpos, false);
-       if (!chunk)
-               return terrain_node_create(NODE_UNLOADED, (Blob) {0, NULL});
-       return chunk->data[offset.x][offset.y][offset.z];
+               if (callback)
+                       callback(node, serialized->data);
+       }
+
+       SerializedTerrainChunk_free(&serialized_chunk);
+       return success;
 }
 
-void terrain_set_node(Terrain *terrain, v3s32 pos, TerrainNode node, bool create, void *arg)
+void terrain_lock_chunk(TerrainChunk *chunk)
 {
-       v3u8 offset;
-       TerrainChunk *chunk = terrain_get_chunk(terrain, terrain_node_to_chunk_pos(pos, &offset), create);
-
-       if (!chunk)
+       if (pthread_mutex_lock(&chunk->mtx) == 0)
                return;
 
-       pthread_mutex_lock(&chunk->mtx);
-       if (!terrain->callbacks.set_node || terrain->callbacks.set_node(chunk, offset, &node, arg)) {
-               chunk->data[offset.x][offset.y][offset.z] = node;
-               if (terrain->callbacks.after_set_node)
-                       terrain->callbacks.after_set_node(chunk, offset, arg);
-       } else {
-               terrain_node_delete(node);
-       }
-       pthread_mutex_unlock(&chunk->mtx);
+       fprintf(stderr, "[error] failed to lock terrain chunk mutex\n");
+       abort();
 }
 
-TerrainNode terrain_node_create(NodeType type, Blob buffer)
+TerrainNode terrain_get_node(Terrain *terrain, v3s32 pos)
 {
-       if (type >= NODE_UNLOADED)
-               type = NODE_UNKNOWN;
-
-       NodeDef *def = &node_def[type];
-
-       TerrainNode node;
-       node.type = type;
-       node.data = def->data_size ? malloc(def->data_size) : NULL;
-
-       if (def->callbacks.create)
-               def->callbacks.create(&node);
+       v3s32 offset;
+       TerrainChunk *chunk = terrain_get_chunk_nodep(terrain, pos, &offset, false);
+       if (!chunk)
+               return (TerrainNode) {COUNT_NODE, NULL};
 
-       if (def->callbacks.deserialize)
-               def->callbacks.deserialize(&buffer, node.data);
+       terrain_lock_chunk(chunk);
+       TerrainNode node = chunk->data[offset.x][offset.y][offset.z];
+       pthread_mutex_unlock(&chunk->mtx);
 
        return node;
 }
 
-void terrain_node_delete(TerrainNode node)
+v3s32 terrain_chunkp(v3s32 pos)
 {
-       NodeDef *def = &node_def[node.type];
-
-       if (def->callbacks.delete)
-               def->callbacks.delete(&node);
+       return (v3s32) {
+               floor((double) pos.x / (double) CHUNK_SIZE),
+               floor((double) pos.y / (double) CHUNK_SIZE),
+               floor((double) pos.z / (double) CHUNK_SIZE)};
+}
 
-       if (node.data)
-               free(node.data);
+v3s32 terrain_offset(v3s32 pos)
+{
+       return (v3s32) {
+               (u32) pos.x % CHUNK_SIZE,
+               (u32) pos.y % CHUNK_SIZE,
+               (u32) pos.z % CHUNK_SIZE};
 }
index 44098c74c8802a705be8dad918505e3f50d6c78b..54869e411c202dca2c7c5b1cf0041dcb3db12cfa 100644 (file)
@@ -18,23 +18,14 @@ typedef struct TerrainNode {
        void *data;
 } TerrainNode;
 
-typedef TerrainNode TerrainChunkData[CHUNK_SIZE][CHUNK_SIZE][CHUNK_SIZE];
-
 typedef struct {
        s32 level;
        v3s32 pos;
-       TerrainChunkData data;
+       TerrainNode data[CHUNK_SIZE][CHUNK_SIZE][CHUNK_SIZE];
        void *extra;
        pthread_mutex_t mtx;
 } TerrainChunk;
 
-typedef struct
-{
-       v2s32 pos;
-       Tree chunks;
-       pthread_rwlock_t lock;
-} TerrainSector;
-
 typedef struct {
        Tree sectors;
        pthread_rwlock_t lock;
@@ -44,29 +35,24 @@ typedef struct {
                void (*create_chunk)(TerrainChunk *chunk);
                void (*delete_chunk)(TerrainChunk *chunk);
                bool (*get_chunk)(TerrainChunk *chunk, bool create);
-               bool (*set_node) (TerrainChunk *chunk, v3u8 offset, TerrainNode *node, void *arg);
-               void (*after_set_node)(TerrainChunk *chunk, v3u8 offset, void *arg);
+               void (*delete_node)(TerrainNode *node);
        } callbacks;
 } Terrain;
 
 Terrain *terrain_create();
 void terrain_delete(Terrain *terrain);
 
-TerrainSector *terrain_get_sector(Terrain *terrain, v2s32 pos, bool create);
 TerrainChunk *terrain_get_chunk(Terrain *terrain, v3s32 pos, bool create);
+TerrainChunk *terrain_get_chunk_nodep(Terrain *terrain, v3s32 node_pos, v3s32 *offset, bool create);
 
-TerrainChunk *terrain_allocate_chunk(v3s32 pos);
-void terrain_free_chunk(TerrainChunk *chunk);
-
-Blob terrain_serialize_chunk(TerrainChunk *chunk);
-bool terrain_deserialize_chunk(TerrainChunk *chunk, Blob buffer);
-
-v3s32 terrain_node_to_chunk_pos(v3s32 pos, v3u8 *offset);
+Blob terrain_serialize_chunk(Terrain *terrain, TerrainChunk *chunk, void (*callback)(TerrainNode *node, Blob *buffer));
+bool terrain_deserialize_chunk(Terrain *terrain, TerrainChunk *chunk, Blob buffer, void (*callback)(TerrainNode *node, Blob buffer));
 
 TerrainNode terrain_get_node(Terrain *terrain, v3s32 pos);
-void terrain_set_node(Terrain *terrain, v3s32 pos, TerrainNode node, bool create, void *arg);
 
-TerrainNode terrain_node_create(NodeType type, Blob buffer);
-void terrain_node_delete(TerrainNode node);
+void terrain_lock_chunk(TerrainChunk *chunk);
+
+v3s32 terrain_chunkp(v3s32 pos);
+v3s32 terrain_offset(v3s32 pos);
 
 #endif
index 6d187239d79a3d269687f0aecb7f381e6fc8a055..1cd4a31d7d8e37cf2bb3ee5b5653b4adcfd8cf14 100644 (file)
@@ -3,6 +3,11 @@
 ColorData
        v3f32 color
 
+TreeData
+       v3f32 color
+       u8 has_root
+       v3s32 root
+
 SerializedTerrainNode
        u32 type
        Blob data