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 #pragma once
21 
22 #include <set>
23 #include "irr_v3d.h"
24 #include "mapnode.h"
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
32 #include "settings.h"
33 #include "mapgen/mapgen.h"
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 ////
44 //// MapBlock modified reason flags
45 ////
46 
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)
68 
69 ////
70 //// MapBlock itself
71 ////
72 
73 class MapBlock
74 {
75 public:
76 	MapBlock(Map *parent, v3s16 pos, IGameDef *gamedef, bool dummy=false);
77 	~MapBlock();
78 
79 	/*virtual u16 nodeContainerId() const
80 	{
81 		return NODECONTAINER_ID_MAPBLOCK;
82 	}*/
83 
getParent()84 	Map * getParent()
85 	{
86 		return m_parent;
87 	}
88 
reallocate()89 	void reallocate()
90 	{
91 		delete[] data;
92 		data = new MapNode[nodecount];
93 		for (u32 i = 0; i < nodecount; i++)
94 			data[i] = MapNode(CONTENT_IGNORE);
95 
96 		raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_REALLOCATE);
97 	}
98 
getData()99 	MapNode* getData()
100 	{
101 		return data;
102 	}
103 
104 	////
105 	//// Modification tracking methods
106 	////
107 	void raiseModified(u32 mod, u32 reason=MOD_REASON_UNKNOWN)
108 	{
109 		if (mod > m_modified) {
110 			m_modified = mod;
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;
116 		}
117 		if (mod == MOD_STATE_WRITE_NEEDED)
118 			contents_cached = false;
119 	}
120 
getModified()121 	inline u32 getModified()
122 	{
123 		return m_modified;
124 	}
125 
getModifiedReason()126 	inline u32 getModifiedReason()
127 	{
128 		return m_modified_reason;
129 	}
130 
131 	std::string getModifiedReasonString();
132 
resetModified()133 	inline void resetModified()
134 	{
135 		m_modified = MOD_STATE_CLEAN;
136 		m_modified_reason = 0;
137 	}
138 
139 	////
140 	//// Flags
141 	////
142 
isDummy()143 	inline bool isDummy()
144 	{
145 		return !data;
146 	}
147 
unDummify()148 	inline void unDummify()
149 	{
150 		assert(isDummy()); // Pre-condition
151 		reallocate();
152 	}
153 
154 	// is_underground getter/setter
getIsUnderground()155 	inline bool getIsUnderground()
156 	{
157 		return is_underground;
158 	}
159 
setIsUnderground(bool a_is_underground)160 	inline void setIsUnderground(bool a_is_underground)
161 	{
162 		is_underground = a_is_underground;
163 		raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_SET_IS_UNDERGROUND);
164 	}
165 
setLightingComplete(u16 newflags)166 	inline void setLightingComplete(u16 newflags)
167 	{
168 		if (newflags != m_lighting_complete) {
169 			m_lighting_complete = newflags;
170 			raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_SET_LIGHTING_COMPLETE);
171 		}
172 	}
173 
getLightingComplete()174 	inline u16 getLightingComplete()
175 	{
176 		return m_lighting_complete;
177 	}
178 
setLightingComplete(LightBank bank,u8 direction,bool is_complete)179 	inline void setLightingComplete(LightBank bank, u8 direction,
180 		bool is_complete)
181 	{
182 		assert(direction >= 0 && direction <= 5);
183 		if (bank == LIGHTBANK_NIGHT) {
184 			direction += 6;
185 		}
186 		u16 newflags = m_lighting_complete;
187 		if (is_complete) {
188 			newflags |= 1 << direction;
189 		} else {
190 			newflags &= ~(1 << direction);
191 		}
192 		setLightingComplete(newflags);
193 	}
194 
isLightingComplete(LightBank bank,u8 direction)195 	inline bool isLightingComplete(LightBank bank, u8 direction)
196 	{
197 		assert(direction >= 0 && direction <= 5);
198 		if (bank == LIGHTBANK_NIGHT) {
199 			direction += 6;
200 		}
201 		return (m_lighting_complete & (1 << direction)) != 0;
202 	}
203 
isGenerated()204 	inline bool isGenerated()
205 	{
206 		return m_generated;
207 	}
208 
setGenerated(bool b)209 	inline void setGenerated(bool b)
210 	{
211 		if (b != m_generated) {
212 			raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_SET_GENERATED);
213 			m_generated = b;
214 		}
215 	}
216 
217 	////
218 	//// Position stuff
219 	////
220 
getPos()221 	inline v3s16 getPos()
222 	{
223 		return m_pos;
224 	}
225 
getPosRelative()226 	inline v3s16 getPosRelative()
227 	{
228 		return m_pos_relative;
229 	}
230 
getBox()231 	inline core::aabbox3d<s16> getBox()
232 	{
233 		return core::aabbox3d<s16>(getPosRelative(),
234 				getPosRelative()
235 				+ v3s16(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE)
236 				- v3s16(1,1,1));
237 	}
238 
239 	////
240 	//// Regular MapNode get-setters
241 	////
242 
isValidPosition(s16 x,s16 y,s16 z)243 	inline bool isValidPosition(s16 x, s16 y, s16 z)
244 	{
245 		return data
246 			&& x >= 0 && x < MAP_BLOCKSIZE
247 			&& y >= 0 && y < MAP_BLOCKSIZE
248 			&& z >= 0 && z < MAP_BLOCKSIZE;
249 	}
250 
isValidPosition(v3s16 p)251 	inline bool isValidPosition(v3s16 p)
252 	{
253 		return isValidPosition(p.X, p.Y, p.Z);
254 	}
255 
getNode(s16 x,s16 y,s16 z,bool * valid_position)256 	inline MapNode getNode(s16 x, s16 y, s16 z, bool *valid_position)
257 	{
258 		*valid_position = isValidPosition(x, y, z);
259 
260 		if (!*valid_position)
261 			return {CONTENT_IGNORE};
262 
263 		return data[z * zstride + y * ystride + x];
264 	}
265 
getNode(v3s16 p,bool * valid_position)266 	inline MapNode getNode(v3s16 p, bool *valid_position)
267 	{
268 		return getNode(p.X, p.Y, p.Z, valid_position);
269 	}
270 
getNodeNoEx(v3s16 p)271 	inline MapNode getNodeNoEx(v3s16 p)
272 	{
273 		bool is_valid;
274 		return getNode(p.X, p.Y, p.Z, &is_valid);
275 	}
276 
setNode(s16 x,s16 y,s16 z,MapNode & n)277 	inline void setNode(s16 x, s16 y, s16 z, MapNode & n)
278 	{
279 		if (!isValidPosition(x, y, z))
280 			throw InvalidPositionException();
281 
282 		data[z * zstride + y * ystride + x] = n;
283 		raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_SET_NODE);
284 	}
285 
setNode(v3s16 p,MapNode & n)286 	inline void setNode(v3s16 p, MapNode & n)
287 	{
288 		setNode(p.X, p.Y, p.Z, n);
289 	}
290 
291 	////
292 	//// Non-checking variants of the above
293 	////
294 
getNodeNoCheck(s16 x,s16 y,s16 z,bool * valid_position)295 	inline MapNode getNodeNoCheck(s16 x, s16 y, s16 z, bool *valid_position)
296 	{
297 		*valid_position = data != nullptr;
298 		if (!*valid_position)
299 			return {CONTENT_IGNORE};
300 
301 		return data[z * zstride + y * ystride + x];
302 	}
303 
getNodeNoCheck(v3s16 p,bool * valid_position)304 	inline MapNode getNodeNoCheck(v3s16 p, bool *valid_position)
305 	{
306 		return getNodeNoCheck(p.X, p.Y, p.Z, valid_position);
307 	}
308 
309 	////
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())
313 	////
314 
getNodeUnsafe(s16 x,s16 y,s16 z)315 	inline const MapNode &getNodeUnsafe(s16 x, s16 y, s16 z)
316 	{
317 		return data[z * zstride + y * ystride + x];
318 	}
319 
getNodeUnsafe(v3s16 & p)320 	inline const MapNode &getNodeUnsafe(v3s16 &p)
321 	{
322 		return getNodeUnsafe(p.X, p.Y, p.Z);
323 	}
324 
setNodeNoCheck(s16 x,s16 y,s16 z,MapNode & n)325 	inline void setNodeNoCheck(s16 x, s16 y, s16 z, MapNode & n)
326 	{
327 		if (!data)
328 			throw InvalidPositionException();
329 
330 		data[z * zstride + y * ystride + x] = n;
331 		raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_SET_NODE_NO_CHECK);
332 	}
333 
setNodeNoCheck(v3s16 p,MapNode & n)334 	inline void setNodeNoCheck(v3s16 p, MapNode & n)
335 	{
336 		setNodeNoCheck(p.X, p.Y, p.Z, n);
337 	}
338 
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);
343 
344 	// Copies data to VoxelManipulator to getPosRelative()
345 	void copyTo(VoxelManipulator &dst);
346 
347 	// Copies data from VoxelManipulator getPosRelative()
348 	void copyFrom(VoxelManipulator &dst);
349 
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();
354 
355 	// Call this to schedule what the previous function does to be done
356 	// when the value is actually needed.
357 	void expireDayNightDiff();
358 
getDayNightDiff()359 	inline bool getDayNightDiff()
360 	{
361 		if (m_day_night_differs_expired)
362 			actuallyUpdateDayNightDiff();
363 		return m_day_night_differs;
364 	}
365 
366 	////
367 	//// Miscellaneous stuff
368 	////
369 
370 	/*
371 		Tries to measure ground level.
372 		Return value:
373 			-1 = only air
374 			-2 = only ground
375 			-3 = random fail
376 			0...MAP_BLOCKSIZE-1 = ground level
377 	*/
378 	s16 getGroundLevel(v2s16 p2d);
379 
380 	////
381 	//// Timestamp (see m_timestamp)
382 	////
383 
384 	// NOTE: BLOCK_TIMESTAMP_UNDEFINED=0xffffffff means there is no timestamp.
385 
setTimestamp(u32 time)386 	inline void setTimestamp(u32 time)
387 	{
388 		m_timestamp = time;
389 		raiseModified(MOD_STATE_WRITE_AT_UNLOAD, MOD_REASON_SET_TIMESTAMP);
390 	}
391 
setTimestampNoChangedFlag(u32 time)392 	inline void setTimestampNoChangedFlag(u32 time)
393 	{
394 		m_timestamp = time;
395 	}
396 
getTimestamp()397 	inline u32 getTimestamp()
398 	{
399 		return m_timestamp;
400 	}
401 
getDiskTimestamp()402 	inline u32 getDiskTimestamp()
403 	{
404 		return m_disk_timestamp;
405 	}
406 
407 	////
408 	//// Usage timer (see m_usage_timer)
409 	////
410 
resetUsageTimer()411 	inline void resetUsageTimer()
412 	{
413 		m_usage_timer = 0;
414 	}
415 
incrementUsageTimer(float dtime)416 	inline void incrementUsageTimer(float dtime)
417 	{
418 		m_usage_timer += dtime;
419 	}
420 
getUsageTimer()421 	inline float getUsageTimer()
422 	{
423 		return m_usage_timer;
424 	}
425 
426 	////
427 	//// Reference counting (see m_refcount)
428 	////
429 
refGrab()430 	inline void refGrab()
431 	{
432 		m_refcount++;
433 	}
434 
refDrop()435 	inline void refDrop()
436 	{
437 		m_refcount--;
438 	}
439 
refGet()440 	inline int refGet()
441 	{
442 		return m_refcount;
443 	}
444 
445 	////
446 	//// Node Timers
447 	////
448 
getNodeTimer(const v3s16 & p)449 	inline NodeTimer getNodeTimer(const v3s16 &p)
450 	{
451 		return m_node_timers.get(p);
452 	}
453 
removeNodeTimer(const v3s16 & p)454 	inline void removeNodeTimer(const v3s16 &p)
455 	{
456 		m_node_timers.remove(p);
457 	}
458 
setNodeTimer(const NodeTimer & t)459 	inline void setNodeTimer(const NodeTimer &t)
460 	{
461 		m_node_timers.set(t);
462 	}
463 
clearNodeTimers()464 	inline void clearNodeTimers()
465 	{
466 		m_node_timers.clear();
467 	}
468 
469 	////
470 	//// Serialization
471 	///
472 
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);
480 
481 	void serializeNetworkSpecific(std::ostream &os);
482 	void deSerializeNetworkSpecific(std::istream &is);
483 private:
484 	/*
485 		Private methods
486 	*/
487 
488 	void deSerialize_pre22(std::istream &is, u8 version, bool disk);
489 
490 	/*
491 		Used only internally, because changes can't be tracked
492 	*/
493 
getNodeRef(s16 x,s16 y,s16 z)494 	inline MapNode &getNodeRef(s16 x, s16 y, s16 z)
495 	{
496 		if (!isValidPosition(x, y, z))
497 			throw InvalidPositionException();
498 
499 		return data[z * zstride + y * ystride + x];
500 	}
501 
getNodeRef(v3s16 & p)502 	inline MapNode &getNodeRef(v3s16 &p)
503 	{
504 		return getNodeRef(p.X, p.Y, p.Z);
505 	}
506 
507 public:
508 	/*
509 		Public member variables
510 	*/
511 
512 #ifndef SERVER // Only on client
513 	MapBlockMesh *mesh = nullptr;
514 #endif
515 
516 	NodeMetadataList m_node_metadata;
517 	NodeTimerList m_node_timers;
518 	StaticObjectList m_static_objects;
519 
520 	static const u32 ystride = MAP_BLOCKSIZE;
521 	static const u32 zstride = MAP_BLOCKSIZE * MAP_BLOCKSIZE;
522 
523 	static const u32 nodecount = MAP_BLOCKSIZE * MAP_BLOCKSIZE * MAP_BLOCKSIZE;
524 
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;
532 
533 private:
534 	/*
535 		Private member variables
536 	*/
537 
538 	// NOTE: Lots of things rely on this being the Map
539 	Map *m_parent;
540 	// Position in blocks on parent
541 	v3s16 m_pos;
542 
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
548 	*/
549 	v3s16 m_pos_relative;
550 
551 	IGameDef *m_gamedef;
552 
553 	/*
554 		If NULL, block is a dummy block.
555 		Dummy blocks are used for caching not-found-on-disk blocks.
556 	*/
557 	MapNode *data = nullptr;
558 
559 	/*
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.
563 	*/
564 	u32 m_modified = MOD_STATE_WRITE_NEEDED;
565 	u32 m_modified_reason = MOD_REASON_INITIAL;
566 
567 	/*
568 		When propagating sunlight and the above block doesn't exist,
569 		sunlight is assumed if this is false.
570 
571 		In practice this is set to true if the block is completely
572 		undeground with nothing visible above the ground except
573 		caves.
574 	*/
575 	bool is_underground = false;
576 
577 	/*!
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+.
584 	*/
585 	u16 m_lighting_complete = 0xFFFF;
586 
587 	// Whether day and night lighting differs
588 	bool m_day_night_differs = false;
589 	bool m_day_night_differs_expired = true;
590 
591 	bool m_generated = false;
592 
593 	/*
594 		When block is removed from active blocks, this is set to gametime.
595 		Value BLOCK_TIMESTAMP_UNDEFINED=0xffffffff means there is no timestamp.
596 	*/
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;
600 
601 	/*
602 		When the block is accessed, this is set to 0.
603 		Map will unload the block when this reaches a timeout.
604 	*/
605 	float m_usage_timer = 0;
606 
607 	/*
608 		Reference count; currently used for determining if this block is in
609 		the list of blocks to be drawn.
610 	*/
611 	int m_refcount = 0;
612 };
613 
614 typedef std::vector<MapBlock*> MapBlockVect;
615 
objectpos_over_limit(v3f p)616 inline bool objectpos_over_limit(v3f p)
617 {
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 ||
624 		p.Z >  max_limit_bs;
625 }
626 
blockpos_over_max_limit(v3s16 p)627 inline bool blockpos_over_max_limit(v3s16 p)
628 {
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 ||
635 		p.Z >  max_limit_bp;
636 }
637 
638 /*
639 	Returns the position of the block where the node is located
640 */
getNodeBlockPos(const v3s16 & p)641 inline v3s16 getNodeBlockPos(const v3s16 &p)
642 {
643 	return getContainerPos(p, MAP_BLOCKSIZE);
644 }
645 
getNodeBlockPosWithOffset(const v3s16 & p,v3s16 & block,v3s16 & offset)646 inline void getNodeBlockPosWithOffset(const v3s16 &p, v3s16 &block, v3s16 &offset)
647 {
648 	getContainerPosWithOffset(p, MAP_BLOCKSIZE, block, offset);
649 }
650 
651 /*
652 	Get a quick string to describe what a block actually contains
653 */
654 std::string analyze_block(MapBlock *block);
655