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