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