]> git.lizzy.rs Git - dragonblocks_alpha.git/blob - src/server/server_map.c
Add oceans and toggle keys for flight & collision
[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/database.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         database_save_block(server->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, unused void *value, 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, unused void *value, 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 (! database_load_block(server->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                         extra->mgs_buffer[x][y][z] = MGS_VOID;
125                 }
126         }
127 }
128
129 // callback for deleting a block
130 // free extra data
131 static void on_delete_block(MapBlock *block)
132 {
133         MapBlockExtraData *extra = block->extra;
134
135         if (extra->data)
136                 free(extra->data);
137
138         free(extra);
139 }
140
141 // callback for determining whether a block should be returned by map_get_block
142 // hold back blocks that are not fully generated except when the create flag is set to true
143 static bool on_get_block(MapBlock *block, bool create)
144 {
145         MapBlockExtraData *extra = block->extra;
146
147         if (extra->state < MBS_READY && ! create)
148                 return false;
149
150         return true;
151 }
152
153 // callback for deciding whether a set_node call succeeds or not
154 // 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
155 static bool on_set_node(MapBlock *block, v3u8 offset, unused MapNode *node, void *arg)
156 {
157         MapgenSetNodeArg *msn_arg = arg;
158
159         MapgenStage mgs;
160
161         if (msn_arg)
162                 mgs = msn_arg->mgs;
163         else
164                 mgs = MGS_PLAYER;
165
166         MapgenStage *old_mgs = &((MapBlockExtraData *) block->extra)->mgs_buffer[offset.x][offset.y][offset.z];
167
168         if (mgs >= *old_mgs) {
169                 *old_mgs = mgs;
170
171                 if (msn_arg)
172                         list_put(msn_arg->changed_blocks, block, NULL);
173
174                 return true;
175         }
176
177         return false;
178 }
179
180 // callback for when a block changes
181 // send block to near clients if not part of map generation
182 static void on_after_set_node(MapBlock *block, unused v3u8 offset, void *arg)
183 {
184         if (! arg)
185                 send_block_to_near(block);
186 }
187
188 // public functions
189
190 // ServerMap singleton constructor
191 void server_map_init(Server *srv)
192 {
193         server = srv;
194
195         server_map.map = map_create((MapCallbacks) {
196                 .create_block = &on_create_block,
197                 .delete_block = &on_delete_block,
198                 .get_block = &on_get_block,
199                 .set_node = &on_set_node,
200                 .after_set_node = &on_after_set_node,
201         });
202         server_map.shutting_down = false;
203         server_map.mapgen_threads = list_create(NULL);
204         pthread_mutex_init(&server_map.mapgen_threads_mtx, NULL);
205 }
206
207 // ServerMap singleton destructor
208 void server_map_deinit()
209 {
210         server_map.shutting_down = true;
211
212         pthread_mutex_lock(&server_map.mapgen_threads_mtx);
213         list_clear_func(&server_map.mapgen_threads, &list_join_thread, NULL);
214         pthread_mutex_destroy(&server_map.mapgen_threads_mtx);
215
216         map_delete(server_map.map);
217 }
218
219 // handle block request from client (thread safe)
220 void server_map_requested_block(Client *client, v3s32 pos)
221 {
222         if (within_simulation_distance(client->pos, pos, server->config.simulation_distance)) {
223                 MapBlock *block = map_get_block(server_map.map, pos, true);
224
225                 pthread_mutex_lock(&block->mtx);
226                 MapBlockExtraData *extra = block->extra;
227
228                 switch (extra->state) {
229                         case MBS_CREATED:
230                                 launch_mapgen_thread(block);
231                                 break;
232
233                         case MBS_GENERATING:
234                                 break;
235
236                         case MBS_READY:
237                                 send_block(client, block);
238                 };
239                 pthread_mutex_unlock(&block->mtx);
240         }
241 }