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