]> git.lizzy.rs Git - minetest.git/blob - src/mapblock.h
c48f337e07efc6a14dc714db0d985d56004b297b
[minetest.git] / src / mapblock.h
1 /*
2 Minetest
3 Copyright (C) 2013 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 <set>
24 #include "debug.h"
25 #include "irr_v3d.h"
26 #include "mapnode.h"
27 #include "exceptions.h"
28 #include "constants.h"
29 #include "staticobject.h"
30 #include "nodemetadata.h"
31 #include "nodetimer.h"
32 #include "modifiedstate.h"
33 #include "util/numeric.h" // getContainerPos
34 #include "settings.h"
35 #include "mapgen.h"
36
37 class Map;
38 class NodeMetadataList;
39 class IGameDef;
40 class MapBlockMesh;
41 class VoxelManipulator;
42
43 #define BLOCK_TIMESTAMP_UNDEFINED 0xffffffff
44
45 /*// Named by looking towards z+
46 enum{
47         FACE_BACK=0,
48         FACE_TOP,
49         FACE_RIGHT,
50         FACE_FRONT,
51         FACE_BOTTOM,
52         FACE_LEFT
53 };*/
54
55 // NOTE: If this is enabled, set MapBlock to be initialized with
56 //       CONTENT_IGNORE.
57 /*enum BlockGenerationStatus
58 {
59         // Completely non-generated (filled with CONTENT_IGNORE).
60         BLOCKGEN_UNTOUCHED=0,
61         // Trees or similar might have been blitted from other blocks to here.
62         // Otherwise, the block contains CONTENT_IGNORE
63         BLOCKGEN_FROM_NEIGHBORS=2,
64         // Has been generated, but some neighbors might put some stuff in here
65         // when they are generated.
66         // Does not contain any CONTENT_IGNORE
67         BLOCKGEN_SELF_GENERATED=4,
68         // The block and all its neighbors have been generated
69         BLOCKGEN_FULLY_GENERATED=6
70 };*/
71
72 #if 0
73 enum
74 {
75         NODECONTAINER_ID_MAPBLOCK,
76         NODECONTAINER_ID_MAPSECTOR,
77         NODECONTAINER_ID_MAP,
78         NODECONTAINER_ID_MAPBLOCKCACHE,
79         NODECONTAINER_ID_VOXELMANIPULATOR,
80 };
81
82 class NodeContainer
83 {
84 public:
85         virtual bool isValidPosition(v3s16 p) = 0;
86         virtual MapNode getNode(v3s16 p) = 0;
87         virtual void setNode(v3s16 p, MapNode & n) = 0;
88         virtual u16 nodeContainerId() const = 0;
89
90         MapNode getNodeNoEx(v3s16 p)
91         {
92                 try{
93                         return getNode(p);
94                 }
95                 catch(InvalidPositionException &e){
96                         return MapNode(CONTENT_IGNORE);
97                 }
98         }
99 };
100 #endif
101
102 ////
103 //// MapBlock modified reason flags
104 ////
105
106 #define MOD_REASON_INITIAL                   (1 << 0)
107 #define MOD_REASON_REALLOCATE                (1 << 1)
108 #define MOD_REASON_SET_IS_UNDERGROUND        (1 << 2)
109 #define MOD_REASON_SET_LIGHTING_COMPLETE     (1 << 3)
110 #define MOD_REASON_SET_GENERATED             (1 << 4)
111 #define MOD_REASON_SET_NODE                  (1 << 5)
112 #define MOD_REASON_SET_NODE_NO_CHECK         (1 << 6)
113 #define MOD_REASON_SET_TIMESTAMP             (1 << 7)
114 #define MOD_REASON_REPORT_META_CHANGE        (1 << 8)
115 #define MOD_REASON_CLEAR_ALL_OBJECTS         (1 << 9)
116 #define MOD_REASON_BLOCK_EXPIRED             (1 << 10)
117 #define MOD_REASON_ADD_ACTIVE_OBJECT_RAW     (1 << 11)
118 #define MOD_REASON_REMOVE_OBJECTS_REMOVE     (1 << 12)
119 #define MOD_REASON_REMOVE_OBJECTS_DEACTIVATE (1 << 13)
120 #define MOD_REASON_TOO_MANY_OBJECTS          (1 << 14)
121 #define MOD_REASON_STATIC_DATA_ADDED         (1 << 15)
122 #define MOD_REASON_STATIC_DATA_REMOVED       (1 << 16)
123 #define MOD_REASON_STATIC_DATA_CHANGED       (1 << 17)
124 #define MOD_REASON_EXPIRE_DAYNIGHTDIFF       (1 << 18)
125 #define MOD_REASON_UNKNOWN                   (1 << 19)
126
127 ////
128 //// MapBlock itself
129 ////
130
131 class MapBlock /*: public NodeContainer*/
132 {
133 public:
134         MapBlock(Map *parent, v3s16 pos, IGameDef *gamedef, bool dummy=false);
135         ~MapBlock();
136
137         /*virtual u16 nodeContainerId() const
138         {
139                 return NODECONTAINER_ID_MAPBLOCK;
140         }*/
141
142         Map * getParent()
143         {
144                 return m_parent;
145         }
146
147         void reallocate()
148         {
149                 delete[] data;
150                 data = new MapNode[nodecount];
151                 for (u32 i = 0; i < nodecount; i++)
152                         data[i] = MapNode(CONTENT_IGNORE);
153
154                 raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_REALLOCATE);
155         }
156
157         ////
158         //// Modification tracking methods
159         ////
160         void raiseModified(u32 mod, u32 reason=MOD_REASON_UNKNOWN)
161         {
162                 if (mod > m_modified) {
163                         m_modified = mod;
164                         m_modified_reason = reason;
165                         if (m_modified >= MOD_STATE_WRITE_AT_UNLOAD)
166                                 m_disk_timestamp = m_timestamp;
167                 } else if (mod == m_modified) {
168                         m_modified_reason |= reason;
169                 }
170         }
171
172         inline u32 getModified()
173         {
174                 return m_modified;
175         }
176
177         inline u32 getModifiedReason()
178         {
179                 return m_modified_reason;
180         }
181
182         std::string getModifiedReasonString();
183
184         inline void resetModified()
185         {
186                 m_modified = MOD_STATE_CLEAN;
187                 m_modified_reason = 0;
188         }
189
190         ////
191         //// Flags
192         ////
193
194         inline bool isDummy()
195         {
196                 return (data == NULL);
197         }
198
199         inline void unDummify()
200         {
201                 assert(isDummy()); // Pre-condition
202                 reallocate();
203         }
204
205         // is_underground getter/setter
206         inline bool getIsUnderground()
207         {
208                 return is_underground;
209         }
210
211         inline void setIsUnderground(bool a_is_underground)
212         {
213                 is_underground = a_is_underground;
214                 raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_SET_IS_UNDERGROUND);
215         }
216
217         inline void setLightingComplete(u16 newflags)
218         {
219                 if (newflags != m_lighting_complete) {
220                         m_lighting_complete = newflags;
221                         raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_SET_LIGHTING_COMPLETE);
222                 }
223         }
224
225         inline u16 getLightingComplete()
226         {
227                 return m_lighting_complete;
228         }
229
230         inline void setLightingComplete(LightBank bank, u8 direction,
231                 bool is_complete)
232         {
233                 assert(direction >= 0 && direction <= 5);
234                 if (bank == LIGHTBANK_NIGHT) {
235                         direction += 6;
236                 }
237                 u16 newflags = m_lighting_complete;
238                 if (is_complete) {
239                         newflags |= 1 << direction;
240                 } else {
241                         newflags &= ~(1 << direction);
242                 }
243                 setLightingComplete(newflags);
244         }
245
246         inline bool isLightingComplete(LightBank bank, u8 direction)
247         {
248                 assert(direction >= 0 && direction <= 5);
249                 if (bank == LIGHTBANK_NIGHT) {
250                         direction += 6;
251                 }
252                 return (m_lighting_complete & (1 << direction)) != 0;
253         }
254
255         inline bool isGenerated()
256         {
257                 return m_generated;
258         }
259
260         inline void setGenerated(bool b)
261         {
262                 if (b != m_generated) {
263                         raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_SET_GENERATED);
264                         m_generated = b;
265                 }
266         }
267
268         ////
269         //// Position stuff
270         ////
271
272         inline v3s16 getPos()
273         {
274                 return m_pos;
275         }
276
277         inline v3s16 getPosRelative()
278         {
279                 return m_pos_relative;
280         }
281
282         inline core::aabbox3d<s16> getBox()
283         {
284                 return core::aabbox3d<s16>(getPosRelative(),
285                                 getPosRelative()
286                                 + v3s16(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE)
287                                 - v3s16(1,1,1));
288         }
289
290         ////
291         //// Regular MapNode get-setters
292         ////
293
294         inline bool isValidPosition(s16 x, s16 y, s16 z)
295         {
296                 return data != NULL
297                         && x >= 0 && x < MAP_BLOCKSIZE
298                         && y >= 0 && y < MAP_BLOCKSIZE
299                         && z >= 0 && z < MAP_BLOCKSIZE;
300         }
301
302         inline bool isValidPosition(v3s16 p)
303         {
304                 return isValidPosition(p.X, p.Y, p.Z);
305         }
306
307         inline MapNode getNode(s16 x, s16 y, s16 z, bool *valid_position)
308         {
309                 *valid_position = isValidPosition(x, y, z);
310
311                 if (!*valid_position)
312                         return MapNode(CONTENT_IGNORE);
313
314                 return data[z * zstride + y * ystride + x];
315         }
316
317         inline MapNode getNode(v3s16 p, bool *valid_position)
318         {
319                 return getNode(p.X, p.Y, p.Z, valid_position);
320         }
321
322         inline MapNode getNodeNoEx(v3s16 p)
323         {
324                 bool is_valid;
325                 return getNode(p.X, p.Y, p.Z, &is_valid);
326         }
327
328         inline void setNode(s16 x, s16 y, s16 z, MapNode & n)
329         {
330                 if (!isValidPosition(x, y, z))
331                         throw InvalidPositionException();
332
333                 data[z * zstride + y * ystride + x] = n;
334                 raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_SET_NODE);
335         }
336
337         inline void setNode(v3s16 p, MapNode & n)
338         {
339                 setNode(p.X, p.Y, p.Z, n);
340         }
341
342         ////
343         //// Non-checking variants of the above
344         ////
345
346         inline MapNode getNodeNoCheck(s16 x, s16 y, s16 z, bool *valid_position)
347         {
348                 *valid_position = data != NULL;
349                 if (!valid_position)
350                         return MapNode(CONTENT_IGNORE);
351
352                 return data[z * zstride + y * ystride + x];
353         }
354
355         inline MapNode getNodeNoCheck(v3s16 p, bool *valid_position)
356         {
357                 return getNodeNoCheck(p.X, p.Y, p.Z, valid_position);
358         }
359
360         ////
361         //// Non-checking, unsafe variants of the above
362         //// MapBlock must be loaded by another function in the same scope/function
363         //// Caller must ensure that this is not a dummy block (by calling isDummy())
364         ////
365
366         inline const MapNode &getNodeUnsafe(s16 x, s16 y, s16 z)
367         {
368                 return data[z * zstride + y * ystride + x];
369         }
370
371         inline const MapNode &getNodeUnsafe(v3s16 &p)
372         {
373                 return getNodeUnsafe(p.X, p.Y, p.Z);
374         }
375
376         inline void setNodeNoCheck(s16 x, s16 y, s16 z, MapNode & n)
377         {
378                 if (data == NULL)
379                         throw InvalidPositionException();
380
381                 data[z * zstride + y * ystride + x] = n;
382                 raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_SET_NODE_NO_CHECK);
383         }
384
385         inline void setNodeNoCheck(v3s16 p, MapNode & n)
386         {
387                 setNodeNoCheck(p.X, p.Y, p.Z, n);
388         }
389
390         // These functions consult the parent container if the position
391         // is not valid on this MapBlock.
392         bool isValidPositionParent(v3s16 p);
393         MapNode getNodeParent(v3s16 p, bool *is_valid_position = NULL);
394         void setNodeParent(v3s16 p, MapNode & n);
395
396         inline void drawbox(s16 x0, s16 y0, s16 z0, s16 w, s16 h, s16 d, MapNode node)
397         {
398                 for (u16 z = 0; z < d; z++)
399                 for (u16 y = 0; y < h; y++)
400                 for (u16 x = 0; x < w; x++)
401                         setNode(x0 + x, y0 + y, z0 + z, node);
402         }
403
404         // See comments in mapblock.cpp
405         bool propagateSunlight(std::set<v3s16> &light_sources,
406                 bool remove_light=false, bool *black_air_left=NULL);
407
408         // Copies data to VoxelManipulator to getPosRelative()
409         void copyTo(VoxelManipulator &dst);
410
411         // Copies data from VoxelManipulator getPosRelative()
412         void copyFrom(VoxelManipulator &dst);
413
414         // Update day-night lighting difference flag.
415         // Sets m_day_night_differs to appropriate value.
416         // These methods don't care about neighboring blocks.
417         void actuallyUpdateDayNightDiff();
418
419         // Call this to schedule what the previous function does to be done
420         // when the value is actually needed.
421         void expireDayNightDiff();
422
423         inline bool getDayNightDiff()
424         {
425                 if (m_day_night_differs_expired)
426                         actuallyUpdateDayNightDiff();
427                 return m_day_night_differs;
428         }
429
430         ////
431         //// Miscellaneous stuff
432         ////
433
434         /*
435                 Tries to measure ground level.
436                 Return value:
437                         -1 = only air
438                         -2 = only ground
439                         -3 = random fail
440                         0...MAP_BLOCKSIZE-1 = ground level
441         */
442         s16 getGroundLevel(v2s16 p2d);
443
444         ////
445         //// Timestamp (see m_timestamp)
446         ////
447
448         // NOTE: BLOCK_TIMESTAMP_UNDEFINED=0xffffffff means there is no timestamp.
449
450         inline void setTimestamp(u32 time)
451         {
452                 m_timestamp = time;
453                 raiseModified(MOD_STATE_WRITE_AT_UNLOAD, MOD_REASON_SET_TIMESTAMP);
454         }
455
456         inline void setTimestampNoChangedFlag(u32 time)
457         {
458                 m_timestamp = time;
459         }
460
461         inline u32 getTimestamp()
462         {
463                 return m_timestamp;
464         }
465
466         inline u32 getDiskTimestamp()
467         {
468                 return m_disk_timestamp;
469         }
470
471         ////
472         //// Usage timer (see m_usage_timer)
473         ////
474
475         inline void resetUsageTimer()
476         {
477                 m_usage_timer = 0;
478         }
479
480         inline void incrementUsageTimer(float dtime)
481         {
482                 m_usage_timer += dtime;
483         }
484
485         inline float getUsageTimer()
486         {
487                 return m_usage_timer;
488         }
489
490         ////
491         //// Reference counting (see m_refcount)
492         ////
493
494         inline void refGrab()
495         {
496                 m_refcount++;
497         }
498
499         inline void refDrop()
500         {
501                 m_refcount--;
502         }
503
504         inline int refGet()
505         {
506                 return m_refcount;
507         }
508
509         ////
510         //// Node Timers
511         ////
512
513         inline NodeTimer getNodeTimer(v3s16 p)
514         {
515                 return m_node_timers.get(p);
516         }
517
518         inline void removeNodeTimer(v3s16 p)
519         {
520                 m_node_timers.remove(p);
521         }
522
523         inline void setNodeTimer(const NodeTimer &t)
524         {
525                 m_node_timers.set(t);
526         }
527
528         inline void clearNodeTimers()
529         {
530                 m_node_timers.clear();
531         }
532
533         ////
534         //// Serialization
535         ///
536
537         // These don't write or read version by itself
538         // Set disk to true for on-disk format, false for over-the-network format
539         // Precondition: version >= SER_FMT_VER_LOWEST_WRITE
540         void serialize(std::ostream &os, u8 version, bool disk);
541         // If disk == true: In addition to doing other things, will add
542         // unknown blocks from id-name mapping to wndef
543         void deSerialize(std::istream &is, u8 version, bool disk);
544
545         void serializeNetworkSpecific(std::ostream &os);
546         void deSerializeNetworkSpecific(std::istream &is);
547 private:
548         /*
549                 Private methods
550         */
551
552         void deSerialize_pre22(std::istream &is, u8 version, bool disk);
553
554         /*
555                 Used only internally, because changes can't be tracked
556         */
557
558         inline MapNode &getNodeRef(s16 x, s16 y, s16 z)
559         {
560                 if (!isValidPosition(x, y, z))
561                         throw InvalidPositionException();
562
563                 return data[z * zstride + y * ystride + x];
564         }
565
566         inline MapNode &getNodeRef(v3s16 &p)
567         {
568                 return getNodeRef(p.X, p.Y, p.Z);
569         }
570
571 public:
572         /*
573                 Public member variables
574         */
575
576 #ifndef SERVER // Only on client
577         MapBlockMesh *mesh;
578 #endif
579
580         NodeMetadataList m_node_metadata;
581         NodeTimerList m_node_timers;
582         StaticObjectList m_static_objects;
583
584         static const u32 ystride = MAP_BLOCKSIZE;
585         static const u32 zstride = MAP_BLOCKSIZE * MAP_BLOCKSIZE;
586
587         static const u32 nodecount = MAP_BLOCKSIZE * MAP_BLOCKSIZE * MAP_BLOCKSIZE;
588
589 private:
590         /*
591                 Private member variables
592         */
593
594         // NOTE: Lots of things rely on this being the Map
595         Map *m_parent;
596         // Position in blocks on parent
597         v3s16 m_pos;
598
599         /* This is the precalculated m_pos_relative value
600         * This caches the value, improving performance by removing 3 s16 multiplications
601         * at runtime on each getPosRelative call
602         * For a 5 minutes runtime with valgrind this removes 3 * 19M s16 multiplications
603         * The gain can be estimated in Release Build to 3 * 100M multiply operations for 5 mins
604         */
605         v3s16 m_pos_relative;
606
607         IGameDef *m_gamedef;
608
609         /*
610                 If NULL, block is a dummy block.
611                 Dummy blocks are used for caching not-found-on-disk blocks.
612         */
613         MapNode *data;
614
615         /*
616                 - On the server, this is used for telling whether the
617                   block has been modified from the one on disk.
618                 - On the client, this is used for nothing.
619         */
620         u32 m_modified;
621         u32 m_modified_reason;
622
623         /*
624                 When propagating sunlight and the above block doesn't exist,
625                 sunlight is assumed if this is false.
626
627                 In practice this is set to true if the block is completely
628                 undeground with nothing visible above the ground except
629                 caves.
630         */
631         bool is_underground;
632
633         /*!
634          * Each bit indicates if light spreading was finished
635          * in a direction. (Because the neighbor could also be unloaded.)
636          * Bits: day X+, day Y+, day Z+, day Z-, day Y-, day X-,
637          * night X+, night Y+, night Z+, night Z-, night Y-, night X-,
638          * nothing, nothing, nothing, nothing.
639         */
640         u16 m_lighting_complete;
641
642         // Whether day and night lighting differs
643         bool m_day_night_differs;
644         bool m_day_night_differs_expired;
645
646         bool m_generated;
647
648         /*
649                 When block is removed from active blocks, this is set to gametime.
650                 Value BLOCK_TIMESTAMP_UNDEFINED=0xffffffff means there is no timestamp.
651         */
652         u32 m_timestamp;
653         // The on-disk (or to-be on-disk) timestamp value
654         u32 m_disk_timestamp;
655
656         /*
657                 When the block is accessed, this is set to 0.
658                 Map will unload the block when this reaches a timeout.
659         */
660         float m_usage_timer;
661
662         /*
663                 Reference count; currently used for determining if this block is in
664                 the list of blocks to be drawn.
665         */
666         int m_refcount;
667 };
668
669 typedef std::vector<MapBlock*> MapBlockVect;
670
671 inline bool objectpos_over_limit(v3f p)
672 {
673         const float max_limit_bs = MAX_MAP_GENERATION_LIMIT * BS;
674         return p.X < -max_limit_bs ||
675                 p.X >  max_limit_bs ||
676                 p.Y < -max_limit_bs ||
677                 p.Y >  max_limit_bs ||
678                 p.Z < -max_limit_bs ||
679                 p.Z >  max_limit_bs;
680 }
681
682 inline bool blockpos_over_max_limit(v3s16 p)
683 {
684         const s16 max_limit_bp = MAX_MAP_GENERATION_LIMIT / MAP_BLOCKSIZE;
685         return p.X < -max_limit_bp ||
686                 p.X >  max_limit_bp ||
687                 p.Y < -max_limit_bp ||
688                 p.Y >  max_limit_bp ||
689                 p.Z < -max_limit_bp ||
690                 p.Z >  max_limit_bp;
691 }
692
693 /*
694         Returns the position of the block where the node is located
695 */
696 inline v3s16 getNodeBlockPos(v3s16 p)
697 {
698         return getContainerPos(p, MAP_BLOCKSIZE);
699 }
700
701 inline v2s16 getNodeSectorPos(v2s16 p)
702 {
703         return getContainerPos(p, MAP_BLOCKSIZE);
704 }
705
706 inline s16 getNodeBlockY(s16 y)
707 {
708         return getContainerPos(y, MAP_BLOCKSIZE);
709 }
710
711 inline void getNodeBlockPosWithOffset(const v3s16 &p, v3s16 &block, v3s16 &offset)
712 {
713         getContainerPosWithOffset(p, MAP_BLOCKSIZE, block, offset);
714 }
715
716 inline void getNodeSectorPosWithOffset(const v2s16 &p, v2s16 &block, v2s16 &offset)
717 {
718         getContainerPosWithOffset(p, MAP_BLOCKSIZE, block, offset);
719 }
720
721 /*
722         Get a quick string to describe what a block actually contains
723 */
724 std::string analyze_block(MapBlock *block);
725
726 #endif