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