]> git.lizzy.rs Git - dragonblocks_alpha.git/blob - src/server/server_map.c
854afe15d548e8820a5a04e893a41ae90b11215e
[dragonblocks_alpha.git] / src / server / server_map.c
1 #include <stdlib.h>
2 #include <string.h>
3 #include <unistd.h>
4 #include <stdio.h>
5 #include "interrupt.h"
6 #include "map.h"
7 #include "server/database.h"
8 #include "server/mapgen.h"
9 #include "server/server_config.h"
10 #include "server/server_map.h"
11 #include "util.h"
12
13 // this file is too long
14 struct ServerMap server_map;
15
16 // utility functions
17
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)
20 {
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);
24
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;
28 }
29
30 // send a block to a client and reset block request
31 static void send_block(ServerPlayer *player, MapBlock *block)
32 {
33         if (! within_simulation_distance(player, block->pos, server_config.simulation_distance))
34                 return;
35
36         dragonnet_peer_send_ToClientBlock(player->peer, &(ToClientBlock) {
37                 .pos = block->pos,
38                 .data = ((MapBlockExtraData *) block->extra)->data,
39         });
40 }
41
42 // send block to near clients
43 // block mutex has to be locked
44 static void send_block_to_near(MapBlock *block)
45 {
46         MapBlockExtraData *extra = block->extra;
47
48         if (extra->state == MBS_GENERATING)
49                 return;
50
51         Blob_free(&extra->data);
52         extra->data = map_serialize_block(block);
53
54         database_save_block(block);
55
56         if (extra->state == MBS_CREATED)
57                 return;
58
59         server_player_iterate((void *) &send_block, block);
60 }
61
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)
64 {
65         MapBlock *block = key;
66
67         pthread_mutex_lock(&block->mtx);
68         send_block_to_near(block);
69         pthread_mutex_unlock(&block->mtx);
70 }
71
72 // me when the
73 static void mapgen_step()
74 {
75         MapBlock *block = queue_dequeue(server_map.mapgen_tasks);
76
77         if (! block)
78                 return;
79
80         MapBlockExtraData *extra = block->extra;
81
82         List changed_blocks = list_create(NULL);
83         list_put(&changed_blocks, block, NULL);
84
85         mapgen_generate_block(block, &changed_blocks);
86
87         pthread_mutex_lock(&block->mtx);
88         extra->state = MBS_READY;
89         pthread_mutex_unlock(&block->mtx);
90
91         list_clear_func(&changed_blocks, &list_send_block, NULL);
92
93         pthread_mutex_lock(&server_map.num_blocks_mtx);
94         server_map.num_blocks--;
95         pthread_mutex_unlock(&server_map.num_blocks_mtx);
96 }
97
98 // there was a time when i wrote actually useful comments lol
99 static void *mapgen_thread(unused void *arg)
100 {
101         while (! server_map.cancel)
102                 mapgen_step();
103
104         return NULL;
105 }
106
107 // enqueue block
108 static void generate_block(MapBlock *block)
109 {
110         if (server_map.cancel)
111                 return;
112
113         pthread_mutex_lock(&server_map.num_blocks_mtx);
114         server_map.num_blocks++;
115         pthread_mutex_unlock(&server_map.num_blocks_mtx);
116
117         MapBlockExtraData *extra = block->extra;
118         extra->state = MBS_GENERATING;
119         queue_enqueue(server_map.mapgen_tasks, block);
120 }
121
122 // map callbacks
123 // note: all these functions require the block mutex to be locked, which is always the case when a map callback is invoked
124
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)
128 {
129         MapBlockExtraData *extra = block->extra = malloc(sizeof(MapBlockExtraData));
130
131         if (! database_load_block(block)) {
132                 extra->state = MBS_CREATED;
133                 extra->data = (Blob) {0, NULL};
134
135                 ITERATE_MAPBLOCK {
136                         block->data[x][y][z] = map_node_create(NODE_AIR, (Blob) {0, NULL});
137                         extra->mgsb.raw.nodes[x][y][z] = MGS_VOID;
138                 }
139         }
140 }
141
142 // callback for deleting a block
143 // free extra data
144 static void on_delete_block(MapBlock *block)
145 {
146         MapBlockExtraData *extra = block->extra;
147
148         Blob_free(&extra->data);
149         free(extra);
150 }
151
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)
155 {
156         MapBlockExtraData *extra = block->extra;
157
158         if (extra->state < MBS_READY && ! create)
159                 return false;
160
161         return true;
162 }
163
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)
167 {
168         MapgenSetNodeArg *msn_arg = arg;
169
170         MapgenStage mgs;
171
172         if (msn_arg)
173                 mgs = msn_arg->mgs;
174         else
175                 mgs = MGS_PLAYER;
176
177         MapgenStage *old_mgs = &((MapBlockExtraData *) block->extra)->mgsb.raw.nodes[offset.x][offset.y][offset.z];
178
179         if (mgs >= *old_mgs) {
180                 *old_mgs = mgs;
181
182                 if (msn_arg)
183                         list_put(msn_arg->changed_blocks, block, NULL);
184
185                 return true;
186         }
187
188         return false;
189 }
190
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)
194 {
195         if (! arg)
196                 send_block_to_near(block);
197 }
198
199 // generate a hut for new players to spawn in
200 static void generate_spawn_hut()
201 {
202         Blob wood_color = {0, NULL};
203         HSLData_write(&wood_color, &(HSLData) {{0.11f, 1.0f, 0.29f}});
204
205         List changed_blocks = list_create(NULL);
206
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);
211                         }
212                 }
213         }
214
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);
219                 }
220         }
221
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);
226                 }
227         }
228
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);
233                 }
234         }
235
236         v2s32 posts[6] = {
237                 {-4, -3},
238                 {-4, +2},
239                 {+4, -3},
240                 {+4, +2},
241                 {+5, -1},
242                 {+5, +0},
243         };
244
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;
249
250                         if (i >= 4) {
251                                 if (node != NODE_AIR)
252                                         break;
253
254                                 pos.y++;
255                         }
256
257                         if (node_definitions[node].solid)
258                                 break;
259
260                         mapgen_set_node(pos, map_node_create(node == NODE_LAVA ? NODE_VULCANO_STONE : NODE_OAK_WOOD, wood_color), MGS_PLAYER, &changed_blocks);
261                 }
262         }
263
264         list_clear_func(&changed_blocks, &list_send_block, NULL);
265 }
266
267 // public functions
268
269 // ServerMap singleton constructor
270 void server_map_init()
271 {
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,
278         });
279
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);
285
286         for (unsigned int i = 0; i < server_config.mapgen_threads; i++)
287                 pthread_create(&server_map.mapgen_threads[i], NULL, &mapgen_thread, NULL);
288 }
289
290 // ServerMap singleton destructor
291 void server_map_deinit()
292 {
293         queue_finish(server_map.mapgen_tasks);
294         server_map.cancel = true;
295         queue_cancel(server_map.mapgen_tasks);
296
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);
300
301         pthread_mutex_destroy(&server_map.num_blocks_mtx);
302         queue_delete(server_map.mapgen_tasks);
303         map_delete(server_map.map);
304 }
305
306 // handle block request from client (thread safe)
307 void server_map_requested_block(ServerPlayer *player, v3s32 pos)
308 {
309         if (within_simulation_distance(player, pos, server_config.simulation_distance)) {
310                 MapBlock *block = map_get_block(server_map.map, pos, true);
311
312                 pthread_mutex_lock(&block->mtx);
313
314                 MapBlockExtraData *extra = block->extra;
315                 switch (extra->state) {
316                         case MBS_CREATED:
317                                 generate_block(block);
318                                 break;
319
320                         case MBS_GENERATING:
321                                 break;
322
323                         case MBS_READY:
324                                 send_block(player, block);
325                 };
326
327                 pthread_mutex_unlock(&block->mtx);
328         }
329 }
330
331 static void update_percentage()
332 {
333         static s32 total = 3 * 3 * 21;
334         static s32 done = -1;
335         static s32 last_percentage = -1;
336
337         if (done < total)
338                 done++;
339
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);
343
344         if (percentage > last_percentage) {
345                 last_percentage = percentage;
346                 printf("Preparing spawn... %d%%\n", percentage);
347         }
348
349 }
350
351 // prepare spawn region
352 void server_map_prepare_spawn()
353 {
354         update_percentage();
355
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++) {
359                                 if (interrupt->done)
360                                         return;
361
362                                 MapBlock *block = map_get_block(server_map.map, (v3s32) {x, y, z}, true);
363
364                                 pthread_mutex_lock(&block->mtx);
365                                 if (((MapBlockExtraData *) block->extra)->state == MBS_CREATED)
366                                         generate_block(block);
367                                 pthread_mutex_unlock(&block->mtx);
368
369                                 update_percentage();
370                         }
371                 }
372         }
373
374         while (true) {
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);
378
379                 if (done)
380                         break;
381
382                 update_percentage();
383                 sched_yield();
384         }
385
386         s64 saved_spawn_height;
387         if (database_load_meta("spawn_height", &saved_spawn_height)) {
388                 server_map.spawn_height = saved_spawn_height;
389         } else {
390                 s32 spawn_height = -1;
391
392                 while (map_get_node(server_map.map, (v3s32) {0, ++spawn_height, 0}).type != NODE_AIR)
393                         ;
394
395                 server_map.spawn_height = spawn_height + 5;
396                 generate_spawn_hut();
397                 database_save_meta("spawn_height", server_map.spawn_height);
398         }
399 }