5 #include "server/database.h"
6 #include "server/mapgen.h"
7 #include "server/server_map.h"
11 struct ServerMap server_map;
12 static Server *server;
16 // send a block to a client and reset block request
17 static void send_block(Client *client, MapBlock *block)
19 MapBlockExtraData *extra = block->extra;
21 pthread_mutex_lock(&client->mtx);
22 if (client->state == CS_ACTIVE)
23 (void) (write_u32(client->fd, CC_BLOCK) && write_v3s32(client->fd, block->pos) && write_u64(client->fd, extra->size) && write(client->fd, extra->data, extra->size) != -1);
24 pthread_mutex_unlock(&client->mtx);
27 // send block to near clients
28 // block mutex has to be locked
29 static void send_block_to_near(MapBlock *block)
31 MapBlockExtraData *extra = block->extra;
33 if (extra->state == MBS_GENERATING)
36 map_serialize_block(block, &extra->data, &extra->size);
37 database_save_block(server->db, block);
39 if (extra->state == MBS_CREATED)
42 pthread_rwlock_rdlock(&server->players_rwlck);
43 ITERATE_LIST(&server->players, pair) {
44 Client *client = pair->value;
46 if (within_simulation_distance(client->pos, block->pos, server->config.simulation_distance))
47 send_block(client, block);
49 pthread_rwlock_unlock(&server->players_rwlck);
52 // list_clear_func callback for sending changed blocks to near clients
53 static void list_send_block(void *key, unused void *value, unused void *arg)
55 MapBlock *block = key;
57 pthread_mutex_lock(&block->mtx);
58 send_block_to_near(block);
59 pthread_mutex_unlock(&block->mtx);
62 // pthread start routine for mapgen thread
63 static void *mapgen_thread(void *arg)
65 MapBlock *block = arg;
66 MapBlockExtraData *extra = block->extra;
68 pthread_mutex_lock(&block->mtx);
69 extra->state = MBS_GENERATING;
70 pthread_mutex_unlock(&block->mtx);
72 List changed_blocks = list_create(NULL);
73 list_put(&changed_blocks, block, NULL);
75 mapgen_generate_block(block, &changed_blocks);
77 pthread_mutex_lock(&block->mtx);
78 extra->state = MBS_READY;
79 pthread_mutex_unlock(&block->mtx);
81 list_clear_func(&changed_blocks, &list_send_block, NULL);
83 if (! server_map.shutting_down) {
84 pthread_mutex_lock(&server_map.mapgen_threads_mtx);
85 list_delete(&server_map.mapgen_threads, &extra->mapgen_thread);
86 pthread_mutex_unlock(&server_map.mapgen_threads_mtx);
92 // launch mapgen thread for block
93 // block mutex has to be locked
94 static void launch_mapgen_thread(MapBlock *block)
96 pthread_mutex_lock(&server_map.mapgen_threads_mtx);
97 pthread_t *thread_ptr = &((MapBlockExtraData *) block->extra)->mapgen_thread;
98 pthread_create(thread_ptr, NULL, mapgen_thread, block);
99 list_put(&server_map.mapgen_threads, thread_ptr, NULL);
100 pthread_mutex_unlock(&server_map.mapgen_threads_mtx);
103 // list_clear_func callback used to join running generator threads on shutdown
104 static void list_join_thread(void *key, unused void *value, unused void *arg)
106 pthread_join(*(pthread_t *) key, NULL);
110 // note: all these functions require the block mutex to be locked, which is always the case when a map callback is invoked
112 // callback for initializing a newly created block
113 // load block from database or initialize state, mgstage buffer and data
114 static void on_create_block(MapBlock *block)
116 MapBlockExtraData *extra = block->extra = malloc(sizeof(MapBlockExtraData));
118 if (! database_load_block(server->db, block)) {
119 extra->state = MBS_CREATED;
123 block->data[x][y][z] = map_node_create(NODE_AIR);
124 extra->mgs_buffer[x][y][z] = MGS_VOID;
129 // callback for deleting a block
131 static void on_delete_block(MapBlock *block)
133 MapBlockExtraData *extra = block->extra;
141 // callback for determining whether a block should be returned by map_get_block
142 // hold back blocks that are not fully generated except when the create flag is set to true
143 static bool on_get_block(MapBlock *block, bool create)
145 MapBlockExtraData *extra = block->extra;
147 if (extra->state < MBS_READY && ! create)
153 // callback for deciding whether a set_node call succeeds or not
154 // 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
155 static bool on_set_node(MapBlock *block, v3u8 offset, unused MapNode *node, void *arg)
157 MapgenSetNodeArg *msn_arg = arg;
166 MapgenStage *old_mgs = &((MapBlockExtraData *) block->extra)->mgs_buffer[offset.x][offset.y][offset.z];
168 if (mgs >= *old_mgs) {
172 list_put(msn_arg->changed_blocks, block, NULL);
180 // callback for when a block changes
181 // send block to near clients if not part of map generation
182 static void on_after_set_node(MapBlock *block, unused v3u8 offset, void *arg)
185 send_block_to_near(block);
190 // ServerMap singleton constructor
191 void server_map_init(Server *srv)
195 server_map.map = map_create((MapCallbacks) {
196 .create_block = &on_create_block,
197 .delete_block = &on_delete_block,
198 .get_block = &on_get_block,
199 .set_node = &on_set_node,
200 .after_set_node = &on_after_set_node,
202 server_map.shutting_down = false;
203 server_map.mapgen_threads = list_create(NULL);
204 pthread_mutex_init(&server_map.mapgen_threads_mtx, NULL);
207 // ServerMap singleton destructor
208 void server_map_deinit()
210 server_map.shutting_down = true;
212 pthread_mutex_lock(&server_map.mapgen_threads_mtx);
213 list_clear_func(&server_map.mapgen_threads, &list_join_thread, NULL);
214 pthread_mutex_destroy(&server_map.mapgen_threads_mtx);
216 map_delete(server_map.map);
219 // handle block request from client (thread safe)
220 void server_map_requested_block(Client *client, v3s32 pos)
222 if (within_simulation_distance(client->pos, pos, server->config.simulation_distance)) {
223 MapBlock *block = map_get_block(server_map.map, pos, true);
225 pthread_mutex_lock(&block->mtx);
226 MapBlockExtraData *extra = block->extra;
228 switch (extra->state) {
230 launch_mapgen_thread(block);
237 send_block(client, block);
239 pthread_mutex_unlock(&block->mtx);