Record MapBlock modification reasons as flags instead of strings
[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
35 class Map;
36 class NodeMetadataList;
37 class IGameDef;
38 class MapBlockMesh;
39 class VoxelManipulator;
40
41 #define BLOCK_TIMESTAMP_UNDEFINED 0xffffffff
42
43 /*// Named by looking towards z+
44 enum{
45         FACE_BACK=0,
46         FACE_TOP,
47         FACE_RIGHT,
48         FACE_FRONT,
49         FACE_BOTTOM,
50         FACE_LEFT
51 };*/
52
53 // NOTE: If this is enabled, set MapBlock to be initialized with
54 //       CONTENT_IGNORE.
55 /*enum BlockGenerationStatus
56 {
57         // Completely non-generated (filled with CONTENT_IGNORE).
58         BLOCKGEN_UNTOUCHED=0,
59         // Trees or similar might have been blitted from other blocks to here.
60         // Otherwise, the block contains CONTENT_IGNORE
61         BLOCKGEN_FROM_NEIGHBORS=2,
62         // Has been generated, but some neighbors might put some stuff in here
63         // when they are generated.
64         // Does not contain any CONTENT_IGNORE
65         BLOCKGEN_SELF_GENERATED=4,
66         // The block and all its neighbors have been generated
67         BLOCKGEN_FULLY_GENERATED=6
68 };*/
69
70 #if 0
71 enum
72 {
73         NODECONTAINER_ID_MAPBLOCK,
74         NODECONTAINER_ID_MAPSECTOR,
75         NODECONTAINER_ID_MAP,
76         NODECONTAINER_ID_MAPBLOCKCACHE,
77         NODECONTAINER_ID_VOXELMANIPULATOR,
78 };
79
80 class NodeContainer
81 {
82 public:
83         virtual bool isValidPosition(v3s16 p) = 0;
84         virtual MapNode getNode(v3s16 p) = 0;
85         virtual void setNode(v3s16 p, MapNode & n) = 0;
86         virtual u16 nodeContainerId() const = 0;
87
88         MapNode getNodeNoEx(v3s16 p)
89         {
90                 try{
91                         return getNode(p);
92                 }
93                 catch(InvalidPositionException &e){
94                         return MapNode(CONTENT_IGNORE);
95                 }
96         }
97 };
98 #endif
99
100 ////
101 //// MapBlock modified reason flags
102 ////
103
104 #define MOD_REASON_INITIAL                   (1 << 0)
105 #define MOD_REASON_REALLOCATE                (1 << 1)
106 #define MOD_REASON_SET_IS_UNDERGROUND        (1 << 2)
107 #define MOD_REASON_SET_LIGHTING_EXPIRED      (1 << 3)
108 #define MOD_REASON_SET_GENERATED             (1 << 4)
109 #define MOD_REASON_SET_NODE                  (1 << 5)
110 #define MOD_REASON_SET_NODE_NO_CHECK         (1 << 6)
111 #define MOD_REASON_SET_TIMESTAMP             (1 << 7)
112 #define MOD_REASON_REPORT_META_CHANGE        (1 << 8)
113 #define MOD_REASON_CLEAR_ALL_OBJECTS         (1 << 9)
114 #define MOD_REASON_BLOCK_EXPIRED             (1 << 10)
115 #define MOD_REASON_ADD_ACTIVE_OBJECT_RAW     (1 << 11)
116 #define MOD_REASON_REMOVE_OBJECTS_REMOVE     (1 << 12)
117 #define MOD_REASON_REMOVE_OBJECTS_DEACTIVATE (1 << 13)
118 #define MOD_REASON_TOO_MANY_OBJECTS          (1 << 14)
119 #define MOD_REASON_STATIC_DATA_ADDED         (1 << 15)
120 #define MOD_REASON_STATIC_DATA_REMOVED       (1 << 16)
121 #define MOD_REASON_STATIC_DATA_CHANGED       (1 << 17)
122 #define MOD_REASON_EXPIRE_DAYNIGHTDIFF       (1 << 18)
123 #define MOD_REASON_UNKNOWN                   (1 << 19)
124
125 ////
126 //// MapBlock itself
127 ////
128
129 class MapBlock /*: public NodeContainer*/
130 {
131 public:
132         MapBlock(Map *parent, v3s16 pos, IGameDef *gamedef, bool dummy=false);
133         ~MapBlock();
134
135         /*virtual u16 nodeContainerId() const
136         {
137                 return NODECONTAINER_ID_MAPBLOCK;
138         }*/
139
140         Map * getParent()
141         {
142                 return m_parent;
143         }
144
145         void reallocate()
146         {
147                 delete[] data;
148                 u32 datasize = MAP_BLOCKSIZE * MAP_BLOCKSIZE * MAP_BLOCKSIZE;
149                 data = new MapNode[datasize];
150                 for (u32 i = 0; i < datasize; 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 * MAP_BLOCKSIZE;
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 * MAP_BLOCKSIZE * MAP_BLOCKSIZE + y * MAP_BLOCKSIZE + 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                 MapNode node = getNode(p.X, p.Y, p.Z, &is_valid);
309                 return is_valid ? node : MapNode(CONTENT_IGNORE);
310         }
311
312         inline void setNode(s16 x, s16 y, s16 z, MapNode & n)
313         {
314                 if (!isValidPosition(x, y, z))
315                         throw InvalidPositionException();
316
317                 data[z * zstride + y * ystride + x] = n;
318                 raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_SET_NODE);
319         }
320
321         inline void setNode(v3s16 p, MapNode & n)
322         {
323                 setNode(p.X, p.Y, p.Z, n);
324         }
325
326         ////
327         //// Non-checking variants of the above
328         ////
329
330         inline MapNode getNodeNoCheck(s16 x, s16 y, s16 z, bool *valid_position)
331         {
332                 *valid_position = data != NULL;
333                 if (!valid_position)
334                         return MapNode(CONTENT_IGNORE);
335
336                 return data[z * zstride + y * ystride + x];
337         }
338
339         inline MapNode getNodeNoCheck(v3s16 p, bool *valid_position)
340         {
341                 return getNodeNoCheck(p.X, p.Y, p.Z, valid_position);
342         }
343
344         inline void setNodeNoCheck(s16 x, s16 y, s16 z, MapNode & n)
345         {
346                 if (data == NULL)
347                         throw InvalidPositionException();
348
349                 data[z * zstride + y * ystride + x] = n;
350                 raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_SET_NODE_NO_CHECK);
351         }
352
353         inline void setNodeNoCheck(v3s16 p, MapNode & n)
354         {
355                 setNodeNoCheck(p.X, p.Y, p.Z, n);
356         }
357
358         // These functions consult the parent container if the position
359         // is not valid on this MapBlock.
360         bool isValidPositionParent(v3s16 p);
361         MapNode getNodeParent(v3s16 p, bool *is_valid_position = NULL);
362         void setNodeParent(v3s16 p, MapNode & n);
363
364         inline void drawbox(s16 x0, s16 y0, s16 z0, s16 w, s16 h, s16 d, MapNode node)
365         {
366                 for (u16 z = 0; z < d; z++)
367                 for (u16 y = 0; y < h; y++)
368                 for (u16 x = 0; x < w; x++)
369                         setNode(x0 + x, y0 + y, z0 + z, node);
370         }
371
372         // See comments in mapblock.cpp
373         bool propagateSunlight(std::set<v3s16> &light_sources,
374                 bool remove_light=false, bool *black_air_left=NULL);
375
376         // Copies data to VoxelManipulator to getPosRelative()
377         void copyTo(VoxelManipulator &dst);
378
379         // Copies data from VoxelManipulator getPosRelative()
380         void copyFrom(VoxelManipulator &dst);
381
382         // Update day-night lighting difference flag.
383         // Sets m_day_night_differs to appropriate value.
384         // These methods don't care about neighboring blocks.
385         void actuallyUpdateDayNightDiff();
386
387         // Call this to schedule what the previous function does to be done
388         // when the value is actually needed.
389         void expireDayNightDiff();
390
391         inline bool getDayNightDiff()
392         {
393                 if (m_day_night_differs_expired)
394                         actuallyUpdateDayNightDiff();
395                 return m_day_night_differs;
396         }
397
398         ////
399         //// Miscellaneous stuff
400         ////
401
402         /*
403                 Tries to measure ground level.
404                 Return value:
405                         -1 = only air
406                         -2 = only ground
407                         -3 = random fail
408                         0...MAP_BLOCKSIZE-1 = ground level
409         */
410         s16 getGroundLevel(v2s16 p2d);
411
412         ////
413         //// Timestamp (see m_timestamp)
414         ////
415
416         // NOTE: BLOCK_TIMESTAMP_UNDEFINED=0xffffffff means there is no timestamp.
417
418         inline void setTimestamp(u32 time)
419         {
420                 m_timestamp = time;
421                 raiseModified(MOD_STATE_WRITE_AT_UNLOAD, MOD_REASON_SET_TIMESTAMP);
422         }
423
424         inline void setTimestampNoChangedFlag(u32 time)
425         {
426                 m_timestamp = time;
427         }
428
429         inline u32 getTimestamp()
430         {
431                 return m_timestamp;
432         }
433
434         inline u32 getDiskTimestamp()
435         {
436                 return m_disk_timestamp;
437         }
438
439         ////
440         //// Usage timer (see m_usage_timer)
441         ////
442
443         inline void resetUsageTimer()
444         {
445                 m_usage_timer = 0;
446         }
447
448         inline void incrementUsageTimer(float dtime)
449         {
450                 m_usage_timer += dtime;
451         }
452
453         inline float getUsageTimer()
454         {
455                 return m_usage_timer;
456         }
457
458         ////
459         //// Reference counting (see m_refcount)
460         ////
461
462         inline void refGrab()
463         {
464                 m_refcount++;
465         }
466
467         inline void refDrop()
468         {
469                 m_refcount--;
470         }
471
472         inline int refGet()
473         {
474                 return m_refcount;
475         }
476
477         ////
478         //// Node Timers
479         ////
480
481         inline NodeTimer getNodeTimer(v3s16 p)
482         {
483                 return m_node_timers.get(p);
484         }
485
486         inline void removeNodeTimer(v3s16 p)
487         {
488                 m_node_timers.remove(p);
489         }
490
491         inline void setNodeTimer(v3s16 p, NodeTimer t)
492         {
493                 m_node_timers.set(p,t);
494         }
495
496         inline void clearNodeTimers()
497         {
498                 m_node_timers.clear();
499         }
500
501         ////
502         //// Serialization
503         ///
504
505         // These don't write or read version by itself
506         // Set disk to true for on-disk format, false for over-the-network format
507         // Precondition: version >= SER_FMT_CLIENT_VER_LOWEST
508         void serialize(std::ostream &os, u8 version, bool disk);
509         // If disk == true: In addition to doing other things, will add
510         // unknown blocks from id-name mapping to wndef
511         void deSerialize(std::istream &is, u8 version, bool disk);
512
513         void serializeNetworkSpecific(std::ostream &os, u16 net_proto_version);
514         void deSerializeNetworkSpecific(std::istream &is);
515
516 private:
517         /*
518                 Private methods
519         */
520
521         void deSerialize_pre22(std::istream &is, u8 version, bool disk);
522
523         /*
524                 Used only internally, because changes can't be tracked
525         */
526
527         inline MapNode &getNodeRef(s16 x, s16 y, s16 z)
528         {
529                 if (!isValidPosition(x, y, z))
530                         throw InvalidPositionException();
531
532                 return data[z * zstride + y * ystride + x];
533         }
534
535         inline MapNode &getNodeRef(v3s16 &p)
536         {
537                 return getNodeRef(p.X, p.Y, p.Z);
538         }
539
540 public:
541         /*
542                 Public member variables
543         */
544
545 #ifndef SERVER // Only on client
546         MapBlockMesh *mesh;
547 #endif
548
549         NodeMetadataList m_node_metadata;
550         NodeTimerList m_node_timers;
551         StaticObjectList m_static_objects;
552
553         static const u32 ystride = MAP_BLOCKSIZE;
554         static const u32 zstride = MAP_BLOCKSIZE * MAP_BLOCKSIZE;
555
556 private:
557         /*
558                 Private member variables
559         */
560
561         // NOTE: Lots of things rely on this being the Map
562         Map *m_parent;
563         // Position in blocks on parent
564         v3s16 m_pos;
565
566         IGameDef *m_gamedef;
567
568         /*
569                 If NULL, block is a dummy block.
570                 Dummy blocks are used for caching not-found-on-disk blocks.
571         */
572         MapNode *data;
573
574         /*
575                 - On the server, this is used for telling whether the
576                   block has been modified from the one on disk.
577                 - On the client, this is used for nothing.
578         */
579         u32 m_modified;
580         u32 m_modified_reason;
581
582         /*
583                 When propagating sunlight and the above block doesn't exist,
584                 sunlight is assumed if this is false.
585
586                 In practice this is set to true if the block is completely
587                 undeground with nothing visible above the ground except
588                 caves.
589         */
590         bool is_underground;
591
592         /*
593                 Set to true if changes has been made that make the old lighting
594                 values wrong but the lighting hasn't been actually updated.
595
596                 If this is false, lighting is exactly right.
597                 If this is true, lighting might be wrong or right.
598         */
599         bool m_lighting_expired;
600
601         // Whether day and night lighting differs
602         bool m_day_night_differs;
603         bool m_day_night_differs_expired;
604
605         bool m_generated;
606
607         /*
608                 When block is removed from active blocks, this is set to gametime.
609                 Value BLOCK_TIMESTAMP_UNDEFINED=0xffffffff means there is no timestamp.
610         */
611         u32 m_timestamp;
612         // The on-disk (or to-be on-disk) timestamp value
613         u32 m_disk_timestamp;
614
615         /*
616                 When the block is accessed, this is set to 0.
617                 Map will unload the block when this reaches a timeout.
618         */
619         float m_usage_timer;
620
621         /*
622                 Reference count; currently used for determining if this block is in
623                 the list of blocks to be drawn.
624         */
625         int m_refcount;
626 };
627
628 typedef std::vector<MapBlock*> MapBlockVect;
629
630 inline bool blockpos_over_limit(v3s16 p)
631 {
632         return
633           (p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
634         || p.X >  MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
635         || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
636         || p.Y >  MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
637         || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
638         || p.Z >  MAP_GENERATION_LIMIT / MAP_BLOCKSIZE);
639 }
640
641 /*
642         Returns the position of the block where the node is located
643 */
644 inline v3s16 getNodeBlockPos(v3s16 p)
645 {
646         return getContainerPos(p, MAP_BLOCKSIZE);
647 }
648
649 inline v2s16 getNodeSectorPos(v2s16 p)
650 {
651         return getContainerPos(p, MAP_BLOCKSIZE);
652 }
653
654 inline s16 getNodeBlockY(s16 y)
655 {
656         return getContainerPos(y, MAP_BLOCKSIZE);
657 }
658
659 inline void getNodeBlockPosWithOffset(const v3s16 &p, v3s16 &block, v3s16 &offset)
660 {
661         getContainerPosWithOffset(p, MAP_BLOCKSIZE, block, offset);
662 }
663
664 inline void getNodeSectorPosWithOffset(const v2s16 &p, v2s16 &block, v2s16 &offset)
665 {
666         getContainerPosWithOffset(p, MAP_BLOCKSIZE, block, offset);
667 }
668
669 /*
670         Get a quick string to describe what a block actually contains
671 */
672 std::string analyze_block(MapBlock *block);
673
674 #endif
675