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