3 #include "client/blockmesh.h"
4 #include "client/facecache.h"
5 #include "client/client_map.h"
6 #include "client/client_player.h"
8 #define MAX_BLOCK_REQUESTS 4
10 struct ClientMap client_map;
15 // dequeue callback to thread-safely update
16 static void set_dequeued(void *arg)
18 MapBlock *block = arg;
20 pthread_mutex_lock(&block->mtx);
21 ((MapBlockExtraData *) block->extra)->queue = false;
22 pthread_mutex_unlock(&block->mtx);
25 // mesh generator step
26 static void meshgen_step()
30 if ((block = queue_dequeue_callback(client_map.queue, &set_dequeued)))
31 blockmesh_make(block);
36 // pthread start routine for meshgen thread
37 static void *meshgen_thread(unused void *arg)
39 while (! client_map.cancel)
45 // enqueue mesh to block update queue
46 static void schedule_update_block_mesh(MapBlock *block)
51 pthread_mutex_lock(&block->mtx);
52 MapBlockExtraData *extra = block->extra;
55 queue_enqueue(client_map.queue, block);
57 pthread_mutex_unlock(&block->mtx);
62 // send block request command to server
63 static void request_position(v3s32 pos)
65 pthread_mutex_lock(&client->mtx);
66 (void) (write_u32(client->fd, SC_REQUEST_BLOCK) && write_v3s32(client->fd, pos));
67 pthread_mutex_unlock(&client->mtx);
70 // mapblock synchronisation step
71 static void sync_step()
74 static v3s32 *old_requested_positions = NULL;
75 static size_t old_requested_positions_count = 0;
77 u64 last_tick = tick++;
79 v3f64 player_pos = client_player_get_position();
80 v3s32 center = map_node_to_block_pos((v3s32) {player_pos.x, player_pos.y, player_pos.z}, NULL);
82 v3s32 *requested_positions = malloc(sizeof(v3s32) * MAX_BLOCK_REQUESTS);
83 size_t requested_positions_count = 0;
85 for (size_t i = 0; i < client_map.blocks_count; i++) {
86 v3s32 pos = facecache_face(i, ¢er);
87 MapBlock *block = map_get_block(client_map.map, pos, false);
90 pthread_mutex_lock(&block->mtx);
91 MapBlockExtraData *extra = block->extra;
93 switch (extra->state) {
95 if (extra->last_synced < last_tick)
96 request_position(pos);
100 extra->state = MBS_READY;
101 extra->last_synced = tick;
107 pthread_mutex_unlock(&block->mtx);
108 } else if (requested_positions_count < MAX_BLOCK_REQUESTS) {
109 bool should_request = true;
111 for (size_t i = 0; i < old_requested_positions_count; i++) {
112 if (v3s32_equals(old_requested_positions[i], pos)) {
113 should_request = false;
119 request_position(pos);
121 requested_positions[requested_positions_count++] = pos;
125 if (old_requested_positions)
126 free(old_requested_positions);
128 old_requested_positions = requested_positions;
129 old_requested_positions_count = requested_positions_count;
132 // pthread start routine for sync thread
133 static void *sync_thread(unused void *arg)
135 while (! client_map.cancel)
142 // note: all these functions require the block mutex to be locked, which is always the case when a map callback is invoked
144 // callback for initializing a newly created block
145 // allocate and initialize extra data
146 static void on_create_block(MapBlock *block)
148 MapBlockExtraData *extra = block->extra = malloc(sizeof(MapBlockExtraData));
150 extra->state = MBS_RECIEVING;
151 extra->queue = false;
152 extra->last_synced = 0;
156 // callback for deleting a block
158 static void on_delete_block(MapBlock *block)
163 // callback for determining whether a block should be returned by map_get_block
164 // hold back blocks that have not been fully read from server yet when the create flag is set to true
165 static bool on_get_block(MapBlock *block, bool create)
167 return create || ((MapBlockExtraData *) block->extra)->state > MBS_RECIEVING;
172 // ClientMap singleton constructor
173 void client_map_init(Client *cli)
177 client_map.map = map_create((MapCallbacks) {
178 .create_block = &on_create_block,
179 .delete_block = &on_delete_block,
180 .get_block = &on_get_block,
182 .after_set_node = NULL,
184 client_map.queue = queue_create();
185 client_map.cancel = false;
186 client_map.meshgen_thread = client_map.sync_thread = 0;
187 client_map_set_simulation_distance(10);
190 // ClientMap singleton destructor
191 void client_map_deinit()
193 queue_delete(client_map.queue);
194 map_delete(client_map.map);
197 // start meshgen and sync threads
198 void client_map_start()
200 pthread_create(&client_map.meshgen_thread, NULL, &meshgen_thread, NULL);
201 pthread_create(&client_map.sync_thread, NULL, &sync_thread, NULL);
204 // stop meshgen and sync threads
205 void client_map_stop()
207 client_map.cancel = true;
209 if (client_map.meshgen_thread)
210 pthread_join(client_map.meshgen_thread, NULL);
212 if (client_map.sync_thread)
213 pthread_join(client_map.sync_thread, NULL);
216 // update simulation distance
217 void client_map_set_simulation_distance(u32 simulation_distance)
219 client_map.simulation_distance = simulation_distance;
220 client_map.blocks_count = facecache_count(simulation_distance);
223 // called when a block was actually recieved from server
224 void client_map_block_received(MapBlock *block)
226 pthread_mutex_lock(&block->mtx);
227 MapBlockExtraData *extra = block->extra;
228 if (extra->state == MBS_RECIEVING)
229 extra->state = MBS_FRESH;
230 pthread_mutex_unlock(&block->mtx);
232 schedule_update_block_mesh(block);
234 schedule_update_block_mesh(map_get_block(client_map.map, (v3s32) {block->pos.x + 1, block->pos.y + 0, block->pos.z + 0}, false));
235 schedule_update_block_mesh(map_get_block(client_map.map, (v3s32) {block->pos.x + 0, block->pos.y + 1, block->pos.z + 0}, false));
236 schedule_update_block_mesh(map_get_block(client_map.map, (v3s32) {block->pos.x + 0, block->pos.y + 0, block->pos.z + 1}, false));
237 schedule_update_block_mesh(map_get_block(client_map.map, (v3s32) {block->pos.x - 1, block->pos.y - 0, block->pos.z - 0}, false));
238 schedule_update_block_mesh(map_get_block(client_map.map, (v3s32) {block->pos.x - 0, block->pos.y - 1, block->pos.z - 0}, false));
239 schedule_update_block_mesh(map_get_block(client_map.map, (v3s32) {block->pos.x - 0, block->pos.y - 0, block->pos.z - 1}, false));