]> git.lizzy.rs Git - dragonblocks_alpha.git/blob - src/client/client_map.c
Update meshgen threads to use queue waiting
[dragonblocks_alpha.git] / src / client / client_map.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include "client/blockmesh.h"
4 #include "client/facecache.h"
5 #include "client/client_map.h"
6 #include "client/client_player.h"
7 #include "client/debug_menu.h"
8 #include "util.h"
9 #define MAX_BLOCK_REQUESTS 4
10
11 struct ClientMap client_map;
12
13 // meshgen functions
14
15 // dequeue callback to thread-safely update
16 static void set_dequeued(MapBlock *block)
17 {
18         pthread_mutex_lock(&block->mtx);
19         ((MapBlockExtraData *) block->extra)->queue = false;
20         pthread_mutex_unlock(&block->mtx);
21 }
22
23 // mesh generator step
24 static void meshgen_step()
25 {
26         MapBlock *block = queue_dequeue_callback(client_map.queue, (void *) &set_dequeued);
27
28         if (block)
29                 blockmesh_make(block);
30 }
31
32 // pthread start routine for meshgen thread
33 static void *meshgen_thread(unused void *arg)
34 {
35         while (! client_map.cancel)
36                 meshgen_step();
37
38         return NULL;
39 }
40
41 // sync functions
42
43 // send block request command to server
44 static void request_position(v3s32 pos)
45 {
46         dragonnet_peer_send_ToServerRequestBlock(client, &(ToServerRequestBlock) {
47                 .pos = pos
48         });
49 }
50
51 // mapblock synchronisation step
52 static void sync_step()
53 {
54         static u64 tick = 0;
55         static v3s32 *old_requested_positions = NULL;
56         static size_t old_requested_positions_count = 0;
57
58         u64 last_tick = tick++;
59
60         v3f64 player_pos = client_player_get_position();
61         v3s32 center = map_node_to_block_pos((v3s32) {player_pos.x, player_pos.y, player_pos.z}, NULL);
62
63         v3s32 *requested_positions = malloc(sizeof(v3s32) * MAX_BLOCK_REQUESTS);
64         size_t requested_positions_count = 0;
65
66         for (size_t i = 0; i < client_map.blocks_count; i++) {
67                 v3s32 pos = facecache_face(i, &center);
68                 MapBlock *block = map_get_block(client_map.map, pos, false);
69
70                 if (block) {
71                         pthread_mutex_lock(&block->mtx);
72                         MapBlockExtraData *extra = block->extra;
73
74                         switch (extra->state) {
75                                 case MBS_READY:
76                                         if (extra->last_synced < last_tick)
77                                                 request_position(pos);
78                                         fallthrough;
79
80                                 case MBS_FRESH:
81                                         extra->state = MBS_READY;
82                                         extra->last_synced = tick;
83                                         break;
84
85                                 case MBS_RECIEVING:
86                                         break;
87                         }
88                         pthread_mutex_unlock(&block->mtx);
89                 } else if (requested_positions_count < MAX_BLOCK_REQUESTS) {
90                         bool should_request = true;
91
92                         for (size_t i = 0; i < old_requested_positions_count; i++) {
93                                 if (v3s32_equals(old_requested_positions[i], pos)) {
94                                         should_request = false;
95                                         break;
96                                 }
97                         }
98
99                         if (should_request)
100                                 request_position(pos);
101
102                         requested_positions[requested_positions_count++] = pos;
103                 }
104         }
105
106         if (old_requested_positions)
107                 free(old_requested_positions);
108
109         old_requested_positions = requested_positions;
110         old_requested_positions_count = requested_positions_count;
111 }
112
113 // pthread start routine for sync thread
114 static void *sync_thread(unused void *arg)
115 {
116         while (! client_map.cancel)
117                 sync_step();
118
119         return NULL;
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 // allocate and initialize extra data
127 static void on_create_block(MapBlock *block)
128 {
129         MapBlockExtraData *extra = block->extra = malloc(sizeof(MapBlockExtraData));
130
131         extra->state = MBS_RECIEVING;
132         extra->queue = false;
133         extra->last_synced = 0;
134         extra->obj = NULL;
135 }
136
137 // callback for deleting a block
138 // free extra data
139 static void on_delete_block(MapBlock *block)
140 {
141         free(block->extra);
142 }
143
144 // callback for determining whether a block should be returned by map_get_block
145 // hold back blocks that have not been fully read from server yet when the create flag is set to true
146 static bool on_get_block(MapBlock *block, bool create)
147 {
148         return create || ((MapBlockExtraData *) block->extra)->state > MBS_RECIEVING;
149 }
150
151 // public functions
152
153 // ClientMap singleton constructor
154 void client_map_init()
155 {
156         client_map.map = map_create((MapCallbacks) {
157                 .create_block = &on_create_block,
158                 .delete_block = &on_delete_block,
159                 .get_block = &on_get_block,
160                 .set_node = NULL,
161                 .after_set_node = NULL,
162         });
163         client_map.queue = queue_create();
164         client_map.cancel = false;
165         client_map.sync_thread = 0;
166         client_map_set_simulation_distance(10);
167
168         for (int i = 0; i < NUM_MESHGEN_THREADS; i++)
169                 client_map.meshgen_threads[i] = 0;
170 }
171
172 // ClientMap singleton destructor
173 void client_map_deinit()
174 {
175         queue_delete(client_map.queue);
176         map_delete(client_map.map);
177 }
178
179 // start meshgen and sync threads
180 void client_map_start()
181 {
182         for (int i = 0; i < NUM_MESHGEN_THREADS; i++)
183                 pthread_create(&client_map.meshgen_threads[i], NULL, &meshgen_thread, NULL);
184
185         pthread_create(&client_map.sync_thread, NULL, &sync_thread, NULL);
186 }
187
188 // stop meshgen and sync threads
189 void client_map_stop()
190 {
191         client_map.cancel = true;
192         queue_cancel(client_map.queue);
193
194         for (int i = 0; i < NUM_MESHGEN_THREADS; i++)
195                 if (client_map.meshgen_threads[i])
196                         pthread_join(client_map.meshgen_threads[i], NULL);
197
198         if (client_map.sync_thread)
199                 pthread_join(client_map.sync_thread, NULL);
200 }
201
202 // update simulation distance
203 void client_map_set_simulation_distance(u32 simulation_distance)
204 {
205         client_map.simulation_distance = simulation_distance;
206         client_map.blocks_count = facecache_count(simulation_distance);
207 }
208
209 // called when a block was actually recieved from server
210 void client_map_block_received(MapBlock *block)
211 {
212         pthread_mutex_lock(&block->mtx);
213         MapBlockExtraData *extra = block->extra;
214         if (extra->state == MBS_RECIEVING)
215                 extra->state = MBS_FRESH;
216         pthread_mutex_unlock(&block->mtx);
217
218         client_map_schedule_update_block_mesh(block);
219
220         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));
221         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));
222         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));
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 }
227
228 // enqueue block to mesh update queue
229 void client_map_schedule_update_block_mesh(MapBlock *block)
230 {
231         if (! block)
232                 return;
233
234         pthread_mutex_lock(&block->mtx);
235         MapBlockExtraData *extra = block->extra;
236         if (! extra->queue) {
237                 extra->queue = true;
238                 queue_enqueue(client_map.queue, block);
239         }
240         pthread_mutex_unlock(&block->mtx);
241 }