a mutex added to TempMods which hopefully fixes rare segfaults on client
[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 "mapblockobject.h"
33 #include "voxel.h"
34
35 #define MAP_BLOCKSIZE 16
36
37 // Named by looking towards z+
38 enum{
39         FACE_BACK=0,
40         FACE_TOP,
41         FACE_RIGHT,
42         FACE_FRONT,
43         FACE_BOTTOM,
44         FACE_LEFT
45 };
46
47 struct FastFace
48 {
49         TileSpec tile;
50         video::S3DVertex vertices[4]; // Precalculated vertices
51 };
52
53 enum NodeModType
54 {
55         NODEMOD_NONE,
56         NODEMOD_CHANGECONTENT, //param is content id
57         NODEMOD_CRACK // param is crack progression
58 };
59
60 struct NodeMod
61 {
62         NodeMod(enum NodeModType a_type=NODEMOD_NONE, u16 a_param=0)
63         {
64                 type = a_type;
65                 param = a_param;
66         }
67         enum NodeModType type;
68         u16 param;
69 };
70
71 enum
72 {
73         NODECONTAINER_ID_MAPBLOCK,
74         NODECONTAINER_ID_MAPSECTOR,
75         NODECONTAINER_ID_MAP,
76         NODECONTAINER_ID_MAPBLOCKCACHE,
77         NODECONTAINER_ID_VOXELMANIPULATOR,
78 };
79
80 class NodeContainer
81 {
82 public:
83         virtual bool isValidPosition(v3s16 p) = 0;
84         virtual MapNode getNode(v3s16 p) = 0;
85         virtual void setNode(v3s16 p, MapNode & n) = 0;
86         virtual u16 nodeContainerId() const = 0;
87 };
88
89 class MapBlock : public NodeContainer
90 {
91 public:
92         MapBlock(NodeContainer *parent, v3s16 pos, bool dummy=false);
93         ~MapBlock();
94         
95         virtual u16 nodeContainerId() const
96         {
97                 return NODECONTAINER_ID_MAPBLOCK;
98         }
99
100         NodeContainer * getParent()
101         {
102                 return m_parent;
103         }
104
105         bool isDummy()
106         {
107                 return (data == NULL);
108         }
109
110         void unDummify()
111         {
112                 assert(isDummy());
113                 reallocate();
114         }
115         
116         bool getChangedFlag()
117         {
118                 return changed;
119         }
120
121         void resetChangedFlag()
122         {
123                 changed = false;
124         }
125
126         void setChangedFlag()
127         {
128                 changed = true;
129         }
130 #ifndef SERVER
131         void setMeshExpired(bool expired)
132         {
133                 m_mesh_expired = expired;
134         }
135         
136         bool getMeshExpired()
137         {
138                 return m_mesh_expired;
139         }
140 #endif
141         v3s16 getPos()
142         {
143                 return m_pos;
144         }
145                 
146         v3s16 getPosRelative()
147         {
148                 return m_pos * MAP_BLOCKSIZE;
149         }
150                 
151         bool getIsUnderground()
152         {
153                 return is_underground;
154         }
155
156         void setIsUnderground(bool a_is_underground)
157         {
158                 is_underground = a_is_underground;
159                 setChangedFlag();
160         }
161
162         core::aabbox3d<s16> getBox()
163         {
164                 return core::aabbox3d<s16>(getPosRelative(),
165                                 getPosRelative()
166                                 + v3s16(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE)
167                                 - v3s16(1,1,1));
168         }
169         
170         void reallocate()
171         {
172                 if(data != NULL)
173                         delete[] data;
174                 u32 l = MAP_BLOCKSIZE * MAP_BLOCKSIZE * MAP_BLOCKSIZE;
175                 data = new MapNode[l];
176                 for(u32 i=0; i<l; i++){
177                         data[i] = MapNode();
178                 }
179                 setChangedFlag();
180         }
181
182         bool isValidPosition(v3s16 p)
183         {
184                 if(data == NULL)
185                         return false;
186                 return (p.X >= 0 && p.X < MAP_BLOCKSIZE
187                                 && p.Y >= 0 && p.Y < MAP_BLOCKSIZE
188                                 && p.Z >= 0 && p.Z < MAP_BLOCKSIZE);
189         }
190
191         /*
192                 Regular MapNode get-setters
193         */
194         
195         MapNode getNode(s16 x, s16 y, s16 z)
196         {
197                 if(data == NULL)
198                         throw InvalidPositionException();
199                 if(x < 0 || x >= MAP_BLOCKSIZE) throw InvalidPositionException();
200                 if(y < 0 || y >= MAP_BLOCKSIZE) throw InvalidPositionException();
201                 if(z < 0 || z >= MAP_BLOCKSIZE) throw InvalidPositionException();
202                 return data[z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + y*MAP_BLOCKSIZE + x];
203         }
204         
205         MapNode getNode(v3s16 p)
206         {
207                 return getNode(p.X, p.Y, p.Z);
208         }
209         
210         void setNode(s16 x, s16 y, s16 z, MapNode & n)
211         {
212                 if(data == NULL)
213                         throw InvalidPositionException();
214                 if(x < 0 || x >= MAP_BLOCKSIZE) throw InvalidPositionException();
215                 if(y < 0 || y >= MAP_BLOCKSIZE) throw InvalidPositionException();
216                 if(z < 0 || z >= MAP_BLOCKSIZE) throw InvalidPositionException();
217                 data[z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + y*MAP_BLOCKSIZE + x] = n;
218                 setChangedFlag();
219         }
220         
221         void setNode(v3s16 p, MapNode & n)
222         {
223                 setNode(p.X, p.Y, p.Z, n);
224         }
225
226         /*
227                 Non-checking variants of the above
228         */
229
230         MapNode getNodeNoCheck(s16 x, s16 y, s16 z)
231         {
232                 if(data == NULL)
233                         throw InvalidPositionException();
234                 return data[z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + y*MAP_BLOCKSIZE + x];
235         }
236         
237         MapNode getNodeNoCheck(v3s16 p)
238         {
239                 return getNodeNoCheck(p.X, p.Y, p.Z);
240         }
241         
242         void setNodeNoCheck(s16 x, s16 y, s16 z, MapNode & n)
243         {
244                 if(data == NULL)
245                         throw InvalidPositionException();
246                 data[z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + y*MAP_BLOCKSIZE + x] = n;
247                 setChangedFlag();
248         }
249         
250         void setNodeNoCheck(v3s16 p, MapNode & n)
251         {
252                 setNodeNoCheck(p.X, p.Y, p.Z, n);
253         }
254
255         /*
256                 These functions consult the parent container if the position
257                 is not valid on this MapBlock.
258         */
259         bool isValidPositionParent(v3s16 p);
260         MapNode getNodeParent(v3s16 p);
261         void setNodeParent(v3s16 p, MapNode & n);
262         MapNode getNodeParentNoEx(v3s16 p);
263
264         void drawbox(s16 x0, s16 y0, s16 z0, s16 w, s16 h, s16 d, MapNode node)
265         {
266                 for(u16 z=0; z<d; z++)
267                         for(u16 y=0; y<h; y++)
268                                 for(u16 x=0; x<w; x++)
269                                         setNode(x0+x, y0+y, z0+z, node);
270         }
271
272         u8 getFaceLight(u32 daynight_ratio, MapNode n, MapNode n2,
273                         v3s16 face_dir);
274         
275         u8 getFaceLight(u32 daynight_ratio, v3s16 p, v3s16 face_dir)
276         {
277                 return getFaceLight(daynight_ratio,
278                                 getNodeParentNoEx(p),
279                                 getNodeParentNoEx(p + face_dir),
280                                 face_dir);
281         }
282         
283 #ifndef SERVER
284         // light = 0...255
285         static void makeFastFace(TileSpec tile, u8 light, v3f p,
286                         v3s16 dir, v3f scale, v3f posRelative_f,
287                         core::array<FastFace> &dest);
288         
289         TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir);
290         u8 getNodeContent(v3s16 p, MapNode mn);
291
292         /*
293                 startpos:
294                 translate_dir: unit vector with only one of x, y or z
295                 face_dir: unit vector with only one of x, y or z
296         */
297         void updateFastFaceRow(
298                         u32 daynight_ratio,
299                         v3f posRelative_f,
300                         v3s16 startpos,
301                         u16 length,
302                         v3s16 translate_dir,
303                         v3f translate_dir_f,
304                         v3s16 face_dir,
305                         v3f face_dir_f,
306                         core::array<FastFace> &dest);
307
308         void updateMesh(u32 daynight_ratio);
309         /*void updateMesh(s32 daynight_i);
310         // Updates all DAYNIGHT_CACHE_COUNT meshes
311         void updateMeshes(s32 first_i=0);*/
312 #endif // !SERVER
313
314         bool propagateSunlight(core::map<v3s16, bool> & light_sources);
315         
316         // Copies data to VoxelManipulator to getPosRelative()
317         void copyTo(VoxelManipulator &dst);
318
319         /*
320                 Object stuff
321         */
322         
323         void serializeObjects(std::ostream &os, u8 version)
324         {
325                 m_objects.serialize(os, version);
326         }
327         // If smgr!=NULL, new objects are added to the scene
328         void updateObjects(std::istream &is, u8 version,
329                         scene::ISceneManager *smgr, u32 daynight_ratio)
330         {
331                 m_objects.update(is, version, smgr, daynight_ratio);
332
333                 setChangedFlag();
334         }
335         void clearObjects()
336         {
337                 m_objects.clear();
338
339                 setChangedFlag();
340         }
341         void addObject(MapBlockObject *object)
342                         throw(ContainerFullException, AlreadyExistsException)
343         {
344                 m_objects.add(object);
345
346                 setChangedFlag();
347         }
348         void removeObject(s16 id)
349         {
350                 m_objects.remove(id);
351
352                 setChangedFlag();
353         }
354         MapBlockObject * getObject(s16 id)
355         {
356                 return m_objects.get(id);
357         }
358         JMutexAutoLock * getObjectLock()
359         {
360                 return m_objects.getLock();
361         }
362
363         /*
364                 Moves objects, deletes objects and spawns new objects
365         */
366         void stepObjects(float dtime, bool server, u32 daynight_ratio);
367
368         /*void wrapObject(MapBlockObject *object)
369         {
370                 m_objects.wrapObject(object);
371
372                 setChangedFlag();
373         }*/
374
375         // origin is relative to block
376         void getObjects(v3f origin, f32 max_d,
377                         core::array<DistanceSortedObject> &dest)
378         {
379                 m_objects.getObjects(origin, max_d, dest);
380         }
381
382         /*void getPseudoObjects(v3f origin, f32 max_d,
383                         core::array<DistanceSortedObject> &dest);*/
384
385         s32 getObjectCount()
386         {
387                 return m_objects.getCount();
388         }
389
390 #ifndef SERVER
391         /*
392                 Methods for setting temporary modifications to nodes for
393                 drawing
394         */
395         void setTempMod(v3s16 p, NodeMod mod)
396         {
397                 /*dstream<<"setTempMod called on block"
398                                 <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
399                                 <<", mod.type="<<mod.type
400                                 <<", mod.param="<<mod.param
401                                 <<std::endl;*/
402                 JMutexAutoLock lock(m_temp_mods_mutex);
403                 m_temp_mods[p] = mod;
404         }
405         // Returns true if there was one
406         bool getTempMod(v3s16 p, struct NodeMod *mod)
407         {
408                 JMutexAutoLock lock(m_temp_mods_mutex);
409                 core::map<v3s16, NodeMod>::Node *n;
410                 n = m_temp_mods.find(p);
411                 if(n == NULL)
412                         return false;
413                 if(mod)
414                         *mod = n->getValue();
415                 return true;
416         }
417         void clearTempMod(v3s16 p)
418         {
419                 JMutexAutoLock lock(m_temp_mods_mutex);
420                 if(m_temp_mods.find(p))
421                         m_temp_mods.remove(p);
422         }
423         void clearTempMods()
424         {
425                 JMutexAutoLock lock(m_temp_mods_mutex);
426                 m_temp_mods.clear();
427         }
428 #endif
429
430         /*
431                 Day-night lighting difference
432                 
433                 These methods don't care about neighboring blocks.
434                 It means that to know if a block really doesn't need a mesh
435                 update between day and night, the neighboring blocks have
436                 to be taken into account. Use Map::dayNightDiffed().
437         */
438         void updateDayNightDiff();
439
440         bool dayNightDiffed()
441         {
442                 return m_day_night_differs;
443         }
444
445         /*
446                 Miscellaneous stuff
447         */
448         
449         /*
450                 Tries to measure ground level.
451                 Return value:
452                         -1 = only air
453                         -2 = only ground
454                         -3 = random fail
455                         0...MAP_BLOCKSIZE-1 = ground level
456         */
457         s16 getGroundLevel(v2s16 p2d);
458
459         /*
460                 Serialization
461         */
462         
463         // Doesn't write version by itself
464         void serialize(std::ostream &os, u8 version);
465
466         void deSerialize(std::istream &is, u8 version);
467
468         /*
469                 Public member variables
470         */
471
472 #ifndef SERVER
473         //scene::SMesh *mesh[DAYNIGHT_CACHE_COUNT];
474         scene::SMesh *mesh;
475         JMutex mesh_mutex;
476 #endif
477
478 private:
479
480         /*
481                 Used only internally, because changes can't be tracked
482         */
483
484         MapNode & getNodeRef(s16 x, s16 y, s16 z)
485         {
486                 if(data == NULL)
487                         throw InvalidPositionException();
488                 if(x < 0 || x >= MAP_BLOCKSIZE) throw InvalidPositionException();
489                 if(y < 0 || y >= MAP_BLOCKSIZE) throw InvalidPositionException();
490                 if(z < 0 || z >= MAP_BLOCKSIZE) throw InvalidPositionException();
491                 return data[z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + y*MAP_BLOCKSIZE + x];
492         }
493         MapNode & getNodeRef(v3s16 &p)
494         {
495                 return getNodeRef(p.X, p.Y, p.Z);
496         }
497
498         
499         NodeContainer *m_parent;
500         // Position in blocks on parent
501         v3s16 m_pos;
502         /*
503                 If NULL, block is a dummy block.
504                 Dummy blocks are used for caching not-found-on-disk blocks.
505         */
506         MapNode * data;
507         /*
508                 - On the client, this is used for checking whether to
509                   recalculate the face cache. (Is it anymore?)
510                 - On the server, this is used for telling whether the
511                   block has been changed from the one on disk.
512         */
513         bool changed;
514         /*
515                 Used for some initial lighting stuff.
516                 At least /has been/ used. 8)
517                 It's probably useless now.
518         */
519         bool is_underground;
520         
521         // Whether day and night lighting differs
522         bool m_day_night_differs;
523         
524         MapBlockObjectList m_objects;
525
526         // Object spawning stuff
527         float m_spawn_timer;
528         
529 #ifndef SERVER
530         bool m_mesh_expired;
531         
532         // Temporary modifications to nodes
533         // These are only used when drawing
534         core::map<v3s16, NodeMod> m_temp_mods;
535         JMutex m_temp_mods_mutex;
536 #endif
537 };
538
539 inline bool blockpos_over_limit(v3s16 p)
540 {
541         return
542           (p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
543         || p.X >  MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
544         || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
545         || p.Y >  MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
546         || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
547         || p.Z >  MAP_GENERATION_LIMIT / MAP_BLOCKSIZE);
548 }
549
550 /*
551         Returns the position of the block where the node is located
552 */
553 inline v3s16 getNodeBlockPos(v3s16 p)
554 {
555         return getContainerPos(p, MAP_BLOCKSIZE);
556 }
557
558 inline v2s16 getNodeSectorPos(v2s16 p)
559 {
560         return getContainerPos(p, MAP_BLOCKSIZE);
561 }
562
563 inline s16 getNodeBlockY(s16 y)
564 {
565         return getContainerPos(y, MAP_BLOCKSIZE);
566 }
567
568 #endif
569