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.
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
33 #include "mapgen/mapgen.h"
36 class NodeMetadataList;
39 class VoxelManipulator;
41 #define BLOCK_TIMESTAMP_UNDEFINED 0xffffffff
44 //// MapBlock modified reason flags
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)
76 MapBlock(Map *parent, v3s16 pos, IGameDef *gamedef, bool dummy=false);
79 /*virtual u16 nodeContainerId() const
81 return NODECONTAINER_ID_MAPBLOCK;
92 data = new MapNode[nodecount];
93 for (u32 i = 0; i < nodecount; i++)
94 data[i] = MapNode(CONTENT_IGNORE);
96 raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_REALLOCATE);
105 //// Modification tracking methods
107 void raiseModified(u32 mod, u32 reason=MOD_REASON_UNKNOWN)
109 if (mod > m_modified) {
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;
117 if (mod == MOD_STATE_WRITE_NEEDED)
118 contents_cached = false;
121 inline u32 getModified()
126 inline u32 getModifiedReason()
128 return m_modified_reason;
131 std::string getModifiedReasonString();
133 inline void resetModified()
135 m_modified = MOD_STATE_CLEAN;
136 m_modified_reason = 0;
143 inline bool isDummy()
148 inline void unDummify()
150 assert(isDummy()); // Pre-condition
154 // is_underground getter/setter
155 inline bool getIsUnderground()
157 return is_underground;
160 inline void setIsUnderground(bool a_is_underground)
162 is_underground = a_is_underground;
163 raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_SET_IS_UNDERGROUND);
166 inline void setLightingComplete(u16 newflags)
168 if (newflags != m_lighting_complete) {
169 m_lighting_complete = newflags;
170 raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_SET_LIGHTING_COMPLETE);
174 inline u16 getLightingComplete()
176 return m_lighting_complete;
179 inline void setLightingComplete(LightBank bank, u8 direction,
182 assert(direction >= 0 && direction <= 5);
183 if (bank == LIGHTBANK_NIGHT) {
186 u16 newflags = m_lighting_complete;
188 newflags |= 1 << direction;
190 newflags &= ~(1 << direction);
192 setLightingComplete(newflags);
195 inline bool isLightingComplete(LightBank bank, u8 direction)
197 assert(direction >= 0 && direction <= 5);
198 if (bank == LIGHTBANK_NIGHT) {
201 return (m_lighting_complete & (1 << direction)) != 0;
204 inline bool isGenerated()
209 inline void setGenerated(bool b)
211 if (b != m_generated) {
212 raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_SET_GENERATED);
221 inline v3s16 getPos()
226 inline v3s16 getPosRelative()
228 return m_pos_relative;
231 inline core::aabbox3d<s16> getBox()
233 return core::aabbox3d<s16>(getPosRelative(),
235 + v3s16(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE)
240 //// Regular MapNode get-setters
243 inline bool isValidPosition(s16 x, s16 y, s16 z)
246 && x >= 0 && x < MAP_BLOCKSIZE
247 && y >= 0 && y < MAP_BLOCKSIZE
248 && z >= 0 && z < MAP_BLOCKSIZE;
251 inline bool isValidPosition(v3s16 p)
253 return isValidPosition(p.X, p.Y, p.Z);
256 inline MapNode getNode(s16 x, s16 y, s16 z, bool *valid_position)
258 *valid_position = isValidPosition(x, y, z);
260 if (!*valid_position)
261 return {CONTENT_IGNORE};
263 return data[z * zstride + y * ystride + x];
266 inline MapNode getNode(v3s16 p, bool *valid_position)
268 return getNode(p.X, p.Y, p.Z, valid_position);
271 inline MapNode getNodeNoEx(v3s16 p)
274 return getNode(p.X, p.Y, p.Z, &is_valid);
277 inline void setNode(s16 x, s16 y, s16 z, MapNode & n)
279 if (!isValidPosition(x, y, z))
280 throw InvalidPositionException();
282 data[z * zstride + y * ystride + x] = n;
283 raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_SET_NODE);
286 inline void setNode(v3s16 p, MapNode & n)
288 setNode(p.X, p.Y, p.Z, n);
292 //// Non-checking variants of the above
295 inline MapNode getNodeNoCheck(s16 x, s16 y, s16 z, bool *valid_position)
297 *valid_position = data != nullptr;
298 if (!*valid_position)
299 return {CONTENT_IGNORE};
301 return data[z * zstride + y * ystride + x];
304 inline MapNode getNodeNoCheck(v3s16 p, bool *valid_position)
306 return getNodeNoCheck(p.X, p.Y, p.Z, valid_position);
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())
315 inline const MapNode &getNodeUnsafe(s16 x, s16 y, s16 z)
317 return data[z * zstride + y * ystride + x];
320 inline const MapNode &getNodeUnsafe(v3s16 &p)
322 return getNodeUnsafe(p.X, p.Y, p.Z);
325 inline void setNodeNoCheck(s16 x, s16 y, s16 z, MapNode & n)
328 throw InvalidPositionException();
330 data[z * zstride + y * ystride + x] = n;
331 raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_SET_NODE_NO_CHECK);
334 inline void setNodeNoCheck(v3s16 p, MapNode & n)
336 setNodeNoCheck(p.X, p.Y, p.Z, n);
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);
344 // Copies data to VoxelManipulator to getPosRelative()
345 void copyTo(VoxelManipulator &dst);
347 // Copies data from VoxelManipulator getPosRelative()
348 void copyFrom(VoxelManipulator &dst);
350 // Update day-night lighting difference flag.
351 // Sets m_day_night_differs to appropriate value.
352 // These methods don't care about neighboring blocks.
353 void actuallyUpdateDayNightDiff();
355 // Call this to schedule what the previous function does to be done
356 // when the value is actually needed.
357 void expireDayNightDiff();
359 inline bool getDayNightDiff()
361 if (m_day_night_differs_expired)
362 actuallyUpdateDayNightDiff();
363 return m_day_night_differs;
367 //// Miscellaneous stuff
371 Tries to measure ground level.
376 0...MAP_BLOCKSIZE-1 = ground level
378 s16 getGroundLevel(v2s16 p2d);
381 //// Timestamp (see m_timestamp)
384 // NOTE: BLOCK_TIMESTAMP_UNDEFINED=0xffffffff means there is no timestamp.
386 inline void setTimestamp(u32 time)
389 raiseModified(MOD_STATE_WRITE_AT_UNLOAD, MOD_REASON_SET_TIMESTAMP);
392 inline void setTimestampNoChangedFlag(u32 time)
397 inline u32 getTimestamp()
402 inline u32 getDiskTimestamp()
404 return m_disk_timestamp;
408 //// Usage timer (see m_usage_timer)
411 inline void resetUsageTimer()
416 inline void incrementUsageTimer(float dtime)
418 m_usage_timer += dtime;
421 inline float getUsageTimer()
423 return m_usage_timer;
427 //// Reference counting (see m_refcount)
430 inline void refGrab()
435 inline void refDrop()
449 inline NodeTimer getNodeTimer(const v3s16 &p)
451 return m_node_timers.get(p);
454 inline void removeNodeTimer(const v3s16 &p)
456 m_node_timers.remove(p);
459 inline void setNodeTimer(const NodeTimer &t)
461 m_node_timers.set(t);
464 inline void clearNodeTimers()
466 m_node_timers.clear();
473 // These don't write or read version by itself
474 // Set disk to true for on-disk format, false for over-the-network format
475 // Precondition: version >= SER_FMT_VER_LOWEST_WRITE
476 void serialize(std::ostream &os, u8 version, bool disk, int compression_level);
477 // If disk == true: In addition to doing other things, will add
478 // unknown blocks from id-name mapping to wndef
479 void deSerialize(std::istream &is, u8 version, bool disk);
481 void serializeNetworkSpecific(std::ostream &os);
482 void deSerializeNetworkSpecific(std::istream &is);
488 void deSerialize_pre22(std::istream &is, u8 version, bool disk);
491 Used only internally, because changes can't be tracked
494 inline MapNode &getNodeRef(s16 x, s16 y, s16 z)
496 if (!isValidPosition(x, y, z))
497 throw InvalidPositionException();
499 return data[z * zstride + y * ystride + x];
502 inline MapNode &getNodeRef(v3s16 &p)
504 return getNodeRef(p.X, p.Y, p.Z);
509 Public member variables
512 #ifndef SERVER // Only on client
513 MapBlockMesh *mesh = nullptr;
516 NodeMetadataList m_node_metadata;
517 NodeTimerList m_node_timers;
518 StaticObjectList m_static_objects;
520 static const u32 ystride = MAP_BLOCKSIZE;
521 static const u32 zstride = MAP_BLOCKSIZE * MAP_BLOCKSIZE;
523 static const u32 nodecount = MAP_BLOCKSIZE * MAP_BLOCKSIZE * MAP_BLOCKSIZE;
525 //// ABM optimizations ////
526 // Cache of content types
527 std::unordered_set<content_t> contents;
528 // True if content types are cached
529 bool contents_cached = false;
530 // True if we never want to cache content types for this block
531 bool do_not_cache_contents = false;
535 Private member variables
538 // NOTE: Lots of things rely on this being the Map
540 // Position in blocks on parent
543 /* This is the precalculated m_pos_relative value
544 * This caches the value, improving performance by removing 3 s16 multiplications
545 * at runtime on each getPosRelative call
546 * For a 5 minutes runtime with valgrind this removes 3 * 19M s16 multiplications
547 * The gain can be estimated in Release Build to 3 * 100M multiply operations for 5 mins
549 v3s16 m_pos_relative;
554 If NULL, block is a dummy block.
555 Dummy blocks are used for caching not-found-on-disk blocks.
557 MapNode *data = nullptr;
560 - On the server, this is used for telling whether the
561 block has been modified from the one on disk.
562 - On the client, this is used for nothing.
564 u32 m_modified = MOD_STATE_WRITE_NEEDED;
565 u32 m_modified_reason = MOD_REASON_INITIAL;
568 When propagating sunlight and the above block doesn't exist,
569 sunlight is assumed if this is false.
571 In practice this is set to true if the block is completely
572 undeground with nothing visible above the ground except
575 bool is_underground = false;
578 * Each bit indicates if light spreading was finished
579 * in a direction. (Because the neighbor could also be unloaded.)
580 * Bits (most significant first):
581 * nothing, nothing, nothing, nothing,
582 * night X-, night Y-, night Z-, night Z+, night Y+, night X+,
583 * day X-, day Y-, day Z-, day Z+, day Y+, day X+.
585 u16 m_lighting_complete = 0xFFFF;
587 // Whether day and night lighting differs
588 bool m_day_night_differs = false;
589 bool m_day_night_differs_expired = true;
591 bool m_generated = false;
594 When block is removed from active blocks, this is set to gametime.
595 Value BLOCK_TIMESTAMP_UNDEFINED=0xffffffff means there is no timestamp.
597 u32 m_timestamp = BLOCK_TIMESTAMP_UNDEFINED;
598 // The on-disk (or to-be on-disk) timestamp value
599 u32 m_disk_timestamp = BLOCK_TIMESTAMP_UNDEFINED;
602 When the block is accessed, this is set to 0.
603 Map will unload the block when this reaches a timeout.
605 float m_usage_timer = 0;
608 Reference count; currently used for determining if this block is in
609 the list of blocks to be drawn.
614 typedef std::vector<MapBlock*> MapBlockVect;
616 inline bool objectpos_over_limit(v3f p)
618 const float max_limit_bs = MAX_MAP_GENERATION_LIMIT * BS;
619 return p.X < -max_limit_bs ||
620 p.X > max_limit_bs ||
621 p.Y < -max_limit_bs ||
622 p.Y > max_limit_bs ||
623 p.Z < -max_limit_bs ||
627 inline bool blockpos_over_max_limit(v3s16 p)
629 const s16 max_limit_bp = MAX_MAP_GENERATION_LIMIT / MAP_BLOCKSIZE;
630 return p.X < -max_limit_bp ||
631 p.X > max_limit_bp ||
632 p.Y < -max_limit_bp ||
633 p.Y > max_limit_bp ||
634 p.Z < -max_limit_bp ||
639 Returns the position of the block where the node is located
641 inline v3s16 getNodeBlockPos(const v3s16 &p)
643 return getContainerPos(p, MAP_BLOCKSIZE);
646 inline void getNodeBlockPosWithOffset(const v3s16 &p, v3s16 &block, v3s16 &offset)
648 getContainerPosWithOffset(p, MAP_BLOCKSIZE, block, offset);
652 Get a quick string to describe what a block actually contains
654 std::string analyze_block(MapBlock *block);