Optimize headers
[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 Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser 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 "irrlichttypes.h"
28 #include "irr_v3d.h"
29 #include "irr_aabb3d.h"
30 #include "mapnode.h"
31 #include "exceptions.h"
32 #include "serialization.h"
33 #include "constants.h"
34 #include "voxel.h"
35 #include "staticobject.h"
36 #include "nodemetadata.h"
37 #include "nodetimer.h"
38 #include "modifiedstate.h"
39 #include "util/numeric.h" // getContainerPos
40
41 class Map;
42 class NodeMetadataList;
43 class IGameDef;
44 class MapBlockMesh;
45
46 #define BLOCK_TIMESTAMP_UNDEFINED 0xffffffff
47
48 /*// Named by looking towards z+
49 enum{
50         FACE_BACK=0,
51         FACE_TOP,
52         FACE_RIGHT,
53         FACE_FRONT,
54         FACE_BOTTOM,
55         FACE_LEFT
56 };*/
57
58 // NOTE: If this is enabled, set MapBlock to be initialized with
59 //       CONTENT_IGNORE.
60 /*enum BlockGenerationStatus
61 {
62         // Completely non-generated (filled with CONTENT_IGNORE).
63         BLOCKGEN_UNTOUCHED=0,
64         // Trees or similar might have been blitted from other blocks to here.
65         // Otherwise, the block contains CONTENT_IGNORE
66         BLOCKGEN_FROM_NEIGHBORS=2,
67         // Has been generated, but some neighbors might put some stuff in here
68         // when they are generated.
69         // Does not contain any CONTENT_IGNORE
70         BLOCKGEN_SELF_GENERATED=4,
71         // The block and all its neighbors have been generated
72         BLOCKGEN_FULLY_GENERATED=6
73 };*/
74
75 #if 0
76 enum
77 {
78         NODECONTAINER_ID_MAPBLOCK,
79         NODECONTAINER_ID_MAPSECTOR,
80         NODECONTAINER_ID_MAP,
81         NODECONTAINER_ID_MAPBLOCKCACHE,
82         NODECONTAINER_ID_VOXELMANIPULATOR,
83 };
84
85 class NodeContainer
86 {
87 public:
88         virtual bool isValidPosition(v3s16 p) = 0;
89         virtual MapNode getNode(v3s16 p) = 0;
90         virtual void setNode(v3s16 p, MapNode & n) = 0;
91         virtual u16 nodeContainerId() const = 0;
92
93         MapNode getNodeNoEx(v3s16 p)
94         {
95                 try{
96                         return getNode(p);
97                 }
98                 catch(InvalidPositionException &e){
99                         return MapNode(CONTENT_IGNORE);
100                 }
101         }
102 };
103 #endif
104
105 /*
106         MapBlock itself
107 */
108
109 class MapBlock /*: public NodeContainer*/
110 {
111 public:
112         MapBlock(Map *parent, v3s16 pos, IGameDef *gamedef, bool dummy=false);
113         ~MapBlock();
114         
115         /*virtual u16 nodeContainerId() const
116         {
117                 return NODECONTAINER_ID_MAPBLOCK;
118         }*/
119         
120         Map * getParent()
121         {
122                 return m_parent;
123         }
124
125         void reallocate()
126         {
127                 if(data != NULL)
128                         delete[] data;
129                 u32 l = MAP_BLOCKSIZE * MAP_BLOCKSIZE * MAP_BLOCKSIZE;
130                 data = new MapNode[l];
131                 for(u32 i=0; i<l; i++){
132                         //data[i] = MapNode();
133                         data[i] = MapNode(CONTENT_IGNORE);
134                 }
135                 raiseModified(MOD_STATE_WRITE_NEEDED, "reallocate");
136         }
137
138         /*
139                 Flags
140         */
141
142         bool isDummy()
143         {
144                 return (data == NULL);
145         }
146         void unDummify()
147         {
148                 assert(isDummy());
149                 reallocate();
150         }
151         
152         // m_modified methods
153         void raiseModified(u32 mod, const std::string &reason="unknown")
154         {
155                 if(mod > m_modified){
156                         m_modified = mod;
157                         m_modified_reason = reason;
158                         m_modified_reason_too_long = false;
159
160                         if(m_modified >= MOD_STATE_WRITE_AT_UNLOAD){
161                                 m_disk_timestamp = m_timestamp;
162                         }
163                 } else if(mod == m_modified){
164                         if(!m_modified_reason_too_long){
165                                 if(m_modified_reason.size() < 40)
166                                         m_modified_reason += ", " + reason;
167                                 else{
168                                         m_modified_reason += "...";
169                                         m_modified_reason_too_long = true;
170                                 }
171                         }
172                 }
173         }
174         u32 getModified()
175         {
176                 return m_modified;
177         }
178         std::string getModifiedReason()
179         {
180                 return m_modified_reason;
181         }
182         void resetModified()
183         {
184                 m_modified = MOD_STATE_CLEAN;
185                 m_modified_reason = "none";
186                 m_modified_reason_too_long = false;
187         }
188         
189         // is_underground getter/setter
190         bool getIsUnderground()
191         {
192                 return is_underground;
193         }
194         void setIsUnderground(bool a_is_underground)
195         {
196                 is_underground = a_is_underground;
197                 raiseModified(MOD_STATE_WRITE_NEEDED, "setIsUnderground");
198         }
199
200         void setLightingExpired(bool expired)
201         {
202                 if(expired != m_lighting_expired){
203                         m_lighting_expired = expired;
204                         raiseModified(MOD_STATE_WRITE_NEEDED, "setLightingExpired");
205                 }
206         }
207         bool getLightingExpired()
208         {
209                 return m_lighting_expired;
210         }
211
212         bool isGenerated()
213         {
214                 return m_generated;
215         }
216         void setGenerated(bool b)
217         {
218                 if(b != m_generated){
219                         raiseModified(MOD_STATE_WRITE_NEEDED, "setGenerated");
220                         m_generated = b;
221                 }
222         }
223
224         bool isValid()
225         {
226                 if(m_lighting_expired)
227                         return false;
228                 if(data == NULL)
229                         return false;
230                 return true;
231         }
232
233         /*
234                 Position stuff
235         */
236
237         v3s16 getPos()
238         {
239                 return m_pos;
240         }
241                 
242         v3s16 getPosRelative()
243         {
244                 return m_pos * MAP_BLOCKSIZE;
245         }
246                 
247         core::aabbox3d<s16> getBox()
248         {
249                 return core::aabbox3d<s16>(getPosRelative(),
250                                 getPosRelative()
251                                 + v3s16(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE)
252                                 - v3s16(1,1,1));
253         }
254
255         /*
256                 Regular MapNode get-setters
257         */
258         
259         bool isValidPosition(v3s16 p)
260         {
261                 if(data == NULL)
262                         return false;
263                 return (p.X >= 0 && p.X < MAP_BLOCKSIZE
264                                 && p.Y >= 0 && p.Y < MAP_BLOCKSIZE
265                                 && p.Z >= 0 && p.Z < MAP_BLOCKSIZE);
266         }
267
268         MapNode getNode(s16 x, s16 y, s16 z)
269         {
270                 if(data == NULL)
271                         throw InvalidPositionException();
272                 if(x < 0 || x >= MAP_BLOCKSIZE) throw InvalidPositionException();
273                 if(y < 0 || y >= MAP_BLOCKSIZE) throw InvalidPositionException();
274                 if(z < 0 || z >= MAP_BLOCKSIZE) throw InvalidPositionException();
275                 return data[z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + y*MAP_BLOCKSIZE + x];
276         }
277         
278         MapNode getNode(v3s16 p)
279         {
280                 return getNode(p.X, p.Y, p.Z);
281         }
282         
283         MapNode getNodeNoEx(v3s16 p)
284         {
285                 try{
286                         return getNode(p.X, p.Y, p.Z);
287                 }catch(InvalidPositionException &e){
288                         return MapNode(CONTENT_IGNORE);
289                 }
290         }
291         
292         void setNode(s16 x, s16 y, s16 z, MapNode & n)
293         {
294                 if(data == NULL)
295                         throw InvalidPositionException();
296                 if(x < 0 || x >= MAP_BLOCKSIZE) throw InvalidPositionException();
297                 if(y < 0 || y >= MAP_BLOCKSIZE) throw InvalidPositionException();
298                 if(z < 0 || z >= MAP_BLOCKSIZE) throw InvalidPositionException();
299                 data[z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + y*MAP_BLOCKSIZE + x] = n;
300                 raiseModified(MOD_STATE_WRITE_NEEDED, "setNode");
301         }
302         
303         void setNode(v3s16 p, MapNode & n)
304         {
305                 setNode(p.X, p.Y, p.Z, n);
306         }
307
308         /*
309                 Non-checking variants of the above
310         */
311
312         MapNode getNodeNoCheck(s16 x, s16 y, s16 z)
313         {
314                 if(data == NULL)
315                         throw InvalidPositionException();
316                 return data[z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + y*MAP_BLOCKSIZE + x];
317         }
318         
319         MapNode getNodeNoCheck(v3s16 p)
320         {
321                 return getNodeNoCheck(p.X, p.Y, p.Z);
322         }
323         
324         void setNodeNoCheck(s16 x, s16 y, s16 z, MapNode & n)
325         {
326                 if(data == NULL)
327                         throw InvalidPositionException();
328                 data[z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + y*MAP_BLOCKSIZE + x] = n;
329                 raiseModified(MOD_STATE_WRITE_NEEDED, "setNodeNoCheck");
330         }
331         
332         void setNodeNoCheck(v3s16 p, MapNode & n)
333         {
334                 setNodeNoCheck(p.X, p.Y, p.Z, n);
335         }
336
337         /*
338                 These functions consult the parent container if the position
339                 is not valid on this MapBlock.
340         */
341         bool isValidPositionParent(v3s16 p);
342         MapNode getNodeParent(v3s16 p);
343         void setNodeParent(v3s16 p, MapNode & n);
344         MapNode getNodeParentNoEx(v3s16 p);
345
346         void drawbox(s16 x0, s16 y0, s16 z0, s16 w, s16 h, s16 d, MapNode node)
347         {
348                 for(u16 z=0; z<d; z++)
349                         for(u16 y=0; y<h; y++)
350                                 for(u16 x=0; x<w; x++)
351                                         setNode(x0+x, y0+y, z0+z, node);
352         }
353
354         // See comments in mapblock.cpp
355         bool propagateSunlight(core::map<v3s16, bool> & light_sources,
356                         bool remove_light=false, bool *black_air_left=NULL);
357         
358         // Copies data to VoxelManipulator to getPosRelative()
359         void copyTo(VoxelManipulator &dst);
360         // Copies data from VoxelManipulator getPosRelative()
361         void copyFrom(VoxelManipulator &dst);
362
363         /*
364                 Update day-night lighting difference flag.
365                 Sets m_day_night_differs to appropriate value.
366                 These methods don't care about neighboring blocks.
367         */
368         void actuallyUpdateDayNightDiff();
369         /*
370                 Call this to schedule what the previous function does to be done
371                 when the value is actually needed.
372         */
373         void expireDayNightDiff();
374
375         bool getDayNightDiff()
376         {
377                 if(m_day_night_differs_expired)
378                         actuallyUpdateDayNightDiff();
379                 return m_day_night_differs;
380         }
381
382         /*
383                 Miscellaneous stuff
384         */
385         
386         /*
387                 Tries to measure ground level.
388                 Return value:
389                         -1 = only air
390                         -2 = only ground
391                         -3 = random fail
392                         0...MAP_BLOCKSIZE-1 = ground level
393         */
394         s16 getGroundLevel(v2s16 p2d);
395
396         /*
397                 Timestamp (see m_timestamp)
398                 NOTE: BLOCK_TIMESTAMP_UNDEFINED=0xffffffff means there is no timestamp.
399         */
400         void setTimestamp(u32 time)
401         {
402                 m_timestamp = time;
403                 raiseModified(MOD_STATE_WRITE_AT_UNLOAD, "setTimestamp");
404         }
405         void setTimestampNoChangedFlag(u32 time)
406         {
407                 m_timestamp = time;
408         }
409         u32 getTimestamp()
410         {
411                 return m_timestamp;
412         }
413         u32 getDiskTimestamp()
414         {
415                 return m_disk_timestamp;
416         }
417         
418         /*
419                 See m_usage_timer
420         */
421         void resetUsageTimer()
422         {
423                 m_usage_timer = 0;
424         }
425         void incrementUsageTimer(float dtime)
426         {
427                 m_usage_timer += dtime;
428         }
429         u32 getUsageTimer()
430         {
431                 return m_usage_timer;
432         }
433
434         /*
435                 Serialization
436         */
437         
438         // These don't write or read version by itself
439         // Set disk to true for on-disk format, false for over-the-network format
440         void serialize(std::ostream &os, u8 version, bool disk);
441         // If disk == true: In addition to doing other things, will add
442         // unknown blocks from id-name mapping to wndef
443         void deSerialize(std::istream &is, u8 version, bool disk);
444
445 private:
446         /*
447                 Private methods
448         */
449
450         void serialize_pre22(std::ostream &os, u8 version, bool disk);
451         void deSerialize_pre22(std::istream &is, u8 version, bool disk);
452
453         /*
454                 Used only internally, because changes can't be tracked
455         */
456
457         MapNode & getNodeRef(s16 x, s16 y, s16 z)
458         {
459                 if(data == NULL)
460                         throw InvalidPositionException();
461                 if(x < 0 || x >= MAP_BLOCKSIZE) throw InvalidPositionException();
462                 if(y < 0 || y >= MAP_BLOCKSIZE) throw InvalidPositionException();
463                 if(z < 0 || z >= MAP_BLOCKSIZE) throw InvalidPositionException();
464                 return data[z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + y*MAP_BLOCKSIZE + x];
465         }
466         MapNode & getNodeRef(v3s16 &p)
467         {
468                 return getNodeRef(p.X, p.Y, p.Z);
469         }
470
471 public:
472         /*
473                 Public member variables
474         */
475
476 #ifndef SERVER // Only on client
477         MapBlockMesh *mesh;
478         //JMutex mesh_mutex;
479 #endif
480         
481         NodeMetadataList m_node_metadata;
482         NodeTimerList m_node_timers;
483         StaticObjectList m_static_objects;
484         
485 private:
486         /*
487                 Private member variables
488         */
489
490         // NOTE: Lots of things rely on this being the Map
491         Map *m_parent;
492         // Position in blocks on parent
493         v3s16 m_pos;
494
495         IGameDef *m_gamedef;
496         
497         /*
498                 If NULL, block is a dummy block.
499                 Dummy blocks are used for caching not-found-on-disk blocks.
500         */
501         MapNode * data;
502
503         /*
504                 - On the server, this is used for telling whether the
505                   block has been modified from the one on disk.
506                 - On the client, this is used for nothing.
507         */
508         u32 m_modified;
509         std::string m_modified_reason;
510         bool m_modified_reason_too_long;
511
512         /*
513                 When propagating sunlight and the above block doesn't exist,
514                 sunlight is assumed if this is false.
515
516                 In practice this is set to true if the block is completely
517                 undeground with nothing visible above the ground except
518                 caves.
519         */
520         bool is_underground;
521
522         /*
523                 Set to true if changes has been made that make the old lighting
524                 values wrong but the lighting hasn't been actually updated.
525
526                 If this is false, lighting is exactly right.
527                 If this is true, lighting might be wrong or right.
528         */
529         bool m_lighting_expired;
530         
531         // Whether day and night lighting differs
532         bool m_day_night_differs;
533         bool m_day_night_differs_expired;
534
535         bool m_generated;
536         
537         /*
538                 When block is removed from active blocks, this is set to gametime.
539                 Value BLOCK_TIMESTAMP_UNDEFINED=0xffffffff means there is no timestamp.
540         */
541         u32 m_timestamp;
542         // The on-disk (or to-be on-disk) timestamp value
543         u32 m_disk_timestamp;
544
545         /*
546                 When the block is accessed, this is set to 0.
547                 Map will unload the block when this reaches a timeout.
548         */
549         float m_usage_timer;
550 };
551
552 inline bool blockpos_over_limit(v3s16 p)
553 {
554         return
555           (p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
556         || p.X >  MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
557         || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
558         || p.Y >  MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
559         || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
560         || p.Z >  MAP_GENERATION_LIMIT / MAP_BLOCKSIZE);
561 }
562
563 /*
564         Returns the position of the block where the node is located
565 */
566 inline v3s16 getNodeBlockPos(v3s16 p)
567 {
568         return getContainerPos(p, MAP_BLOCKSIZE);
569 }
570
571 inline v2s16 getNodeSectorPos(v2s16 p)
572 {
573         return getContainerPos(p, MAP_BLOCKSIZE);
574 }
575
576 inline s16 getNodeBlockY(s16 y)
577 {
578         return getContainerPos(y, MAP_BLOCKSIZE);
579 }
580
581 /*
582         Get a quick string to describe what a block actually contains
583 */
584 std::string analyze_block(MapBlock *block);
585
586 #endif
587