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