]> git.lizzy.rs Git - dragonfireclient.git/blob - src/mapblock.h
Merge pull request #35 from arydevy/patch-1
[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()
85         {
86                 return m_parent;
87         }
88
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
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
121         inline u32 getModified()
122         {
123                 return m_modified;
124         }
125
126         inline u32 getModifiedReason()
127         {
128                 return m_modified_reason;
129         }
130
131         std::string getModifiedReasonString();
132
133         inline void resetModified()
134         {
135                 m_modified = MOD_STATE_CLEAN;
136                 m_modified_reason = 0;
137         }
138
139         ////
140         //// Flags
141         ////
142
143         inline bool isDummy()
144         {
145                 return !data;
146         }
147
148         inline void unDummify()
149         {
150                 assert(isDummy()); // Pre-condition
151                 reallocate();
152         }
153
154         // is_underground getter/setter
155         inline bool getIsUnderground()
156         {
157                 return is_underground;
158         }
159
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
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
174         inline u16 getLightingComplete()
175         {
176                 return m_lighting_complete;
177         }
178
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
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
204         inline bool isGenerated()
205         {
206                 return m_generated;
207         }
208
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
221         inline v3s16 getPos()
222         {
223                 return m_pos;
224         }
225
226         inline v3s16 getPosRelative()
227         {
228                 return m_pos_relative;
229         }
230
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
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
251         inline bool isValidPosition(v3s16 p)
252         {
253                 return isValidPosition(p.X, p.Y, p.Z);
254         }
255
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
266         inline MapNode getNode(v3s16 p, bool *valid_position)
267         {
268                 return getNode(p.X, p.Y, p.Z, valid_position);
269         }
270
271         inline MapNode getNodeNoEx(v3s16 p)
272         {
273                 bool is_valid;
274                 return getNode(p.X, p.Y, p.Z, &is_valid);
275         }
276
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
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
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
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
315         inline const MapNode &getNodeUnsafe(s16 x, s16 y, s16 z)
316         {
317                 return data[z * zstride + y * ystride + x];
318         }
319
320         inline const MapNode &getNodeUnsafe(v3s16 &p)
321         {
322                 return getNodeUnsafe(p.X, p.Y, p.Z);
323         }
324
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
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
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
386         inline void setTimestamp(u32 time)
387         {
388                 m_timestamp = time;
389                 raiseModified(MOD_STATE_WRITE_AT_UNLOAD, MOD_REASON_SET_TIMESTAMP);
390         }
391
392         inline void setTimestampNoChangedFlag(u32 time)
393         {
394                 m_timestamp = time;
395         }
396
397         inline u32 getTimestamp()
398         {
399                 return m_timestamp;
400         }
401
402         inline u32 getDiskTimestamp()
403         {
404                 return m_disk_timestamp;
405         }
406
407         ////
408         //// Usage timer (see m_usage_timer)
409         ////
410
411         inline void resetUsageTimer()
412         {
413                 m_usage_timer = 0;
414         }
415
416         inline void incrementUsageTimer(float dtime)
417         {
418                 m_usage_timer += dtime;
419         }
420
421         inline float getUsageTimer()
422         {
423                 return m_usage_timer;
424         }
425
426         ////
427         //// Reference counting (see m_refcount)
428         ////
429
430         inline void refGrab()
431         {
432                 m_refcount++;
433         }
434
435         inline void refDrop()
436         {
437                 m_refcount--;
438         }
439
440         inline int refGet()
441         {
442                 return m_refcount;
443         }
444
445         ////
446         //// Node Timers
447         ////
448
449         inline NodeTimer getNodeTimer(const v3s16 &p)
450         {
451                 return m_node_timers.get(p);
452         }
453
454         inline void removeNodeTimer(const v3s16 &p)
455         {
456                 m_node_timers.remove(p);
457         }
458
459         inline void setNodeTimer(const NodeTimer &t)
460         {
461                 m_node_timers.set(t);
462         }
463
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
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
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
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
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 */
641 inline v3s16 getNodeBlockPos(const v3s16 &p)
642 {
643         return getContainerPos(p, MAP_BLOCKSIZE);
644 }
645
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);