3 #include "client/blockmesh.h"
4 #include "client/facecache.h"
5 #include "client/client_config.h"
6 #include "client/client_map.h"
7 #include "client/client_player.h"
8 #include "client/debug_menu.h"
10 #define MAX_BLOCK_REQUESTS 4
12 struct ClientMap client_map;
16 // dequeue callback to thread-safely update
17 static void set_dequeued(MapBlock *block)
19 pthread_mutex_lock(&block->mtx);
20 ((MapBlockExtraData *) block->extra)->queue = false;
21 pthread_mutex_unlock(&block->mtx);
24 // mesh generator step
25 static void meshgen_step()
27 MapBlock *block = queue_dequeue_callback(client_map.queue, (void *) &set_dequeued);
30 blockmesh_make(block);
33 // pthread start routine for meshgen thread
34 static void *meshgen_thread(unused void *arg)
36 while (! client_map.cancel)
44 // send block request command to server
45 static void request_position(v3s32 pos)
47 dragonnet_peer_send_ToServerRequestBlock(client, &(ToServerRequestBlock) {
52 // mapblock synchronisation step
53 static void sync_step()
56 static v3s32 *old_requested_positions = NULL;
57 static size_t old_requested_positions_count = 0;
59 u64 last_tick = tick++;
61 v3f64 player_pos = client_player_get_position();
62 v3s32 center = map_node_to_block_pos((v3s32) {player_pos.x, player_pos.y, player_pos.z}, NULL);
64 v3s32 *requested_positions = malloc(sizeof(v3s32) * MAX_BLOCK_REQUESTS);
65 size_t requested_positions_count = 0;
67 for (size_t i = 0; i < client_map.blocks_count; i++) {
68 v3s32 pos = facecache_face(i, ¢er);
69 MapBlock *block = map_get_block(client_map.map, pos, false);
72 pthread_mutex_lock(&block->mtx);
73 MapBlockExtraData *extra = block->extra;
75 switch (extra->state) {
77 if (extra->last_synced < last_tick)
78 request_position(pos);
82 extra->state = MBS_READY;
83 extra->last_synced = tick;
89 pthread_mutex_unlock(&block->mtx);
90 } else if (requested_positions_count < MAX_BLOCK_REQUESTS) {
91 bool should_request = true;
93 for (size_t i = 0; i < old_requested_positions_count; i++) {
94 if (v3s32_equals(old_requested_positions[i], pos)) {
95 should_request = false;
101 request_position(pos);
103 requested_positions[requested_positions_count++] = pos;
107 if (old_requested_positions)
108 free(old_requested_positions);
110 old_requested_positions = requested_positions;
111 old_requested_positions_count = requested_positions_count;
114 // pthread start routine for sync thread
115 static void *sync_thread(unused void *arg)
117 while (! client_map.cancel)
124 // note: all these functions require the block mutex to be locked, which is always the case when a map callback is invoked
126 // callback for initializing a newly created block
127 // allocate and initialize extra data
128 static void on_create_block(MapBlock *block)
130 MapBlockExtraData *extra = block->extra = malloc(sizeof(MapBlockExtraData));
132 extra->state = MBS_RECIEVING;
133 extra->queue = false;
134 extra->last_synced = 0;
138 // callback for deleting a block
140 static void on_delete_block(MapBlock *block)
145 // callback for determining whether a block should be returned by map_get_block
146 // hold back blocks that have not been fully read from server yet when the create flag is set to true
147 static bool on_get_block(MapBlock *block, bool create)
149 return create || ((MapBlockExtraData *) block->extra)->state > MBS_RECIEVING;
154 // ClientMap singleton constructor
155 void client_map_init()
157 client_map.map = map_create((MapCallbacks) {
158 .create_block = &on_create_block,
159 .delete_block = &on_delete_block,
160 .get_block = &on_get_block,
162 .after_set_node = NULL,
164 client_map.queue = queue_create();
165 client_map.cancel = false;
166 client_map.sync_thread = 0;
167 client_map_set_simulation_distance(10);
169 client_map.meshgen_threads = malloc(sizeof *client_map.meshgen_threads * client_config.meshgen_threads);
170 for (unsigned int i = 0; i < client_config.meshgen_threads; i++)
171 client_map.meshgen_threads[i] = 0;
174 // ClientMap singleton destructor
175 void client_map_deinit()
177 queue_delete(client_map.queue);
178 map_delete(client_map.map);
181 // start meshgen and sync threads
182 void client_map_start()
184 for (unsigned int i = 0; i < client_config.meshgen_threads; i++)
185 pthread_create(&client_map.meshgen_threads[i], NULL, &meshgen_thread, NULL);
187 pthread_create(&client_map.sync_thread, NULL, &sync_thread, NULL);
190 // stop meshgen and sync threads
191 void client_map_stop()
193 client_map.cancel = true;
194 queue_cancel(client_map.queue);
196 for (unsigned int i = 0; i < client_config.meshgen_threads; i++)
197 if (client_map.meshgen_threads[i])
198 pthread_join(client_map.meshgen_threads[i], NULL);
199 free(client_map.meshgen_threads);
201 if (client_map.sync_thread)
202 pthread_join(client_map.sync_thread, NULL);
205 // update simulation distance
206 void client_map_set_simulation_distance(u32 simulation_distance)
208 client_map.simulation_distance = simulation_distance;
209 client_map.blocks_count = facecache_count(simulation_distance);
212 // called when a block was actually recieved from server
213 void client_map_block_received(MapBlock *block)
215 pthread_mutex_lock(&block->mtx);
216 MapBlockExtraData *extra = block->extra;
217 if (extra->state == MBS_RECIEVING)
218 extra->state = MBS_FRESH;
219 pthread_mutex_unlock(&block->mtx);
221 client_map_schedule_update_block_mesh(block);
223 client_map_schedule_update_block_mesh(map_get_block(client_map.map, (v3s32) {block->pos.x + 1, block->pos.y + 0, block->pos.z + 0}, false));
224 client_map_schedule_update_block_mesh(map_get_block(client_map.map, (v3s32) {block->pos.x + 0, block->pos.y + 1, block->pos.z + 0}, false));
225 client_map_schedule_update_block_mesh(map_get_block(client_map.map, (v3s32) {block->pos.x + 0, block->pos.y + 0, block->pos.z + 1}, false));
226 client_map_schedule_update_block_mesh(map_get_block(client_map.map, (v3s32) {block->pos.x - 1, block->pos.y - 0, block->pos.z - 0}, false));
227 client_map_schedule_update_block_mesh(map_get_block(client_map.map, (v3s32) {block->pos.x - 0, block->pos.y - 1, block->pos.z - 0}, false));
228 client_map_schedule_update_block_mesh(map_get_block(client_map.map, (v3s32) {block->pos.x - 0, block->pos.y - 0, block->pos.z - 1}, false));
231 // enqueue block to mesh update queue
232 void client_map_schedule_update_block_mesh(MapBlock *block)
237 pthread_mutex_lock(&block->mtx);
238 MapBlockExtraData *extra = block->extra;
239 if (! extra->queue) {
241 queue_enqueue(client_map.queue, block);
243 pthread_mutex_unlock(&block->mtx);