]> git.lizzy.rs Git - dragonblocks_alpha.git/blob - src/terrain.c
refactoring
[dragonblocks_alpha.git] / src / terrain.c
1 #include <math.h>
2 #include <stdbool.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <unistd.h>
6 #include "terrain.h"
7
8 static void delete_chunk(TerrainChunk *chunk, Terrain *terrain)
9 {
10         if (terrain->callbacks.delete_chunk)
11                 terrain->callbacks.delete_chunk(chunk);
12
13         terrain_free_chunk(chunk);
14 }
15
16 static void delete_sector(TerrainSector *sector, Terrain *terrain)
17 {
18         tree_clr(&sector->chunks, (void *) &delete_chunk, terrain, NULL, 0);
19         pthread_rwlock_destroy(&sector->lock);
20         free(sector);
21 }
22
23 Terrain *terrain_create()
24 {
25         Terrain *terrain = malloc(sizeof *terrain);
26         tree_ini(&terrain->sectors);
27         pthread_rwlock_init(&terrain->lock, NULL);
28         terrain->cache = NULL;
29         pthread_rwlock_init(&terrain->cache_lock, NULL);
30         return terrain;
31 }
32
33 void terrain_delete(Terrain *terrain)
34 {
35         tree_clr(&terrain->sectors, (void *) &delete_sector, terrain, NULL, 0);
36         pthread_rwlock_destroy(&terrain->lock);
37         pthread_rwlock_destroy(&terrain->cache_lock);
38         free(terrain);
39 }
40
41 TerrainSector *terrain_get_sector(Terrain *terrain, v2s32 pos, bool create)
42 {
43         if (create)
44                 pthread_rwlock_wrlock(&terrain->lock);
45         else
46                 pthread_rwlock_rdlock(&terrain->lock);
47
48         TreeNode **loc = tree_nfd(&terrain->sectors, &pos, &v2s32_cmp);
49         TerrainSector *sector = NULL;
50
51         if (*loc) {
52                 sector = (*loc)->dat;
53         } else if (create) {
54                 sector = malloc(sizeof *sector);
55                 sector->pos = pos;
56                 tree_ini(&sector->chunks);
57                 pthread_rwlock_init(&sector->lock, NULL);
58
59                 tree_nmk(&terrain->sectors, loc, sector);
60         }
61
62         pthread_rwlock_unlock(&terrain->lock);
63
64         return sector;
65 }
66
67 TerrainChunk *terrain_get_chunk(Terrain *terrain, v3s32 pos, bool create)
68 {
69         TerrainChunk *cache = NULL;
70
71         pthread_rwlock_rdlock(&terrain->cache_lock);
72         cache = terrain->cache;
73         pthread_rwlock_unlock(&terrain->cache_lock);
74
75         if (cache && v3s32_equals(cache->pos, pos))
76                 return cache;
77
78         TerrainSector *sector = terrain_get_sector(terrain, (v2s32) {pos.x, pos.z}, create);
79         if (!sector)
80                 return NULL;
81
82         if (create)
83                 pthread_rwlock_wrlock(&sector->lock);
84         else
85                 pthread_rwlock_rdlock(&sector->lock);
86
87         TreeNode **loc = tree_nfd(&sector->chunks, &pos.y, &s32_cmp);
88         TerrainChunk *chunk = NULL;
89
90         if (*loc) {
91                 chunk = (*loc)->dat;
92
93                 pthread_mutex_lock(&chunk->mtx);
94                 if (terrain->callbacks.get_chunk && !terrain->callbacks.get_chunk(chunk, create)) {
95                         pthread_mutex_unlock(&chunk->mtx);
96                         chunk = NULL;
97                 } else {
98                         pthread_mutex_unlock(&chunk->mtx);
99                         pthread_rwlock_wrlock(&terrain->cache_lock);
100                         terrain->cache = chunk;
101                         pthread_rwlock_unlock(&terrain->cache_lock);
102                 }
103         } else if (create) {
104                 tree_nmk(&sector->chunks, loc, chunk = terrain_allocate_chunk(pos));
105
106                 if (terrain->callbacks.create_chunk)
107                         terrain->callbacks.create_chunk(chunk);
108         }
109
110         pthread_rwlock_unlock(&sector->lock);
111
112         return chunk;
113 }
114
115 TerrainChunk *terrain_allocate_chunk(v3s32 pos)
116 {
117         TerrainChunk *chunk = malloc(sizeof * chunk);
118         chunk->level = pos.y;
119         chunk->pos = pos;
120         chunk->extra = NULL;
121         pthread_mutexattr_t attr;
122         pthread_mutexattr_init(&attr);
123         pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
124         pthread_mutex_init(&chunk->mtx, &attr);
125
126         CHUNK_ITERATE
127                 chunk->data[x][y][z] = terrain_node_create(NODE_UNKNOWN, (Blob) {0, NULL});
128
129         return chunk;
130 }
131
132 void terrain_free_chunk(TerrainChunk *chunk)
133 {
134         CHUNK_ITERATE
135                 terrain_node_delete(chunk->data[x][y][z]);
136
137         pthread_mutex_destroy(&chunk->mtx);
138         free(chunk);
139 }
140
141 Blob terrain_serialize_chunk(TerrainChunk *chunk)
142 {
143         bool empty = true;
144
145         CHUNK_ITERATE {
146                 if (chunk->data[x][y][z].type != NODE_AIR) {
147                         empty = false;
148                         break;
149                 }
150         }
151
152         if (empty)
153                 return (Blob) {0, NULL};
154
155         SerializedTerrainChunk chunk_data;
156
157         CHUNK_ITERATE {
158                 TerrainNode *node = &chunk->data[x][y][z];
159                 SerializedTerrainNode *node_data = &chunk_data.raw.nodes[x][y][z];
160
161                 *node_data = (SerializedTerrainNode) {
162                         .type = node->type,
163                         .data = {
164                                 .siz = 0,
165                                 .data = NULL,
166                         },
167                 };
168
169                 NodeDefinition *def = &node_definitions[node->type];
170
171                 if (def->serialize)
172                         def->serialize(&node_data->data, node->data);
173         }
174
175         Blob buffer = {0, NULL};
176         SerializedTerrainChunk_write(&buffer, &chunk_data);
177         SerializedTerrainChunk_free(&chunk_data);
178
179         return buffer;
180 }
181
182 bool terrain_deserialize_chunk(TerrainChunk *chunk, Blob buffer)
183 {
184         if (buffer.siz == 0) {
185                 CHUNK_ITERATE
186                         chunk->data[x][y][z] = terrain_node_create(NODE_AIR, (Blob) {0, NULL});
187
188                 return true;
189         }
190
191         // it's important to copy Blobs that have been malloc'd before reading from them
192         // because reading from a Blob modifies its data and size pointer,
193         // but does not free anything
194         SerializedTerrainChunk chunk_data = {0};
195         bool success = SerializedTerrainChunk_read(&buffer, &chunk_data);
196
197         if (success) CHUNK_ITERATE
198                 chunk->data[x][y][z] = terrain_node_create(chunk_data.raw.nodes[x][y][z].type, chunk_data.raw.nodes[x][y][z].data);
199
200         SerializedTerrainChunk_free(&chunk_data);
201         return success;
202 }
203
204 v3s32 terrain_node_to_chunk_pos(v3s32 pos, v3u8 *offset)
205 {
206         if (offset)
207                 *offset = (v3u8) {(u32) pos.x % CHUNK_SIZE, (u32) pos.y % CHUNK_SIZE, (u32) pos.z % CHUNK_SIZE};
208         return (v3s32) {floor((double) pos.x / (double) CHUNK_SIZE), floor((double) pos.y / (double) CHUNK_SIZE), floor((double) pos.z / (double) CHUNK_SIZE)};
209 }
210
211 TerrainNode terrain_get_node(Terrain *terrain, v3s32 pos)
212 {
213         v3u8 offset;
214         v3s32 chunkpos = terrain_node_to_chunk_pos(pos, &offset);
215         TerrainChunk *chunk = terrain_get_chunk(terrain, chunkpos, false);
216         if (!chunk)
217                 return terrain_node_create(NODE_UNLOADED, (Blob) {0, NULL});
218         return chunk->data[offset.x][offset.y][offset.z];
219 }
220
221 void terrain_set_node(Terrain *terrain, v3s32 pos, TerrainNode node, bool create, void *arg)
222 {
223         v3u8 offset;
224         TerrainChunk *chunk = terrain_get_chunk(terrain, terrain_node_to_chunk_pos(pos, &offset), create);
225
226         if (!chunk)
227                 return;
228
229         pthread_mutex_lock(&chunk->mtx);
230         if (!terrain->callbacks.set_node || terrain->callbacks.set_node(chunk, offset, &node, arg)) {
231                 chunk->data[offset.x][offset.y][offset.z] = node;
232                 if (terrain->callbacks.after_set_node)
233                         terrain->callbacks.after_set_node(chunk, offset, arg);
234         } else {
235                 terrain_node_delete(node);
236         }
237         pthread_mutex_unlock(&chunk->mtx);
238 }
239
240 TerrainNode terrain_node_create(NodeType type, Blob buffer)
241 {
242         if (type >= NODE_UNLOADED)
243                 type = NODE_UNKNOWN;
244
245         NodeDefinition *def = &node_definitions[type];
246
247         TerrainNode node;
248         node.type = type;
249         node.data = def->data_size ? malloc(def->data_size) : NULL;
250
251         if (def->create)
252                 def->create(&node);
253
254         if (def->deserialize)
255                 def->deserialize(&buffer, node.data);
256
257         return node;
258 }
259
260 void terrain_node_delete(TerrainNode node)
261 {
262         NodeDefinition *def = &node_definitions[node.type];
263
264         if (def->delete)
265                 def->delete(&node);
266
267         if (node.data)
268                 free(node.data);
269 }