6 #include "server/database.h"
7 #include "server/mapgen.h"
8 #include "server/server_config.h"
9 #include "server/server_map.h"
10 #include "signal_handlers.h"
13 struct ServerMap server_map;
17 // return true if a player is close enough to a block to access it
18 static bool within_simulation_distance(ServerPlayer *player, v3s32 blkp, u32 dist)
20 pthread_rwlock_rdlock(&player->pos_lock);
21 v3s32 ppos = map_node_to_block_pos((v3s32) {player->pos.x, player->pos.y, player->pos.z}, NULL);
22 pthread_rwlock_unlock(&player->pos_lock);
24 return abs(ppos.x - blkp.x) <= (s32) dist
25 && abs(ppos.y - blkp.y) <= (s32) dist
26 && abs(ppos.z - blkp.z) <= (s32) dist;
29 // send a block to a client and reset block request
30 static void send_block(ServerPlayer *player, MapBlock *block)
32 if (! within_simulation_distance(player, block->pos, server_config.simulation_distance))
35 dragonnet_peer_send_ToClientBlock(player->peer, &(ToClientBlock) {
37 .data = ((MapBlockExtraData *) block->extra)->data,
41 // send block to near clients
42 // block mutex has to be locked
43 static void send_block_to_near(MapBlock *block)
45 MapBlockExtraData *extra = block->extra;
47 if (extra->state == MBS_GENERATING)
50 Blob_free(&extra->data);
51 extra->data = map_serialize_block(block);
53 database_save_block(block);
55 if (extra->state == MBS_CREATED)
58 server_player_iterate((void *) &send_block, block);
61 // list_clear_func callback for sending changed blocks to near clients
62 static void list_send_block(void *key, unused void *value, unused void *arg)
64 MapBlock *block = key;
66 pthread_mutex_lock(&block->mtx);
67 send_block_to_near(block);
68 pthread_mutex_unlock(&block->mtx);
71 // pthread start routine for mapgen thread
72 static void *mapgen_thread(void *arg)
74 MapBlock *block = arg;
75 MapBlockExtraData *extra = block->extra;
77 pthread_mutex_lock(&block->mtx);
78 extra->state = MBS_GENERATING;
79 pthread_mutex_unlock(&block->mtx);
81 List changed_blocks = list_create(NULL);
82 list_put(&changed_blocks, block, NULL);
84 mapgen_generate_block(block, &changed_blocks);
86 pthread_mutex_lock(&block->mtx);
87 extra->state = MBS_READY;
88 pthread_mutex_unlock(&block->mtx);
90 list_clear_func(&changed_blocks, &list_send_block, NULL);
92 pthread_mutex_lock(&server_map.joining_threads_mtx);
93 if (! server_map.joining_threads) {
94 pthread_mutex_lock(&server_map.mapgen_threads_mtx);
95 list_delete(&server_map.mapgen_threads, &extra->mapgen_thread);
96 pthread_mutex_unlock(&server_map.mapgen_threads_mtx);
98 pthread_mutex_unlock(&server_map.joining_threads_mtx);
103 // launch mapgen thread for block
104 // block mutex has to be locked
105 static void launch_mapgen_thread(MapBlock *block)
107 pthread_mutex_lock(&server_map.mapgen_threads_mtx);
108 pthread_t *thread_ptr = &((MapBlockExtraData *) block->extra)->mapgen_thread;
109 pthread_create(thread_ptr, NULL, mapgen_thread, block);
110 list_put(&server_map.mapgen_threads, thread_ptr, NULL);
111 pthread_mutex_unlock(&server_map.mapgen_threads_mtx);
114 // list_clear_func callback used to join running generator threads on shutdown
115 static void list_join_thread(void *key, unused void *value, unused void *arg)
117 pthread_join(*(pthread_t *) key, NULL);
121 // note: all these functions require the block mutex to be locked, which is always the case when a map callback is invoked
123 // callback for initializing a newly created block
124 // load block from database or initialize state, mgstage buffer and data
125 static void on_create_block(MapBlock *block)
127 MapBlockExtraData *extra = block->extra = malloc(sizeof(MapBlockExtraData));
129 if (! database_load_block(block)) {
130 extra->state = MBS_CREATED;
131 extra->data = (Blob) {0, NULL};
134 block->data[x][y][z] = map_node_create(NODE_AIR, (Blob) {0, NULL});
135 extra->mgsb.raw.nodes[x][y][z] = MGS_VOID;
140 // callback for deleting a block
142 static void on_delete_block(MapBlock *block)
144 MapBlockExtraData *extra = block->extra;
146 Blob_free(&extra->data);
150 // callback for determining whether a block should be returned by map_get_block
151 // hold back blocks that are not fully generated except when the create flag is set to true
152 static bool on_get_block(MapBlock *block, bool create)
154 MapBlockExtraData *extra = block->extra;
156 if (extra->state < MBS_READY && ! create)
162 // callback for deciding whether a set_node call succeeds or not
163 // reject set_node calls that try to override nodes placed by later mapgen stages, else update mgs buffer - also make sure block is inserted into changed blocks list
164 static bool on_set_node(MapBlock *block, v3u8 offset, unused MapNode *node, void *arg)
166 MapgenSetNodeArg *msn_arg = arg;
175 MapgenStage *old_mgs = &((MapBlockExtraData *) block->extra)->mgsb.raw.nodes[offset.x][offset.y][offset.z];
177 if (mgs >= *old_mgs) {
181 list_put(msn_arg->changed_blocks, block, NULL);
189 // callback for when a block changes
190 // send block to near clients if not part of map generation
191 static void on_after_set_node(MapBlock *block, unused v3u8 offset, void *arg)
194 send_block_to_near(block);
197 // join all map generation threads
198 static void join_mapgen_threads()
200 pthread_mutex_lock(&server_map.joining_threads_mtx);
201 server_map.joining_threads = true;
202 pthread_mutex_unlock(&server_map.joining_threads_mtx);
204 pthread_mutex_lock(&server_map.mapgen_threads_mtx);
205 list_clear_func(&server_map.mapgen_threads, &list_join_thread, NULL);
206 pthread_mutex_unlock(&server_map.mapgen_threads_mtx);
208 pthread_mutex_lock(&server_map.joining_threads_mtx);
209 server_map.joining_threads = false;
210 pthread_mutex_unlock(&server_map.joining_threads_mtx);
213 // generate a hut for new players to spawn in
214 static void generate_spawn_hut()
216 Blob wood_color = {0, NULL};
217 HSLData_write(&wood_color, &(HSLData) {{0.11f, 1.0f, 0.29f}});
219 List changed_blocks = list_create(NULL);
221 for (s32 x = -4; x <= +4; x++) {
222 for (s32 y = 0; y <= 3; y++) {
223 for (s32 z = -3; z <= +2; z++) {
224 mapgen_set_node((v3s32) {x, server_map.spawn_height + y, z}, map_node_create(NODE_AIR, (Blob) {0, NULL}), MGS_PLAYER, &changed_blocks);
229 for (s32 x = -5; x <= +5; x++) {
230 for (s32 z = -4; z <= +3; z++) {
231 mapgen_set_node((v3s32) {x, server_map.spawn_height - 1, z}, map_node_create(NODE_OAK_WOOD, wood_color), MGS_PLAYER, &changed_blocks);
232 mapgen_set_node((v3s32) {x, server_map.spawn_height + 4, z}, map_node_create(NODE_OAK_WOOD, wood_color), MGS_PLAYER, &changed_blocks);
236 for (s32 y = 0; y <= 3; y++) {
237 for (s32 x = -5; x <= +5; x++) {
238 mapgen_set_node((v3s32) {x, server_map.spawn_height + y, -4}, map_node_create(((y == 1 || y == 2) && ((x >= -3 && x <= -1) || (x >= +1 && x <= +2))) ? NODE_AIR : NODE_OAK_WOOD, wood_color), MGS_PLAYER, &changed_blocks);
239 mapgen_set_node((v3s32) {x, server_map.spawn_height + y, +3}, map_node_create(((y == 1 || y == 2) && ((x >= -3 && x <= -2) || (x >= +1 && x <= +3))) ? NODE_AIR : NODE_OAK_WOOD, wood_color), MGS_PLAYER, &changed_blocks);
243 for (s32 y = 0; y <= 3; y++) {
244 for (s32 z = -3; z <= +2; z++) {
245 mapgen_set_node((v3s32) {-5, server_map.spawn_height + y, z}, map_node_create(NODE_OAK_WOOD, wood_color), MGS_PLAYER, &changed_blocks);
246 mapgen_set_node((v3s32) {+5, server_map.spawn_height + y, z}, map_node_create(((y != 3) && (z == -1 || z == +0)) ? NODE_AIR : NODE_OAK_WOOD, wood_color), MGS_PLAYER, &changed_blocks);
259 for (int i = 0; i < 6; i++) {
260 for (s32 y = server_map.spawn_height - 2;; y--) {
261 v3s32 pos = {posts[i].x, y, posts[i].y};
262 Node node = map_get_node(server_map.map, pos).type;
265 if (node != NODE_AIR)
271 if (node_definitions[node].solid)
274 mapgen_set_node(pos, map_node_create(node == NODE_LAVA ? NODE_VULCANO_STONE : NODE_OAK_WOOD, wood_color), MGS_PLAYER, &changed_blocks);
278 list_clear_func(&changed_blocks, &list_send_block, NULL);
283 // ServerMap singleton constructor
284 void server_map_init()
286 server_map.map = map_create((MapCallbacks) {
287 .create_block = &on_create_block,
288 .delete_block = &on_delete_block,
289 .get_block = &on_get_block,
290 .set_node = &on_set_node,
291 .after_set_node = &on_after_set_node,
293 server_map.joining_threads = false;
294 server_map.mapgen_threads = list_create(NULL);
295 pthread_mutex_init(&server_map.joining_threads_mtx, NULL);
296 pthread_mutex_init(&server_map.mapgen_threads_mtx, NULL);
299 // ServerMap singleton destructor
300 void server_map_deinit()
302 join_mapgen_threads();
303 pthread_mutex_destroy(&server_map.joining_threads_mtx);
304 pthread_mutex_destroy(&server_map.mapgen_threads_mtx);
305 map_delete(server_map.map);
308 // handle block request from client (thread safe)
309 void server_map_requested_block(ServerPlayer *player, v3s32 pos)
311 if (within_simulation_distance(player, pos, server_config.simulation_distance)) {
312 MapBlock *block = map_get_block(server_map.map, pos, true);
314 pthread_mutex_lock(&block->mtx);
315 MapBlockExtraData *extra = block->extra;
317 switch (extra->state) {
319 launch_mapgen_thread(block);
326 send_block(player, block);
328 pthread_mutex_unlock(&block->mtx);
332 // prepare spawn region
333 void server_map_prepare_spawn()
336 s32 dist = server_config.simulation_distance;
337 s32 total = (dist * 2 + 1);
338 total *= total * total;
339 s32 last_percentage = -1;
341 for (s32 x = -dist; x <= (s32) dist; x++) {
342 for (s32 y = -dist; y <= (s32) dist; y++) {
343 for (s32 z = -dist; z <= (s32) dist; z++) {
345 join_mapgen_threads();
349 MapBlock *block = map_get_block(server_map.map, (v3s32) {x, y, z}, true);
351 pthread_mutex_lock(&block->mtx);
352 if (((MapBlockExtraData *) block->extra)->state == MBS_CREATED)
353 launch_mapgen_thread(block);
354 pthread_mutex_unlock(&block->mtx);
358 s32 percentage = 100.0 * done / total;
360 if (percentage > last_percentage) {
361 last_percentage = percentage;
362 printf("Preparing spawn... %d%%\n", percentage);
368 join_mapgen_threads();
370 s64 saved_spawn_height;
371 if (database_load_meta("spawn_height", &saved_spawn_height)) {
372 server_map.spawn_height = saved_spawn_height;
374 s32 spawn_height = -1;
376 while (map_get_node(server_map.map, (v3s32) {0, ++spawn_height, 0}).type != NODE_AIR)
379 server_map.spawn_height = spawn_height + 5;
380 generate_spawn_hut();
381 database_save_meta("spawn_height", server_map.spawn_height);