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