]> git.lizzy.rs Git - minetest.git/blob - src/mapblock.h
48f8775519fe8e47b713dba47e43beda4a6c9b2e
[minetest.git] / src / mapblock.h
1 /*
2 (c) 2010 Perttu Ahola <celeron55@gmail.com>
3 */
4
5 #ifndef MAPBLOCK_HEADER
6 #define MAPBLOCK_HEADER
7
8 #include <jmutex.h>
9 #include <jmutexautolock.h>
10 #include <exception>
11 #include "debug.h"
12 #include "common_irrlicht.h"
13 #include "mapnode.h"
14 #include "exceptions.h"
15 #include "serialization.h"
16 #include "constants.h"
17 #include "mapblockobject.h"
18
19 #define MAP_BLOCKSIZE 16
20
21 // Named by looking towards z+
22 enum{
23         FACE_BACK=0,
24         FACE_TOP,
25         FACE_RIGHT,
26         FACE_FRONT,
27         FACE_BOTTOM,
28         FACE_LEFT
29 };
30
31 struct FastFace
32 {
33         u8 material;
34         video::S3DVertex vertices[4]; // Precalculated vertices
35 };
36
37 enum
38 {
39         NODECONTAINER_ID_MAPBLOCK,
40         NODECONTAINER_ID_MAPSECTOR,
41         NODECONTAINER_ID_MAP,
42         NODECONTAINER_ID_MAPBLOCKCACHE,
43         NODECONTAINER_ID_VOXELMANIPULATOR,
44 };
45
46 class NodeContainer
47 {
48 public:
49         virtual bool isValidPosition(v3s16 p) = 0;
50         virtual MapNode getNode(v3s16 p) = 0;
51         virtual void setNode(v3s16 p, MapNode & n) = 0;
52         virtual u16 nodeContainerId() const = 0;
53 };
54
55 class MapBlock : public NodeContainer
56 {
57 private:
58
59         NodeContainer *m_parent;
60         // Position in blocks on parent
61         v3s16 m_pos;
62         /*
63                 If NULL, block is a dummy block.
64                 Dummy blocks are used for caching not-found-on-disk blocks.
65         */
66         MapNode * data;
67         /*
68                 - On the client, this is used for checking whether to
69                   recalculate the face cache. (Is it anymore?)
70                 - On the server, this is used for telling whether the
71                   block has been changed from the one on disk.
72         */
73         bool changed;
74         /*
75                 Used for some initial lighting stuff.
76                 At least /has been/ used. 8)
77         */
78         bool is_underground;
79         
80         MapBlockObjectList m_objects;
81         
82 public:
83
84         /*
85                 This used by Server's block creation stuff for not sending
86                 blocks that are waiting a lighting update.
87
88                 If true, the block needs some work by the one who set this
89                 to true.
90
91                 While true, nobody else should touch the block.
92         */
93         //bool is_incomplete;
94         
95         scene::SMesh *mesh;
96         JMutex mesh_mutex;
97
98         MapBlock(NodeContainer *parent, v3s16 pos, bool dummy=false):
99                         m_parent(parent),
100                         m_pos(pos),
101                         changed(true),
102                         is_underground(false),
103                         m_objects(this)
104                         //is_incomplete(false)
105         {
106                 data = NULL;
107                 if(dummy == false)
108                         reallocate();
109                 mesh_mutex.Init();
110                 mesh = NULL;
111         }
112
113         ~MapBlock()
114         {
115                 {
116                         JMutexAutoLock lock(mesh_mutex);
117                         
118                         if(mesh != NULL)
119                         {
120                                 mesh->drop();
121                                 mesh = NULL;
122                         }
123                 }
124
125                 if(data)
126                         delete[] data;
127         }
128         
129         virtual u16 nodeContainerId() const
130         {
131                 return NODECONTAINER_ID_MAPBLOCK;
132         }
133
134         NodeContainer * getParent()
135         {
136                 return m_parent;
137         }
138
139         bool isDummy()
140         {
141                 return (data == NULL);
142         }
143
144         void unDummify()
145         {
146                 assert(isDummy());
147                 reallocate();
148         }
149         
150         bool getChangedFlag()
151         {
152                 return changed;
153         }
154
155         void resetChangedFlag()
156         {
157                 changed = false;
158         }
159
160         void setChangedFlag()
161         {
162                 changed = true;
163         }
164
165         v3s16 getPos()
166         {
167                 return m_pos;
168         }
169                 
170         v3s16 getPosRelative()
171         {
172                 return m_pos * MAP_BLOCKSIZE;
173         }
174                 
175         bool getIsUnderground()
176         {
177                 return is_underground;
178         }
179
180         void setIsUnderground(bool a_is_underground)
181         {
182                 is_underground = a_is_underground;
183                 setChangedFlag();
184         }
185
186         core::aabbox3d<s16> getBox()
187         {
188                 return core::aabbox3d<s16>(getPosRelative(),
189                                 getPosRelative()
190                                 + v3s16(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE)
191                                 - v3s16(1,1,1));
192         }
193         
194         void reallocate()
195         {
196                 if(data != NULL)
197                         delete[] data;
198                 u32 l = MAP_BLOCKSIZE * MAP_BLOCKSIZE * MAP_BLOCKSIZE;
199                 data = new MapNode[l];
200                 for(u32 i=0; i<l; i++){
201                         data[i] = MapNode();
202                 }
203                 setChangedFlag();
204         }
205
206         bool isValidPosition(v3s16 p)
207         {
208                 if(data == NULL)
209                         return false;
210                 return (p.X >= 0 && p.X < MAP_BLOCKSIZE
211                                 && p.Y >= 0 && p.Y < MAP_BLOCKSIZE
212                                 && p.Z >= 0 && p.Z < MAP_BLOCKSIZE);
213         }
214
215         /*
216                 Regular MapNode get-setters
217         */
218         
219         MapNode getNode(s16 x, s16 y, s16 z)
220         {
221                 if(data == NULL)
222                         throw InvalidPositionException();
223                 if(x < 0 || x >= MAP_BLOCKSIZE) throw InvalidPositionException();
224                 if(y < 0 || y >= MAP_BLOCKSIZE) throw InvalidPositionException();
225                 if(z < 0 || z >= MAP_BLOCKSIZE) throw InvalidPositionException();
226                 return data[z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + y*MAP_BLOCKSIZE + x];
227         }
228         
229         MapNode getNode(v3s16 p)
230         {
231                 return getNode(p.X, p.Y, p.Z);
232         }
233         
234         void setNode(s16 x, s16 y, s16 z, MapNode & n)
235         {
236                 if(data == NULL)
237                         throw InvalidPositionException();
238                 if(x < 0 || x >= MAP_BLOCKSIZE) throw InvalidPositionException();
239                 if(y < 0 || y >= MAP_BLOCKSIZE) throw InvalidPositionException();
240                 if(z < 0 || z >= MAP_BLOCKSIZE) throw InvalidPositionException();
241                 data[z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + y*MAP_BLOCKSIZE + x] = n;
242                 setChangedFlag();
243         }
244         
245         void setNode(v3s16 p, MapNode & n)
246         {
247                 setNode(p.X, p.Y, p.Z, n);
248         }
249
250         /*
251                 Non-checking variants of the above
252         */
253
254         MapNode getNodeNoCheck(s16 x, s16 y, s16 z)
255         {
256                 if(data == NULL)
257                         throw InvalidPositionException();
258                 return data[z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + y*MAP_BLOCKSIZE + x];
259         }
260         
261         MapNode getNodeNoCheck(v3s16 p)
262         {
263                 return getNodeNoCheck(p.X, p.Y, p.Z);
264         }
265         
266         void setNodeNoCheck(s16 x, s16 y, s16 z, MapNode & n)
267         {
268                 if(data == NULL)
269                         throw InvalidPositionException();
270                 data[z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + y*MAP_BLOCKSIZE + x] = n;
271                 setChangedFlag();
272         }
273         
274         void setNodeNoCheck(v3s16 p, MapNode & n)
275         {
276                 setNodeNoCheck(p.X, p.Y, p.Z, n);
277         }
278
279         /*
280                 These functions consult the parent container if the position
281                 is not valid on this MapBlock.
282         */
283         bool isValidPositionParent(v3s16 p);
284         MapNode getNodeParent(v3s16 p);
285         void setNodeParent(v3s16 p, MapNode & n);
286
287         void drawbox(s16 x0, s16 y0, s16 z0, s16 w, s16 h, s16 d, MapNode node)
288         {
289                 for(u16 z=0; z<d; z++)
290                         for(u16 y=0; y<h; y++)
291                                 for(u16 x=0; x<w; x++)
292                                         setNode(x0+x, y0+y, z0+z, node);
293         }
294
295         static FastFace * makeFastFace(u8 material, u8 light, v3f p,
296                         v3f dir, v3f scale, v3f posRelative_f);
297         
298         u8 getFaceLight(v3s16 p, v3s16 face_dir);
299         
300         /*
301                 Gets node material from any place relative to block.
302                 Returns MATERIAL_AIR if doesn't exist.
303         */
304         u8 getNodeMaterial(v3s16 p);
305
306         /*
307                 startpos:
308                 translate_dir: unit vector with only one of x, y or z
309                 face_dir: unit vector with only one of x, y or z
310         */
311         void updateFastFaceRow(v3s16 startpos,
312                         u16 length,
313                         v3s16 translate_dir,
314                         v3s16 face_dir,
315                         core::list<FastFace*> &dest);
316
317         void updateMesh();
318
319         bool propagateSunlight(core::map<v3s16, bool> & light_sources);
320         
321         // Doesn't write version by itself
322         void serialize(std::ostream &os, u8 version);
323
324         void deSerialize(std::istream &is, u8 version);
325         
326         void serializeObjects(std::ostream &os, u8 version)
327         {
328                 m_objects.serialize(os, version);
329         }
330         // If smgr!=NULL, new objects are added to the scene
331         void updateObjects(std::istream &is, u8 version,
332                         scene::ISceneManager *smgr)
333         {
334                 m_objects.update(is, version, smgr);
335
336                 setChangedFlag();
337         }
338         void clearObjects()
339         {
340                 m_objects.clear();
341
342                 setChangedFlag();
343         }
344         void addObject(MapBlockObject *object)
345                         throw(ContainerFullException, AlreadyExistsException)
346         {
347                 m_objects.add(object);
348
349                 setChangedFlag();
350         }
351         void removeObject(s16 id)
352         {
353                 m_objects.remove(id);
354
355                 setChangedFlag();
356         }
357         MapBlockObject * getObject(s16 id)
358         {
359                 return m_objects.get(id);
360         }
361         JMutexAutoLock * getObjectLock()
362         {
363                 return m_objects.getLock();
364         }
365         void stepObjects(float dtime, bool server)
366         {
367                 m_objects.step(dtime, server);
368
369                 setChangedFlag();
370         }
371
372         /*void wrapObject(MapBlockObject *object)
373         {
374                 m_objects.wrapObject(object);
375
376                 setChangedFlag();
377         }*/
378
379         // origin is relative to block
380         void getObjects(v3f origin, f32 max_d,
381                         core::array<DistanceSortedObject> &dest)
382         {
383                 m_objects.getObjects(origin, max_d, dest);
384         }
385
386         s32 getObjectCount()
387         {
388                 return m_objects.getCount();
389         }
390
391 private:
392
393         /*
394                 Used only internally, because changes can't be tracked
395         */
396
397         MapNode & getNodeRef(s16 x, s16 y, s16 z)
398         {
399                 if(x < 0 || x >= MAP_BLOCKSIZE) throw InvalidPositionException();
400                 if(y < 0 || y >= MAP_BLOCKSIZE) throw InvalidPositionException();
401                 if(z < 0 || z >= MAP_BLOCKSIZE) throw InvalidPositionException();
402                 return data[z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + y*MAP_BLOCKSIZE + x];
403         }
404         MapNode & getNodeRef(v3s16 &p)
405         {
406                 return getNodeRef(p.X, p.Y, p.Z);
407         }
408 };
409
410 inline bool blockpos_over_limit(v3s16 p)
411 {
412         return
413           (p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
414         || p.X >  MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
415         || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
416         || p.Y >  MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
417         || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
418         || p.Z >  MAP_GENERATION_LIMIT / MAP_BLOCKSIZE);
419 }
420
421 /*
422         Returns the position of the block where the node is located
423 */
424 inline v3s16 getNodeBlockPos(v3s16 p)
425 {
426         return getContainerPos(p, MAP_BLOCKSIZE);
427 }
428
429 inline v2s16 getNodeSectorPos(v2s16 p)
430 {
431         return getContainerPos(p, MAP_BLOCKSIZE);
432 }
433
434 inline s16 getNodeBlockY(s16 y)
435 {
436         return getContainerPos(y, MAP_BLOCKSIZE);
437 }
438
439 #endif
440