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