7 #include "server/database.h"
8 #include "server/mapgen.h"
9 #include "server/server_config.h"
10 #include "server/server_map.h"
13 // this file is too long
14 struct ServerMap server_map;
18 // return true if a player is close enough to a block to access it
19 static bool within_simulation_distance(ServerPlayer *player, v3s32 blkp, u32 dist)
21 pthread_rwlock_rdlock(&player->pos_lock);
22 v3s32 ppos = map_node_to_block_pos((v3s32) {player->pos.x, player->pos.y, player->pos.z}, NULL);
23 pthread_rwlock_unlock(&player->pos_lock);
25 return abs(ppos.x - blkp.x) <= (s32) dist
26 && abs(ppos.y - blkp.y) <= (s32) dist
27 && abs(ppos.z - blkp.z) <= (s32) dist;
30 // send a block to a client and reset block request
31 static void send_block(ServerPlayer *player, MapBlock *block)
33 if (! within_simulation_distance(player, block->pos, server_config.simulation_distance))
36 dragonnet_peer_send_ToClientBlock(player->peer, &(ToClientBlock) {
38 .data = ((MapBlockExtraData *) block->extra)->data,
42 // send block to near clients
43 // block mutex has to be locked
44 static void send_block_to_near(MapBlock *block)
46 MapBlockExtraData *extra = block->extra;
48 if (extra->state == MBS_GENERATING)
51 Blob_free(&extra->data);
52 extra->data = map_serialize_block(block);
54 database_save_block(block);
56 if (extra->state == MBS_CREATED)
59 server_player_iterate((void *) &send_block, block);
62 // list_clear_func callback for sending changed blocks to near clients
63 static void list_send_block(void *key, unused void *value, unused void *arg)
65 MapBlock *block = key;
67 pthread_mutex_lock(&block->mtx);
68 send_block_to_near(block);
69 pthread_mutex_unlock(&block->mtx);
73 static void mapgen_step()
75 MapBlock *block = queue_dequeue(server_map.mapgen_tasks);
80 MapBlockExtraData *extra = block->extra;
82 List changed_blocks = list_create(NULL);
83 list_put(&changed_blocks, block, NULL);
85 mapgen_generate_block(block, &changed_blocks);
87 pthread_mutex_lock(&block->mtx);
88 extra->state = MBS_READY;
89 pthread_mutex_unlock(&block->mtx);
91 list_clear_func(&changed_blocks, &list_send_block, NULL);
93 pthread_mutex_lock(&server_map.num_blocks_mtx);
94 server_map.num_blocks--;
95 pthread_mutex_unlock(&server_map.num_blocks_mtx);
98 // there was a time when i wrote actually useful comments lol
99 static void *mapgen_thread(unused void *arg)
101 while (! server_map.cancel)
108 static void generate_block(MapBlock *block)
110 if (server_map.cancel)
113 pthread_mutex_lock(&server_map.num_blocks_mtx);
114 server_map.num_blocks++;
115 pthread_mutex_unlock(&server_map.num_blocks_mtx);
117 MapBlockExtraData *extra = block->extra;
118 extra->state = MBS_GENERATING;
119 queue_enqueue(server_map.mapgen_tasks, block);
123 // note: all these functions require the block mutex to be locked, which is always the case when a map callback is invoked
125 // callback for initializing a newly created block
126 // load block from database or initialize state, mgstage buffer and data
127 static void on_create_block(MapBlock *block)
129 MapBlockExtraData *extra = block->extra = malloc(sizeof(MapBlockExtraData));
131 if (! database_load_block(block)) {
132 extra->state = MBS_CREATED;
133 extra->data = (Blob) {0, NULL};
136 block->data[x][y][z] = map_node_create(NODE_AIR, (Blob) {0, NULL});
137 extra->mgsb.raw.nodes[x][y][z] = MGS_VOID;
142 // callback for deleting a block
144 static void on_delete_block(MapBlock *block)
146 MapBlockExtraData *extra = block->extra;
148 Blob_free(&extra->data);
152 // callback for determining whether a block should be returned by map_get_block
153 // hold back blocks that are not fully generated except when the create flag is set to true
154 static bool on_get_block(MapBlock *block, bool create)
156 MapBlockExtraData *extra = block->extra;
158 if (extra->state < MBS_READY && ! create)
164 // callback for deciding whether a set_node call succeeds or not
165 // 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
166 static bool on_set_node(MapBlock *block, v3u8 offset, unused MapNode *node, void *arg)
168 MapgenSetNodeArg *msn_arg = arg;
177 MapgenStage *old_mgs = &((MapBlockExtraData *) block->extra)->mgsb.raw.nodes[offset.x][offset.y][offset.z];
179 if (mgs >= *old_mgs) {
183 list_put(msn_arg->changed_blocks, block, NULL);
191 // callback for when a block changes
192 // send block to near clients if not part of map generation
193 static void on_after_set_node(MapBlock *block, unused v3u8 offset, void *arg)
196 send_block_to_near(block);
199 // generate a hut for new players to spawn in
200 static void generate_spawn_hut()
202 Blob wood_color = {0, NULL};
203 HSLData_write(&wood_color, &(HSLData) {{0.11f, 1.0f, 0.29f}});
205 List changed_blocks = list_create(NULL);
207 for (s32 x = -4; x <= +4; x++) {
208 for (s32 y = 0; y <= 3; y++) {
209 for (s32 z = -3; z <= +2; z++) {
210 mapgen_set_node((v3s32) {x, server_map.spawn_height + y, z}, map_node_create(NODE_AIR, (Blob) {0, NULL}), MGS_PLAYER, &changed_blocks);
215 for (s32 x = -5; x <= +5; x++) {
216 for (s32 z = -4; z <= +3; z++) {
217 mapgen_set_node((v3s32) {x, server_map.spawn_height - 1, z}, map_node_create(NODE_OAK_WOOD, wood_color), MGS_PLAYER, &changed_blocks);
218 mapgen_set_node((v3s32) {x, server_map.spawn_height + 4, z}, map_node_create(NODE_OAK_WOOD, wood_color), MGS_PLAYER, &changed_blocks);
222 for (s32 y = 0; y <= 3; y++) {
223 for (s32 x = -5; x <= +5; x++) {
224 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);
225 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);
229 for (s32 y = 0; y <= 3; y++) {
230 for (s32 z = -3; z <= +2; z++) {
231 mapgen_set_node((v3s32) {-5, server_map.spawn_height + y, z}, map_node_create(NODE_OAK_WOOD, wood_color), MGS_PLAYER, &changed_blocks);
232 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);
245 for (int i = 0; i < 6; i++) {
246 for (s32 y = server_map.spawn_height - 2;; y--) {
247 v3s32 pos = {posts[i].x, y, posts[i].y};
248 Node node = map_get_node(server_map.map, pos).type;
251 if (node != NODE_AIR)
257 if (node_definitions[node].solid)
260 mapgen_set_node(pos, map_node_create(node == NODE_LAVA ? NODE_VULCANO_STONE : NODE_OAK_WOOD, wood_color), MGS_PLAYER, &changed_blocks);
264 list_clear_func(&changed_blocks, &list_send_block, NULL);
269 // ServerMap singleton constructor
270 void server_map_init()
272 server_map.map = map_create((MapCallbacks) {
273 .create_block = &on_create_block,
274 .delete_block = &on_delete_block,
275 .get_block = &on_get_block,
276 .set_node = &on_set_node,
277 .after_set_node = &on_after_set_node,
280 server_map.cancel = false;
281 server_map.mapgen_tasks = queue_create();
282 server_map.mapgen_threads = malloc(sizeof *server_map.mapgen_threads * server_config.mapgen_threads);
283 server_map.num_blocks = 0;
284 pthread_mutex_init(&server_map.num_blocks_mtx, NULL);
286 for (unsigned int i = 0; i < server_config.mapgen_threads; i++)
287 pthread_create(&server_map.mapgen_threads[i], NULL, &mapgen_thread, NULL);
290 // ServerMap singleton destructor
291 void server_map_deinit()
293 queue_finish(server_map.mapgen_tasks);
294 server_map.cancel = true;
295 queue_cancel(server_map.mapgen_tasks);
297 for (unsigned int i = 0; i < server_config.mapgen_threads; i++)
298 pthread_join(server_map.mapgen_threads[i], NULL);
299 free(server_map.mapgen_threads);
301 pthread_mutex_destroy(&server_map.num_blocks_mtx);
302 queue_delete(server_map.mapgen_tasks);
303 map_delete(server_map.map);
306 // handle block request from client (thread safe)
307 void server_map_requested_block(ServerPlayer *player, v3s32 pos)
309 if (within_simulation_distance(player, pos, server_config.simulation_distance)) {
310 MapBlock *block = map_get_block(server_map.map, pos, true);
312 pthread_mutex_lock(&block->mtx);
314 MapBlockExtraData *extra = block->extra;
315 switch (extra->state) {
317 generate_block(block);
324 send_block(player, block);
327 pthread_mutex_unlock(&block->mtx);
331 static void update_percentage()
333 static s32 total = 3 * 3 * 21;
334 static s32 done = -1;
335 static s32 last_percentage = -1;
340 pthread_mutex_lock(&server_map.num_blocks_mtx);
341 s32 percentage = 100.0 * (done - server_map.num_blocks) / total;
342 pthread_mutex_unlock(&server_map.num_blocks_mtx);
344 if (percentage > last_percentage) {
345 last_percentage = percentage;
346 printf("Preparing spawn... %d%%\n", percentage);
351 // prepare spawn region
352 void server_map_prepare_spawn()
356 for (s32 x = -1; x <= (s32) 1; x++) {
357 for (s32 y = -10; y <= (s32) 10; y++) {
358 for (s32 z = -1; z <= (s32) 1; z++) {
362 MapBlock *block = map_get_block(server_map.map, (v3s32) {x, y, z}, true);
364 pthread_mutex_lock(&block->mtx);
365 if (((MapBlockExtraData *) block->extra)->state == MBS_CREATED)
366 generate_block(block);
367 pthread_mutex_unlock(&block->mtx);
375 pthread_mutex_lock(&server_map.num_blocks_mtx);
376 bool done = (server_map.num_blocks == 0);
377 pthread_mutex_unlock(&server_map.num_blocks_mtx);
386 s64 saved_spawn_height;
387 if (database_load_meta("spawn_height", &saved_spawn_height)) {
388 server_map.spawn_height = saved_spawn_height;
390 s32 spawn_height = -1;
392 while (map_get_node(server_map.map, (v3s32) {0, ++spawn_height, 0}).type != NODE_AIR)
395 server_map.spawn_height = spawn_height + 5;
396 generate_spawn_hut();
397 database_save_meta("spawn_height", server_map.spawn_height);