]> git.lizzy.rs Git - dragonblocks_alpha.git/blob - src/server/server_map.c
f9b65214164eef35bb7a617fe2e66a58b721d156
[dragonblocks_alpha.git] / src / server / server_map.c
1 #include <stdlib.h>
2 #include <string.h>
3 #include <unistd.h>
4 #include <stdio.h>
5 #include "server/mapdb.h"
6 #include "server/mapgen.h"
7 #include "server/server_map.h"
8 #include "map.h"
9 #include "util.h"
10
11 struct ServerMap server_map;
12 static Server *server;
13
14 // utility functions
15
16 // send a block to a client and reset block request
17 static void send_block(Client *client, MapBlock *block)
18 {
19         MapBlockExtraData *extra = block->extra;
20
21         pthread_mutex_lock(&client->mtx);
22         if (client->state == CS_ACTIVE)
23                 (void) (write_u32(client->fd, CC_BLOCK) && write_v3s32(client->fd, block->pos) && write_u64(client->fd, extra->size) && write(client->fd, extra->data, extra->size) != -1);
24         pthread_mutex_unlock(&client->mtx);
25 }
26
27 // send block to near clients
28 // block mutex has to be locked
29 static void send_block_to_near(MapBlock *block)
30 {
31         MapBlockExtraData *extra = block->extra;
32
33         if (extra->state == MBS_GENERATING)
34                 return;
35
36         map_serialize_block(block, &extra->data, &extra->size);
37         mapdb_save_block(server_map.db, block);
38
39         if (extra->state == MBS_CREATED)
40                 return;
41
42         pthread_rwlock_rdlock(&server->players_rwlck);
43         ITERATE_LIST(&server->players, pair) {
44                 Client *client = pair->value;
45
46                 if (within_simulation_distance(client->pos, block->pos, server->config.simulation_distance))
47                         send_block(client, block);
48         }
49         pthread_rwlock_unlock(&server->players_rwlck);
50 }
51
52 // list_clear_func callback for sending changed blocks to near clients
53 static void list_send_block(void *key, __attribute__((unused)) void *value, __attribute__((unused)) void *arg)
54 {
55         MapBlock *block = key;
56
57         pthread_mutex_lock(&block->mtx);
58         send_block_to_near(block);
59         pthread_mutex_unlock(&block->mtx);
60 }
61
62 // pthread start routine for mapgen thread
63 static void *mapgen_thread(void *arg)
64 {
65         MapBlock *block = arg;
66         MapBlockExtraData *extra = block->extra;
67
68         pthread_mutex_lock(&block->mtx);
69         extra->state = MBS_GENERATING;
70         pthread_mutex_unlock(&block->mtx);
71
72         List changed_blocks = list_create(NULL);
73         list_put(&changed_blocks, block, NULL);
74
75         mapgen_generate_block(block, &changed_blocks);
76
77         pthread_mutex_lock(&block->mtx);
78         extra->state = MBS_READY;
79         pthread_mutex_unlock(&block->mtx);
80
81         list_clear_func(&changed_blocks, &list_send_block, NULL);
82
83         if (! server_map.shutting_down) {
84                 pthread_mutex_lock(&server_map.mapgen_threads_mtx);
85                 list_delete(&server_map.mapgen_threads, &extra->mapgen_thread);
86                 pthread_mutex_unlock(&server_map.mapgen_threads_mtx);
87         }
88
89         return NULL;
90 }
91
92 // launch mapgen thread for block
93 // block mutex has to be locked
94 static void launch_mapgen_thread(MapBlock *block)
95 {
96         pthread_mutex_lock(&server_map.mapgen_threads_mtx);
97         pthread_t *thread_ptr = &((MapBlockExtraData *) block->extra)->mapgen_thread;
98         pthread_create(thread_ptr, NULL, mapgen_thread, block);
99         list_put(&server_map.mapgen_threads, thread_ptr, NULL);
100         pthread_mutex_unlock(&server_map.mapgen_threads_mtx);
101 }
102
103 // list_clear_func callback used to join running generator threads on shutdown
104 static void list_join_thread(void *key, __attribute__((unused)) void *value, __attribute__((unused)) void *arg)
105 {
106         pthread_join(*(pthread_t *) key, NULL);
107 }
108
109 // map callbacks
110 // note: all these functions require the block mutex to be locked, which is always the case when a map callback is invoked
111
112 // callback for initializing a newly created block
113 // load block from database or initialize state, mgstage buffer and data
114 static void on_create_block(MapBlock *block)
115 {
116         MapBlockExtraData *extra = block->extra = malloc(sizeof(MapBlockExtraData));
117
118         if (! mapdb_load_block(server_map.db, block)) {
119                 extra->state = MBS_CREATED;
120                 extra->data = NULL;
121
122                 ITERATE_MAPBLOCK {
123                         block->data[x][y][z] = map_node_create(NODE_AIR);
124                         block->metadata[x][y][z] = list_create(&list_compare_string);
125                         extra->mgs_buffer[x][y][z] = MGS_VOID;
126                 }
127         }
128 }
129
130 // callback for deleting a block
131 // free extra data
132 static void on_delete_block(MapBlock *block)
133 {
134         MapBlockExtraData *extra = block->extra;
135
136         if (extra->data)
137                 free(extra->data);
138
139         free(extra);
140 }
141
142 // callback for determining whether a block should be returned by map_get_block
143 // hold back blocks that are not fully generated except when the create flag is set to true
144 static bool on_get_block(MapBlock *block, bool create)
145 {
146         MapBlockExtraData *extra = block->extra;
147
148         if (extra->state < MBS_READY && ! create)
149                 return false;
150
151         return true;
152 }
153
154 // callback for deciding whether a set_node call succeeds or not
155 // reject set_node calls that try to override nodes placed by later mapgen stages, else update mgs buffer - also make sure block is inserted into changed blocks list
156 static bool on_set_node(MapBlock *block, v3u8 offset, __attribute__((unused)) MapNode *node, void *arg)
157 {
158         MapgenSetNodeArg *msn_arg = arg;
159
160         MapgenStage mgs;
161
162         if (msn_arg)
163                 mgs = msn_arg->mgs;
164         else
165                 mgs = MGS_PLAYER;
166
167         MapgenStage *old_mgs = &((MapBlockExtraData *) block->extra)->mgs_buffer[offset.x][offset.y][offset.z];
168
169         if (mgs >= *old_mgs) {
170                 *old_mgs = mgs;
171
172                 if (msn_arg)
173                         list_put(msn_arg->changed_blocks, block, NULL);
174
175                 return true;
176         }
177
178         return false;
179 }
180
181 // callback for when a block changes
182 // send block to near clients if not part of map generation
183 static void on_after_set_node(MapBlock *block, __attribute__((unused)) v3u8 offset, void *arg)
184 {
185         if (! arg)
186                 send_block_to_near(block);
187 }
188
189 // public functions
190
191 // ServerMap singleton constructor
192 void server_map_init(Server *srv)
193 {
194         server = srv;
195
196         server_map.map = map_create((MapCallbacks) {
197                 .create_block = &on_create_block,
198                 .delete_block = &on_delete_block,
199                 .get_block = &on_get_block,
200                 .set_node = &on_set_node,
201                 .after_set_node = &on_after_set_node,
202         });
203         server_map.shutting_down = false;
204         server_map.db = mapdb_open("map.sqlite");
205         server_map.mapgen_threads = list_create(NULL);
206         pthread_mutex_init(&server_map.mapgen_threads_mtx, NULL);
207 }
208
209 // ServerMap singleton destructor
210 void server_map_deinit()
211 {
212         server_map.shutting_down = true;
213
214         pthread_mutex_lock(&server_map.mapgen_threads_mtx);
215         list_clear_func(&server_map.mapgen_threads, &list_join_thread, NULL);
216         // pthread_mutex_unlock(&server_map.mapgen_threads_mtx);
217         pthread_mutex_destroy(&server_map.mapgen_threads_mtx);
218
219         sqlite3_close(server_map.db);
220         map_delete(server_map.map);
221 }
222
223 // handle block request from client (thread safe)
224 void server_map_requested_block(Client *client, v3s32 pos)
225 {
226         if (within_simulation_distance(client->pos, pos, server->config.simulation_distance)) {
227                 MapBlock *block = map_get_block(server_map.map, pos, true);
228
229                 pthread_mutex_lock(&block->mtx);
230                 MapBlockExtraData *extra = block->extra;
231
232                 switch (extra->state) {
233                         case MBS_CREATED:
234                                 launch_mapgen_thread(block);
235                                 break;
236
237                         case MBS_GENERATING:
238                                 break;
239
240                         case MBS_READY:
241                                 send_block(client, block);
242                 };
243                 pthread_mutex_unlock(&block->mtx);
244         }
245 }