Falling sand and gravel
[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 General Public License as published by
7 the Free Software Foundation; either version 2 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 General Public License for more details.
14
15 You should have received a copy of the GNU 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 "common_irrlicht.h"
28 #include "mapnode.h"
29 #include "exceptions.h"
30 #include "serialization.h"
31 #include "constants.h"
32 #include "voxel.h"
33 #include "staticobject.h"
34 #include "mapblock_nodemod.h"
35 #ifndef SERVER
36         #include "mapblock_mesh.h"
37 #endif
38
39 class Map;
40 class NodeMetadataList;
41 class IGameDef;
42 class IWritableNodeDefManager;
43
44 #define BLOCK_TIMESTAMP_UNDEFINED 0xffffffff
45
46 /*// Named by looking towards z+
47 enum{
48         FACE_BACK=0,
49         FACE_TOP,
50         FACE_RIGHT,
51         FACE_FRONT,
52         FACE_BOTTOM,
53         FACE_LEFT
54 };*/
55
56 enum ModifiedState
57 {
58         // Has not been modified.
59         MOD_STATE_CLEAN = 0,
60         MOD_RESERVED1 = 1,
61         // Has been modified, and will be saved when being unloaded.
62         MOD_STATE_WRITE_AT_UNLOAD = 2,
63         MOD_RESERVED3 = 3,
64         // Has been modified, and will be saved as soon as possible.
65         MOD_STATE_WRITE_NEEDED = 4,
66         MOD_RESERVED5 = 5,
67 };
68
69 // NOTE: If this is enabled, set MapBlock to be initialized with
70 //       CONTENT_IGNORE.
71 /*enum BlockGenerationStatus
72 {
73         // Completely non-generated (filled with CONTENT_IGNORE).
74         BLOCKGEN_UNTOUCHED=0,
75         // Trees or similar might have been blitted from other blocks to here.
76         // Otherwise, the block contains CONTENT_IGNORE
77         BLOCKGEN_FROM_NEIGHBORS=2,
78         // Has been generated, but some neighbors might put some stuff in here
79         // when they are generated.
80         // Does not contain any CONTENT_IGNORE
81         BLOCKGEN_SELF_GENERATED=4,
82         // The block and all its neighbors have been generated
83         BLOCKGEN_FULLY_GENERATED=6
84 };*/
85
86 #if 0
87 enum
88 {
89         NODECONTAINER_ID_MAPBLOCK,
90         NODECONTAINER_ID_MAPSECTOR,
91         NODECONTAINER_ID_MAP,
92         NODECONTAINER_ID_MAPBLOCKCACHE,
93         NODECONTAINER_ID_VOXELMANIPULATOR,
94 };
95
96 class NodeContainer
97 {
98 public:
99         virtual bool isValidPosition(v3s16 p) = 0;
100         virtual MapNode getNode(v3s16 p) = 0;
101         virtual void setNode(v3s16 p, MapNode & n) = 0;
102         virtual u16 nodeContainerId() const = 0;
103
104         MapNode getNodeNoEx(v3s16 p)
105         {
106                 try{
107                         return getNode(p);
108                 }
109                 catch(InvalidPositionException &e){
110                         return MapNode(CONTENT_IGNORE);
111                 }
112         }
113 };
114 #endif
115
116 /*
117         MapBlock itself
118 */
119
120 class MapBlock /*: public NodeContainer*/
121 {
122 public:
123         MapBlock(Map *parent, v3s16 pos, IGameDef *gamedef, bool dummy=false);
124         ~MapBlock();
125         
126         /*virtual u16 nodeContainerId() const
127         {
128                 return NODECONTAINER_ID_MAPBLOCK;
129         }*/
130         
131         Map * getParent()
132         {
133                 return m_parent;
134         }
135
136         void reallocate()
137         {
138                 if(data != NULL)
139                         delete[] data;
140                 u32 l = MAP_BLOCKSIZE * MAP_BLOCKSIZE * MAP_BLOCKSIZE;
141                 data = new MapNode[l];
142                 for(u32 i=0; i<l; i++){
143                         //data[i] = MapNode();
144                         data[i] = MapNode(CONTENT_IGNORE);
145                 }
146                 raiseModified(MOD_STATE_WRITE_NEEDED);
147         }
148
149         /*
150                 Flags
151         */
152
153         bool isDummy()
154         {
155                 return (data == NULL);
156         }
157         void unDummify()
158         {
159                 assert(isDummy());
160                 reallocate();
161         }
162         
163         /*
164                 This is called internally or externally after the block is
165                 modified, so that the block is saved and possibly not deleted from
166                 memory.
167         */
168         // DEPRECATED, use *Modified()
169         void setChangedFlag()
170         {
171                 //dstream<<"Deprecated setChangedFlag() called"<<std::endl;
172                 raiseModified(MOD_STATE_WRITE_NEEDED);
173         }
174         // DEPRECATED, use *Modified()
175         void resetChangedFlag()
176         {
177                 //dstream<<"Deprecated resetChangedFlag() called"<<std::endl;
178                 resetModified();
179         }
180         // DEPRECATED, use *Modified()
181         bool getChangedFlag()
182         {
183                 //dstream<<"Deprecated getChangedFlag() called"<<std::endl;
184                 if(getModified() == MOD_STATE_CLEAN)
185                         return false;
186                 else
187                         return true;
188         }
189         
190         // m_modified methods
191         void raiseModified(u32 mod)
192         {
193                 m_modified = MYMAX(m_modified, mod);
194         }
195         u32 getModified()
196         {
197                 return m_modified;
198         }
199         void resetModified()
200         {
201                 m_modified = MOD_STATE_CLEAN;
202         }
203         
204         // is_underground getter/setter
205         bool getIsUnderground()
206         {
207                 return is_underground;
208         }
209         void setIsUnderground(bool a_is_underground)
210         {
211                 is_underground = a_is_underground;
212                 raiseModified(MOD_STATE_WRITE_NEEDED);
213         }
214
215 #ifndef SERVER
216         void setMeshExpired(bool expired)
217         {
218                 m_mesh_expired = expired;
219         }
220         
221         bool getMeshExpired()
222         {
223                 return m_mesh_expired;
224         }
225 #endif
226
227         void setLightingExpired(bool expired)
228         {
229                 if(expired != m_lighting_expired){
230                         m_lighting_expired = expired;
231                         raiseModified(MOD_STATE_WRITE_NEEDED);
232                 }
233         }
234         bool getLightingExpired()
235         {
236                 return m_lighting_expired;
237         }
238
239         bool isGenerated()
240         {
241                 return m_generated;
242         }
243         void setGenerated(bool b)
244         {
245                 if(b != m_generated){
246                         raiseModified(MOD_STATE_WRITE_NEEDED);
247                         m_generated = b;
248                 }
249         }
250
251         bool isValid()
252         {
253                 if(m_lighting_expired)
254                         return false;
255                 if(data == NULL)
256                         return false;
257                 return true;
258         }
259
260         /*
261                 Position stuff
262         */
263
264         v3s16 getPos()
265         {
266                 return m_pos;
267         }
268                 
269         v3s16 getPosRelative()
270         {
271                 return m_pos * MAP_BLOCKSIZE;
272         }
273                 
274         core::aabbox3d<s16> getBox()
275         {
276                 return core::aabbox3d<s16>(getPosRelative(),
277                                 getPosRelative()
278                                 + v3s16(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE)
279                                 - v3s16(1,1,1));
280         }
281
282         /*
283                 Regular MapNode get-setters
284         */
285         
286         bool isValidPosition(v3s16 p)
287         {
288                 if(data == NULL)
289                         return false;
290                 return (p.X >= 0 && p.X < MAP_BLOCKSIZE
291                                 && p.Y >= 0 && p.Y < MAP_BLOCKSIZE
292                                 && p.Z >= 0 && p.Z < MAP_BLOCKSIZE);
293         }
294
295         MapNode getNode(s16 x, s16 y, s16 z)
296         {
297                 if(data == NULL)
298                         throw InvalidPositionException();
299                 if(x < 0 || x >= MAP_BLOCKSIZE) throw InvalidPositionException();
300                 if(y < 0 || y >= MAP_BLOCKSIZE) throw InvalidPositionException();
301                 if(z < 0 || z >= MAP_BLOCKSIZE) throw InvalidPositionException();
302                 return data[z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + y*MAP_BLOCKSIZE + x];
303         }
304         
305         MapNode getNode(v3s16 p)
306         {
307                 return getNode(p.X, p.Y, p.Z);
308         }
309         
310         MapNode getNodeNoEx(v3s16 p)
311         {
312                 try{
313                         return getNode(p.X, p.Y, p.Z);
314                 }catch(InvalidPositionException &e){
315                         return MapNode(CONTENT_IGNORE);
316                 }
317         }
318         
319         void setNode(s16 x, s16 y, s16 z, MapNode & n)
320         {
321                 if(data == NULL)
322                         throw InvalidPositionException();
323                 if(x < 0 || x >= MAP_BLOCKSIZE) throw InvalidPositionException();
324                 if(y < 0 || y >= MAP_BLOCKSIZE) throw InvalidPositionException();
325                 if(z < 0 || z >= MAP_BLOCKSIZE) throw InvalidPositionException();
326                 data[z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + y*MAP_BLOCKSIZE + x] = n;
327                 raiseModified(MOD_STATE_WRITE_NEEDED);
328         }
329         
330         void setNode(v3s16 p, MapNode & n)
331         {
332                 setNode(p.X, p.Y, p.Z, n);
333         }
334
335         /*
336                 Non-checking variants of the above
337         */
338
339         MapNode getNodeNoCheck(s16 x, s16 y, s16 z)
340         {
341                 if(data == NULL)
342                         throw InvalidPositionException();
343                 return data[z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + y*MAP_BLOCKSIZE + x];
344         }
345         
346         MapNode getNodeNoCheck(v3s16 p)
347         {
348                 return getNodeNoCheck(p.X, p.Y, p.Z);
349         }
350         
351         void setNodeNoCheck(s16 x, s16 y, s16 z, MapNode & n)
352         {
353                 if(data == NULL)
354                         throw InvalidPositionException();
355                 data[z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + y*MAP_BLOCKSIZE + x] = n;
356                 raiseModified(MOD_STATE_WRITE_NEEDED);
357         }
358         
359         void setNodeNoCheck(v3s16 p, MapNode & n)
360         {
361                 setNodeNoCheck(p.X, p.Y, p.Z, n);
362         }
363
364         /*
365                 These functions consult the parent container if the position
366                 is not valid on this MapBlock.
367         */
368         bool isValidPositionParent(v3s16 p);
369         MapNode getNodeParent(v3s16 p);
370         void setNodeParent(v3s16 p, MapNode & n);
371         MapNode getNodeParentNoEx(v3s16 p);
372
373         void drawbox(s16 x0, s16 y0, s16 z0, s16 w, s16 h, s16 d, MapNode node)
374         {
375                 for(u16 z=0; z<d; z++)
376                         for(u16 y=0; y<h; y++)
377                                 for(u16 x=0; x<w; x++)
378                                         setNode(x0+x, y0+y, z0+z, node);
379         }
380
381         /*
382                 Graphics-related methods
383         */
384         
385         /*// A quick version with nodes passed as parameters
386         u8 getFaceLight(u32 daynight_ratio, MapNode n, MapNode n2,
387                         v3s16 face_dir);*/
388         /*// A more convenient version
389         u8 getFaceLight(u32 daynight_ratio, v3s16 p, v3s16 face_dir)
390         {
391                 return getFaceLight(daynight_ratio,
392                                 getNodeParentNoEx(p),
393                                 getNodeParentNoEx(p + face_dir),
394                                 face_dir);
395         }*/
396         u8 getFaceLight2(u32 daynight_ratio, v3s16 p, v3s16 face_dir,
397                         INodeDefManager *nodemgr)
398         {
399                 return getFaceLight(daynight_ratio,
400                                 getNodeParentNoEx(p),
401                                 getNodeParentNoEx(p + face_dir),
402                                 face_dir, nodemgr);
403         }
404         
405 #ifndef SERVER // Only on client
406
407 #if 1
408         /*
409                 Thread-safely updates the whole mesh of the mapblock.
410                 NOTE: Prefer generating the mesh separately and then using
411                 replaceMesh().
412         */
413         void updateMesh(u32 daynight_ratio);
414 #endif
415         // Replace the mesh with a new one
416         void replaceMesh(scene::SMesh *mesh_new);
417 #endif
418         
419         // See comments in mapblock.cpp
420         bool propagateSunlight(core::map<v3s16, bool> & light_sources,
421                         bool remove_light=false, bool *black_air_left=NULL);
422         
423         // Copies data to VoxelManipulator to getPosRelative()
424         void copyTo(VoxelManipulator &dst);
425         // Copies data from VoxelManipulator getPosRelative()
426         void copyFrom(VoxelManipulator &dst);
427
428 #ifndef SERVER // Only on client
429         /*
430                 Methods for setting temporary modifications to nodes for
431                 drawing
432
433                 returns true if the mod was different last time
434         */
435         bool setTempMod(v3s16 p, const NodeMod &mod)
436         {
437                 /*dstream<<"setTempMod called on block"
438                                 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
439                                 <<", mod.type="<<mod.type
440                                 <<", mod.param="<<mod.param
441                                 <<std::endl;*/
442                 JMutexAutoLock lock(m_temp_mods_mutex);
443
444                 return m_temp_mods.set(p, mod);
445         }
446         // Returns true if there was one
447         bool getTempMod(v3s16 p, NodeMod *mod)
448         {
449                 JMutexAutoLock lock(m_temp_mods_mutex);
450
451                 return m_temp_mods.get(p, mod);
452         }
453         bool clearTempMod(v3s16 p)
454         {
455                 JMutexAutoLock lock(m_temp_mods_mutex);
456
457                 return m_temp_mods.clear(p);
458         }
459         bool clearTempMods()
460         {
461                 JMutexAutoLock lock(m_temp_mods_mutex);
462                 
463                 return m_temp_mods.clear();
464         }
465         void copyTempMods(NodeModMap &dst)
466         {
467                 JMutexAutoLock lock(m_temp_mods_mutex);
468                 m_temp_mods.copy(dst);
469         }
470 #endif
471
472         /*
473                 Update day-night lighting difference flag.
474                 
475                 Sets m_day_night_differs to appropriate value.
476                 
477                 These methods don't care about neighboring blocks.
478                 It means that to know if a block really doesn't need a mesh
479                 update between day and night, the neighboring blocks have
480                 to be taken into account. Use Map::dayNightDiffed().
481         */
482         void updateDayNightDiff();
483
484         bool dayNightDiffed()
485         {
486                 return m_day_night_differs;
487         }
488
489         /*
490                 Miscellaneous stuff
491         */
492         
493         /*
494                 Tries to measure ground level.
495                 Return value:
496                         -1 = only air
497                         -2 = only ground
498                         -3 = random fail
499                         0...MAP_BLOCKSIZE-1 = ground level
500         */
501         s16 getGroundLevel(v2s16 p2d);
502
503         /*
504                 Timestamp (see m_timestamp)
505                 NOTE: BLOCK_TIMESTAMP_UNDEFINED=0xffffffff means there is no timestamp.
506         */
507         void setTimestamp(u32 time)
508         {
509                 m_timestamp = time;
510                 raiseModified(MOD_STATE_WRITE_AT_UNLOAD);
511         }
512         void setTimestampNoChangedFlag(u32 time)
513         {
514                 m_timestamp = time;
515         }
516         u32 getTimestamp()
517         {
518                 return m_timestamp;
519         }
520         
521         /*
522                 See m_usage_timer
523         */
524         void resetUsageTimer()
525         {
526                 m_usage_timer = 0;
527         }
528         void incrementUsageTimer(float dtime)
529         {
530                 m_usage_timer += dtime;
531         }
532         u32 getUsageTimer()
533         {
534                 return m_usage_timer;
535         }
536
537         /*
538                 Serialization
539         */
540         
541         // These don't write or read version by itself
542         void serialize(std::ostream &os, u8 version);
543         void deSerialize(std::istream &is, u8 version);
544
545         // Used after the basic ones when writing on disk (serverside)
546         void serializeDiskExtra(std::ostream &os, u8 version);
547         // In addition to doing other things, will add unknown blocks from
548         // id-name mapping to wndef
549         void deSerializeDiskExtra(std::istream &is, u8 version);
550
551 private:
552         /*
553                 Private methods
554         */
555
556         /*
557                 Used only internally, because changes can't be tracked
558         */
559
560         MapNode & getNodeRef(s16 x, s16 y, s16 z)
561         {
562                 if(data == NULL)
563                         throw InvalidPositionException();
564                 if(x < 0 || x >= MAP_BLOCKSIZE) throw InvalidPositionException();
565                 if(y < 0 || y >= MAP_BLOCKSIZE) throw InvalidPositionException();
566                 if(z < 0 || z >= MAP_BLOCKSIZE) throw InvalidPositionException();
567                 return data[z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + y*MAP_BLOCKSIZE + x];
568         }
569         MapNode & getNodeRef(v3s16 &p)
570         {
571                 return getNodeRef(p.X, p.Y, p.Z);
572         }
573
574 public:
575         /*
576                 Public member variables
577         */
578
579 #ifndef SERVER // Only on client
580         scene::SMesh *mesh;
581         JMutex mesh_mutex;
582 #endif
583         
584         NodeMetadataList *m_node_metadata;
585         StaticObjectList m_static_objects;
586         
587 private:
588         /*
589                 Private member variables
590         */
591
592         // NOTE: Lots of things rely on this being the Map
593         Map *m_parent;
594         // Position in blocks on parent
595         v3s16 m_pos;
596
597         IGameDef *m_gamedef;
598         
599         /*
600                 If NULL, block is a dummy block.
601                 Dummy blocks are used for caching not-found-on-disk blocks.
602         */
603         MapNode * data;
604
605         /*
606                 - On the server, this is used for telling whether the
607                   block has been modified from the one on disk.
608                 - On the client, this is used for nothing.
609         */
610         u32 m_modified;
611
612         /*
613                 When propagating sunlight and the above block doesn't exist,
614                 sunlight is assumed if this is false.
615
616                 In practice this is set to true if the block is completely
617                 undeground with nothing visible above the ground except
618                 caves.
619         */
620         bool is_underground;
621
622         /*
623                 Set to true if changes has been made that make the old lighting
624                 values wrong but the lighting hasn't been actually updated.
625
626                 If this is false, lighting is exactly right.
627                 If this is true, lighting might be wrong or right.
628         */
629         bool m_lighting_expired;
630         
631         // Whether day and night lighting differs
632         bool m_day_night_differs;
633
634         bool m_generated;
635         
636 #ifndef SERVER // Only on client
637         /*
638                 Set to true if the mesh has been ordered to be updated
639                 sometime in the background.
640                 In practice this is set when the day/night lighting switches.
641         */
642         bool m_mesh_expired;
643         
644         // Temporary modifications to nodes
645         // These are only used when drawing
646         NodeModMap m_temp_mods;
647         JMutex m_temp_mods_mutex;
648 #endif
649         
650         /*
651                 When block is removed from active blocks, this is set to gametime.
652                 Value BLOCK_TIMESTAMP_UNDEFINED=0xffffffff means there is no timestamp.
653         */
654         u32 m_timestamp;
655
656         /*
657                 When the block is accessed, this is set to 0.
658                 Map will unload the block when this reaches a timeout.
659         */
660         float m_usage_timer;
661 };
662
663 inline bool blockpos_over_limit(v3s16 p)
664 {
665         return
666           (p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
667         || p.X >  MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
668         || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
669         || p.Y >  MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
670         || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
671         || p.Z >  MAP_GENERATION_LIMIT / MAP_BLOCKSIZE);
672 }
673
674 /*
675         Returns the position of the block where the node is located
676 */
677 inline v3s16 getNodeBlockPos(v3s16 p)
678 {
679         return getContainerPos(p, MAP_BLOCKSIZE);
680 }
681
682 inline v2s16 getNodeSectorPos(v2s16 p)
683 {
684         return getContainerPos(p, MAP_BLOCKSIZE);
685 }
686
687 inline s16 getNodeBlockY(s16 y)
688 {
689         return getContainerPos(y, MAP_BLOCKSIZE);
690 }
691
692 /*
693         Get a quick string to describe what a block actually contains
694 */
695 std::string analyze_block(MapBlock *block);
696
697 #endif
698