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