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