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