]> git.lizzy.rs Git - dragonblocks_alpha.git/blob - src/map.c
59097b86260f0167153ab1f0c6a4f7086b2c0f0a
[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/endian.h>
6 #include <string.h>
7 #include "map.h"
8 #include "util.h"
9
10 Map *map_create(MapCallbacks callbacks)
11 {
12         Map *map = malloc(sizeof(Map));
13         pthread_rwlock_init(&map->rwlck, NULL);
14         pthread_rwlock_init(&map->cached_rwlck, NULL);
15         map->sectors = bintree_create(sizeof(v2s32), NULL);
16         map->cached = NULL;
17         map->callbacks = callbacks;
18         return map;
19 }
20
21 static void free_block(BintreeNode *node, void *arg)
22 {
23         Map *map = arg;
24
25         if (map->callbacks.delete_block)
26                 map->callbacks.delete_block(node->value);
27
28         map_free_block(node->value);
29 }
30
31 static void free_sector(BintreeNode *node, void *arg)
32 {
33         MapSector *sector = node->value;
34
35         bintree_clear(&sector->blocks, &free_block, arg);
36         pthread_rwlock_destroy(&sector->rwlck);
37         free(sector);
38 }
39
40 void map_delete(Map *map)
41 {
42         pthread_rwlock_destroy(&map->rwlck);
43         pthread_rwlock_destroy(&map->cached_rwlck);
44         bintree_clear(&map->sectors, &free_sector, map);
45         free(map);
46 }
47
48 MapSector *map_get_sector(Map *map, v2s32 pos, bool create)
49 {
50         if (create)
51                 pthread_rwlock_wrlock(&map->rwlck);
52         else
53                 pthread_rwlock_rdlock(&map->rwlck);
54
55         BintreeNode **nodeptr = bintree_search(&map->sectors, &pos);
56
57         MapSector *sector = NULL;
58
59         if (*nodeptr) {
60                 sector = (*nodeptr)->value;
61         } else if (create) {
62                 sector = malloc(sizeof(MapSector));
63                 pthread_rwlock_init(&sector->rwlck, NULL);
64                 sector->pos = pos;
65                 sector->blocks = bintree_create(sizeof(s32), NULL);
66
67                 bintree_add_node(&map->sectors, nodeptr, &pos, sector);
68         }
69
70         pthread_rwlock_unlock(&map->rwlck);
71
72         return sector;
73 }
74
75 MapBlock *map_get_block(Map *map, v3s32 pos, bool create)
76 {
77         MapBlock *cached = NULL;
78
79         pthread_rwlock_rdlock(&map->cached_rwlck);
80         cached = map->cached;
81         pthread_rwlock_unlock(&map->cached_rwlck);
82
83         if (cached && v3s32_equals(cached->pos, pos))
84                 return cached;
85
86         MapSector *sector = map_get_sector(map, (v2s32) {pos.x, pos.z}, create);
87         if (! sector)
88                 return NULL;
89
90         if (create)
91                 pthread_rwlock_wrlock(&sector->rwlck);
92         else
93                 pthread_rwlock_rdlock(&sector->rwlck);
94
95         BintreeNode **nodeptr = bintree_search(&sector->blocks, &pos.y);
96
97         MapBlock *block = NULL;
98
99         if (*nodeptr) {
100                 block = (*nodeptr)->value;
101
102                 pthread_mutex_lock(&block->mtx);
103                 if (map->callbacks.get_block && ! map->callbacks.get_block(block, create)) {
104                         pthread_mutex_unlock(&block->mtx);
105                         block = NULL;
106                 } else {
107                         pthread_mutex_unlock(&block->mtx);
108                         pthread_rwlock_wrlock(&map->cached_rwlck);
109                         map->cached = block;
110                         pthread_rwlock_unlock(&map->cached_rwlck);
111                 }
112         } else if (create) {
113                 bintree_add_node(&sector->blocks, nodeptr, &pos.y, block = map_allocate_block(pos));
114
115                 if (map->callbacks.create_block)
116                         map->callbacks.create_block(block);
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->extra = NULL;
129         pthread_mutexattr_t attr;
130         pthread_mutexattr_init(&attr);
131         pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
132         pthread_mutex_init(&block->mtx, &attr);
133
134         ITERATE_MAPBLOCK block->data[x][y][z] = map_node_create(NODE_UNKNOWN, NULL, 0);
135
136         return block;
137 }
138
139 void map_free_block(MapBlock *block)
140 {
141         ITERATE_MAPBLOCK map_node_delete(block->data[x][y][z]);
142
143         pthread_mutex_destroy(&block->mtx);
144         free(block);
145 }
146
147 void map_serialize_block(MapBlock *block, char **dataptr, size_t *sizeptr, size_t *rawsizeptr)
148 {
149         unsigned char *uncompressed = NULL;
150         size_t uncompressed_size = 0;
151
152         ITERATE_MAPBLOCK {
153                 MapNode node = block->data[x][y][z];
154                 NodeDefinition *def = &node_definitions[node.type];
155
156                 u32 type = htobe32(node.type);
157                 buffer_write(&uncompressed, &uncompressed_size, &type, sizeof(u32));
158
159                 unsigned char *data_buffer = NULL;
160                 size_t data_bufsiz = 0;
161
162                 if (def->serialize)
163                         def->serialize(&node, &data_buffer, &data_bufsiz);
164
165                 u16 data_size = htobe16(data_bufsiz);
166                 buffer_write(&uncompressed, &uncompressed_size, &data_size, sizeof(u16));
167                 buffer_write(&uncompressed, &uncompressed_size, data_buffer, data_bufsiz);
168
169                 if (data_buffer)
170                         free(data_buffer);
171         }
172
173         my_compress(uncompressed, uncompressed_size, dataptr, sizeptr);
174         *rawsizeptr = uncompressed_size;
175
176         if (uncompressed)
177                 free(uncompressed);
178 }
179
180 bool map_deserialize_block(MapBlock *block, const char *data, size_t size, size_t rawsize)
181 {
182         unsigned char decompressed[rawsize];
183         size_t decompressed_size = rawsize;
184
185         if (! my_decompress(data, size, decompressed, decompressed_size))
186                 return false;
187
188         unsigned char *ptr = decompressed;
189
190         ITERATE_MAPBLOCK {
191                 // node type
192                 u32 *type_ptr = buffer_read(&ptr, &decompressed_size, sizeof(u32));
193
194                 if (! type_ptr)
195                         return false;
196
197                 u32 type = be32toh(*type_ptr);
198
199                 // data size
200                 u16 *data_size_ptr = buffer_read(&ptr, &decompressed_size, sizeof(u16));
201
202                 if (! data_size_ptr)
203                         return false;
204
205                 u16 data_size = be16toh(*data_size_ptr);
206
207                 // data
208                 void *data = buffer_read(&ptr, &decompressed_size, data_size);
209
210                 if (! data && data_size)
211                         return false;
212
213                 // set node
214                 block->data[x][y][z] = map_node_create(type, data, data_size);
215         }
216
217         return true;
218 }
219
220 v3s32 map_node_to_block_pos(v3s32 pos, v3u8 *offset)
221 {
222         if (offset)
223                 *offset = (v3u8) {(u32) pos.x % MAPBLOCK_SIZE, (u32) pos.y % MAPBLOCK_SIZE, (u32) pos.z % MAPBLOCK_SIZE};
224         return (v3s32) {floor((double) pos.x / (double) MAPBLOCK_SIZE), floor((double) pos.y / (double) MAPBLOCK_SIZE), floor((double) pos.z / (double) MAPBLOCK_SIZE)};
225 }
226
227 MapNode map_get_node(Map *map, v3s32 pos)
228 {
229         v3u8 offset;
230         v3s32 blockpos = map_node_to_block_pos(pos, &offset);
231         MapBlock *block = map_get_block(map, blockpos, false);
232         if (! block)
233                 return map_node_create(NODE_UNLOADED, NULL, 0);
234         return block->data[offset.x][offset.y][offset.z];
235 }
236
237 void map_set_node(Map *map, v3s32 pos, MapNode node, bool create, void *arg)
238 {
239         v3u8 offset;
240         MapBlock *block = map_get_block(map, map_node_to_block_pos(pos, &offset), create);
241         if (block) {
242                 pthread_mutex_lock(&block->mtx);
243                 if (! map->callbacks.set_node || map->callbacks.set_node(block, offset, &node, arg)) {
244                         block->data[offset.x][offset.y][offset.z] = node;
245                         if (map->callbacks.after_set_node)
246                                 map->callbacks.after_set_node(block, offset, arg);
247                 } else {
248                         map_node_delete(node);
249                 }
250                 pthread_mutex_unlock(&block->mtx);
251         }
252 }
253
254 MapNode map_node_create(Node type, void *data, size_t size)
255 {
256         if (type >= NODE_UNLOADED)
257                 type = NODE_UNKNOWN;
258
259         NodeDefinition *def = &node_definitions[type];
260
261         MapNode node;
262         node.type = type;
263         node.data = def->data_size ? malloc(def->data_size) : NULL;
264
265         if (def->create)
266                 def->create(&node);
267
268         if (def->deserialize && size)
269                 def->deserialize(&node, data, size);
270
271         return node;
272 }
273
274 void map_node_delete(MapNode node)
275 {
276         NodeDefinition *def = &node_definitions[node.type];
277
278         if (def->delete)
279                 def->delete(&node);
280
281         if (node.data)
282                 free(node.data);
283 }