]> git.lizzy.rs Git - dragonblocks_alpha.git/blob - src/map.c
Append .zip to ZIP files generated by release script
[dragonblocks_alpha.git] / src / map.c
1 #include <stdlib.h>
2 #include <stdbool.h>
3 #include <math.h>
4 #include "map.h"
5 #include "util.h"
6
7 #define CMPBOUNDS(x) x == 0 ? 0 : x > 0 ? 1 : -1
8
9 static s8 sector_compare(void *hash, void *sector)
10 {
11         s64 d = *((u64 *) hash) - (*(MapSector **) sector)->hash;
12         return CMPBOUNDS(d);
13 }
14
15 static s8 block_compare(void *level, void *block)
16 {
17         s32 d = *((s32 *) level) - (*(MapBlock **) block)->pos.y;
18         return CMPBOUNDS(d);
19 }
20
21 static MapBlock *allocate_block(v3s32 pos)
22 {
23         MapBlock *block = malloc(sizeof(MapBlock));
24         block->pos = pos;
25         block->ready = false;
26         block->extra = NULL;
27         return block;
28 }
29
30 Map *map_create()
31 {
32         Map *map = malloc(sizeof(Map));
33         map->sectors = array_create(sizeof(MapSector *));
34         map->sectors.cmp = &sector_compare;
35         return map;
36 }
37
38 void map_delete(Map *map)
39 {
40         for (size_t s = 0; s < map->sectors.siz; s++) {
41                 MapSector *sector = map_get_sector_raw(map, s);
42                 for (size_t b = 0; b < sector->blocks.siz; b++)
43                         map_free_block(map_get_block_raw(sector, b));
44                 if (sector->blocks.ptr)
45                         free(sector->blocks.ptr);
46                 free(sector);
47         }
48         if (map->sectors.ptr)
49                 free(map->sectors.ptr);
50         free(map);
51 }
52
53 MapSector *map_get_sector_raw(Map *map, size_t idx)
54 {
55         return ((MapSector **) map->sectors.ptr)[idx];
56 }
57
58 MapBlock *map_get_block_raw(MapSector *sector, size_t idx)
59 {
60         return ((MapBlock **) sector->blocks.ptr)[idx];
61 }
62
63 MapSector *map_get_sector(Map *map, v2s32 pos, bool create)
64 {
65         u64 hash = ((u64) pos.x << 32) + (u64) pos.y;
66         ArraySearchResult res = array_search(&map->sectors, &hash);
67
68         if (res.success)
69                 return map_get_sector_raw(map, res.index);
70         if (! create)
71                 return NULL;
72
73         MapSector *sector = malloc(sizeof(MapSector));
74         sector->pos = pos;
75         sector->hash = hash;
76         sector->blocks = array_create(sizeof(MapBlock *));
77         sector->blocks.cmp = &block_compare;
78
79         array_insert(&map->sectors, &sector, res.index);
80
81         return sector;
82 }
83
84 MapBlock *map_get_block(Map *map, v3s32 pos, bool create)
85 {
86         MapSector *sector = map_get_sector(map, (v2s32) {pos.x, pos.z}, create);
87         if (! sector)
88                 return NULL;
89
90         ArraySearchResult res = array_search(&sector->blocks, &pos.y);
91
92         MapBlock *block = NULL;
93
94         if (res.success) {
95                 block = map_get_block_raw(sector, res.index);
96         } else if (create) {
97                 block = allocate_block(pos);
98                 array_insert(&sector->blocks, &block, res.index);
99
100                 if (map->on_block_create)
101                         map->on_block_create(block);
102         } else {
103                 return NULL;
104         }
105
106         return block->ready ? block : NULL;
107 }
108
109 void map_free_block(MapBlock *block)
110 {
111         ITERATE_MAPBLOCK map_node_clear(&block->data[x][y][z]);
112         free(block);
113 }
114
115 bool map_deserialize_node(int fd, MapNode *node)
116 {
117         Node type;
118
119         if (! read_u32(fd, &type))
120                 return false;
121
122         if (type >= NODE_UNLOADED)
123                 type = NODE_INVALID;
124
125         *node = map_node_create(type);
126
127         return true;
128 }
129
130 bool map_serialize_block(int fd, MapBlock *block)
131 {
132         if (! write_v3s32(fd, block->pos))
133                 return false;
134
135         ITERATE_MAPBLOCK {
136                 if (! write_u32(fd, block->data[x][y][z].type))
137                         return false;
138         }
139
140         return true;
141 }
142
143 bool map_deserialize_block(int fd, Map *map, bool dummy)
144 {
145         v3s32 pos;
146
147         if (! read_v3s32(fd, &pos))
148                 return false;
149
150         MapSector *sector = map_get_sector(map, (v2s32) {pos.x, pos.z}, true);
151         ArraySearchResult res = array_search(&sector->blocks, &pos.y);
152
153         MapBlock *block;
154
155         if (dummy) {
156                 block = allocate_block(pos);
157         } else if (res.success) {
158                 block = map_get_block_raw(sector, res.index);
159         } else {
160                 block = allocate_block(pos);
161                 array_insert(&sector->blocks, &block, res.index);
162         }
163
164         ITERATE_MAPBLOCK {
165                 if (! map_deserialize_node(fd, &block->data[x][y][z]))
166                         return false;
167         }
168
169         if (dummy) {
170                 map_free_block(block);
171         } else {
172                 block->ready = true;
173
174                 if (map->on_block_add)
175                         map->on_block_add(block);
176         }
177
178         return true;
179 }
180
181 bool map_serialize(int fd, Map *map)
182 {
183         for (size_t s = 0; s < map->sectors.siz; s++) {
184                 MapSector *sector = map_get_sector_raw(map, s);
185                 for (size_t b = 0; b < sector->blocks.siz; b++)
186                         if (! map_serialize_block(fd, map_get_block_raw(sector, b)))
187                                 return false;
188         }
189         return true;
190 }
191
192 void map_deserialize(int fd, Map *map)
193 {
194         while (map_deserialize_block(fd, map, false))
195                 ;
196 }
197
198 v3s32 map_node_to_block_pos(v3s32 pos, v3u8 *offset)
199 {
200         if (offset)
201                 *offset = (v3u8) {(u32) pos.x % 16, (u32) pos.y % 16, (u32) pos.z % 16};
202         return (v3s32) {floor((double) pos.x / 16.0), floor((double) pos.y / 16.0), floor((double) pos.z / 16.0)};
203 }
204
205 MapNode map_get_node(Map *map, v3s32 pos)
206 {
207         v3u8 offset;
208         v3s32 blockpos = map_node_to_block_pos(pos, &offset);
209         MapBlock *block = map_get_block(map, blockpos, false);
210         if (! block)
211                 return map_node_create(NODE_UNLOADED);
212         return block->data[offset.x][offset.y][offset.z];
213 }
214
215 void map_set_node(Map *map, v3s32 pos, MapNode node)
216 {
217         v3u8 offset;
218         MapBlock *block = map_get_block(map, map_node_to_block_pos(pos, &offset), false);
219         if (block) {
220                 MapNode *current_node = &block->data[offset.x][offset.y][offset.z];
221                 map_node_clear(current_node);
222                 *current_node = node;
223         }
224 }
225
226 MapNode map_node_create(Node type)
227 {
228         return (MapNode) {type, list_create(&list_compare_string)};
229 }
230
231 void map_node_clear(MapNode *node)
232 {
233         list_clear(&node->meta);
234 }