]> git.lizzy.rs Git - dragonblocks_alpha.git/blob - src/map.c
8c75756e79e713d752d64eba8ee3dd7a43d31aa7
[dragonblocks_alpha.git] / src / map.c
1 #include <stdlib.h>
2 #include <stdbool.h>
3 #include <unistd.h>
4 #include <math.h>
5 #include <string.h>
6 #include "map.h"
7 #include "util.h"
8
9 Map *map_create(MapCallbacks callbacks)
10 {
11         Map *map = malloc(sizeof(Map));
12         pthread_rwlock_init(&map->rwlck, NULL);
13         pthread_rwlock_init(&map->cached_rwlck, NULL);
14         map->sectors = bintree_create(sizeof(v2s32), NULL);
15         map->cached = NULL;
16         map->callbacks = callbacks;
17         return map;
18 }
19
20 static void free_block(BintreeNode *node, void *arg)
21 {
22         Map *map = arg;
23
24         if (map->callbacks.delete_block)
25                 map->callbacks.delete_block(node->value);
26
27         map_free_block(node->value);
28 }
29
30 static void free_sector(BintreeNode *node, void *arg)
31 {
32         MapSector *sector = node->value;
33
34         bintree_clear(&sector->blocks, &free_block, arg);
35         pthread_rwlock_destroy(&sector->rwlck);
36         free(sector);
37 }
38
39 void map_delete(Map *map)
40 {
41         pthread_rwlock_destroy(&map->rwlck);
42         pthread_rwlock_destroy(&map->cached_rwlck);
43         bintree_clear(&map->sectors, &free_sector, map);
44         free(map);
45 }
46
47 MapSector *map_get_sector(Map *map, v2s32 pos, bool create)
48 {
49         if (create)
50                 pthread_rwlock_wrlock(&map->rwlck);
51         else
52                 pthread_rwlock_rdlock(&map->rwlck);
53
54         BintreeNode **nodeptr = bintree_search(&map->sectors, &pos);
55
56         MapSector *sector = NULL;
57
58         if (*nodeptr) {
59                 sector = (*nodeptr)->value;
60         } else if (create) {
61                 sector = malloc(sizeof(MapSector));
62                 pthread_rwlock_init(&sector->rwlck, NULL);
63                 sector->pos = pos;
64                 sector->blocks = bintree_create(sizeof(s32), NULL);
65
66                 bintree_add_node(&map->sectors, nodeptr, &pos, sector);
67         }
68
69         pthread_rwlock_unlock(&map->rwlck);
70
71         return sector;
72 }
73
74 MapBlock *map_get_block(Map *map, v3s32 pos, bool create)
75 {
76         MapBlock *cached = NULL;
77
78         pthread_rwlock_rdlock(&map->cached_rwlck);
79         cached = map->cached;
80         pthread_rwlock_unlock(&map->cached_rwlck);
81
82         if (cached && v3s32_equals(cached->pos, pos))
83                 return cached;
84
85         MapSector *sector = map_get_sector(map, (v2s32) {pos.x, pos.z}, create);
86         if (! sector)
87                 return NULL;
88
89         if (create)
90                 pthread_rwlock_wrlock(&sector->rwlck);
91         else
92                 pthread_rwlock_rdlock(&sector->rwlck);
93
94         BintreeNode **nodeptr = bintree_search(&sector->blocks, &pos.y);
95
96         MapBlock *block = NULL;
97
98         if (*nodeptr) {
99                 block = (*nodeptr)->value;
100
101                 pthread_mutex_lock(&block->mtx);
102                 if (map->callbacks.get_block && ! map->callbacks.get_block(block, create)) {
103                         pthread_mutex_unlock(&block->mtx);
104                         block = NULL;
105                 } else {
106                         pthread_mutex_unlock(&block->mtx);
107                         pthread_rwlock_wrlock(&map->cached_rwlck);
108                         map->cached = block;
109                         pthread_rwlock_unlock(&map->cached_rwlck);
110                 }
111         } else if (create) {
112                 bintree_add_node(&sector->blocks, nodeptr, &pos.y, block = map_allocate_block(pos));
113
114                 if (map->callbacks.create_block)
115                         map->callbacks.create_block(block);
116         }
117
118         pthread_rwlock_unlock(&sector->rwlck);
119
120         return block;
121 }
122
123 MapBlock *map_allocate_block(v3s32 pos)
124 {
125         MapBlock *block = malloc(sizeof(MapBlock));
126         block->pos = pos;
127         block->extra = NULL;
128         pthread_mutexattr_t attr;
129         pthread_mutexattr_init(&attr);
130         pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
131         pthread_mutex_init(&block->mtx, &attr);
132
133         ITERATE_MAPBLOCK block->data[x][y][z] = map_node_create(NODE_UNKNOWN, (Blob) {0, NULL});
134
135         return block;
136 }
137
138 void map_free_block(MapBlock *block)
139 {
140         ITERATE_MAPBLOCK map_node_delete(block->data[x][y][z]);
141
142         pthread_mutex_destroy(&block->mtx);
143         free(block);
144 }
145
146 Blob map_serialize_block(MapBlock *block)
147 {
148         bool all_air = true;
149
150         ITERATE_MAPBLOCK {
151                 if (block->data[x][y][z].type != NODE_AIR) {
152                         all_air = false;
153                         break;
154                 }
155         }
156
157         if (all_air)
158                 return (Blob) {0, NULL};
159
160         SerializedMapBlock block_data;
161
162         ITERATE_MAPBLOCK {
163                 MapNode *node = &block->data[x][y][z];
164                 SerializedMapNode *node_data = &block_data.raw.nodes[x][y][z];
165
166                 *node_data = (SerializedMapNode) {
167                         .type = node->type,
168                         .data = {
169                                 .siz = 0,
170                                 .data = NULL,
171                         },
172                 };
173
174                 NodeDefinition *def = &node_definitions[node->type];
175
176                 if (def->serialize)
177                         def->serialize(&node_data->data, node->data);
178         }
179
180         Blob buffer = {0, NULL};
181         SerializedMapBlock_write(&buffer, &block_data);
182         SerializedMapBlock_free(&block_data);
183
184         return buffer;
185 }
186
187 bool map_deserialize_block(MapBlock *block, Blob buffer)
188 {
189         if (buffer.siz == 0) {
190                 ITERATE_MAPBLOCK
191                         block->data[x][y][z] = map_node_create(NODE_AIR, (Blob) {0, NULL});
192
193                 return true;
194         }
195
196         // it's important to copy Blobs that have been malloc'd before reading from them
197         // because reading from a Blob modifies its data and size pointer,
198         // but does not free anything
199         SerializedMapBlock block_data = {0};
200         bool success = SerializedMapBlock_read(&buffer, &block_data);
201
202         if (success) ITERATE_MAPBLOCK
203                 block->data[x][y][z] = map_node_create(block_data.raw.nodes[x][y][z].type, block_data.raw.nodes[x][y][z].data);
204
205         SerializedMapBlock_free(&block_data);
206         return success;
207 }
208
209 v3s32 map_node_to_block_pos(v3s32 pos, v3u8 *offset)
210 {
211         if (offset)
212                 *offset = (v3u8) {(u32) pos.x % MAPBLOCK_SIZE, (u32) pos.y % MAPBLOCK_SIZE, (u32) pos.z % MAPBLOCK_SIZE};
213         return (v3s32) {floor((double) pos.x / (double) MAPBLOCK_SIZE), floor((double) pos.y / (double) MAPBLOCK_SIZE), floor((double) pos.z / (double) MAPBLOCK_SIZE)};
214 }
215
216 MapNode map_get_node(Map *map, v3s32 pos)
217 {
218         v3u8 offset;
219         v3s32 blockpos = map_node_to_block_pos(pos, &offset);
220         MapBlock *block = map_get_block(map, blockpos, false);
221         if (! block)
222                 return map_node_create(NODE_UNLOADED, (Blob) {0, NULL});
223         return block->data[offset.x][offset.y][offset.z];
224 }
225
226 void map_set_node(Map *map, v3s32 pos, MapNode node, bool create, void *arg)
227 {
228         v3u8 offset;
229         MapBlock *block = map_get_block(map, map_node_to_block_pos(pos, &offset), create);
230         if (block) {
231                 pthread_mutex_lock(&block->mtx);
232                 if (! map->callbacks.set_node || map->callbacks.set_node(block, offset, &node, arg)) {
233                         block->data[offset.x][offset.y][offset.z] = node;
234                         if (map->callbacks.after_set_node)
235                                 map->callbacks.after_set_node(block, offset, arg);
236                 } else {
237                         map_node_delete(node);
238                 }
239                 pthread_mutex_unlock(&block->mtx);
240         }
241 }
242
243 MapNode map_node_create(Node type, Blob buffer)
244 {
245         if (type >= NODE_UNLOADED)
246                 type = NODE_UNKNOWN;
247
248         NodeDefinition *def = &node_definitions[type];
249
250         MapNode node;
251         node.type = type;
252         node.data = def->data_size ? malloc(def->data_size) : NULL;
253
254         if (def->create)
255                 def->create(&node);
256
257         if (def->deserialize)
258                 def->deserialize(&buffer, node.data);
259
260         return node;
261 }
262
263 void map_node_delete(MapNode node)
264 {
265         NodeDefinition *def = &node_definitions[node.type];
266
267         if (def->delete)
268                 def->delete(&node);
269
270         if (node.data)
271                 free(node.data);
272 }