]> git.lizzy.rs Git - minetest.git/blob - src/mapblock.h
97129bd346d1eadaf2b4fa8e7cc3b338824abf45
[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 "mapblockobject.h"
33 #include "voxel.h"
34
35 // Named by looking towards z+
36 enum{
37         FACE_BACK=0,
38         FACE_TOP,
39         FACE_RIGHT,
40         FACE_FRONT,
41         FACE_BOTTOM,
42         FACE_LEFT
43 };
44
45 struct FastFace
46 {
47         TileSpec tile;
48         video::S3DVertex vertices[4]; // Precalculated vertices
49 };
50
51 enum NodeModType
52 {
53         NODEMOD_NONE,
54         NODEMOD_CHANGECONTENT, //param is content id
55         NODEMOD_CRACK // param is crack progression
56 };
57
58 struct NodeMod
59 {
60         NodeMod(enum NodeModType a_type=NODEMOD_NONE, u16 a_param=0)
61         {
62                 type = a_type;
63                 param = a_param;
64         }
65         bool operator==(const NodeMod &other)
66         {
67                 return (type == other.type && param == other.param);
68         }
69         enum NodeModType type;
70         u16 param;
71 };
72
73 class NodeModMap
74 {
75 public:
76         /*
77                 returns true if the mod was different last time
78         */
79         bool set(v3s16 p, const NodeMod &mod)
80         {
81                 // See if old is different, cancel if it is not different.
82                 core::map<v3s16, NodeMod>::Node *n = m_mods.find(p);
83                 if(n)
84                 {
85                         NodeMod old = n->getValue();
86                         if(old == mod)
87                                 return false;
88
89                         n->setValue(mod);
90                 }
91                 else
92                 {
93                         m_mods.insert(p, mod);
94                 }
95                 
96                 return true;
97         }
98         // Returns true if there was one
99         bool get(v3s16 p, NodeMod *mod)
100         {
101                 core::map<v3s16, NodeMod>::Node *n;
102                 n = m_mods.find(p);
103                 if(n == NULL)
104                         return false;
105                 if(mod)
106                         *mod = n->getValue();
107                 return true;
108         }
109         bool clear(v3s16 p)
110         {
111                 if(m_mods.find(p))
112                 {
113                         m_mods.remove(p);
114                         return true;
115                 }
116                 return false;
117         }
118         bool clear()
119         {
120                 if(m_mods.size() == 0)
121                         return false;
122                 m_mods.clear();
123                 return true;
124         }
125         void copy(NodeModMap &dest)
126         {
127                 dest.m_mods.clear();
128
129                 for(core::map<v3s16, NodeMod>::Iterator
130                                 i = m_mods.getIterator();
131                                 i.atEnd() == false; i++)
132                 {
133                         dest.m_mods.insert(i.getNode()->getKey(), i.getNode()->getValue());
134                 }
135         }
136
137 private:
138         core::map<v3s16, NodeMod> m_mods;
139 };
140
141 enum
142 {
143         NODECONTAINER_ID_MAPBLOCK,
144         NODECONTAINER_ID_MAPSECTOR,
145         NODECONTAINER_ID_MAP,
146         NODECONTAINER_ID_MAPBLOCKCACHE,
147         NODECONTAINER_ID_VOXELMANIPULATOR,
148 };
149
150 class NodeContainer
151 {
152 public:
153         virtual bool isValidPosition(v3s16 p) = 0;
154         virtual MapNode getNode(v3s16 p) = 0;
155         virtual void setNode(v3s16 p, MapNode & n) = 0;
156         virtual u16 nodeContainerId() const = 0;
157 };
158
159 class MapBlock : public NodeContainer
160 {
161 public:
162         MapBlock(NodeContainer *parent, v3s16 pos, bool dummy=false);
163         ~MapBlock();
164         
165         virtual u16 nodeContainerId() const
166         {
167                 return NODECONTAINER_ID_MAPBLOCK;
168         }
169
170         NodeContainer * getParent()
171         {
172                 return m_parent;
173         }
174
175         void reallocate()
176         {
177                 if(data != NULL)
178                         delete[] data;
179                 u32 l = MAP_BLOCKSIZE * MAP_BLOCKSIZE * MAP_BLOCKSIZE;
180                 data = new MapNode[l];
181                 for(u32 i=0; i<l; i++){
182                         data[i] = MapNode();
183                 }
184                 setChangedFlag();
185         }
186
187         /*
188                 Flags
189         */
190
191         bool isDummy()
192         {
193                 return (data == NULL);
194         }
195         void unDummify()
196         {
197                 assert(isDummy());
198                 reallocate();
199         }
200         
201         bool getChangedFlag()
202         {
203                 return changed;
204         }
205         void resetChangedFlag()
206         {
207                 changed = false;
208         }
209         void setChangedFlag()
210         {
211                 changed = true;
212         }
213
214         bool getIsUnderground()
215         {
216                 return is_underground;
217         }
218
219         void setIsUnderground(bool a_is_underground)
220         {
221                 is_underground = a_is_underground;
222                 setChangedFlag();
223         }
224
225 #ifndef SERVER
226         void setMeshExpired(bool expired)
227         {
228                 m_mesh_expired = expired;
229         }
230         
231         bool getMeshExpired()
232         {
233                 return m_mesh_expired;
234         }
235 #endif
236
237         void setLightingExpired(bool expired)
238         {
239                 m_lighting_expired = expired;
240                 setChangedFlag();
241         }
242         bool getLightingExpired()
243         {
244                 return m_lighting_expired;
245         }
246
247         bool isFullyGenerated()
248         {
249                 return !m_not_fully_generated;
250         }
251
252         void setFullyGenerated(bool b)
253         {
254                 setChangedFlag();
255                 m_not_fully_generated = !b;
256         }
257
258         bool isValid()
259         {
260                 if(m_lighting_expired)
261                         return false;
262                 if(data == NULL)
263                         return false;
264                 return true;
265         }
266
267         /*
268                 Position stuff
269         */
270
271         v3s16 getPos()
272         {
273                 return m_pos;
274         }
275                 
276         v3s16 getPosRelative()
277         {
278                 return m_pos * MAP_BLOCKSIZE;
279         }
280                 
281         core::aabbox3d<s16> getBox()
282         {
283                 return core::aabbox3d<s16>(getPosRelative(),
284                                 getPosRelative()
285                                 + v3s16(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE)
286                                 - v3s16(1,1,1));
287         }
288
289         /*
290                 Regular MapNode get-setters
291         */
292         
293         bool isValidPosition(v3s16 p)
294         {
295                 if(data == NULL)
296                         return false;
297                 return (p.X >= 0 && p.X < MAP_BLOCKSIZE
298                                 && p.Y >= 0 && p.Y < MAP_BLOCKSIZE
299                                 && p.Z >= 0 && p.Z < MAP_BLOCKSIZE);
300         }
301
302         MapNode getNode(s16 x, s16 y, s16 z)
303         {
304                 if(data == NULL)
305                         throw InvalidPositionException();
306                 if(x < 0 || x >= MAP_BLOCKSIZE) throw InvalidPositionException();
307                 if(y < 0 || y >= MAP_BLOCKSIZE) throw InvalidPositionException();
308                 if(z < 0 || z >= MAP_BLOCKSIZE) throw InvalidPositionException();
309                 return data[z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + y*MAP_BLOCKSIZE + x];
310         }
311         
312         MapNode getNode(v3s16 p)
313         {
314                 return getNode(p.X, p.Y, p.Z);
315         }
316         
317         void setNode(s16 x, s16 y, s16 z, MapNode & n)
318         {
319                 if(data == NULL)
320                         throw InvalidPositionException();
321                 if(x < 0 || x >= MAP_BLOCKSIZE) throw InvalidPositionException();
322                 if(y < 0 || y >= MAP_BLOCKSIZE) throw InvalidPositionException();
323                 if(z < 0 || z >= MAP_BLOCKSIZE) throw InvalidPositionException();
324                 data[z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + y*MAP_BLOCKSIZE + x] = n;
325                 setChangedFlag();
326         }
327         
328         void setNode(v3s16 p, MapNode & n)
329         {
330                 setNode(p.X, p.Y, p.Z, n);
331         }
332
333         /*
334                 Non-checking variants of the above
335         */
336
337         MapNode getNodeNoCheck(s16 x, s16 y, s16 z)
338         {
339                 if(data == NULL)
340                         throw InvalidPositionException();
341                 return data[z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + y*MAP_BLOCKSIZE + x];
342         }
343         
344         MapNode getNodeNoCheck(v3s16 p)
345         {
346                 return getNodeNoCheck(p.X, p.Y, p.Z);
347         }
348         
349         void setNodeNoCheck(s16 x, s16 y, s16 z, MapNode & n)
350         {
351                 if(data == NULL)
352                         throw InvalidPositionException();
353                 data[z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + y*MAP_BLOCKSIZE + x] = n;
354                 setChangedFlag();
355         }
356         
357         void setNodeNoCheck(v3s16 p, MapNode & n)
358         {
359                 setNodeNoCheck(p.X, p.Y, p.Z, n);
360         }
361
362         /*
363                 These functions consult the parent container if the position
364                 is not valid on this MapBlock.
365         */
366         bool isValidPositionParent(v3s16 p);
367         MapNode getNodeParent(v3s16 p);
368         void setNodeParent(v3s16 p, MapNode & n);
369         MapNode getNodeParentNoEx(v3s16 p);
370
371         void drawbox(s16 x0, s16 y0, s16 z0, s16 w, s16 h, s16 d, MapNode node)
372         {
373                 for(u16 z=0; z<d; z++)
374                         for(u16 y=0; y<h; y++)
375                                 for(u16 x=0; x<w; x++)
376                                         setNode(x0+x, y0+y, z0+z, node);
377         }
378
379         /*
380                 Graphics-related methods
381         */
382         
383         // A quick version with nodes passed as parameters
384         u8 getFaceLight(u32 daynight_ratio, MapNode n, MapNode n2,
385                         v3s16 face_dir);
386         // A more convenient version
387         u8 getFaceLight(u32 daynight_ratio, v3s16 p, v3s16 face_dir)
388         {
389                 return getFaceLight(daynight_ratio,
390                                 getNodeParentNoEx(p),
391                                 getNodeParentNoEx(p + face_dir),
392                                 face_dir);
393         }
394         
395 #ifndef SERVER
396         // light = 0...255
397         static void makeFastFace(TileSpec tile, u8 light, v3f p,
398                         v3s16 dir, v3f scale, v3f posRelative_f,
399                         core::array<FastFace> &dest);
400         
401         TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir,
402                         NodeModMap &temp_mods);
403         u8 getNodeContent(v3s16 p, MapNode mn,
404                         NodeModMap &temp_mods);
405
406         /*
407                 Generates the FastFaces of a node row. This has a
408                 ridiculous amount of parameters because that way they
409                 can be precalculated by the caller.
410
411                 translate_dir: unit vector with only one of x, y or z
412                 face_dir: unit vector with only one of x, y or z
413         */
414         void updateFastFaceRow(
415                         u32 daynight_ratio,
416                         v3f posRelative_f,
417                         v3s16 startpos,
418                         u16 length,
419                         v3s16 translate_dir,
420                         v3f translate_dir_f,
421                         v3s16 face_dir,
422                         v3f face_dir_f,
423                         core::array<FastFace> &dest,
424                         NodeModMap &temp_mods);
425         
426         /*
427                 Thread-safely updates the whole mesh of the mapblock.
428         */
429         void updateMesh(u32 daynight_ratio);
430         
431 #endif // !SERVER
432         
433         // See comments in mapblock.cpp
434         bool propagateSunlight(core::map<v3s16, bool> & light_sources,
435                         bool remove_light=false, bool *black_air_left=NULL,
436                         bool grow_grass=false);
437         
438         // Copies data to VoxelManipulator to getPosRelative()
439         void copyTo(VoxelManipulator &dst);
440         // Copies data from VoxelManipulator getPosRelative()
441         void copyFrom(VoxelManipulator &dst);
442
443         /*
444                 MapBlockObject stuff
445         */
446         
447         void serializeObjects(std::ostream &os, u8 version)
448         {
449                 m_objects.serialize(os, version);
450         }
451         // If smgr!=NULL, new objects are added to the scene
452         void updateObjects(std::istream &is, u8 version,
453                         scene::ISceneManager *smgr, u32 daynight_ratio)
454         {
455                 m_objects.update(is, version, smgr, daynight_ratio);
456
457                 setChangedFlag();
458         }
459         void clearObjects()
460         {
461                 m_objects.clear();
462
463                 setChangedFlag();
464         }
465         void addObject(MapBlockObject *object)
466                         throw(ContainerFullException, AlreadyExistsException)
467         {
468                 m_objects.add(object);
469
470                 setChangedFlag();
471         }
472         void removeObject(s16 id)
473         {
474                 m_objects.remove(id);
475
476                 setChangedFlag();
477         }
478         MapBlockObject * getObject(s16 id)
479         {
480                 return m_objects.get(id);
481         }
482         JMutexAutoLock * getObjectLock()
483         {
484                 return m_objects.getLock();
485         }
486
487         /*
488                 Moves objects, deletes objects and spawns new objects
489         */
490         void stepObjects(float dtime, bool server, u32 daynight_ratio);
491
492         /*void wrapObject(MapBlockObject *object)
493         {
494                 m_objects.wrapObject(object);
495
496                 setChangedFlag();
497         }*/
498
499         // origin is relative to block
500         void getObjects(v3f origin, f32 max_d,
501                         core::array<DistanceSortedObject> &dest)
502         {
503                 m_objects.getObjects(origin, max_d, dest);
504         }
505
506         s32 getObjectCount()
507         {
508                 return m_objects.getCount();
509         }
510
511 #ifndef SERVER
512         /*
513                 Methods for setting temporary modifications to nodes for
514                 drawing
515
516                 returns true if the mod was different last time
517         */
518         bool setTempMod(v3s16 p, const NodeMod &mod)
519         {
520                 /*dstream<<"setTempMod called on block"
521                                 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
522                                 <<", mod.type="<<mod.type
523                                 <<", mod.param="<<mod.param
524                                 <<std::endl;*/
525                 JMutexAutoLock lock(m_temp_mods_mutex);
526
527                 return m_temp_mods.set(p, mod);
528         }
529         // Returns true if there was one
530         bool getTempMod(v3s16 p, NodeMod *mod)
531         {
532                 JMutexAutoLock lock(m_temp_mods_mutex);
533
534                 return m_temp_mods.get(p, mod);
535         }
536         bool clearTempMod(v3s16 p)
537         {
538                 JMutexAutoLock lock(m_temp_mods_mutex);
539
540                 return m_temp_mods.clear(p);
541         }
542         bool clearTempMods()
543         {
544                 JMutexAutoLock lock(m_temp_mods_mutex);
545                 
546                 return m_temp_mods.clear();
547         }
548 #endif
549
550         /*
551                 Update day-night lighting difference flag.
552                 
553                 Sets m_day_night_differs to appropriate value.
554                 
555                 These methods don't care about neighboring blocks.
556                 It means that to know if a block really doesn't need a mesh
557                 update between day and night, the neighboring blocks have
558                 to be taken into account. Use Map::dayNightDiffed().
559         */
560         void updateDayNightDiff();
561
562         bool dayNightDiffed()
563         {
564                 return m_day_night_differs;
565         }
566
567         /*
568                 Miscellaneous stuff
569         */
570         
571         /*
572                 Tries to measure ground level.
573                 Return value:
574                         -1 = only air
575                         -2 = only ground
576                         -3 = random fail
577                         0...MAP_BLOCKSIZE-1 = ground level
578         */
579         s16 getGroundLevel(v2s16 p2d);
580
581         /*
582                 Serialization
583         */
584         
585         // Doesn't write version by itself
586         void serialize(std::ostream &os, u8 version);
587
588         void deSerialize(std::istream &is, u8 version);
589
590 private:
591         /*
592                 Private methods
593         */
594
595         /*
596                 Used only internally, because changes can't be tracked
597         */
598
599         MapNode & getNodeRef(s16 x, s16 y, s16 z)
600         {
601                 if(data == NULL)
602                         throw InvalidPositionException();
603                 if(x < 0 || x >= MAP_BLOCKSIZE) throw InvalidPositionException();
604                 if(y < 0 || y >= MAP_BLOCKSIZE) throw InvalidPositionException();
605                 if(z < 0 || z >= MAP_BLOCKSIZE) throw InvalidPositionException();
606                 return data[z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + y*MAP_BLOCKSIZE + x];
607         }
608         MapNode & getNodeRef(v3s16 &p)
609         {
610                 return getNodeRef(p.X, p.Y, p.Z);
611         }
612
613 public:
614         /*
615                 Public member variables
616         */
617
618 #ifndef SERVER
619         scene::SMesh *mesh;
620         JMutex mesh_mutex;
621 #endif
622         
623 private:
624         /*
625                 Private member variables
626         */
627
628         // Parent container (practically the Map)
629         // Not a MapSector, it is just a structural element.
630         NodeContainer *m_parent;
631         // Position in blocks on parent
632         v3s16 m_pos;
633         
634         /*
635                 If NULL, block is a dummy block.
636                 Dummy blocks are used for caching not-found-on-disk blocks.
637         */
638         MapNode * data;
639
640         /*
641                 - On the server, this is used for telling whether the
642                   block has been changed from the one on disk.
643                 - On the client, this is used for nothing.
644         */
645         bool changed;
646
647         /*
648                 When propagating sunlight and the above block doesn't exist,
649                 sunlight is assumed if this is false.
650
651                 In practice this is set to true if the block is completely
652                 undeground with nothing visible above the ground except
653                 caves.
654         */
655         bool is_underground;
656
657         /*
658                 Set to true if changes has been made that make the old lighting
659                 values wrong but the lighting hasn't been actually updated.
660
661                 If this is false, lighting is exactly right.
662                 If this is true, lighting might be wrong or right.
663         */
664         bool m_lighting_expired;
665         
666         // Whether day and night lighting differs
667         bool m_day_night_differs;
668         
669         /*
670                 Whether everything that is mainly located on this block has
671                 been added to the world.
672
673                 While this is false, a block can still be changed a bit when
674                 stuff is added to the neighboring blocks that extends to this
675                 one.
676
677                 When this is false on every one of a 3x3x3 chunk of blocks, the
678                 central one will not be changed by the map generator in the
679                 future.
680
681                 TODO: Save in file
682         */
683         bool m_not_fully_generated;
684         
685         MapBlockObjectList m_objects;
686
687         // Object spawning stuff
688         float m_spawn_timer;
689         
690 #ifndef SERVER // Only on client
691         /*
692                 Set to true if the mesh has been ordered to be updated
693                 sometime in the background.
694                 In practice this is set when the day/night lighting switches.
695         */
696         bool m_mesh_expired;
697         
698         // Temporary modifications to nodes
699         // These are only used when drawing
700         NodeModMap m_temp_mods;
701         JMutex m_temp_mods_mutex;
702 #endif
703 };
704
705 inline bool blockpos_over_limit(v3s16 p)
706 {
707         return
708           (p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
709         || p.X >  MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
710         || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
711         || p.Y >  MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
712         || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
713         || p.Z >  MAP_GENERATION_LIMIT / MAP_BLOCKSIZE);
714 }
715
716 /*
717         Returns the position of the block where the node is located
718 */
719 inline v3s16 getNodeBlockPos(v3s16 p)
720 {
721         return getContainerPos(p, MAP_BLOCKSIZE);
722 }
723
724 inline v2s16 getNodeSectorPos(v2s16 p)
725 {
726         return getContainerPos(p, MAP_BLOCKSIZE);
727 }
728
729 inline s16 getNodeBlockY(s16 y)
730 {
731         return getContainerPos(y, MAP_BLOCKSIZE);
732 }
733
734 #endif
735