adb3324f6e6f5ec06f789ad4a14974b7444a0101
[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 serialize_pre22(std::ostream &os, u8 version, bool disk);
471         void deSerialize_pre22(std::istream &is, u8 version, bool disk);
472
473         /*
474                 Used only internally, because changes can't be tracked
475         */
476
477         MapNode & getNodeRef(s16 x, s16 y, s16 z)
478         {
479                 if(data == NULL)
480                         throw InvalidPositionException();
481                 if(x < 0 || x >= MAP_BLOCKSIZE) throw InvalidPositionException();
482                 if(y < 0 || y >= MAP_BLOCKSIZE) throw InvalidPositionException();
483                 if(z < 0 || z >= MAP_BLOCKSIZE) throw InvalidPositionException();
484                 return data[z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + y*MAP_BLOCKSIZE + x];
485         }
486         MapNode & getNodeRef(v3s16 &p)
487         {
488                 return getNodeRef(p.X, p.Y, p.Z);
489         }
490
491 public:
492         /*
493                 Public member variables
494         */
495
496 #ifndef SERVER // Only on client
497         MapBlockMesh *mesh;
498         //JMutex mesh_mutex;
499 #endif
500         
501         NodeMetadataList m_node_metadata;
502         NodeTimerList m_node_timers;
503         StaticObjectList m_static_objects;
504         
505 private:
506         /*
507                 Private member variables
508         */
509
510         // NOTE: Lots of things rely on this being the Map
511         Map *m_parent;
512         // Position in blocks on parent
513         v3s16 m_pos;
514
515         IGameDef *m_gamedef;
516         
517         /*
518                 If NULL, block is a dummy block.
519                 Dummy blocks are used for caching not-found-on-disk blocks.
520         */
521         MapNode * data;
522
523         /*
524                 - On the server, this is used for telling whether the
525                   block has been modified from the one on disk.
526                 - On the client, this is used for nothing.
527         */
528         u32 m_modified;
529         std::string m_modified_reason;
530         bool m_modified_reason_too_long;
531
532         /*
533                 When propagating sunlight and the above block doesn't exist,
534                 sunlight is assumed if this is false.
535
536                 In practice this is set to true if the block is completely
537                 undeground with nothing visible above the ground except
538                 caves.
539         */
540         bool is_underground;
541
542         /*
543                 Set to true if changes has been made that make the old lighting
544                 values wrong but the lighting hasn't been actually updated.
545
546                 If this is false, lighting is exactly right.
547                 If this is true, lighting might be wrong or right.
548         */
549         bool m_lighting_expired;
550         
551         // Whether day and night lighting differs
552         bool m_day_night_differs;
553         bool m_day_night_differs_expired;
554
555         bool m_generated;
556         
557         /*
558                 When block is removed from active blocks, this is set to gametime.
559                 Value BLOCK_TIMESTAMP_UNDEFINED=0xffffffff means there is no timestamp.
560         */
561         u32 m_timestamp;
562         // The on-disk (or to-be on-disk) timestamp value
563         u32 m_disk_timestamp;
564
565         /*
566                 When the block is accessed, this is set to 0.
567                 Map will unload the block when this reaches a timeout.
568         */
569         float m_usage_timer;
570 };
571
572 inline bool blockpos_over_limit(v3s16 p)
573 {
574         return
575           (p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
576         || p.X >  MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
577         || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
578         || p.Y >  MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
579         || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
580         || p.Z >  MAP_GENERATION_LIMIT / MAP_BLOCKSIZE);
581 }
582
583 /*
584         Returns the position of the block where the node is located
585 */
586 inline v3s16 getNodeBlockPos(v3s16 p)
587 {
588         return getContainerPos(p, MAP_BLOCKSIZE);
589 }
590
591 inline v2s16 getNodeSectorPos(v2s16 p)
592 {
593         return getContainerPos(p, MAP_BLOCKSIZE);
594 }
595
596 inline s16 getNodeBlockY(s16 y)
597 {
598         return getContainerPos(y, MAP_BLOCKSIZE);
599 }
600
601 /*
602         Get a quick string to describe what a block actually contains
603 */
604 std::string analyze_block(MapBlock *block);
605
606 #endif
607