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;
84 Map *getParent() { return m_parent; }
89 data = new MapNode[nodecount];
90 for (u32 i = 0; i < nodecount; i++)
91 data[i] = MapNode(CONTENT_IGNORE);
93 raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_REALLOCATE);
96 MapNode *getData() { return data; }
99 //// Modification tracking methods
101 void raiseModified(u32 mod, u32 reason = MOD_REASON_UNKNOWN)
103 if (mod > m_modified) {
105 m_modified_reason = reason;
106 if (m_modified >= MOD_STATE_WRITE_AT_UNLOAD)
107 m_disk_timestamp = m_timestamp;
108 } else if (mod == m_modified) {
109 m_modified_reason |= reason;
111 if (mod == MOD_STATE_WRITE_NEEDED)
112 contents_cached = false;
115 inline u32 getModified() { return m_modified; }
117 inline u32 getModifiedReason() { return m_modified_reason; }
119 std::string getModifiedReasonString();
121 inline void resetModified()
123 m_modified = MOD_STATE_CLEAN;
124 m_modified_reason = 0;
131 inline bool isDummy() { return !data; }
133 inline void unDummify()
135 assert(isDummy()); // Pre-condition
139 // is_underground getter/setter
140 inline bool getIsUnderground() { return is_underground; }
142 inline void setIsUnderground(bool a_is_underground)
144 is_underground = a_is_underground;
145 raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_SET_IS_UNDERGROUND);
148 inline void setLightingComplete(u16 newflags)
150 if (newflags != m_lighting_complete) {
151 m_lighting_complete = newflags;
152 raiseModified(MOD_STATE_WRITE_NEEDED,
153 MOD_REASON_SET_LIGHTING_COMPLETE);
157 inline u16 getLightingComplete() { return m_lighting_complete; }
159 inline void setLightingComplete(LightBank bank, u8 direction, bool is_complete)
161 assert(direction >= 0 && direction <= 5);
162 if (bank == LIGHTBANK_NIGHT) {
165 u16 newflags = m_lighting_complete;
167 newflags |= 1 << direction;
169 newflags &= ~(1 << direction);
171 setLightingComplete(newflags);
174 inline bool isLightingComplete(LightBank bank, u8 direction)
176 assert(direction >= 0 && direction <= 5);
177 if (bank == LIGHTBANK_NIGHT) {
180 return (m_lighting_complete & (1 << direction)) != 0;
183 inline bool isGenerated() { return m_generated; }
185 inline void setGenerated(bool b)
187 if (b != m_generated) {
188 raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_SET_GENERATED);
197 inline v3s16 getPos() { return m_pos; }
199 inline v3s16 getPosRelative() { return m_pos_relative; }
201 inline core::aabbox3d<s16> getBox()
203 return core::aabbox3d<s16>(getPosRelative(),
205 v3s16(MAP_BLOCKSIZE, MAP_BLOCKSIZE,
211 //// Regular MapNode get-setters
214 inline bool isValidPosition(s16 x, s16 y, s16 z)
216 return data && x >= 0 && x < MAP_BLOCKSIZE && y >= 0 &&
217 y < MAP_BLOCKSIZE && z >= 0 && z < MAP_BLOCKSIZE;
220 inline bool isValidPosition(v3s16 p) { return isValidPosition(p.X, p.Y, p.Z); }
222 inline MapNode getNode(s16 x, s16 y, s16 z, bool *valid_position)
224 *valid_position = isValidPosition(x, y, z);
226 if (!*valid_position)
227 return {CONTENT_IGNORE};
229 return data[z * zstride + y * ystride + x];
232 inline MapNode getNode(v3s16 p, bool *valid_position)
234 return getNode(p.X, p.Y, p.Z, valid_position);
237 inline MapNode getNodeNoEx(v3s16 p)
240 return getNode(p.X, p.Y, p.Z, &is_valid);
243 inline void setNode(s16 x, s16 y, s16 z, MapNode &n)
245 if (!isValidPosition(x, y, z))
246 throw InvalidPositionException();
248 data[z * zstride + y * ystride + x] = n;
249 raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_SET_NODE);
252 inline void setNode(v3s16 p, MapNode &n) { setNode(p.X, p.Y, p.Z, n); }
255 //// Non-checking variants of the above
258 inline MapNode getNodeNoCheck(s16 x, s16 y, s16 z, bool *valid_position)
260 *valid_position = data != nullptr;
261 if (!*valid_position)
262 return {CONTENT_IGNORE};
264 return data[z * zstride + y * ystride + x];
267 inline MapNode getNodeNoCheck(v3s16 p, bool *valid_position)
269 return getNodeNoCheck(p.X, p.Y, p.Z, valid_position);
273 //// Non-checking, unsafe variants of the above
274 //// MapBlock must be loaded by another function in the same scope/function
275 //// Caller must ensure that this is not a dummy block (by calling isDummy())
278 inline const MapNode &getNodeUnsafe(s16 x, s16 y, s16 z)
280 return data[z * zstride + y * ystride + x];
283 inline const MapNode &getNodeUnsafe(v3s16 &p)
285 return getNodeUnsafe(p.X, p.Y, p.Z);
288 inline void setNodeNoCheck(s16 x, s16 y, s16 z, MapNode &n)
291 throw InvalidPositionException();
293 data[z * zstride + y * ystride + x] = n;
294 raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_SET_NODE_NO_CHECK);
297 inline void setNodeNoCheck(v3s16 p, MapNode &n)
299 setNodeNoCheck(p.X, p.Y, p.Z, n);
302 // These functions consult the parent container if the position
303 // is not valid on this MapBlock.
304 bool isValidPositionParent(v3s16 p);
305 MapNode getNodeParent(v3s16 p, bool *is_valid_position = NULL);
306 void setNodeParent(v3s16 p, MapNode &n);
308 inline void drawbox(s16 x0, s16 y0, s16 z0, s16 w, s16 h, s16 d, MapNode node)
310 for (u16 z = 0; z < d; z++)
311 for (u16 y = 0; y < h; y++)
312 for (u16 x = 0; x < w; x++)
313 setNode(x0 + x, y0 + y, z0 + z, node);
316 // Copies data to VoxelManipulator to getPosRelative()
317 void copyTo(VoxelManipulator &dst);
319 // Copies data from VoxelManipulator getPosRelative()
320 void copyFrom(VoxelManipulator &dst);
322 // Update day-night lighting difference flag.
323 // Sets m_day_night_differs to appropriate value.
324 // These methods don't care about neighboring blocks.
325 void actuallyUpdateDayNightDiff();
327 // Call this to schedule what the previous function does to be done
328 // when the value is actually needed.
329 void expireDayNightDiff();
331 inline bool getDayNightDiff()
333 if (m_day_night_differs_expired)
334 actuallyUpdateDayNightDiff();
335 return m_day_night_differs;
339 //// Miscellaneous stuff
343 Tries to measure ground level.
348 0...MAP_BLOCKSIZE-1 = ground level
350 s16 getGroundLevel(v2s16 p2d);
353 //// Timestamp (see m_timestamp)
356 // NOTE: BLOCK_TIMESTAMP_UNDEFINED=0xffffffff means there is no timestamp.
358 inline void setTimestamp(u32 time)
361 raiseModified(MOD_STATE_WRITE_AT_UNLOAD, MOD_REASON_SET_TIMESTAMP);
364 inline void setTimestampNoChangedFlag(u32 time) { m_timestamp = time; }
366 inline u32 getTimestamp() { return m_timestamp; }
368 inline u32 getDiskTimestamp() { return m_disk_timestamp; }
371 //// Usage timer (see m_usage_timer)
374 inline void resetUsageTimer() { m_usage_timer = 0; }
376 inline void incrementUsageTimer(float dtime) { m_usage_timer += dtime; }
378 inline float getUsageTimer() { return m_usage_timer; }
381 //// Reference counting (see m_refcount)
384 inline void refGrab() { m_refcount++; }
386 inline void refDrop() { m_refcount--; }
388 inline int refGet() { return m_refcount; }
394 inline NodeTimer getNodeTimer(const v3s16 &p) { return m_node_timers.get(p); }
396 inline void removeNodeTimer(const v3s16 &p) { m_node_timers.remove(p); }
398 inline void setNodeTimer(const NodeTimer &t) { m_node_timers.set(t); }
400 inline void clearNodeTimers() { m_node_timers.clear(); }
406 // These don't write or read version by itself
407 // Set disk to true for on-disk format, false for over-the-network format
408 // Precondition: version >= SER_FMT_VER_LOWEST_WRITE
409 void serialize(std::ostream &os, u8 version, bool disk);
410 // If disk == true: In addition to doing other things, will add
411 // unknown blocks from id-name mapping to wndef
412 void deSerialize(std::istream &is, u8 version, bool disk);
414 void serializeNetworkSpecific(std::ostream &os);
415 void deSerializeNetworkSpecific(std::istream &is);
422 void deSerialize_pre22(std::istream &is, u8 version, bool disk);
425 Used only internally, because changes can't be tracked
428 inline MapNode &getNodeRef(s16 x, s16 y, s16 z)
430 if (!isValidPosition(x, y, z))
431 throw InvalidPositionException();
433 return data[z * zstride + y * ystride + x];
436 inline MapNode &getNodeRef(v3s16 &p) { return getNodeRef(p.X, p.Y, p.Z); }
440 Public member variables
443 #ifndef SERVER // Only on client
444 MapBlockMesh *mesh = nullptr;
447 NodeMetadataList m_node_metadata;
448 NodeTimerList m_node_timers;
449 StaticObjectList m_static_objects;
451 static const u32 ystride = MAP_BLOCKSIZE;
452 static const u32 zstride = MAP_BLOCKSIZE * MAP_BLOCKSIZE;
454 static const u32 nodecount = MAP_BLOCKSIZE * MAP_BLOCKSIZE * MAP_BLOCKSIZE;
456 //// ABM optimizations ////
457 // Cache of content types
458 std::unordered_set<content_t> contents;
459 // True if content types are cached
460 bool contents_cached = false;
461 // True if we never want to cache content types for this block
462 bool do_not_cache_contents = false;
466 Private member variables
469 // NOTE: Lots of things rely on this being the Map
471 // Position in blocks on parent
474 /* This is the precalculated m_pos_relative value
475 * This caches the value, improving performance by removing 3 s16 multiplications
476 * at runtime on each getPosRelative call
477 * For a 5 minutes runtime with valgrind this removes 3 * 19M s16 multiplications
478 * The gain can be estimated in Release Build to 3 * 100M multiply operations for
481 v3s16 m_pos_relative;
486 If NULL, block is a dummy block.
487 Dummy blocks are used for caching not-found-on-disk blocks.
489 MapNode *data = nullptr;
492 - On the server, this is used for telling whether the
493 block has been modified from the one on disk.
494 - On the client, this is used for nothing.
496 u32 m_modified = MOD_STATE_WRITE_NEEDED;
497 u32 m_modified_reason = MOD_REASON_INITIAL;
500 When propagating sunlight and the above block doesn't exist,
501 sunlight is assumed if this is false.
503 In practice this is set to true if the block is completely
504 undeground with nothing visible above the ground except
507 bool is_underground = false;
510 * Each bit indicates if light spreading was finished
511 * in a direction. (Because the neighbor could also be unloaded.)
512 * Bits (most significant first):
513 * nothing, nothing, nothing, nothing,
514 * night X-, night Y-, night Z-, night Z+, night Y+, night X+,
515 * day X-, day Y-, day Z-, day Z+, day Y+, day X+.
517 u16 m_lighting_complete = 0xFFFF;
519 // Whether day and night lighting differs
520 bool m_day_night_differs = false;
521 bool m_day_night_differs_expired = true;
523 bool m_generated = false;
526 When block is removed from active blocks, this is set to gametime.
527 Value BLOCK_TIMESTAMP_UNDEFINED=0xffffffff means there is no timestamp.
529 u32 m_timestamp = BLOCK_TIMESTAMP_UNDEFINED;
530 // The on-disk (or to-be on-disk) timestamp value
531 u32 m_disk_timestamp = BLOCK_TIMESTAMP_UNDEFINED;
534 When the block is accessed, this is set to 0.
535 Map will unload the block when this reaches a timeout.
537 float m_usage_timer = 0;
540 Reference count; currently used for determining if this block is in
541 the list of blocks to be drawn.
546 typedef std::vector<MapBlock *> MapBlockVect;
548 inline bool objectpos_over_limit(v3f p)
550 const float max_limit_bs = MAX_MAP_GENERATION_LIMIT * BS;
551 return p.X < -max_limit_bs || p.X > max_limit_bs || p.Y < -max_limit_bs ||
552 p.Y > max_limit_bs || p.Z < -max_limit_bs || p.Z > max_limit_bs;
555 inline bool blockpos_over_max_limit(v3s16 p)
557 const s16 max_limit_bp = MAX_MAP_GENERATION_LIMIT / MAP_BLOCKSIZE;
558 return p.X < -max_limit_bp || p.X > max_limit_bp || p.Y < -max_limit_bp ||
559 p.Y > max_limit_bp || p.Z < -max_limit_bp || p.Z > max_limit_bp;
563 Returns the position of the block where the node is located
565 inline v3s16 getNodeBlockPos(const v3s16 &p)
567 return getContainerPos(p, MAP_BLOCKSIZE);
570 inline void getNodeBlockPosWithOffset(const v3s16 &p, v3s16 &block, v3s16 &offset)
572 getContainerPosWithOffset(p, MAP_BLOCKSIZE, block, offset);
576 Get a quick string to describe what a block actually contains
578 std::string analyze_block(MapBlock *block);