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