This commit updates mapblocks' light if necessary when they are loaded.
This removes ghost lighting.
surely_not_found_on_disk = true;
}
- // Block is valid if lighting is up-to-date and data exists
- if(block->isValid() == false)
- {
- block_is_invalid = true;
- }
-
if(block->isGenerated() == false)
block_is_invalid = true;
// Update lighting
std::vector<std::pair<v3s16, MapNode> > oldnodes;
oldnodes.push_back(std::pair<v3s16, MapNode>(p, oldnode));
- voxalgo::update_lighting_nodes(this, m_nodedef, oldnodes, modified_blocks);
+ voxalgo::update_lighting_nodes(this, oldnodes, modified_blocks);
for(std::map<v3s16, MapBlock*>::iterator
i = modified_blocks.begin();
for (std::deque<v3s16>::iterator iter = must_reflow.begin(); iter != must_reflow.end(); ++iter)
m_transforming_liquid.push_back(*iter);
- voxalgo::update_lighting_nodes(this, m_nodedef, changed_nodes, modified_blocks);
+ voxalgo::update_lighting_nodes(this, changed_nodes, modified_blocks);
/* ----------------------------------------------------------------------
v3s16 bpmax = data->blockpos_max;
v3s16 extra_borders(1, 1, 1);
- v3s16 full_bpmin = bpmin - extra_borders;
- v3s16 full_bpmax = bpmax + extra_borders;
bool enable_mapgen_debug_info = m_emerge->enable_mapgen_debug_info;
EMERGE_DBG_OUT("finishBlockMake(): " PP(bpmin) " - " PP(bpmax));
- /*
- Set lighting to non-expired state in all of them.
- This is cheating, but it is not fast enough if all of them
- would actually be updated.
- */
- for (s16 x = full_bpmin.X; x <= full_bpmax.X; x++)
- for (s16 z = full_bpmin.Z; z <= full_bpmax.Z; z++)
- for (s16 y = full_bpmin.Y; y <= full_bpmax.Y; y++) {
- MapBlock *block = emergeBlock(v3s16(x, y, z), false);
- if (!block)
- continue;
-
- block->setLightingExpired(false);
- }
-
/*
Blit generated stuff to map
NOTE: blitBackAll adds nearly everything to changed_blocks
// We just loaded it from, so it's up-to-date.
block->resetModified();
-
}
catch(SerializationError &e)
{
{
DSTACK(FUNCTION_NAME);
+ bool created_new = (getBlockNoCreateNoEx(blockpos) == NULL);
+
v2s16 p2d(blockpos.X, blockpos.Z);
std::string ret;
dbase->loadBlock(blockpos, &ret);
if (ret != "") {
loadBlock(&ret, blockpos, createSector(p2d), false);
- return getBlockNoCreateNoEx(blockpos);
- }
- // Not found in database, try the files
-
- // The directory layout we're going to load from.
- // 1 - original sectors/xxxxzzzz/
- // 2 - new sectors2/xxx/zzz/
- // If we load from anything but the latest structure, we will
- // immediately save to the new one, and remove the old.
- int loadlayout = 1;
- std::string sectordir1 = getSectorDir(p2d, 1);
- std::string sectordir;
- if(fs::PathExists(sectordir1))
- {
- sectordir = sectordir1;
- }
- else
- {
- loadlayout = 2;
- sectordir = getSectorDir(p2d, 2);
- }
+ } else {
+ // Not found in database, try the files
+
+ // The directory layout we're going to load from.
+ // 1 - original sectors/xxxxzzzz/
+ // 2 - new sectors2/xxx/zzz/
+ // If we load from anything but the latest structure, we will
+ // immediately save to the new one, and remove the old.
+ int loadlayout = 1;
+ std::string sectordir1 = getSectorDir(p2d, 1);
+ std::string sectordir;
+ if (fs::PathExists(sectordir1)) {
+ sectordir = sectordir1;
+ } else {
+ loadlayout = 2;
+ sectordir = getSectorDir(p2d, 2);
+ }
- /*
+ /*
Make sure sector is loaded
- */
+ */
- MapSector *sector = getSectorNoGenerateNoEx(p2d);
- if(sector == NULL)
- {
- try{
- sector = loadSectorMeta(sectordir, loadlayout != 2);
- }
- catch(InvalidFilenameException &e)
- {
- return NULL;
- }
- catch(FileNotGoodException &e)
- {
- return NULL;
- }
- catch(std::exception &e)
- {
- return NULL;
+ MapSector *sector = getSectorNoGenerateNoEx(p2d);
+ if (sector == NULL) {
+ try {
+ sector = loadSectorMeta(sectordir, loadlayout != 2);
+ } catch(InvalidFilenameException &e) {
+ return NULL;
+ } catch(FileNotGoodException &e) {
+ return NULL;
+ } catch(std::exception &e) {
+ return NULL;
+ }
}
- }
- /*
+
+ /*
Make sure file exists
- */
+ */
- std::string blockfilename = getBlockFilename(blockpos);
- if(fs::PathExists(sectordir + DIR_DELIM + blockfilename) == false)
- return NULL;
+ std::string blockfilename = getBlockFilename(blockpos);
+ if (fs::PathExists(sectordir + DIR_DELIM + blockfilename) == false)
+ return NULL;
- /*
+ /*
Load block and save it to the database
- */
- loadBlock(sectordir, blockfilename, sector, true);
- return getBlockNoCreateNoEx(blockpos);
+ */
+ loadBlock(sectordir, blockfilename, sector, true);
+ }
+ MapBlock *block = getBlockNoCreateNoEx(blockpos);
+ if (created_new && (block != NULL)) {
+ std::map<v3s16, MapBlock*> modified_blocks;
+ // Fix lighting if necessary
+ voxalgo::update_block_border_lighting(this, block, modified_blocks);
+ if (!modified_blocks.empty()) {
+ //Modified lighting, send event
+ MapEditEvent event;
+ event.type = MEET_OTHER;
+ std::map<v3s16, MapBlock *>::iterator it;
+ for (it = modified_blocks.begin();
+ it != modified_blocks.end(); ++it)
+ event.modified_blocks.insert(it->first);
+ dispatchEvent(&event);
+ }
+ }
+ return block;
}
bool ServerMap::deleteBlock(v3s16 blockpos)
m_modified(MOD_STATE_WRITE_NEEDED),
m_modified_reason(MOD_REASON_INITIAL),
is_underground(false),
- m_lighting_expired(true),
+ m_lighting_complete(0xFFFF),
m_day_night_differs(false),
m_day_night_differs_expired(true),
m_generated(false),
flags |= 0x01;
if(getDayNightDiff())
flags |= 0x02;
- if(m_lighting_expired)
- flags |= 0x04;
if(m_generated == false)
flags |= 0x08;
writeU8(os, flags);
+ if (version >= 27) {
+ writeU16(os, m_lighting_complete);
+ }
/*
Bulk node data
u8 flags = readU8(is);
is_underground = (flags & 0x01) ? true : false;
m_day_night_differs = (flags & 0x02) ? true : false;
- m_lighting_expired = (flags & 0x04) ? true : false;
+ if (version < 27) {
+ m_lighting_complete = 0xFFFF;
+ } else {
+ m_lighting_complete = readU16(is);
+ }
m_generated = (flags & 0x08) ? false : true;
/*
// Initialize default flags
is_underground = false;
m_day_night_differs = false;
- m_lighting_expired = false;
+ m_lighting_complete = 0xFFFF;
m_generated = true;
// Make a temporary buffer
is.read((char*)&flags, 1);
is_underground = (flags & 0x01) ? true : false;
m_day_night_differs = (flags & 0x02) ? true : false;
- m_lighting_expired = (flags & 0x04) ? true : false;
if(version >= 18)
m_generated = (flags & 0x08) ? false : true;
else
desc<<"is_ug [ ], ";
- if(block->getLightingExpired())
- desc<<"lighting_exp [X], ";
- else
- desc<<"lighting_exp [ ], ";
+ desc<<"lighting_complete: "<<block->getLightingComplete()<<", ";
if(block->isDummy())
{
#define MOD_REASON_INITIAL (1 << 0)
#define MOD_REASON_REALLOCATE (1 << 1)
#define MOD_REASON_SET_IS_UNDERGROUND (1 << 2)
-#define MOD_REASON_SET_LIGHTING_EXPIRED (1 << 3)
+#define MOD_REASON_SET_LIGHTING_COMPLETE (1 << 3)
#define MOD_REASON_SET_GENERATED (1 << 4)
#define MOD_REASON_SET_NODE (1 << 5)
#define MOD_REASON_SET_NODE_NO_CHECK (1 << 6)
raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_SET_IS_UNDERGROUND);
}
- inline void setLightingExpired(bool expired)
+ inline void setLightingComplete(u16 newflags)
{
- if (expired != m_lighting_expired){
- m_lighting_expired = expired;
- raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_SET_LIGHTING_EXPIRED);
+ if (newflags != m_lighting_complete) {
+ m_lighting_complete = newflags;
+ raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_SET_LIGHTING_COMPLETE);
}
}
- inline bool getLightingExpired()
+ inline u16 getLightingComplete()
{
- return m_lighting_expired;
+ return m_lighting_complete;
+ }
+
+ inline void setLightingComplete(LightBank bank, u8 direction,
+ bool is_complete)
+ {
+ assert(direction >= 0 && direction <= 5);
+ if (bank == LIGHTBANK_NIGHT) {
+ direction += 6;
+ }
+ u16 newflags = m_lighting_complete;
+ if (is_complete) {
+ newflags |= 1 << direction;
+ } else {
+ newflags &= ~(1 << direction);
+ }
+ setLightingComplete(newflags);
+ }
+
+ inline bool isLightingComplete(LightBank bank, u8 direction)
+ {
+ assert(direction >= 0 && direction <= 5);
+ if (bank == LIGHTBANK_NIGHT) {
+ direction += 6;
+ }
+ return (m_lighting_complete & (1 << direction)) != 0;
}
inline bool isGenerated()
}
}
- inline bool isValid()
- {
- if (m_lighting_expired)
- return false;
- if (data == NULL)
- return false;
- return true;
- }
-
////
//// Position stuff
////
*/
bool is_underground;
- /*
- Set to true if changes has been made that make the old lighting
- values wrong but the lighting hasn't been actually updated.
-
- If this is false, lighting is exactly right.
- If this is true, lighting might be wrong or right.
+ /*!
+ * Each bit indicates if light spreading was finished
+ * in a direction. (Because the neighbor could also be unloaded.)
+ * Bits: day X+, day Y+, day Z+, day Z-, day Y-, day X-,
+ * night X+, night Y+, night Z+, night Z-, night Y-, night X-,
+ * nothing, nothing, nothing, nothing.
*/
- bool m_lighting_expired;
+ u16 m_lighting_complete;
// Whether day and night lighting differs
bool m_day_night_differs;
24: 16-bit node ids and node timers (never released as stable)
25: Improved node timer format
26: Never written; read the same as 25
+ 27: Added light spreading flags to blocks
*/
// This represents an uninitialized or invalid format
#define SER_FMT_VER_INVALID 255
// Highest supported serialization version
-#define SER_FMT_VER_HIGHEST_READ 26
+#define SER_FMT_VER_HIGHEST_READ 27
// Saved on disk version
-#define SER_FMT_VER_HIGHEST_WRITE 25
+#define SER_FMT_VER_HIGHEST_WRITE 27
// Lowest supported serialization version
#define SER_FMT_VER_LOWEST_READ 0
// Lowest serialization version for writing
if (step_rel_block_pos(i, neighbor_rel_pos, neighbor_block_pos)) {
neighbor_block = map->getBlockNoCreateNoEx(neighbor_block_pos);
if (neighbor_block == NULL) {
+ current.block->setLightingComplete(bank, i, false);
continue;
}
} else {
* \param modified_blocks output, all modified map blocks are added to this
*/
void spread_light(Map *map, INodeDefManager *nodemgr, LightBank bank,
- LightQueue &light_sources, std::map<v3s16, MapBlock*> &modified_blocks)
+ LightQueue &light_sources,
+ std::map<v3s16, MapBlock*> &modified_blocks)
{
// The light the current node can provide to its neighbors.
u8 spreading_light;
if (step_rel_block_pos(i, neighbor_rel_pos, neighbor_block_pos)) {
neighbor_block = map->getBlockNoCreateNoEx(neighbor_block_pos);
if (neighbor_block == NULL) {
+ current.block->setLightingComplete(bank, i, false);
continue;
}
} else {
static const LightBank banks[] = { LIGHTBANK_DAY, LIGHTBANK_NIGHT };
-void update_lighting_nodes(Map *map, INodeDefManager *ndef,
+void update_lighting_nodes(Map *map,
std::vector<std::pair<v3s16, MapNode> > &oldnodes,
std::map<v3s16, MapBlock*> &modified_blocks)
{
+ INodeDefManager *ndef = map->getNodeDefManager();
// For node getter functions
bool is_valid_position;
LightBank bank = banks[i];
UnlightQueue disappearing_lights(256);
ReLightQueue light_sources(256);
+ // Nodes that are brighter than the brightest modified node was
+ // won't change, since they didn't get their light from a
+ // modified node.
+ u8 min_safe_light = 0;
+ for (std::vector<std::pair<v3s16, MapNode> >::iterator it =
+ oldnodes.begin(); it < oldnodes.end(); ++it) {
+ u8 old_light = it->second.getLight(bank, ndef);
+ if (old_light > min_safe_light) {
+ min_safe_light = old_light;
+ }
+ }
+ // If only one node changed, even nodes with the same brightness
+ // didn't get their light from the changed node.
+ if (oldnodes.size() > 1) {
+ min_safe_light++;
+ }
// For each changed node process sunlight and initialize
for (std::vector<std::pair<v3s16, MapNode> >::iterator it =
oldnodes.begin(); it < oldnodes.end(); ++it) {
MapNode n2 = map->getNodeNoEx(p2, &is_valid);
if (is_valid) {
u8 spread = n2.getLight(bank, ndef);
- // If the neighbor is at least as bright as
- // this node then its light is not from
- // this node.
- // Its light can spread to this node.
- if (spread > new_light && spread >= old_light) {
+ // If it is sure that the neighbor won't be
+ // unlighted, its light can spread to this node.
+ if (spread > new_light && spread >= min_safe_light) {
new_light = spread - 1;
}
}
}
}
+/*!
+ * Borders of a map block in relative node coordinates.
+ * Compatible with type 'direction'.
+ */
+const VoxelArea block_borders[] = {
+ VoxelArea(v3s16(15, 0, 0), v3s16(15, 15, 15)), //X+
+ VoxelArea(v3s16(0, 15, 0), v3s16(15, 15, 15)), //Y+
+ VoxelArea(v3s16(0, 0, 15), v3s16(15, 15, 15)), //Z+
+ VoxelArea(v3s16(0, 0, 0), v3s16(15, 15, 0)), //Z-
+ VoxelArea(v3s16(0, 0, 0), v3s16(15, 0, 15)), //Y-
+ VoxelArea(v3s16(0, 0, 0), v3s16(0, 15, 15)) //X-
+};
+
+/*!
+ * Returns true if:
+ * -the node has unloaded neighbors
+ * -the node doesn't have light
+ * -the node's light is the same as the maximum of
+ * its light source and its brightest neighbor minus one.
+ * .
+ */
+bool is_light_locally_correct(Map *map, INodeDefManager *ndef, LightBank bank,
+ v3s16 pos)
+{
+ bool is_valid_position;
+ MapNode n = map->getNodeNoEx(pos, &is_valid_position);
+ const ContentFeatures &f = ndef->get(n);
+ if (f.param_type != CPT_LIGHT) {
+ return true;
+ }
+ u8 light = n.getLightNoChecks(bank, &f);
+ assert(f.light_source <= LIGHT_MAX);
+ u8 brightest_neighbor = f.light_source + 1;
+ for (direction d = 0; d < 6; ++d) {
+ MapNode n2 = map->getNodeNoEx(pos + neighbor_dirs[d],
+ &is_valid_position);
+ u8 light2 = n2.getLight(bank, ndef);
+ if (brightest_neighbor < light2) {
+ brightest_neighbor = light2;
+ }
+ }
+ assert(light <= LIGHT_SUN);
+ return brightest_neighbor == light + 1;
+}
+
+void update_block_border_lighting(Map *map, MapBlock *block,
+ std::map<v3s16, MapBlock*> &modified_blocks)
+{
+ INodeDefManager *ndef = map->getNodeDefManager();
+ bool is_valid_position;
+ for (s32 i = 0; i < 2; i++) {
+ LightBank bank = banks[i];
+ UnlightQueue disappearing_lights(256);
+ ReLightQueue light_sources(256);
+ // Get incorrect lights
+ for (direction d = 0; d < 6; d++) {
+ // For each direction
+ // Get neighbor block
+ v3s16 otherpos = block->getPos() + neighbor_dirs[d];
+ MapBlock *other = map->getBlockNoCreateNoEx(otherpos);
+ if (other == NULL) {
+ continue;
+ }
+ // Only update if lighting was not completed.
+ if (block->isLightingComplete(bank, d) &&
+ other->isLightingComplete(bank, 5 - d))
+ continue;
+ // Reset flags
+ block->setLightingComplete(bank, d, true);
+ other->setLightingComplete(bank, 5 - d, true);
+ // The two blocks and their connecting surfaces
+ MapBlock *blocks[] = {block, other};
+ VoxelArea areas[] = {block_borders[d], block_borders[5 - d]};
+ // For both blocks
+ for (u8 blocknum = 0; blocknum < 2; blocknum++) {
+ MapBlock *b = blocks[blocknum];
+ VoxelArea a = areas[blocknum];
+ // For all nodes
+ for (s32 x = a.MinEdge.X; x <= a.MaxEdge.X; x++)
+ for (s32 z = a.MinEdge.Z; z <= a.MaxEdge.Z; z++)
+ for (s32 y = a.MinEdge.Y; y <= a.MaxEdge.Y; y++) {
+ MapNode n = b->getNodeNoCheck(x, y, z,
+ &is_valid_position);
+ u8 light = n.getLight(bank, ndef);
+ // Sunlight is fixed
+ if (light < LIGHT_SUN) {
+ // Unlight if not correct
+ if (!is_light_locally_correct(map, ndef, bank,
+ v3s16(x, y, z) + b->getPosRelative())) {
+ // Initialize for unlighting
+ n.setLight(bank, 0, ndef);
+ b->setNodeNoCheck(x, y, z, n);
+ modified_blocks[b->getPos()]=b;
+ disappearing_lights.push(light,
+ relative_v3(x, y, z), b->getPos(), b,
+ 6);
+ }
+ }
+ }
+ }
+ }
+ // Remove lights
+ unspread_light(map, ndef, bank, disappearing_lights, light_sources,
+ modified_blocks);
+ // Initialize light values for light spreading.
+ for (u8 i = 0; i <= LIGHT_SUN; i++) {
+ const std::vector<ChangingLight> &lights = light_sources.lights[i];
+ for (std::vector<ChangingLight>::const_iterator it = lights.begin();
+ it < lights.end(); it++) {
+ MapNode n = it->block->getNodeNoCheck(it->rel_position,
+ &is_valid_position);
+ n.setLight(bank, i, ndef);
+ it->block->setNodeNoCheck(it->rel_position, n);
+ }
+ }
+ // Spread lights.
+ spread_light(map, ndef, bank, light_sources, modified_blocks);
+ }
+}
+
VoxelLineIterator::VoxelLineIterator(
const v3f &start_position,
const v3f &line_vector) :
#include "voxel.h"
#include "mapnode.h"
-#include <set>
-#include <map>
+#include "util/container.h"
+#include "util/cpp11_container.h"
class Map;
class MapBlock;
*/
void update_lighting_nodes(
Map *map,
- INodeDefManager *ndef,
std::vector<std::pair<v3s16, MapNode> > &oldnodes,
std::map<v3s16, MapBlock*> &modified_blocks);
+/*!
+ * Updates borders of the given mapblock.
+ * Only updates if the block was marked with incomplete
+ * lighting and the neighbor is also loaded.
+ *
+ * \param block the block to update
+ * \param modified_blocks output, contains all map blocks that
+ * the function modified
+ */
+void update_block_border_lighting(Map *map, MapBlock *block,
+ std::map<v3s16, MapBlock*> &modified_blocks);
+
/*!
* This class iterates trough voxels that intersect with
* a line. The collision detection does not see nodeboxes,