3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
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.
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.
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.
20 #ifndef MAPBLOCK_HEADER
21 #define MAPBLOCK_HEADER
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
38 class NodeMetadataList;
41 class VoxelManipulator;
43 #define BLOCK_TIMESTAMP_UNDEFINED 0xffffffff
45 /*// Named by looking towards z+
55 // NOTE: If this is enabled, set MapBlock to be initialized with
57 /*enum BlockGenerationStatus
59 // Completely non-generated (filled with CONTENT_IGNORE).
61 // Trees or similar might have been blitted from other blocks to here.
62 // Otherwise, the block contains CONTENT_IGNORE
63 BLOCKGEN_FROM_NEIGHBORS=2,
64 // Has been generated, but some neighbors might put some stuff in here
65 // when they are generated.
66 // Does not contain any CONTENT_IGNORE
67 BLOCKGEN_SELF_GENERATED=4,
68 // The block and all its neighbors have been generated
69 BLOCKGEN_FULLY_GENERATED=6
75 NODECONTAINER_ID_MAPBLOCK,
76 NODECONTAINER_ID_MAPSECTOR,
78 NODECONTAINER_ID_MAPBLOCKCACHE,
79 NODECONTAINER_ID_VOXELMANIPULATOR,
85 virtual bool isValidPosition(v3s16 p) = 0;
86 virtual MapNode getNode(v3s16 p) = 0;
87 virtual void setNode(v3s16 p, MapNode & n) = 0;
88 virtual u16 nodeContainerId() const = 0;
90 MapNode getNodeNoEx(v3s16 p)
95 catch(InvalidPositionException &e){
96 return MapNode(CONTENT_IGNORE);
103 //// MapBlock modified reason flags
106 #define MOD_REASON_INITIAL (1 << 0)
107 #define MOD_REASON_REALLOCATE (1 << 1)
108 #define MOD_REASON_SET_IS_UNDERGROUND (1 << 2)
109 #define MOD_REASON_SET_LIGHTING_COMPLETE (1 << 3)
110 #define MOD_REASON_SET_GENERATED (1 << 4)
111 #define MOD_REASON_SET_NODE (1 << 5)
112 #define MOD_REASON_SET_NODE_NO_CHECK (1 << 6)
113 #define MOD_REASON_SET_TIMESTAMP (1 << 7)
114 #define MOD_REASON_REPORT_META_CHANGE (1 << 8)
115 #define MOD_REASON_CLEAR_ALL_OBJECTS (1 << 9)
116 #define MOD_REASON_BLOCK_EXPIRED (1 << 10)
117 #define MOD_REASON_ADD_ACTIVE_OBJECT_RAW (1 << 11)
118 #define MOD_REASON_REMOVE_OBJECTS_REMOVE (1 << 12)
119 #define MOD_REASON_REMOVE_OBJECTS_DEACTIVATE (1 << 13)
120 #define MOD_REASON_TOO_MANY_OBJECTS (1 << 14)
121 #define MOD_REASON_STATIC_DATA_ADDED (1 << 15)
122 #define MOD_REASON_STATIC_DATA_REMOVED (1 << 16)
123 #define MOD_REASON_STATIC_DATA_CHANGED (1 << 17)
124 #define MOD_REASON_EXPIRE_DAYNIGHTDIFF (1 << 18)
125 #define MOD_REASON_UNKNOWN (1 << 19)
131 class MapBlock /*: public NodeContainer*/
134 MapBlock(Map *parent, v3s16 pos, IGameDef *gamedef, bool dummy=false);
137 /*virtual u16 nodeContainerId() const
139 return NODECONTAINER_ID_MAPBLOCK;
150 data = new MapNode[nodecount];
151 for (u32 i = 0; i < nodecount; i++)
152 data[i] = MapNode(CONTENT_IGNORE);
154 raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_REALLOCATE);
158 //// Modification tracking methods
160 void raiseModified(u32 mod, u32 reason=MOD_REASON_UNKNOWN)
162 if (mod > m_modified) {
164 m_modified_reason = reason;
165 if (m_modified >= MOD_STATE_WRITE_AT_UNLOAD)
166 m_disk_timestamp = m_timestamp;
167 } else if (mod == m_modified) {
168 m_modified_reason |= reason;
172 inline u32 getModified()
177 inline u32 getModifiedReason()
179 return m_modified_reason;
182 std::string getModifiedReasonString();
184 inline void resetModified()
186 m_modified = MOD_STATE_CLEAN;
187 m_modified_reason = 0;
194 inline bool isDummy()
196 return (data == NULL);
199 inline void unDummify()
201 assert(isDummy()); // Pre-condition
205 // is_underground getter/setter
206 inline bool getIsUnderground()
208 return is_underground;
211 inline void setIsUnderground(bool a_is_underground)
213 is_underground = a_is_underground;
214 raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_SET_IS_UNDERGROUND);
217 inline void setLightingComplete(u16 newflags)
219 if (newflags != m_lighting_complete) {
220 m_lighting_complete = newflags;
221 raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_SET_LIGHTING_COMPLETE);
225 inline u16 getLightingComplete()
227 return m_lighting_complete;
230 inline void setLightingComplete(LightBank bank, u8 direction,
233 assert(direction >= 0 && direction <= 5);
234 if (bank == LIGHTBANK_NIGHT) {
237 u16 newflags = m_lighting_complete;
239 newflags |= 1 << direction;
241 newflags &= ~(1 << direction);
243 setLightingComplete(newflags);
246 inline bool isLightingComplete(LightBank bank, u8 direction)
248 assert(direction >= 0 && direction <= 5);
249 if (bank == LIGHTBANK_NIGHT) {
252 return (m_lighting_complete & (1 << direction)) != 0;
255 inline bool isGenerated()
260 inline void setGenerated(bool b)
262 if (b != m_generated) {
263 raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_SET_GENERATED);
272 inline v3s16 getPos()
277 inline v3s16 getPosRelative()
279 return m_pos_relative;
282 inline core::aabbox3d<s16> getBox()
284 return core::aabbox3d<s16>(getPosRelative(),
286 + v3s16(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE)
291 //// Regular MapNode get-setters
294 inline bool isValidPosition(s16 x, s16 y, s16 z)
297 && x >= 0 && x < MAP_BLOCKSIZE
298 && y >= 0 && y < MAP_BLOCKSIZE
299 && z >= 0 && z < MAP_BLOCKSIZE;
302 inline bool isValidPosition(v3s16 p)
304 return isValidPosition(p.X, p.Y, p.Z);
307 inline MapNode getNode(s16 x, s16 y, s16 z, bool *valid_position)
309 *valid_position = isValidPosition(x, y, z);
311 if (!*valid_position)
312 return MapNode(CONTENT_IGNORE);
314 return data[z * zstride + y * ystride + x];
317 inline MapNode getNode(v3s16 p, bool *valid_position)
319 return getNode(p.X, p.Y, p.Z, valid_position);
322 inline MapNode getNodeNoEx(v3s16 p)
325 return getNode(p.X, p.Y, p.Z, &is_valid);
328 inline void setNode(s16 x, s16 y, s16 z, MapNode & n)
330 if (!isValidPosition(x, y, z))
331 throw InvalidPositionException();
333 data[z * zstride + y * ystride + x] = n;
334 raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_SET_NODE);
337 inline void setNode(v3s16 p, MapNode & n)
339 setNode(p.X, p.Y, p.Z, n);
343 //// Non-checking variants of the above
346 inline MapNode getNodeNoCheck(s16 x, s16 y, s16 z, bool *valid_position)
348 *valid_position = data != NULL;
350 return MapNode(CONTENT_IGNORE);
352 return data[z * zstride + y * ystride + x];
355 inline MapNode getNodeNoCheck(v3s16 p, bool *valid_position)
357 return getNodeNoCheck(p.X, p.Y, p.Z, valid_position);
361 //// Non-checking, unsafe variants of the above
362 //// MapBlock must be loaded by another function in the same scope/function
363 //// Caller must ensure that this is not a dummy block (by calling isDummy())
366 inline const MapNode &getNodeUnsafe(s16 x, s16 y, s16 z)
368 return data[z * zstride + y * ystride + x];
371 inline const MapNode &getNodeUnsafe(v3s16 &p)
373 return getNodeUnsafe(p.X, p.Y, p.Z);
376 inline void setNodeNoCheck(s16 x, s16 y, s16 z, MapNode & n)
379 throw InvalidPositionException();
381 data[z * zstride + y * ystride + x] = n;
382 raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_SET_NODE_NO_CHECK);
385 inline void setNodeNoCheck(v3s16 p, MapNode & n)
387 setNodeNoCheck(p.X, p.Y, p.Z, n);
390 // These functions consult the parent container if the position
391 // is not valid on this MapBlock.
392 bool isValidPositionParent(v3s16 p);
393 MapNode getNodeParent(v3s16 p, bool *is_valid_position = NULL);
394 void setNodeParent(v3s16 p, MapNode & n);
396 inline void drawbox(s16 x0, s16 y0, s16 z0, s16 w, s16 h, s16 d, MapNode node)
398 for (u16 z = 0; z < d; z++)
399 for (u16 y = 0; y < h; y++)
400 for (u16 x = 0; x < w; x++)
401 setNode(x0 + x, y0 + y, z0 + z, node);
404 // See comments in mapblock.cpp
405 bool propagateSunlight(std::set<v3s16> &light_sources,
406 bool remove_light=false, bool *black_air_left=NULL);
408 // Copies data to VoxelManipulator to getPosRelative()
409 void copyTo(VoxelManipulator &dst);
411 // Copies data from VoxelManipulator getPosRelative()
412 void copyFrom(VoxelManipulator &dst);
414 // Update day-night lighting difference flag.
415 // Sets m_day_night_differs to appropriate value.
416 // These methods don't care about neighboring blocks.
417 void actuallyUpdateDayNightDiff();
419 // Call this to schedule what the previous function does to be done
420 // when the value is actually needed.
421 void expireDayNightDiff();
423 inline bool getDayNightDiff()
425 if (m_day_night_differs_expired)
426 actuallyUpdateDayNightDiff();
427 return m_day_night_differs;
431 //// Miscellaneous stuff
435 Tries to measure ground level.
440 0...MAP_BLOCKSIZE-1 = ground level
442 s16 getGroundLevel(v2s16 p2d);
445 //// Timestamp (see m_timestamp)
448 // NOTE: BLOCK_TIMESTAMP_UNDEFINED=0xffffffff means there is no timestamp.
450 inline void setTimestamp(u32 time)
453 raiseModified(MOD_STATE_WRITE_AT_UNLOAD, MOD_REASON_SET_TIMESTAMP);
456 inline void setTimestampNoChangedFlag(u32 time)
461 inline u32 getTimestamp()
466 inline u32 getDiskTimestamp()
468 return m_disk_timestamp;
472 //// Usage timer (see m_usage_timer)
475 inline void resetUsageTimer()
480 inline void incrementUsageTimer(float dtime)
482 m_usage_timer += dtime;
485 inline float getUsageTimer()
487 return m_usage_timer;
491 //// Reference counting (see m_refcount)
494 inline void refGrab()
499 inline void refDrop()
513 inline NodeTimer getNodeTimer(v3s16 p)
515 return m_node_timers.get(p);
518 inline void removeNodeTimer(v3s16 p)
520 m_node_timers.remove(p);
523 inline void setNodeTimer(const NodeTimer &t)
525 m_node_timers.set(t);
528 inline void clearNodeTimers()
530 m_node_timers.clear();
537 // These don't write or read version by itself
538 // Set disk to true for on-disk format, false for over-the-network format
539 // Precondition: version >= SER_FMT_VER_LOWEST_WRITE
540 void serialize(std::ostream &os, u8 version, bool disk);
541 // If disk == true: In addition to doing other things, will add
542 // unknown blocks from id-name mapping to wndef
543 void deSerialize(std::istream &is, u8 version, bool disk);
545 void serializeNetworkSpecific(std::ostream &os);
546 void deSerializeNetworkSpecific(std::istream &is);
552 void deSerialize_pre22(std::istream &is, u8 version, bool disk);
555 Used only internally, because changes can't be tracked
558 inline MapNode &getNodeRef(s16 x, s16 y, s16 z)
560 if (!isValidPosition(x, y, z))
561 throw InvalidPositionException();
563 return data[z * zstride + y * ystride + x];
566 inline MapNode &getNodeRef(v3s16 &p)
568 return getNodeRef(p.X, p.Y, p.Z);
573 Public member variables
576 #ifndef SERVER // Only on client
580 NodeMetadataList m_node_metadata;
581 NodeTimerList m_node_timers;
582 StaticObjectList m_static_objects;
584 static const u32 ystride = MAP_BLOCKSIZE;
585 static const u32 zstride = MAP_BLOCKSIZE * MAP_BLOCKSIZE;
587 static const u32 nodecount = MAP_BLOCKSIZE * MAP_BLOCKSIZE * MAP_BLOCKSIZE;
591 Private member variables
594 // NOTE: Lots of things rely on this being the Map
596 // Position in blocks on parent
599 /* This is the precalculated m_pos_relative value
600 * This caches the value, improving performance by removing 3 s16 multiplications
601 * at runtime on each getPosRelative call
602 * For a 5 minutes runtime with valgrind this removes 3 * 19M s16 multiplications
603 * The gain can be estimated in Release Build to 3 * 100M multiply operations for 5 mins
605 v3s16 m_pos_relative;
610 If NULL, block is a dummy block.
611 Dummy blocks are used for caching not-found-on-disk blocks.
616 - On the server, this is used for telling whether the
617 block has been modified from the one on disk.
618 - On the client, this is used for nothing.
621 u32 m_modified_reason;
624 When propagating sunlight and the above block doesn't exist,
625 sunlight is assumed if this is false.
627 In practice this is set to true if the block is completely
628 undeground with nothing visible above the ground except
634 * Each bit indicates if light spreading was finished
635 * in a direction. (Because the neighbor could also be unloaded.)
636 * Bits (most significant first):
637 * nothing, nothing, nothing, nothing,
638 * night X-, night Y-, night Z-, night Z+, night Y+, night X+,
639 * day X-, day Y-, day Z-, day Z+, day Y+, day X+.
641 u16 m_lighting_complete;
643 // Whether day and night lighting differs
644 bool m_day_night_differs;
645 bool m_day_night_differs_expired;
650 When block is removed from active blocks, this is set to gametime.
651 Value BLOCK_TIMESTAMP_UNDEFINED=0xffffffff means there is no timestamp.
654 // The on-disk (or to-be on-disk) timestamp value
655 u32 m_disk_timestamp;
658 When the block is accessed, this is set to 0.
659 Map will unload the block when this reaches a timeout.
664 Reference count; currently used for determining if this block is in
665 the list of blocks to be drawn.
670 typedef std::vector<MapBlock*> MapBlockVect;
672 inline bool objectpos_over_limit(v3f p)
674 const float max_limit_bs = MAX_MAP_GENERATION_LIMIT * BS;
675 return p.X < -max_limit_bs ||
676 p.X > max_limit_bs ||
677 p.Y < -max_limit_bs ||
678 p.Y > max_limit_bs ||
679 p.Z < -max_limit_bs ||
683 inline bool blockpos_over_max_limit(v3s16 p)
685 const s16 max_limit_bp = MAX_MAP_GENERATION_LIMIT / MAP_BLOCKSIZE;
686 return p.X < -max_limit_bp ||
687 p.X > max_limit_bp ||
688 p.Y < -max_limit_bp ||
689 p.Y > max_limit_bp ||
690 p.Z < -max_limit_bp ||
695 Returns the position of the block where the node is located
697 inline v3s16 getNodeBlockPos(v3s16 p)
699 return getContainerPos(p, MAP_BLOCKSIZE);
702 inline v2s16 getNodeSectorPos(v2s16 p)
704 return getContainerPos(p, MAP_BLOCKSIZE);
707 inline s16 getNodeBlockY(s16 y)
709 return getContainerPos(y, MAP_BLOCKSIZE);
712 inline void getNodeBlockPosWithOffset(const v3s16 &p, v3s16 &block, v3s16 &offset)
714 getContainerPosWithOffset(p, MAP_BLOCKSIZE, block, offset);
717 inline void getNodeSectorPosWithOffset(const v2s16 &p, v2s16 &block, v2s16 &offset)
719 getContainerPosWithOffset(p, MAP_BLOCKSIZE, block, offset);
723 Get a quick string to describe what a block actually contains
725 std::string analyze_block(MapBlock *block);