7f901e5d35ec24ca48663f6556e7021310383c4e
[oweals/minetest.git] / src / mapblock.h
1 /*
2 Minetest-c55
3 Copyright (C) 2010 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 #ifndef MAPBLOCK_HEADER
21 #define MAPBLOCK_HEADER
22
23 #include <jmutex.h>
24 #include <jmutexautolock.h>
25 #include <exception>
26 #include "debug.h"
27 #include "irrlichttypes.h"
28 #include "irr_v3d.h"
29 #include "irr_aabb3d.h"
30 #include "mapnode.h"
31 #include "exceptions.h"
32 #include "serialization.h"
33 #include "constants.h"
34 #include "voxel.h"
35 #include "staticobject.h"
36 #include "nodemetadata.h"
37 #include "nodetimer.h"
38 #include "modifiedstate.h"
39 #include "util/numeric.h" // getContainerPos
40
41 class Map;
42 class NodeMetadataList;
43 class IGameDef;
44 class MapBlockMesh;
45
46 #define BLOCK_TIMESTAMP_UNDEFINED 0xffffffff
47
48 /*// Named by looking towards z+
49 enum{
50         FACE_BACK=0,
51         FACE_TOP,
52         FACE_RIGHT,
53         FACE_FRONT,
54         FACE_BOTTOM,
55         FACE_LEFT
56 };*/
57
58 // NOTE: If this is enabled, set MapBlock to be initialized with
59 //       CONTENT_IGNORE.
60 /*enum BlockGenerationStatus
61 {
62         // Completely non-generated (filled with CONTENT_IGNORE).
63         BLOCKGEN_UNTOUCHED=0,
64         // Trees or similar might have been blitted from other blocks to here.
65         // Otherwise, the block contains CONTENT_IGNORE
66         BLOCKGEN_FROM_NEIGHBORS=2,
67         // Has been generated, but some neighbors might put some stuff in here
68         // when they are generated.
69         // Does not contain any CONTENT_IGNORE
70         BLOCKGEN_SELF_GENERATED=4,
71         // The block and all its neighbors have been generated
72         BLOCKGEN_FULLY_GENERATED=6
73 };*/
74
75 #if 0
76 enum
77 {
78         NODECONTAINER_ID_MAPBLOCK,
79         NODECONTAINER_ID_MAPSECTOR,
80         NODECONTAINER_ID_MAP,
81         NODECONTAINER_ID_MAPBLOCKCACHE,
82         NODECONTAINER_ID_VOXELMANIPULATOR,
83 };
84
85 class NodeContainer
86 {
87 public:
88         virtual bool isValidPosition(v3s16 p) = 0;
89         virtual MapNode getNode(v3s16 p) = 0;
90         virtual void setNode(v3s16 p, MapNode & n) = 0;
91         virtual u16 nodeContainerId() const = 0;
92
93         MapNode getNodeNoEx(v3s16 p)
94         {
95                 try{
96                         return getNode(p);
97                 }
98                 catch(InvalidPositionException &e){
99                         return MapNode(CONTENT_IGNORE);
100                 }
101         }
102 };
103 #endif
104
105 /*
106         MapBlock itself
107 */
108
109 class MapBlock /*: public NodeContainer*/
110 {
111 public:
112         MapBlock(Map *parent, v3s16 pos, IGameDef *gamedef, bool dummy=false);
113         ~MapBlock();
114         
115         /*virtual u16 nodeContainerId() const
116         {
117                 return NODECONTAINER_ID_MAPBLOCK;
118         }*/
119         
120         Map * getParent()
121         {
122                 return m_parent;
123         }
124
125         void reallocate()
126         {
127                 if(data != NULL)
128                         delete[] data;
129                 u32 l = MAP_BLOCKSIZE * MAP_BLOCKSIZE * MAP_BLOCKSIZE;
130                 data = new MapNode[l];
131                 for(u32 i=0; i<l; i++){
132                         //data[i] = MapNode();
133                         data[i] = MapNode(CONTENT_IGNORE);
134                 }
135                 raiseModified(MOD_STATE_WRITE_NEEDED, "reallocate");
136         }
137
138         /*
139                 Flags
140         */
141
142         bool isDummy()
143         {
144                 return (data == NULL);
145         }
146         void unDummify()
147         {
148                 assert(isDummy());
149                 reallocate();
150         }
151         
152         // m_modified methods
153         void raiseModified(u32 mod, const std::string &reason="unknown")
154         {
155                 if(mod > m_modified){
156                         m_modified = mod;
157                         m_modified_reason = reason;
158                         m_modified_reason_too_long = false;
159
160                         if(m_modified >= MOD_STATE_WRITE_AT_UNLOAD){
161                                 m_disk_timestamp = m_timestamp;
162                         }
163                 } else if(mod == m_modified){
164                         if(!m_modified_reason_too_long){
165                                 if(m_modified_reason.size() < 40)
166                                         m_modified_reason += ", " + reason;
167                                 else{
168                                         m_modified_reason += "...";
169                                         m_modified_reason_too_long = true;
170                                 }
171                         }
172                 }
173         }
174         u32 getModified()
175         {
176                 return m_modified;
177         }
178         std::string getModifiedReason()
179         {
180                 return m_modified_reason;
181         }
182         void resetModified()
183         {
184                 m_modified = MOD_STATE_CLEAN;
185                 m_modified_reason = "none";
186                 m_modified_reason_too_long = false;
187         }
188         
189         // is_underground getter/setter
190         bool getIsUnderground()
191         {
192                 return is_underground;
193         }
194         void setIsUnderground(bool a_is_underground)
195         {
196                 is_underground = a_is_underground;
197                 raiseModified(MOD_STATE_WRITE_NEEDED, "setIsUnderground");
198         }
199
200         void setLightingExpired(bool expired)
201         {
202                 if(expired != m_lighting_expired){
203                         m_lighting_expired = expired;
204                         raiseModified(MOD_STATE_WRITE_NEEDED, "setLightingExpired");
205                 }
206         }
207         bool getLightingExpired()
208         {
209                 return m_lighting_expired;
210         }
211
212         bool isGenerated()
213         {
214                 return m_generated;
215         }
216         void setGenerated(bool b)
217         {
218                 if(b != m_generated){
219                         raiseModified(MOD_STATE_WRITE_NEEDED, "setGenerated");
220                         m_generated = b;
221                 }
222         }
223
224         bool isValid()
225         {
226                 if(m_lighting_expired)
227                         return false;
228                 if(data == NULL)
229                         return false;
230                 return true;
231         }
232
233         /*
234                 Position stuff
235         */
236
237         v3s16 getPos()
238         {
239                 return m_pos;
240         }
241                 
242         v3s16 getPosRelative()
243         {
244                 return m_pos * MAP_BLOCKSIZE;
245         }
246                 
247         core::aabbox3d<s16> getBox()
248         {
249                 return core::aabbox3d<s16>(getPosRelative(),
250                                 getPosRelative()
251                                 + v3s16(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE)
252                                 - v3s16(1,1,1));
253         }
254
255         /*
256                 Regular MapNode get-setters
257         */
258         
259         bool isValidPosition(v3s16 p)
260         {
261                 if(data == NULL)
262                         return false;
263                 return (p.X >= 0 && p.X < MAP_BLOCKSIZE
264                                 && p.Y >= 0 && p.Y < MAP_BLOCKSIZE
265                                 && p.Z >= 0 && p.Z < MAP_BLOCKSIZE);
266         }
267
268         MapNode getNode(s16 x, s16 y, s16 z)
269         {
270                 if(data == NULL)
271                         throw InvalidPositionException();
272                 if(x < 0 || x >= MAP_BLOCKSIZE) throw InvalidPositionException();
273                 if(y < 0 || y >= MAP_BLOCKSIZE) throw InvalidPositionException();
274                 if(z < 0 || z >= MAP_BLOCKSIZE) throw InvalidPositionException();
275                 return data[z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + y*MAP_BLOCKSIZE + x];
276         }
277         
278         MapNode getNode(v3s16 p)
279         {
280                 return getNode(p.X, p.Y, p.Z);
281         }
282         
283         MapNode getNodeNoEx(v3s16 p)
284         {
285                 try{
286                         return getNode(p.X, p.Y, p.Z);
287                 }catch(InvalidPositionException &e){
288                         return MapNode(CONTENT_IGNORE);
289                 }
290         }
291         
292         void setNode(s16 x, s16 y, s16 z, MapNode & n)
293         {
294                 if(data == NULL)
295                         throw InvalidPositionException();
296                 if(x < 0 || x >= MAP_BLOCKSIZE) throw InvalidPositionException();
297                 if(y < 0 || y >= MAP_BLOCKSIZE) throw InvalidPositionException();
298                 if(z < 0 || z >= MAP_BLOCKSIZE) throw InvalidPositionException();
299                 data[z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + y*MAP_BLOCKSIZE + x] = n;
300                 raiseModified(MOD_STATE_WRITE_NEEDED, "setNode");
301         }
302         
303         void setNode(v3s16 p, MapNode & n)
304         {
305                 setNode(p.X, p.Y, p.Z, n);
306         }
307
308         /*
309                 Non-checking variants of the above
310         */
311
312         MapNode getNodeNoCheck(s16 x, s16 y, s16 z)
313         {
314                 if(data == NULL)
315                         throw InvalidPositionException();
316                 return data[z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + y*MAP_BLOCKSIZE + x];
317         }
318         
319         MapNode getNodeNoCheck(v3s16 p)
320         {
321                 return getNodeNoCheck(p.X, p.Y, p.Z);
322         }
323         
324         void setNodeNoCheck(s16 x, s16 y, s16 z, MapNode & n)
325         {
326                 if(data == NULL)
327                         throw InvalidPositionException();
328                 data[z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + y*MAP_BLOCKSIZE + x] = n;
329                 raiseModified(MOD_STATE_WRITE_NEEDED, "setNodeNoCheck");
330         }
331         
332         void setNodeNoCheck(v3s16 p, MapNode & n)
333         {
334                 setNodeNoCheck(p.X, p.Y, p.Z, n);
335         }
336
337         /*
338                 These functions consult the parent container if the position
339                 is not valid on this MapBlock.
340         */
341         bool isValidPositionParent(v3s16 p);
342         MapNode getNodeParent(v3s16 p);
343         void setNodeParent(v3s16 p, MapNode & n);
344         MapNode getNodeParentNoEx(v3s16 p);
345
346         void drawbox(s16 x0, s16 y0, s16 z0, s16 w, s16 h, s16 d, MapNode node)
347         {
348                 for(u16 z=0; z<d; z++)
349                         for(u16 y=0; y<h; y++)
350                                 for(u16 x=0; x<w; x++)
351                                         setNode(x0+x, y0+y, z0+z, node);
352         }
353
354         // See comments in mapblock.cpp
355         bool propagateSunlight(core::map<v3s16, bool> & light_sources,
356                         bool remove_light=false, bool *black_air_left=NULL);
357         
358         // Copies data to VoxelManipulator to getPosRelative()
359         void copyTo(VoxelManipulator &dst);
360         // Copies data from VoxelManipulator getPosRelative()
361         void copyFrom(VoxelManipulator &dst);
362
363         /*
364                 Update day-night lighting difference flag.
365                 Sets m_day_night_differs to appropriate value.
366                 These methods don't care about neighboring blocks.
367         */
368         void actuallyUpdateDayNightDiff();
369         /*
370                 Call this to schedule what the previous function does to be done
371                 when the value is actually needed.
372         */
373         void expireDayNightDiff();
374
375         bool getDayNightDiff()
376         {
377                 if(m_day_night_differs_expired)
378                         actuallyUpdateDayNightDiff();
379                 return m_day_night_differs;
380         }
381
382         /*
383                 Miscellaneous stuff
384         */
385         
386         /*
387                 Tries to measure ground level.
388                 Return value:
389                         -1 = only air
390                         -2 = only ground
391                         -3 = random fail
392                         0...MAP_BLOCKSIZE-1 = ground level
393         */
394         s16 getGroundLevel(v2s16 p2d);
395
396         /*
397                 Timestamp (see m_timestamp)
398                 NOTE: BLOCK_TIMESTAMP_UNDEFINED=0xffffffff means there is no timestamp.
399         */
400         void setTimestamp(u32 time)
401         {
402                 m_timestamp = time;
403                 raiseModified(MOD_STATE_WRITE_AT_UNLOAD, "setTimestamp");
404         }
405         void setTimestampNoChangedFlag(u32 time)
406         {
407                 m_timestamp = time;
408         }
409         u32 getTimestamp()
410         {
411                 return m_timestamp;
412         }
413         u32 getDiskTimestamp()
414         {
415                 return m_disk_timestamp;
416         }
417         
418         /*
419                 See m_usage_timer
420         */
421         void resetUsageTimer()
422         {
423                 m_usage_timer = 0;
424         }
425         void incrementUsageTimer(float dtime)
426         {
427                 m_usage_timer += dtime;
428         }
429         u32 getUsageTimer()
430         {
431                 return m_usage_timer;
432         }
433         
434         /*
435                 Node Timers
436         */
437         // Get timer
438         NodeTimer getNodeTimer(v3s16 p){ 
439                 return m_node_timers.get(p);
440         }
441         // Deletes timer
442         void removeNodeTimer(v3s16 p){
443                 m_node_timers.remove(p);
444         }
445         // Deletes old timer and sets a new one
446         void setNodeTimer(v3s16 p, NodeTimer t){
447                 m_node_timers.set(p,t);
448         }
449         // Deletes all timers
450         void clearNodeTimers(){
451                 m_node_timers.clear();
452         }
453
454         /*
455                 Serialization
456         */
457         
458         // These don't write or read version by itself
459         // Set disk to true for on-disk format, false for over-the-network format
460         void serialize(std::ostream &os, u8 version, bool disk);
461         // If disk == true: In addition to doing other things, will add
462         // unknown blocks from id-name mapping to wndef
463         void deSerialize(std::istream &is, u8 version, bool disk);
464
465 private:
466         /*
467                 Private methods
468         */
469
470         void deSerialize_pre22(std::istream &is, u8 version, bool disk);
471
472         /*
473                 Used only internally, because changes can't be tracked
474         */
475
476         MapNode & getNodeRef(s16 x, s16 y, s16 z)
477         {
478                 if(data == NULL)
479                         throw InvalidPositionException();
480                 if(x < 0 || x >= MAP_BLOCKSIZE) throw InvalidPositionException();
481                 if(y < 0 || y >= MAP_BLOCKSIZE) throw InvalidPositionException();
482                 if(z < 0 || z >= MAP_BLOCKSIZE) throw InvalidPositionException();
483                 return data[z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + y*MAP_BLOCKSIZE + x];
484         }
485         MapNode & getNodeRef(v3s16 &p)
486         {
487                 return getNodeRef(p.X, p.Y, p.Z);
488         }
489
490 public:
491         /*
492                 Public member variables
493         */
494
495 #ifndef SERVER // Only on client
496         MapBlockMesh *mesh;
497         //JMutex mesh_mutex;
498 #endif
499         
500         NodeMetadataList m_node_metadata;
501         NodeTimerList m_node_timers;
502         StaticObjectList m_static_objects;
503         
504 private:
505         /*
506                 Private member variables
507         */
508
509         // NOTE: Lots of things rely on this being the Map
510         Map *m_parent;
511         // Position in blocks on parent
512         v3s16 m_pos;
513
514         IGameDef *m_gamedef;
515         
516         /*
517                 If NULL, block is a dummy block.
518                 Dummy blocks are used for caching not-found-on-disk blocks.
519         */
520         MapNode * data;
521
522         /*
523                 - On the server, this is used for telling whether the
524                   block has been modified from the one on disk.
525                 - On the client, this is used for nothing.
526         */
527         u32 m_modified;
528         std::string m_modified_reason;
529         bool m_modified_reason_too_long;
530
531         /*
532                 When propagating sunlight and the above block doesn't exist,
533                 sunlight is assumed if this is false.
534
535                 In practice this is set to true if the block is completely
536                 undeground with nothing visible above the ground except
537                 caves.
538         */
539         bool is_underground;
540
541         /*
542                 Set to true if changes has been made that make the old lighting
543                 values wrong but the lighting hasn't been actually updated.
544
545                 If this is false, lighting is exactly right.
546                 If this is true, lighting might be wrong or right.
547         */
548         bool m_lighting_expired;
549         
550         // Whether day and night lighting differs
551         bool m_day_night_differs;
552         bool m_day_night_differs_expired;
553
554         bool m_generated;
555         
556         /*
557                 When block is removed from active blocks, this is set to gametime.
558                 Value BLOCK_TIMESTAMP_UNDEFINED=0xffffffff means there is no timestamp.
559         */
560         u32 m_timestamp;
561         // The on-disk (or to-be on-disk) timestamp value
562         u32 m_disk_timestamp;
563
564         /*
565                 When the block is accessed, this is set to 0.
566                 Map will unload the block when this reaches a timeout.
567         */
568         float m_usage_timer;
569 };
570
571 inline bool blockpos_over_limit(v3s16 p)
572 {
573         return
574           (p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
575         || p.X >  MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
576         || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
577         || p.Y >  MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
578         || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
579         || p.Z >  MAP_GENERATION_LIMIT / MAP_BLOCKSIZE);
580 }
581
582 /*
583         Returns the position of the block where the node is located
584 */
585 inline v3s16 getNodeBlockPos(v3s16 p)
586 {
587         return getContainerPos(p, MAP_BLOCKSIZE);
588 }
589
590 inline v2s16 getNodeSectorPos(v2s16 p)
591 {
592         return getContainerPos(p, MAP_BLOCKSIZE);
593 }
594
595 inline s16 getNodeBlockY(s16 y)
596 {
597         return getContainerPos(y, MAP_BLOCKSIZE);
598 }
599
600 /*
601         Get a quick string to describe what a block actually contains
602 */
603 std::string analyze_block(MapBlock *block);
604
605 #endif
606