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