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() const
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 //// Timestamp (see m_timestamp)
370 // NOTE: BLOCK_TIMESTAMP_UNDEFINED=0xffffffff means there is no timestamp.
372 inline void setTimestamp(u32 time)
375 raiseModified(MOD_STATE_WRITE_AT_UNLOAD, MOD_REASON_SET_TIMESTAMP);
378 inline void setTimestampNoChangedFlag(u32 time)
383 inline u32 getTimestamp()
388 inline u32 getDiskTimestamp()
390 return m_disk_timestamp;
394 //// Usage timer (see m_usage_timer)
397 inline void resetUsageTimer()
402 inline void incrementUsageTimer(float dtime)
404 m_usage_timer += dtime;
407 inline float getUsageTimer()
409 return m_usage_timer;
413 //// Reference counting (see m_refcount)
416 inline void refGrab()
421 inline void refDrop()
435 inline NodeTimer getNodeTimer(const v3s16 &p)
437 return m_node_timers.get(p);
440 inline void removeNodeTimer(const v3s16 &p)
442 m_node_timers.remove(p);
445 inline void setNodeTimer(const NodeTimer &t)
447 m_node_timers.set(t);
450 inline void clearNodeTimers()
452 m_node_timers.clear();
459 // These don't write or read version by itself
460 // Set disk to true for on-disk format, false for over-the-network format
461 // Precondition: version >= SER_FMT_VER_LOWEST_WRITE
462 void serialize(std::ostream &result, u8 version, bool disk, int compression_level);
463 // If disk == true: In addition to doing other things, will add
464 // unknown blocks from id-name mapping to wndef
465 void deSerialize(std::istream &is, u8 version, bool disk);
467 void serializeNetworkSpecific(std::ostream &os);
468 void deSerializeNetworkSpecific(std::istream &is);
474 void deSerialize_pre22(std::istream &is, u8 version, bool disk);
477 Used only internally, because changes can't be tracked
480 inline MapNode &getNodeRef(s16 x, s16 y, s16 z)
482 if (!isValidPosition(x, y, z))
483 throw InvalidPositionException();
485 return data[z * zstride + y * ystride + x];
488 inline MapNode &getNodeRef(v3s16 &p)
490 return getNodeRef(p.X, p.Y, p.Z);
495 Public member variables
498 #ifndef SERVER // Only on client
499 MapBlockMesh *mesh = nullptr;
502 NodeMetadataList m_node_metadata;
503 NodeTimerList m_node_timers;
504 StaticObjectList m_static_objects;
506 static const u32 ystride = MAP_BLOCKSIZE;
507 static const u32 zstride = MAP_BLOCKSIZE * MAP_BLOCKSIZE;
509 static const u32 nodecount = MAP_BLOCKSIZE * MAP_BLOCKSIZE * MAP_BLOCKSIZE;
511 //// ABM optimizations ////
512 // Cache of content types
513 std::unordered_set<content_t> contents;
514 // True if content types are cached
515 bool contents_cached = false;
516 // True if we never want to cache content types for this block
517 bool do_not_cache_contents = false;
521 Private member variables
524 // NOTE: Lots of things rely on this being the Map
526 // Position in blocks on parent
529 /* This is the precalculated m_pos_relative value
530 * This caches the value, improving performance by removing 3 s16 multiplications
531 * at runtime on each getPosRelative call
532 * For a 5 minutes runtime with valgrind this removes 3 * 19M s16 multiplications
533 * The gain can be estimated in Release Build to 3 * 100M multiply operations for 5 mins
535 v3s16 m_pos_relative;
540 If NULL, block is a dummy block.
541 Dummy blocks are used for caching not-found-on-disk blocks.
543 MapNode *data = nullptr;
546 - On the server, this is used for telling whether the
547 block has been modified from the one on disk.
548 - On the client, this is used for nothing.
550 u32 m_modified = MOD_STATE_WRITE_NEEDED;
551 u32 m_modified_reason = MOD_REASON_INITIAL;
554 When propagating sunlight and the above block doesn't exist,
555 sunlight is assumed if this is false.
557 In practice this is set to true if the block is completely
558 undeground with nothing visible above the ground except
561 bool is_underground = false;
564 * Each bit indicates if light spreading was finished
565 * in a direction. (Because the neighbor could also be unloaded.)
566 * Bits (most significant first):
567 * nothing, nothing, nothing, nothing,
568 * night X-, night Y-, night Z-, night Z+, night Y+, night X+,
569 * day X-, day Y-, day Z-, day Z+, day Y+, day X+.
571 u16 m_lighting_complete = 0xFFFF;
573 // Whether day and night lighting differs
574 bool m_day_night_differs = false;
575 bool m_day_night_differs_expired = true;
577 bool m_generated = false;
580 When block is removed from active blocks, this is set to gametime.
581 Value BLOCK_TIMESTAMP_UNDEFINED=0xffffffff means there is no timestamp.
583 u32 m_timestamp = BLOCK_TIMESTAMP_UNDEFINED;
584 // The on-disk (or to-be on-disk) timestamp value
585 u32 m_disk_timestamp = BLOCK_TIMESTAMP_UNDEFINED;
588 When the block is accessed, this is set to 0.
589 Map will unload the block when this reaches a timeout.
591 float m_usage_timer = 0;
594 Reference count; currently used for determining if this block is in
595 the list of blocks to be drawn.
600 typedef std::vector<MapBlock*> MapBlockVect;
602 inline bool objectpos_over_limit(v3f p)
604 const float max_limit_bs = (MAX_MAP_GENERATION_LIMIT + 0.5f) * BS;
605 return p.X < -max_limit_bs ||
606 p.X > max_limit_bs ||
607 p.Y < -max_limit_bs ||
608 p.Y > max_limit_bs ||
609 p.Z < -max_limit_bs ||
613 inline bool blockpos_over_max_limit(v3s16 p)
615 const s16 max_limit_bp = MAX_MAP_GENERATION_LIMIT / MAP_BLOCKSIZE;
616 return p.X < -max_limit_bp ||
617 p.X > max_limit_bp ||
618 p.Y < -max_limit_bp ||
619 p.Y > max_limit_bp ||
620 p.Z < -max_limit_bp ||
625 Returns the position of the block where the node is located
627 inline v3s16 getNodeBlockPos(const v3s16 &p)
629 return getContainerPos(p, MAP_BLOCKSIZE);
632 inline void getNodeBlockPosWithOffset(const v3s16 &p, v3s16 &block, v3s16 &offset)
634 getContainerPosWithOffset(p, MAP_BLOCKSIZE, block, offset);
638 Get a quick string to describe what a block actually contains
640 std::string analyze_block(MapBlock *block);