/*
Minetest-c55
-Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
+Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
#include <jmutex.h>
#include <jthread.h>
#include <iostream>
-#include <malloc.h>
#ifdef _WIN32
#include <windows.h>
#endif
#include "common_irrlicht.h"
-#include "heightmap.h"
#include "mapnode.h"
#include "mapblock.h"
#include "mapsector.h"
#include "constants.h"
#include "voxel.h"
#include "mapchunk.h"
+#include "nodemetadata.h"
#define MAPTYPE_BASE 0
#define MAPTYPE_SERVER 1
#define MAPTYPE_CLIENT 2
-class Map : public NodeContainer, public Heightmappish
+enum MapEditEventType{
+ // Node added (changed from air or something else to something)
+ MEET_ADDNODE,
+ // Node removed (changed to air)
+ MEET_REMOVENODE,
+ // Node metadata of block changed (not knowing which node exactly)
+ // p stores block coordinate
+ MEET_BLOCK_NODE_METADATA_CHANGED,
+ // Anything else
+ MEET_OTHER
+};
+
+struct MapEditEvent
+{
+ MapEditEventType type;
+ v3s16 p;
+ MapNode n;
+ core::map<v3s16, bool> modified_blocks;
+ u16 already_known_by_peer;
+
+ MapEditEvent():
+ type(MEET_OTHER),
+ already_known_by_peer(0)
+ {
+ }
+
+ MapEditEvent * clone()
+ {
+ MapEditEvent *event = new MapEditEvent();
+ event->type = type;
+ event->p = p;
+ event->n = n;
+ for(core::map<v3s16, bool>::Iterator
+ i = modified_blocks.getIterator();
+ i.atEnd()==false; i++)
+ {
+ v3s16 p = i.getNode()->getKey();
+ bool v = i.getNode()->getValue();
+ event->modified_blocks.insert(p, v);
+ }
+ return event;
+ }
+};
+
+class MapEventReceiver
+{
+public:
+ // event shall be deleted by caller after the call.
+ virtual void onMapEditEvent(MapEditEvent *event) = 0;
+};
+
+class Map : public NodeContainer
{
public:
{
return MAPTYPE_BASE;
}
-
+
+ /*
+ Drop (client) or delete (server) the map.
+ */
virtual void drop()
{
delete this;
}
- void updateCamera(v3f pos, v3f dir)
- {
- JMutexAutoLock lock(m_camera_mutex);
- m_camera_position = pos;
- m_camera_direction = dir;
- }
+ void addEventReceiver(MapEventReceiver *event_receiver);
+ void removeEventReceiver(MapEventReceiver *event_receiver);
+ // event shall be deleted by caller after the call.
+ void dispatchEvent(MapEditEvent *event);
- static core::aabbox3d<f32> getNodeBox(v3s16 p)
- {
- return core::aabbox3d<f32>(
- (float)p.X * BS - 0.5*BS,
- (float)p.Y * BS - 0.5*BS,
- (float)p.Z * BS - 0.5*BS,
- (float)p.X * BS + 0.5*BS,
- (float)p.Y * BS + 0.5*BS,
- (float)p.Z * BS + 0.5*BS
- );
- }
-
// On failure returns NULL
MapSector * getSectorNoGenerateNoExNoLock(v2s16 p2d);
// On failure returns NULL
// Gets an existing block or creates an empty one
//MapBlock * getBlockCreate(v3s16 p);
- // Returns InvalidPositionException if not found
- f32 getGroundHeight(v2s16 p, bool generate=false);
- void setGroundHeight(v2s16 p, f32 y, bool generate=false);
-
// Returns InvalidPositionException if not found
bool isNodeUnderground(v3s16 p);
v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
blockref->setNodeNoCheck(relpos, n);
}
-
- /*MapNode getNodeGenerate(v3s16 p)
+
+ // Returns a CONTENT_IGNORE node if not found
+ MapNode getNodeNoEx(v3s16 p)
{
- v3s16 blockpos = getNodeBlockPos(p);
- MapBlock * blockref = getBlock(blockpos);
- v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
-
- return blockref->getNode(relpos);
- }*/
+ try{
+ v3s16 blockpos = getNodeBlockPos(p);
+ MapBlock * blockref = getBlockNoCreate(blockpos);
+ v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
- /*void setNodeGenerate(v3s16 p, MapNode & n)
- {
- v3s16 blockpos = getNodeBlockPos(p);
- MapBlock * blockref = getBlock(blockpos);
- v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
- blockref->setNode(relpos, n);
- }*/
+ return blockref->getNodeNoCheck(relpos);
+ }
+ catch(InvalidPositionException &e)
+ {
+ return MapNode(CONTENT_IGNORE);
+ }
+ }
void unspreadLight(enum LightBank bank,
core::map<v3s16, u8> & from_nodes,
core::map<v3s16, MapBlock*> &modified_blocks);
void removeNodeAndUpdate(v3s16 p,
core::map<v3s16, MapBlock*> &modified_blocks);
-
-#ifndef SERVER
- void expireMeshes(bool only_daynight_diffed);
-
+
/*
- Update the faces of the given block and blocks on the
- leading edge.
+ Wrappers for the latter ones.
+ These emit events.
+ Return true if succeeded, false if not.
*/
- void updateMeshes(v3s16 blockpos, u32 daynight_ratio);
+ bool addNodeWithEvent(v3s16 p, MapNode n);
+ bool removeNodeWithEvent(v3s16 p);
- // Update meshes that touch the node
- //void updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio);
-#endif
-
/*
Takes the blocks at the edges into account
*/
void transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks);
+ /*
+ Node metadata
+ These are basically coordinate wrappers to MapBlock
+ */
+
+ NodeMetadata* getNodeMetadata(v3s16 p);
+ void setNodeMetadata(v3s16 p, NodeMetadata *meta);
+ void removeNodeMetadata(v3s16 p);
+ void nodeMetadataStep(float dtime,
+ core::map<v3s16, MapBlock*> &changed_blocks);
+
+ /*
+ Misc.
+ */
+ core::map<v2s16, MapSector*> *getSectorsPtr(){return &m_sectors;}
+
/*
Variables
*/
std::ostream &m_dout;
+ core::map<MapEventReceiver*, bool> m_event_receivers;
+
core::map<v2s16, MapSector*> m_sectors;
- JMutex m_sector_mutex;
-
- v3f m_camera_position;
- v3f m_camera_direction;
- JMutex m_camera_mutex;
+ //JMutex m_sector_mutex;
// Be sure to set this to NULL when the cached sector is deleted
MapSector *m_sector_cache;
v2s16 m_sector_cache_p;
- WrapperHeightmap m_hwrapper;
-
// Queued transforming water nodes
UniqueQueue<v3s16> m_transforming_liquid;
};
-// Master heightmap parameters
-struct HMParams
-{
- HMParams()
- {
- blocksize = 64;
- randmax = "constant 70.0";
- randfactor = "constant 0.6";
- base = "linear 0 80 0";
- }
- s16 blocksize;
- std::string randmax;
- std::string randfactor;
- std::string base;
-};
-
-// Map parameters
-struct MapParams
-{
- MapParams()
- {
- plants_amount = 1.0;
- ravines_amount = 1.0;
- //max_objects_in_block = 30;
- }
- float plants_amount;
- float ravines_amount;
- //u16 max_objects_in_block;
-};
-
/*
ServerMap
This is the only map class that is able to generate map.
*/
+struct ChunkMakeData;
+
class ServerMap : public Map
{
public:
/*
savedir: directory to which map data should be saved
*/
- ServerMap(std::string savedir, HMParams hmp, MapParams mp);
+ ServerMap(std::string savedir);
~ServerMap();
s32 mapType() const
// Returns the position of the chunk where the sector is in
v2s16 sector_to_chunk(v2s16 sectorpos)
{
+ if(m_chunksize == 0)
+ return v2s16(0,0);
sectorpos.X += m_chunksize / 2;
sectorpos.Y += m_chunksize / 2;
v2s16 chunkpos = getContainerPos(sectorpos, m_chunksize);
// Returns the position of the (0,0) sector of the chunk
v2s16 chunk_to_sector(v2s16 chunkpos)
{
+ if(m_chunksize == 0)
+ return v2s16(0,0);
v2s16 sectorpos(
chunkpos.X * m_chunksize,
chunkpos.Y * m_chunksize
*/
bool chunkNonVolatile(v2s16 chunkpos)
{
+ if(m_chunksize == 0)
+ return true;
+
/*for(s16 x=-1; x<=1; x++)
for(s16 y=-1; y<=1; y++)*/
s16 x=0;
}
return true;
}
+
+ /*
+ Returns true if any chunk is marked as modified
+ */
+ bool anyChunkModified()
+ {
+ for(core::map<v2s16, MapChunk*>::Iterator
+ i = m_chunks.getIterator();
+ i.atEnd()==false; i++)
+ {
+ v2s16 p = i.getNode()->getKey();
+ MapChunk *chunk = i.getNode()->getValue();
+ if(chunk->isModified())
+ return true;
+ }
+ return false;
+ }
+
+ void setChunksNonModified()
+ {
+ for(core::map<v2s16, MapChunk*>::Iterator
+ i = m_chunks.getIterator();
+ i.atEnd()==false; i++)
+ {
+ v2s16 p = i.getNode()->getKey();
+ MapChunk *chunk = i.getNode()->getValue();
+ chunk->setModified(false);
+ }
+ }
+
+ /*
+ Chunks are generated by using these and makeChunk().
+ */
+ void initChunkMake(ChunkMakeData &data, v2s16 chunkpos);
+ MapChunk* finishChunkMake(ChunkMakeData &data,
+ core::map<v3s16, MapBlock*> &changed_blocks);
/*
Generate a chunk.
All chunks touching this one can be altered also.
*/
- MapChunk* generateChunkRaw(v2s16 chunkpos,
- core::map<v3s16, MapBlock*> &changed_blocks);
+ /*MapChunk* generateChunkRaw(v2s16 chunkpos,
+ core::map<v3s16, MapBlock*> &changed_blocks,
+ bool force=false);*/
/*
Generate a chunk and its neighbors so that it won't be touched
anymore.
*/
- MapChunk* generateChunk(v2s16 chunkpos,
- core::map<v3s16, MapBlock*> &changed_blocks);
+ /*MapChunk* generateChunk(v2s16 chunkpos,
+ core::map<v3s16, MapBlock*> &changed_blocks);*/
/*
Generate a sector.
- Check disk (loads blocks also)
- Generate chunk
*/
- MapSector * emergeSector(v2s16 p,
- core::map<v3s16, MapBlock*> &changed_blocks);
+ /*MapSector * emergeSector(v2s16 p,
+ core::map<v3s16, MapBlock*> &changed_blocks);*/
- MapSector * emergeSector(v2s16 p)
+ /*MapSector * emergeSector(v2s16 p)
{
core::map<v3s16, MapBlock*> changed_blocks;
return emergeSector(p, changed_blocks);
- }
+ }*/
MapBlock * generateBlock(
v3s16 p,
core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
);
#endif
+
+ // Helper for placing objects on ground level
+ s16 findGroundLevel(v2s16 p2d);
/*
Misc. helper functions for fiddling with directory and file
names when saving
*/
- void createDir(std::string path);
- void createSaveDir();
- // returns something like "xxxxxxxx"
- std::string getSectorSubDir(v2s16 pos);
+ void createDirs(std::string path);
// returns something like "map/sectors/xxxxxxxx"
- std::string getSectorDir(v2s16 pos);
- std::string createSectorDir(v2s16 pos);
+ std::string getSectorDir(v2s16 pos, int layout = 2);
// dirname: final directory name
v2s16 getSectorPos(std::string dirname);
v3s16 getBlockPos(std::string sectordir, std::string blockfile);
void save(bool only_changed);
- void loadAll();
-
- void saveMasterHeightmap();
- void loadMasterHeightmap();
-
+ //void loadAll();
+
+ // Saves map seed and possibly other stuff
+ void saveMapMeta();
+ void loadMapMeta();
+
+ void saveChunkMeta();
+ void loadChunkMeta();
+
// The sector mutex should be locked when calling most of these
// This only saves sector-specific data such as the heightmap
// (no MapBlocks)
+ // DEPRECATED? Sectors have no metadata anymore.
void saveSectorMeta(ServerMapSector *sector);
- MapSector* loadSectorMeta(std::string dirname);
+ MapSector* loadSectorMeta(std::string dirname, bool save_after_load);
// Full load of a sector including all blocks.
// returns true on success, false on failure.
void saveBlock(MapBlock *block);
// This will generate a sector with getSector if not found.
- void loadBlock(std::string sectordir, std::string blockfile, MapSector *sector);
-
- // Gets from master heightmap
- void getSectorCorners(v2s16 p2d, s16 *corners);
+ void loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load=false);
// For debug printing
virtual void PrintInfo(std::ostream &out);
+ bool isSavingEnabled(){ return m_map_saving_enabled; }
+
private:
- // Generator parameters
- UnlimitedHeightmap *m_heightmap;
- MapParams m_params;
- PointAttributeDatabase m_padb;
-
// Seed used for all kinds of randomness
u64 m_seed;
bool m_map_saving_enabled;
// Chunk size in MapSectors
+ // If 0, chunks are disabled.
s16 m_chunksize;
// Chunks
core::map<v2s16, MapChunk*> m_chunks;
+
+ /*
+ Metadata is re-written on disk only if this is true.
+ This is reset to false when written on disk.
+ */
+ bool m_map_metadata_changed;
};
/*
ISceneNode::drop();
}
+ void updateCamera(v3f pos, v3f dir)
+ {
+ JMutexAutoLock lock(m_camera_mutex);
+ m_camera_position = pos;
+ m_camera_direction = dir;
+ }
+
/*
Forcefully get a sector from somewhere
*/
// Efficient implementation needs a cache of TempMods
//void clearTempMods();
+ void expireMeshes(bool only_daynight_diffed);
+
+ /*
+ Update the faces of the given block and blocks on the
+ leading edge.
+ */
+ void updateMeshes(v3s16 blockpos, u32 daynight_ratio);
+
+ // Update meshes that touch the node
+ //void updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio);
+
// For debug printing
virtual void PrintInfo(std::ostream &out);
core::aabbox3d<f32> m_box;
// This is the master heightmap mesh
- scene::SMesh *mesh;
- JMutex mesh_mutex;
+ //scene::SMesh *mesh;
+ //JMutex mesh_mutex;
MapDrawControl &m_control;
+
+ v3f m_camera_position;
+ v3f m_camera_direction;
+ JMutex m_camera_mutex;
+
};
#endif
public:
ManualMapVoxelManipulator(Map *map);
virtual ~ManualMapVoxelManipulator();
+
+ void setMap(Map *map)
+ {m_map = map;}
virtual void emerge(VoxelArea a, s32 caller_id=-1);
bool m_create_area;
};
+struct ChunkMakeData
+{
+ bool no_op;
+ ManualMapVoxelManipulator vmanip;
+ u64 seed;
+ v2s16 chunkpos;
+ s16 y_blocks_min;
+ s16 y_blocks_max;
+ v2s16 sectorpos_base;
+ s16 sectorpos_base_size;
+ v2s16 sectorpos_bigbase;
+ s16 sectorpos_bigbase_size;
+ s16 max_spread_amount;
+ UniqueQueue<v3s16> transforming_liquid;
+
+ ChunkMakeData():
+ no_op(false),
+ vmanip(NULL),
+ seed(0)
+ {}
+};
+
+void makeChunk(ChunkMakeData *data);
+
#endif