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