]> git.lizzy.rs Git - dragonblocks_alpha.git/blob - src/map.c
ZLib compression
[dragonblocks_alpha.git] / src / map.c
1 #include <stdlib.h>
2 #include <stdbool.h>
3 #include <unistd.h>
4 #include <math.h>
5 #include <endian.h>
6 #include <zlib.h>
7 #include "map.h"
8 #include "util.h"
9
10 #define CMPBOUNDS(x) x == 0 ? 0 : x > 0 ? 1 : -1
11
12 static s8 sector_compare(void *hash, void *sector)
13 {
14         s64 d = *((u64 *) hash) - (*(MapSector **) sector)->hash;
15         return CMPBOUNDS(d);
16 }
17
18 static s8 block_compare(void *level, void *block)
19 {
20         s32 d = *((s32 *) level) - (*(MapBlock **) block)->pos.y;
21         return CMPBOUNDS(d);
22 }
23
24 Map *map_create()
25 {
26         Map *map = malloc(sizeof(Map));
27         pthread_rwlock_init(&map->rwlck, NULL);
28         map->sectors = array_create(sizeof(MapSector *));
29         map->sectors.cmp = &sector_compare;
30         return map;
31 }
32
33 void map_delete(Map *map, void (*callback)(void *extra))
34 {
35         pthread_rwlock_destroy(&map->rwlck);
36         for (size_t s = 0; s < map->sectors.siz; s++) {
37                 MapSector *sector = map_get_sector_raw(map, s);
38                 for (size_t b = 0; b < sector->blocks.siz; b++) {
39                         MapBlock *block = map_get_block_raw(sector, b);
40                         if (callback)
41                                 callback(block->extra);
42                         map_free_block(block);
43                 }
44                 if (sector->blocks.ptr)
45                         free(sector->blocks.ptr);
46                 pthread_rwlock_destroy(&sector->rwlck);
47                 free(sector);
48         }
49         if (map->sectors.ptr)
50                 free(map->sectors.ptr);
51         free(map);
52 }
53
54 MapSector *map_get_sector_raw(Map *map, size_t idx)
55 {
56         return ((MapSector **) map->sectors.ptr)[idx];
57 }
58
59 MapBlock *map_get_block_raw(MapSector *sector, size_t idx)
60 {
61         return ((MapBlock **) sector->blocks.ptr)[idx];
62 }
63
64 MapSector *map_get_sector(Map *map, v2s32 pos, bool create)
65 {
66         u64 hash = ((u64) pos.x << 32) + (u64) pos.y;
67
68         if (create)
69                 pthread_rwlock_wrlock(&map->rwlck);
70         else
71                 pthread_rwlock_rdlock(&map->rwlck);
72
73         ArraySearchResult res = array_search(&map->sectors, &hash);
74
75         MapSector *sector = NULL;
76
77         if (res.success) {
78                 sector = map_get_sector_raw(map, res.index);
79         } else if (create) {
80                 sector = malloc(sizeof(MapSector));
81                 pthread_rwlock_init(&sector->rwlck, NULL);
82                 sector->pos = pos;
83                 sector->hash = hash;
84                 sector->blocks = array_create(sizeof(MapBlock *));
85                 sector->blocks.cmp = &block_compare;
86
87                 array_insert(&map->sectors, &sector, res.index);
88         }
89
90         pthread_rwlock_unlock(&map->rwlck);
91
92         return sector;
93 }
94
95 MapBlock *map_get_block(Map *map, v3s32 pos, bool create)
96 {
97         MapSector *sector = map_get_sector(map, (v2s32) {pos.x, pos.z}, create);
98         if (! sector)
99                 return NULL;
100
101         if (create)
102                 pthread_rwlock_wrlock(&sector->rwlck);
103         else
104                 pthread_rwlock_rdlock(&sector->rwlck);
105
106         ArraySearchResult res = array_search(&sector->blocks, &pos.y);
107
108         MapBlock *block = NULL;
109
110         if (res.success) {
111                 block = map_get_block_raw(sector, res.index);
112                 if (block->state < MBS_READY && ! create)
113                         block = NULL;
114         } else if (create) {
115                 block = map_allocate_block(pos);
116                 array_insert(&sector->blocks, &block, res.index);
117         }
118
119         pthread_rwlock_unlock(&sector->rwlck);
120
121         return block;
122 }
123
124 MapBlock *map_allocate_block(v3s32 pos)
125 {
126         MapBlock *block = malloc(sizeof(MapBlock));
127         block->pos = pos;
128         block->state = MBS_CREATED;
129         block->extra = NULL;
130         pthread_mutex_init(&block->mtx, NULL);
131         return block;
132 }
133
134 void map_clear_meta(MapBlock *block)
135 {
136         pthread_mutex_lock(&block->mtx);
137         ITERATE_MAPBLOCK list_clear(&block->metadata[x][y][z]);
138         pthread_mutex_unlock(&block->mtx);
139 }
140
141 void map_free_block(MapBlock *block)
142 {
143         map_clear_meta(block);
144         pthread_mutex_destroy(&block->mtx);
145         free(block);
146 }
147
148 bool map_deserialize_node(int fd, MapNode *node)
149 {
150         Node type;
151
152         if (! read_u32(fd, &type))
153                 return false;
154
155         if (type >= NODE_UNLOADED)
156                 type = NODE_INVALID;
157
158         *node = map_node_create(type);
159
160         return true;
161 }
162
163 void map_serialize_block(MapBlock *block, char **dataptr, size_t *sizeptr)
164 {
165         size_t uncompressed_size = sizeof(MapBlockData);
166         char uncompressed_data[uncompressed_size];
167
168         MapBlockData blockdata;
169         ITERATE_MAPBLOCK {
170                 MapNode node = block->data[x][y][z];
171                 node.type = htobe32(node.type);
172                 blockdata[x][y][z] = node;
173         }
174         memcpy(uncompressed_data, blockdata, sizeof(MapBlockData));
175
176         char compressed_data[uncompressed_size];
177
178         z_stream stream;
179         stream.zalloc = Z_NULL;
180         stream.zfree = Z_NULL;
181         stream.opaque = Z_NULL;
182
183         stream.avail_in = stream.avail_out = uncompressed_size;
184     stream.next_in = (Bytef *) uncompressed_data;
185     stream.next_out = (Bytef *) compressed_data;
186
187     deflateInit(&stream, Z_BEST_COMPRESSION);
188     deflate(&stream, Z_FINISH);
189     deflateEnd(&stream);
190
191         size_t compressed_size = stream.total_out;
192
193         size_t size = sizeof(MapBlockHeader) + sizeof(MapBlockHeader) + compressed_size;
194         char *data = malloc(size);
195         *(MapBlockHeader *) data = htobe64(sizeof(MapBlockHeader) + compressed_size);
196         *(MapBlockHeader *) (data + sizeof(MapBlockHeader)) = htobe64(uncompressed_size);
197         memcpy(data + sizeof(MapBlockHeader) + sizeof(MapBlockHeader), compressed_data, compressed_size);
198
199         *sizeptr = size;
200         *dataptr = data;
201 }
202
203 bool map_deserialize_block(MapBlock *block, const char *data, size_t size)
204 {
205         if (size < sizeof(MapBlockHeader))
206                 return false;
207
208         MapBlockHeader uncompressed_size = be64toh(*(MapBlockHeader *) data);
209
210         if (uncompressed_size < sizeof(MapBlockData))
211                 return false;
212
213         char decompressed_data[uncompressed_size];
214
215         z_stream stream;
216         stream.zalloc = Z_NULL;
217     stream.zfree = Z_NULL;
218     stream.opaque = Z_NULL;
219
220         stream.avail_in = size - sizeof(u64);
221     stream.next_in = (Bytef *) (data + sizeof(u64));
222     stream.avail_out = uncompressed_size;
223     stream.next_out = (Bytef *) decompressed_data;
224
225     inflateInit(&stream);
226     inflate(&stream, Z_NO_FLUSH);
227     inflateEnd(&stream);
228
229     if (stream.total_out < uncompressed_size)
230                 return false;
231
232         MapBlockData blockdata;
233         memcpy(blockdata, decompressed_data, sizeof(MapBlockData));
234
235         ITERATE_MAPBLOCK {
236                 MapNode node = blockdata[x][y][z];
237                 node.type = be32toh(node.type);
238
239                 if (node.type >= NODE_UNLOADED)
240                         node.type = NODE_INVALID;
241
242                 block->data[x][y][z] = node;
243
244                 block->metadata[x][y][z] = list_create(&list_compare_string);
245         }
246
247         block->state = MBS_MODIFIED;
248
249         return true;
250 }
251
252 v3s32 map_node_to_block_pos(v3s32 pos, v3u8 *offset)
253 {
254         if (offset)
255                 *offset = (v3u8) {(u32) pos.x % 16, (u32) pos.y % 16, (u32) pos.z % 16};
256         return (v3s32) {floor((double) pos.x / 16.0), floor((double) pos.y / 16.0), floor((double) pos.z / 16.0)};
257 }
258
259 MapNode map_get_node(Map *map, v3s32 pos)
260 {
261         v3u8 offset;
262         v3s32 blockpos = map_node_to_block_pos(pos, &offset);
263         MapBlock *block = map_get_block(map, blockpos, false);
264         if (! block)
265                 return map_node_create(NODE_UNLOADED);
266         return block->data[offset.x][offset.y][offset.z];
267 }
268
269 void map_set_node(Map *map, v3s32 pos, MapNode node)
270 {
271         v3u8 offset;
272         MapBlock *block = map_get_block(map, map_node_to_block_pos(pos, &offset), false);
273         if (block) {
274                 pthread_mutex_lock(&block->mtx);
275                 block->state = MBS_MODIFIED;
276                 list_clear(&block->metadata[offset.x][offset.y][offset.z]);
277                 block->data[offset.x][offset.y][offset.z] = node;
278                 pthread_mutex_unlock(&block->mtx);
279         }
280 }
281
282 MapNode map_node_create(Node type)
283 {
284         return (MapNode) {type};
285 }