]> git.lizzy.rs Git - dragonfireclient.git/blob - src/mapblock.h
Merge branch 'master' into master
[dragonfireclient.git] / src / mapblock.h
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
84         Map *getParent() { return m_parent; }
85
86         void reallocate()
87         {
88                 delete[] data;
89                 data = new MapNode[nodecount];
90                 for (u32 i = 0; i < nodecount; i++)
91                         data[i] = MapNode(CONTENT_IGNORE);
92
93                 raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_REALLOCATE);
94         }
95
96         MapNode *getData() { return data; }
97
98         ////
99         //// Modification tracking methods
100         ////
101         void raiseModified(u32 mod, u32 reason = MOD_REASON_UNKNOWN)
102         {
103                 if (mod > m_modified) {
104                         m_modified = mod;
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;
110                 }
111                 if (mod == MOD_STATE_WRITE_NEEDED)
112                         contents_cached = false;
113         }
114
115         inline u32 getModified() { return m_modified; }
116
117         inline u32 getModifiedReason() { return m_modified_reason; }
118
119         std::string getModifiedReasonString();
120
121         inline void resetModified()
122         {
123                 m_modified = MOD_STATE_CLEAN;
124                 m_modified_reason = 0;
125         }
126
127         ////
128         //// Flags
129         ////
130
131         inline bool isDummy() { return !data; }
132
133         inline void unDummify()
134         {
135                 assert(isDummy()); // Pre-condition
136                 reallocate();
137         }
138
139         // is_underground getter/setter
140         inline bool getIsUnderground() { return is_underground; }
141
142         inline void setIsUnderground(bool a_is_underground)
143         {
144                 is_underground = a_is_underground;
145                 raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_SET_IS_UNDERGROUND);
146         }
147
148         inline void setLightingComplete(u16 newflags)
149         {
150                 if (newflags != m_lighting_complete) {
151                         m_lighting_complete = newflags;
152                         raiseModified(MOD_STATE_WRITE_NEEDED,
153                                         MOD_REASON_SET_LIGHTING_COMPLETE);
154                 }
155         }
156
157         inline u16 getLightingComplete() { return m_lighting_complete; }
158
159         inline void setLightingComplete(LightBank bank, u8 direction, bool is_complete)
160         {
161                 assert(direction >= 0 && direction <= 5);
162                 if (bank == LIGHTBANK_NIGHT) {
163                         direction += 6;
164                 }
165                 u16 newflags = m_lighting_complete;
166                 if (is_complete) {
167                         newflags |= 1 << direction;
168                 } else {
169                         newflags &= ~(1 << direction);
170                 }
171                 setLightingComplete(newflags);
172         }
173
174         inline bool isLightingComplete(LightBank bank, u8 direction)
175         {
176                 assert(direction >= 0 && direction <= 5);
177                 if (bank == LIGHTBANK_NIGHT) {
178                         direction += 6;
179                 }
180                 return (m_lighting_complete & (1 << direction)) != 0;
181         }
182
183         inline bool isGenerated() { return m_generated; }
184
185         inline void setGenerated(bool b)
186         {
187                 if (b != m_generated) {
188                         raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_SET_GENERATED);
189                         m_generated = b;
190                 }
191         }
192
193         ////
194         //// Position stuff
195         ////
196
197         inline v3s16 getPos() { return m_pos; }
198
199         inline v3s16 getPosRelative() { return m_pos_relative; }
200
201         inline core::aabbox3d<s16> getBox()
202         {
203                 return core::aabbox3d<s16>(getPosRelative(),
204                                 getPosRelative() +
205                                                 v3s16(MAP_BLOCKSIZE, MAP_BLOCKSIZE,
206                                                                 MAP_BLOCKSIZE) -
207                                                 v3s16(1, 1, 1));
208         }
209
210         ////
211         //// Regular MapNode get-setters
212         ////
213
214         inline bool isValidPosition(s16 x, s16 y, s16 z)
215         {
216                 return data && x >= 0 && x < MAP_BLOCKSIZE && y >= 0 &&
217                        y < MAP_BLOCKSIZE && z >= 0 && z < MAP_BLOCKSIZE;
218         }
219
220         inline bool isValidPosition(v3s16 p) { return isValidPosition(p.X, p.Y, p.Z); }
221
222         inline MapNode getNode(s16 x, s16 y, s16 z, bool *valid_position)
223         {
224                 *valid_position = isValidPosition(x, y, z);
225
226                 if (!*valid_position)
227                         return {CONTENT_IGNORE};
228
229                 return data[z * zstride + y * ystride + x];
230         }
231
232         inline MapNode getNode(v3s16 p, bool *valid_position)
233         {
234                 return getNode(p.X, p.Y, p.Z, valid_position);
235         }
236
237         inline MapNode getNodeNoEx(v3s16 p)
238         {
239                 bool is_valid;
240                 return getNode(p.X, p.Y, p.Z, &is_valid);
241         }
242
243         inline void setNode(s16 x, s16 y, s16 z, MapNode &n)
244         {
245                 if (!isValidPosition(x, y, z))
246                         throw InvalidPositionException();
247
248                 data[z * zstride + y * ystride + x] = n;
249                 raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_SET_NODE);
250         }
251
252         inline void setNode(v3s16 p, MapNode &n) { setNode(p.X, p.Y, p.Z, n); }
253
254         ////
255         //// Non-checking variants of the above
256         ////
257
258         inline MapNode getNodeNoCheck(s16 x, s16 y, s16 z, bool *valid_position)
259         {
260                 *valid_position = data != nullptr;
261                 if (!*valid_position)
262                         return {CONTENT_IGNORE};
263
264                 return data[z * zstride + y * ystride + x];
265         }
266
267         inline MapNode getNodeNoCheck(v3s16 p, bool *valid_position)
268         {
269                 return getNodeNoCheck(p.X, p.Y, p.Z, valid_position);
270         }
271
272         ////
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())
276         ////
277
278         inline const MapNode &getNodeUnsafe(s16 x, s16 y, s16 z)
279         {
280                 return data[z * zstride + y * ystride + x];
281         }
282
283         inline const MapNode &getNodeUnsafe(v3s16 &p)
284         {
285                 return getNodeUnsafe(p.X, p.Y, p.Z);
286         }
287
288         inline void setNodeNoCheck(s16 x, s16 y, s16 z, MapNode &n)
289         {
290                 if (!data)
291                         throw InvalidPositionException();
292
293                 data[z * zstride + y * ystride + x] = n;
294                 raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_SET_NODE_NO_CHECK);
295         }
296
297         inline void setNodeNoCheck(v3s16 p, MapNode &n)
298         {
299                 setNodeNoCheck(p.X, p.Y, p.Z, n);
300         }
301
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);
307
308         inline void drawbox(s16 x0, s16 y0, s16 z0, s16 w, s16 h, s16 d, MapNode node)
309         {
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);
314         }
315
316         // Copies data to VoxelManipulator to getPosRelative()
317         void copyTo(VoxelManipulator &dst);
318
319         // Copies data from VoxelManipulator getPosRelative()
320         void copyFrom(VoxelManipulator &dst);
321
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();
326
327         // Call this to schedule what the previous function does to be done
328         // when the value is actually needed.
329         void expireDayNightDiff();
330
331         inline bool getDayNightDiff()
332         {
333                 if (m_day_night_differs_expired)
334                         actuallyUpdateDayNightDiff();
335                 return m_day_night_differs;
336         }
337
338         ////
339         //// Miscellaneous stuff
340         ////
341
342         /*
343                 Tries to measure ground level.
344                 Return value:
345                         -1 = only air
346                         -2 = only ground
347                         -3 = random fail
348                         0...MAP_BLOCKSIZE-1 = ground level
349         */
350         s16 getGroundLevel(v2s16 p2d);
351
352         ////
353         //// Timestamp (see m_timestamp)
354         ////
355
356         // NOTE: BLOCK_TIMESTAMP_UNDEFINED=0xffffffff means there is no timestamp.
357
358         inline void setTimestamp(u32 time)
359         {
360                 m_timestamp = time;
361                 raiseModified(MOD_STATE_WRITE_AT_UNLOAD, MOD_REASON_SET_TIMESTAMP);
362         }
363
364         inline void setTimestampNoChangedFlag(u32 time) { m_timestamp = time; }
365
366         inline u32 getTimestamp() { return m_timestamp; }
367
368         inline u32 getDiskTimestamp() { return m_disk_timestamp; }
369
370         ////
371         //// Usage timer (see m_usage_timer)
372         ////
373
374         inline void resetUsageTimer() { m_usage_timer = 0; }
375
376         inline void incrementUsageTimer(float dtime) { m_usage_timer += dtime; }
377
378         inline float getUsageTimer() { return m_usage_timer; }
379
380         ////
381         //// Reference counting (see m_refcount)
382         ////
383
384         inline void refGrab() { m_refcount++; }
385
386         inline void refDrop() { m_refcount--; }
387
388         inline int refGet() { return m_refcount; }
389
390         ////
391         //// Node Timers
392         ////
393
394         inline NodeTimer getNodeTimer(const v3s16 &p) { return m_node_timers.get(p); }
395
396         inline void removeNodeTimer(const v3s16 &p) { m_node_timers.remove(p); }
397
398         inline void setNodeTimer(const NodeTimer &t) { m_node_timers.set(t); }
399
400         inline void clearNodeTimers() { m_node_timers.clear(); }
401
402         ////
403         //// Serialization
404         ///
405
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);
413
414         void serializeNetworkSpecific(std::ostream &os);
415         void deSerializeNetworkSpecific(std::istream &is);
416
417 private:
418         /*
419                 Private methods
420         */
421
422         void deSerialize_pre22(std::istream &is, u8 version, bool disk);
423
424         /*
425                 Used only internally, because changes can't be tracked
426         */
427
428         inline MapNode &getNodeRef(s16 x, s16 y, s16 z)
429         {
430                 if (!isValidPosition(x, y, z))
431                         throw InvalidPositionException();
432
433                 return data[z * zstride + y * ystride + x];
434         }
435
436         inline MapNode &getNodeRef(v3s16 &p) { return getNodeRef(p.X, p.Y, p.Z); }
437
438 public:
439         /*
440                 Public member variables
441         */
442
443 #ifndef SERVER // Only on client
444         MapBlockMesh *mesh = nullptr;
445 #endif
446
447         NodeMetadataList m_node_metadata;
448         NodeTimerList m_node_timers;
449         StaticObjectList m_static_objects;
450
451         static const u32 ystride = MAP_BLOCKSIZE;
452         static const u32 zstride = MAP_BLOCKSIZE * MAP_BLOCKSIZE;
453
454         static const u32 nodecount = MAP_BLOCKSIZE * MAP_BLOCKSIZE * MAP_BLOCKSIZE;
455
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;
463
464 private:
465         /*
466                 Private member variables
467         */
468
469         // NOTE: Lots of things rely on this being the Map
470         Map *m_parent;
471         // Position in blocks on parent
472         v3s16 m_pos;
473
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
479          * 5 mins
480          */
481         v3s16 m_pos_relative;
482
483         IGameDef *m_gamedef;
484
485         /*
486                 If NULL, block is a dummy block.
487                 Dummy blocks are used for caching not-found-on-disk blocks.
488         */
489         MapNode *data = nullptr;
490
491         /*
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.
495         */
496         u32 m_modified = MOD_STATE_WRITE_NEEDED;
497         u32 m_modified_reason = MOD_REASON_INITIAL;
498
499         /*
500                 When propagating sunlight and the above block doesn't exist,
501                 sunlight is assumed if this is false.
502
503                 In practice this is set to true if the block is completely
504                 undeground with nothing visible above the ground except
505                 caves.
506         */
507         bool is_underground = false;
508
509         /*!
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+.
516          */
517         u16 m_lighting_complete = 0xFFFF;
518
519         // Whether day and night lighting differs
520         bool m_day_night_differs = false;
521         bool m_day_night_differs_expired = true;
522
523         bool m_generated = false;
524
525         /*
526                 When block is removed from active blocks, this is set to gametime.
527                 Value BLOCK_TIMESTAMP_UNDEFINED=0xffffffff means there is no timestamp.
528         */
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;
532
533         /*
534                 When the block is accessed, this is set to 0.
535                 Map will unload the block when this reaches a timeout.
536         */
537         float m_usage_timer = 0;
538
539         /*
540                 Reference count; currently used for determining if this block is in
541                 the list of blocks to be drawn.
542         */
543         int m_refcount = 0;
544 };
545
546 typedef std::vector<MapBlock *> MapBlockVect;
547
548 inline bool objectpos_over_limit(v3f p)
549 {
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;
553 }
554
555 inline bool blockpos_over_max_limit(v3s16 p)
556 {
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;
560 }
561
562 /*
563         Returns the position of the block where the node is located
564 */
565 inline v3s16 getNodeBlockPos(const v3s16 &p)
566 {
567         return getContainerPos(p, MAP_BLOCKSIZE);
568 }
569
570 inline void getNodeBlockPosWithOffset(const v3s16 &p, v3s16 &block, v3s16 &offset)
571 {
572         getContainerPosWithOffset(p, MAP_BLOCKSIZE, block, offset);
573 }
574
575 /*
576         Get a quick string to describe what a block actually contains
577 */
578 std::string analyze_block(MapBlock *block);