]> git.lizzy.rs Git - dragonblocks_alpha.git/blob - src/map.c
Big endian encoding for bulk block (de-)serialisation
[dragonblocks_alpha.git] / src / map.c
1 #include <stdlib.h>
2 #include <stdbool.h>
3 #include <unistd.h>
4 #include <stdio.h>
5 #include <math.h>
6 #include <endian.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 static MapBlock *allocate_block(v3s32 pos)
25 {
26         MapBlock *block = malloc(sizeof(MapBlock));
27         block->pos = pos;
28         block->state = MBS_CREATED;
29         block->extra = NULL;
30         pthread_mutex_init(&block->mtx, NULL);
31         return block;
32 }
33
34 Map *map_create()
35 {
36         Map *map = malloc(sizeof(Map));
37         map->sectors = array_create(sizeof(MapSector *));
38         map->sectors.cmp = &sector_compare;
39         return map;
40 }
41
42 void map_delete(Map *map)
43 {
44         for (size_t s = 0; s < map->sectors.siz; s++) {
45                 MapSector *sector = map_get_sector_raw(map, s);
46                 for (size_t b = 0; b < sector->blocks.siz; b++)
47                         map_free_block(map_get_block_raw(sector, b));
48                 if (sector->blocks.ptr)
49                         free(sector->blocks.ptr);
50                 free(sector);
51         }
52         if (map->sectors.ptr)
53                 free(map->sectors.ptr);
54         free(map);
55 }
56
57 MapSector *map_get_sector_raw(Map *map, size_t idx)
58 {
59         return ((MapSector **) map->sectors.ptr)[idx];
60 }
61
62 MapBlock *map_get_block_raw(MapSector *sector, size_t idx)
63 {
64         return ((MapBlock **) sector->blocks.ptr)[idx];
65 }
66
67 MapSector *map_get_sector(Map *map, v2s32 pos, bool create)
68 {
69         u64 hash = ((u64) pos.x << 32) + (u64) pos.y;
70         ArraySearchResult res = array_search(&map->sectors, &hash);
71
72         if (res.success)
73                 return map_get_sector_raw(map, res.index);
74         if (! create)
75                 return NULL;
76
77         MapSector *sector = malloc(sizeof(MapSector));
78         sector->pos = pos;
79         sector->hash = hash;
80         sector->blocks = array_create(sizeof(MapBlock *));
81         sector->blocks.cmp = &block_compare;
82
83         array_insert(&map->sectors, &sector, res.index);
84
85         return sector;
86 }
87
88 MapBlock *map_get_block(Map *map, v3s32 pos, bool create)
89 {
90         MapSector *sector = map_get_sector(map, (v2s32) {pos.x, pos.z}, create);
91         if (! sector)
92                 return NULL;
93
94         ArraySearchResult res = array_search(&sector->blocks, &pos.y);
95
96         MapBlock *block = NULL;
97
98         if (res.success) {
99                 block = map_get_block_raw(sector, res.index);
100         } else if (create) {
101                 block = allocate_block(pos);
102                 array_insert(&sector->blocks, &block, res.index);
103         } else {
104                 return NULL;
105         }
106
107         return (create || block->state == MBS_READY) ? block : NULL;
108 }
109
110 void map_free_block(MapBlock *block)
111 {
112         ITERATE_MAPBLOCK list_clear(&block->metadata[x][y][z]);
113         pthread_mutex_destroy(&block->mtx);
114         free(block);
115 }
116
117 bool map_deserialize_node(int fd, MapNode *node)
118 {
119         Node type;
120
121         if (! read_u32(fd, &type))
122                 return false;
123
124         if (type >= NODE_UNLOADED)
125                 type = NODE_INVALID;
126
127         *node = map_node_create(type);
128
129         return true;
130 }
131
132 bool map_serialize_block(FILE *file, MapBlock *block)
133 {
134         s32 pos[3] = {
135                 htobe32(block->pos.x),
136                 htobe32(block->pos.y),
137                 htobe32(block->pos.z),
138         };
139         if (fwrite(pos, 1, sizeof(pos), file) != sizeof(pos))
140                 return false;
141
142         MapBlockData data;
143
144         ITERATE_MAPBLOCK {
145                 MapNode node = block->data[x][y][z];
146                 node.type = htobe32(node.type);
147                 data[x][y][z] = node;
148         }
149
150         if (fwrite(data, 1, sizeof(MapBlockData), file) !=  sizeof(MapBlockData))
151                 perror("fwrite");
152         else
153                 return true;
154
155         return false;
156 }
157
158 bool map_deserialize_block(int fd, Map *map, MapBlock **blockptr, bool dummy)
159 {
160         v3s32 pos;
161
162         if (! read_v3s32(fd, &pos))
163                 return false;
164
165         MapSector *sector = map_get_sector(map, (v2s32) {pos.x, pos.z}, true);
166         ArraySearchResult res = array_search(&sector->blocks, &pos.y);
167
168         MapBlock *block;
169
170         if (dummy) {
171                 block = allocate_block(pos);
172         } else if (res.success) {
173                 block = map_get_block_raw(sector, res.index);
174         } else {
175                 block = allocate_block(pos);
176                 array_insert(&sector->blocks, &block, res.index);
177         }
178
179         bool ret = true;
180         size_t n_read_total = 0;
181         int n_read;
182         MapBlockData data;
183
184         while (n_read_total < sizeof(MapBlockData)) {
185                 if ((n_read = read(fd, (char *) data + n_read_total, sizeof(MapBlockData) - n_read_total)) == -1) {
186                         perror("read");
187                         ret = false;
188                         break;
189                 }
190
191                 n_read_total += n_read;
192         }
193
194         if (ret) {
195                 ITERATE_MAPBLOCK {
196                         MapNode node = data[x][y][z];
197                         node.type = be32toh(node.type);
198
199                         if (node.type >= NODE_UNLOADED)
200                                 node.type = NODE_INVALID;
201
202                         block->data[x][y][z] = node;
203                         block->metadata[x][y][z] = list_create(&list_compare_string);
204                 }
205         }
206
207         // ToDo: Deserialize meta
208
209         if (dummy)
210                 map_free_block(block);
211         else if (blockptr && ret)
212                 *blockptr = block;
213
214         return ret;
215 }
216
217 bool map_serialize(FILE *file, Map *map)
218 {
219         for (size_t s = 0; s < map->sectors.siz; s++) {
220                 MapSector *sector = map_get_sector_raw(map, s);
221                 for (size_t b = 0; b < sector->blocks.siz; b++)
222                         if (! map_serialize_block(file, map_get_block_raw(sector, b)))
223                                 return true;
224         }
225         return true;
226 }
227
228 void map_deserialize(int fd, Map *map)
229 {
230         while (map_deserialize_block(fd, map, NULL, false))
231                 ;
232 }
233
234 v3s32 map_node_to_block_pos(v3s32 pos, v3u8 *offset)
235 {
236         if (offset)
237                 *offset = (v3u8) {(u32) pos.x % 16, (u32) pos.y % 16, (u32) pos.z % 16};
238         return (v3s32) {floor((double) pos.x / 16.0), floor((double) pos.y / 16.0), floor((double) pos.z / 16.0)};
239 }
240
241 MapNode map_get_node(Map *map, v3s32 pos)
242 {
243         v3u8 offset;
244         v3s32 blockpos = map_node_to_block_pos(pos, &offset);
245         MapBlock *block = map_get_block(map, blockpos, false);
246         if (! block)
247                 return map_node_create(NODE_UNLOADED);
248         return block->data[offset.x][offset.y][offset.z];
249 }
250
251 void map_set_node(Map *map, v3s32 pos, MapNode node)
252 {
253         v3u8 offset;
254         MapBlock *block = map_get_block(map, map_node_to_block_pos(pos, &offset), false);
255         if (block) {
256                 list_clear(&block->metadata[offset.x][offset.y][offset.z]);
257                 block->data[offset.x][offset.y][offset.z] = node;
258
259                 block->state = MBS_MODIFIED;
260         }
261 }
262
263 MapNode map_node_create(Node type)
264 {
265         return (MapNode) {type};
266 }