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