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