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