Trees break if they have no connection to ground
parent
6fd40203d5
commit
f4c0012041
|
@ -1 +1 @@
|
|||
Subproject commit 431e3bd29073d9c10ab3a853116860ba83ed1c5e
|
||||
Subproject commit 9ab2148e1b8d03dab816bbba6209794663b31576
|
|
@ -1 +1 @@
|
|||
Subproject commit 8878e826fa3e7b1231e652fc13d11c7a61629d13
|
||||
Subproject commit a3a50433ffe0674291d4c5e9d2247cca7f691faa
|
|
@ -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
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#!/bin/bash
|
||||
./dragonblocks_server "[::1]:4000" &
|
||||
echo "singleplayer" | ./dragonblocks "[::1]:4000"
|
||||
pkill -P $$
|
||||
pkill -P $$ -9
|
||||
|
|
|
@ -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}"
|
||||
)
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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_
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#include "client/facedir.h"
|
||||
#include "facedir.h"
|
||||
|
||||
v3s32 facedir[6] = {
|
||||
{+0, +0, -1},
|
64
src/node.c
64
src/node.c
|
@ -3,147 +3,85 @@
|
|||
#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},
|
||||
},
|
||||
};
|
||||
|
|
11
src/node.h
11
src/node.h
|
@ -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[];
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
if (!(node_def[node].dig_class & item_def[stack->type].dig_class))
|
||||
TerrainNode *node = &chunk->data[off.x][off.y][off.z];
|
||||
|
||||
if (!(node_def[node->type].dig_class & item_def[stack->type].dig_class)) {
|
||||
pthread_mutex_unlock(&chunk->mtx);
|
||||
return;
|
||||
}
|
||||
|
||||
terrain_set_node(server_terrain, pos,
|
||||
terrain_node_create(NODE_AIR, (Blob) {0, NULL}),
|
||||
false, NULL);
|
||||
*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);
|
||||
|
||||
// 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] = {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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_
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -1,9 +1,24 @@
|
|||
#include <stdlib.h>
|
||||
#include "server/biomes.h"
|
||||
#include "server/server_node.h"
|
||||
#include "server/server_terrain.h"
|
||||
#include "server/trees.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)
|
||||
|
@ -11,13 +26,14 @@ static bool oak_condition(TreeArgsCondition *args)
|
|||
return args->biome == BIOME_HILLS;
|
||||
}
|
||||
|
||||
static void oak_tree_leaf(VoxelProcedural *proc)
|
||||
static void oak_tree_leaf(VoxelProcedural *proc, v3s32 root)
|
||||
{
|
||||
if (!voxel_procedural_is_alive(proc))
|
||||
return;
|
||||
|
||||
voxel_procedural_push(proc);
|
||||
voxel_procedural_cube(proc, NODE_OAK_LEAVES, true);
|
||||
voxel_procedural_cube(proc, (void *) &create_tree_node,
|
||||
&(ProceduralTreeArg) {NODE_OAK_LEAVES, root});
|
||||
voxel_procedural_pop(proc);
|
||||
|
||||
voxel_procedural_push(proc);
|
||||
|
@ -27,11 +43,11 @@ static void oak_tree_leaf(VoxelProcedural *proc)
|
|||
voxel_procedural_sz(proc, 0.8f);
|
||||
voxel_procedural_ry(proc, 25.0f);
|
||||
voxel_procedural_x(proc, 0.4f);
|
||||
oak_tree_leaf(proc);
|
||||
oak_tree_leaf(proc, root);
|
||||
voxel_procedural_pop(proc);
|
||||
}
|
||||
|
||||
static void oak_tree_top(VoxelProcedural *proc)
|
||||
static void oak_tree_top(VoxelProcedural *proc, v3s32 root)
|
||||
{
|
||||
if (!voxel_procedural_is_alive(proc))
|
||||
return;
|
||||
|
@ -48,13 +64,13 @@ static void oak_tree_top(VoxelProcedural *proc)
|
|||
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);
|
||||
oak_tree_leaf(proc, root);
|
||||
voxel_procedural_pop(proc);
|
||||
}
|
||||
voxel_procedural_pop(proc);
|
||||
}
|
||||
|
||||
static void oak_tree_part(VoxelProcedural *proc, f32 n)
|
||||
static void oak_tree_part(VoxelProcedural *proc, v3s32 root, f32 n)
|
||||
{
|
||||
if (!voxel_procedural_is_alive(proc))
|
||||
return;
|
||||
|
@ -69,21 +85,22 @@ static void oak_tree_part(VoxelProcedural *proc, f32 n)
|
|||
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_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);
|
||||
oak_tree_top(proc, root);
|
||||
voxel_procedural_pop(proc);
|
||||
}
|
||||
}
|
||||
voxel_procedural_pop(proc);
|
||||
}
|
||||
|
||||
static void oak_tree(v3s32 pos, List *changed_chunks)
|
||||
static void oak_tree(v3s32 root, List *changed_chunks)
|
||||
{
|
||||
VoxelProcedural *proc = voxel_procedural_create(changed_chunks, STAGE_TREES, pos);
|
||||
VoxelProcedural *proc = voxel_procedural_create(changed_chunks, STAGE_TREES, root);
|
||||
|
||||
voxel_procedural_hue(proc, 40.0f);
|
||||
voxel_procedural_light(proc, -0.5f);
|
||||
|
@ -97,7 +114,7 @@ static void oak_tree(v3s32 pos, List *changed_chunks)
|
|||
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);
|
||||
oak_tree_part(proc, root, n);
|
||||
voxel_procedural_pop(proc);
|
||||
}
|
||||
voxel_procedural_pop(proc);
|
||||
|
@ -128,12 +145,10 @@ static void pine_tree(v3s32 pos, List *changed_chunks)
|
|||
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}),
|
||||
server_terrain_gen_node(branch_pos, server_node_create(NODE_PINE_WOOD),
|
||||
STAGE_TREES, changed_chunks);
|
||||
|
||||
server_terrain_gen_node(tree_pos,
|
||||
terrain_node_create(NODE_PINE_WOOD, (Blob) {0, NULL}),
|
||||
server_terrain_gen_node(tree_pos, server_node_create(NODE_PINE_WOOD),
|
||||
STAGE_TREES, changed_chunks);
|
||||
}
|
||||
}
|
||||
|
@ -147,24 +162,26 @@ static bool palm_condition(TreeArgsCondition *args)
|
|||
&& 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)
|
||||
static void palm_branch(VoxelProcedural *proc, v3s32 root)
|
||||
{
|
||||
if (!voxel_procedural_is_alive(proc))
|
||||
return;
|
||||
|
||||
voxel_procedural_cube(proc, NODE_PALM_LEAVES, true);
|
||||
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);
|
||||
palm_branch(proc, root);
|
||||
voxel_procedural_pop(proc);
|
||||
}
|
||||
|
||||
static void palm_tree(v3s32 pos, List *changed_chunks)
|
||||
static void palm_tree(v3s32 root, List *changed_chunks)
|
||||
{
|
||||
VoxelProcedural *proc = voxel_procedural_create(changed_chunks, STAGE_TREES, (v3s32) {pos.x, pos.y - 1, pos.z});
|
||||
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);
|
||||
|
||||
|
@ -175,7 +192,8 @@ static void palm_tree(v3s32 pos, List *changed_chunks)
|
|||
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_cube(proc, (void *) &create_tree_node,
|
||||
&(ProceduralTreeArg) {NODE_PALM_WOOD, root});
|
||||
voxel_procedural_pop(proc);
|
||||
}
|
||||
voxel_procedural_pop(proc);
|
||||
|
@ -193,7 +211,7 @@ static void palm_tree(v3s32 pos, List *changed_chunks)
|
|||
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);
|
||||
palm_branch(proc, root);
|
||||
voxel_procedural_pop(proc);
|
||||
}
|
||||
voxel_procedural_pop(proc);
|
|
@ -1,5 +1,5 @@
|
|||
#ifndef _TREES_H_
|
||||
#define _TREES_H_
|
||||
#ifndef _TREE_H_
|
||||
#define _TREE_H_
|
||||
|
||||
#include <dragonstd/list.h>
|
||||
#include <stdbool.h>
|
||||
|
@ -32,4 +32,4 @@ typedef struct {
|
|||
|
||||
extern TreeDef tree_def[];
|
||||
|
||||
#endif // _TREES_H_
|
||||
#endif // _TREE_H_
|
|
@ -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);
|
||||
}
|
|
@ -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_
|
|
@ -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;
|
||||
|
|
|
@ -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_
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -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_
|
||||
|
|
238
src/terrain.c
238
src/terrain.c
|
@ -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(§or->chunks, loc, chunk = terrain_allocate_chunk(pos));
|
||||
tree_nmk(§or->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];
|
||||
SerializedTerrainNode *serialized = &serialized_chunk.raw.nodes[x][y][z];
|
||||
|
||||
*node_data = (SerializedTerrainNode) {
|
||||
.type = node->type,
|
||||
.data = {
|
||||
.siz = 0,
|
||||
.data = NULL,
|
||||
},
|
||||
};
|
||||
serialized->type = node->type;
|
||||
serialized->data = (Blob) {0, NULL};
|
||||
|
||||
NodeDef *def = &node_def[node->type];
|
||||
|
||||
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);
|
||||
TerrainNode *node = &chunk->data[x][y][z];
|
||||
SerializedTerrainNode *serialized = &serialized_chunk.raw.nodes[x][y][z];
|
||||
|
||||
node->type = serialized->type;
|
||||
|
||||
if (callback)
|
||||
callback(node, serialized->data);
|
||||
}
|
||||
|
||||
SerializedTerrainChunk_free(&serialized_chunk);
|
||||
return success;
|
||||
}
|
||||
|
||||
v3s32 terrain_node_to_chunk_pos(v3s32 pos, v3u8 *offset)
|
||||
void terrain_lock_chunk(TerrainChunk *chunk)
|
||||
{
|
||||
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)};
|
||||
if (pthread_mutex_lock(&chunk->mtx) == 0)
|
||||
return;
|
||||
|
||||
fprintf(stderr, "[error] failed to lock terrain chunk mutex\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
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);
|
||||
v3s32 offset;
|
||||
TerrainChunk *chunk = terrain_get_chunk_nodep(terrain, pos, &offset, false);
|
||||
if (!chunk)
|
||||
return terrain_node_create(NODE_UNLOADED, (Blob) {0, NULL});
|
||||
return chunk->data[offset.x][offset.y][offset.z];
|
||||
}
|
||||
return (TerrainNode) {COUNT_NODE, NULL};
|
||||
|
||||
void terrain_set_node(Terrain *terrain, v3s32 pos, TerrainNode node, bool create, void *arg)
|
||||
{
|
||||
v3u8 offset;
|
||||
TerrainChunk *chunk = terrain_get_chunk(terrain, terrain_node_to_chunk_pos(pos, &offset), create);
|
||||
|
||||
if (!chunk)
|
||||
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);
|
||||
}
|
||||
terrain_lock_chunk(chunk);
|
||||
TerrainNode node = chunk->data[offset.x][offset.y][offset.z];
|
||||
pthread_mutex_unlock(&chunk->mtx);
|
||||
}
|
||||
|
||||
TerrainNode terrain_node_create(NodeType type, Blob buffer)
|
||||
{
|
||||
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);
|
||||
|
||||
if (def->callbacks.deserialize)
|
||||
def->callbacks.deserialize(&buffer, node.data);
|
||||
|
||||
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);
|
||||
|
||||
if (node.data)
|
||||
free(node.data);
|
||||
return (v3s32) {
|
||||
floor((double) pos.x / (double) CHUNK_SIZE),
|
||||
floor((double) pos.y / (double) CHUNK_SIZE),
|
||||
floor((double) pos.z / (double) CHUNK_SIZE)};
|
||||
}
|
||||
|
||||
v3s32 terrain_offset(v3s32 pos)
|
||||
{
|
||||
return (v3s32) {
|
||||
(u32) pos.x % CHUNK_SIZE,
|
||||
(u32) pos.y % CHUNK_SIZE,
|
||||
(u32) pos.z % CHUNK_SIZE};
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -3,6 +3,11 @@
|
|||
ColorData
|
||||
v3f32 color
|
||||
|
||||
TreeData
|
||||
v3f32 color
|
||||
u8 has_root
|
||||
v3s32 root
|
||||
|
||||
SerializedTerrainNode
|
||||
u32 type
|
||||
Blob data
|
||||
|
|
Loading…
Reference in New Issue