#include "content_nodemeta.h" // For legacy deserialization
#include "serialization.h"
#ifndef SERVER
-#include "mapblock_mesh.h"
+#include "client/mapblock_mesh.h"
#endif
+#include "porting.h"
#include "util/string.h"
#include "util/serialize.h"
#include "util/basic_macros.h"
MapNode MapBlock::getNodeParent(v3s16 p, bool *is_valid_position)
{
if (!isValidPosition(p))
- return m_parent->getNodeNoEx(getPosRelative() + p, is_valid_position);
+ return m_parent->getNode(getPosRelative() + p, is_valid_position);
if (!data) {
if (is_valid_position)
return reason;
}
-/*
- Propagates sunlight down through the block.
- Doesn't modify nodes that are not affected by sunlight.
-
- Returns false if sunlight at bottom block is invalid.
- Returns true if sunlight at bottom block is valid.
- Returns true if bottom block doesn't exist.
-
- If there is a block above, continues from it.
- If there is no block above, assumes there is sunlight, unless
- is_underground is set or highest node is water.
-
- All sunlighted nodes are added to light_sources.
-
- if remove_light==true, sets non-sunlighted nodes black.
-
- if black_air_left!=NULL, it is set to true if non-sunlighted
- air is left in block.
-*/
-bool MapBlock::propagateSunlight(std::set<v3s16> & light_sources,
- bool remove_light, bool *black_air_left)
-{
- INodeDefManager *nodemgr = m_gamedef->ndef();
-
- // Whether the sunlight at the top of the bottom block is valid
- bool block_below_is_valid = true;
-
- v3s16 pos_relative = getPosRelative();
-
- for(s16 x=0; x<MAP_BLOCKSIZE; x++)
- {
- for(s16 z=0; z<MAP_BLOCKSIZE; z++)
- {
-#if 1
- bool no_sunlight = false;
- //bool no_top_block = false;
-
- // Check if node above block has sunlight
-
- bool is_valid_position;
- MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z),
- &is_valid_position);
- if (is_valid_position)
- {
- if(n.getContent() == CONTENT_IGNORE)
- {
- // Trust heuristics
- no_sunlight = is_underground;
- }
- else if(n.getLight(LIGHTBANK_DAY, m_gamedef->ndef()) != LIGHT_SUN)
- {
- no_sunlight = true;
- }
- }
- else
- {
- //no_top_block = true;
-
- // NOTE: This makes over-ground roofed places sunlighted
- // Assume sunlight, unless is_underground==true
- if(is_underground)
- {
- no_sunlight = true;
- }
- else
- {
- MapNode n = getNodeNoEx(v3s16(x, MAP_BLOCKSIZE-1, z));
- if (!m_gamedef->ndef()->get(n).sunlight_propagates) {
- no_sunlight = true;
- }
- }
- // NOTE: As of now, this just would make everything dark.
- // No sunlight here
- //no_sunlight = true;
- }
-#endif
-#if 0 // Doesn't work; nothing gets light.
- bool no_sunlight = true;
- bool no_top_block = false;
- // Check if node above block has sunlight
- try{
- MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
- if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
- {
- no_sunlight = false;
- }
- }
- catch(InvalidPositionException &e)
- {
- no_top_block = true;
- }
-#endif
-
- /*std::cout<<"("<<x<<","<<z<<"): "
- <<"no_top_block="<<no_top_block
- <<", is_underground="<<is_underground
- <<", no_sunlight="<<no_sunlight
- <<std::endl;*/
-
- s16 y = MAP_BLOCKSIZE-1;
-
- // This makes difference to diminishing in water.
- bool stopped_to_solid_object = false;
-
- u8 current_light = no_sunlight ? 0 : LIGHT_SUN;
-
- for(; y >= 0; y--)
- {
- v3s16 pos(x, y, z);
- MapNode &n = getNodeRef(pos);
-
- if(current_light == 0)
- {
- // Do nothing
- }
- else if(current_light == LIGHT_SUN && nodemgr->get(n).sunlight_propagates)
- {
- // Do nothing: Sunlight is continued
- } else if (!nodemgr->get(n).light_propagates) {
- // A solid object is on the way.
- stopped_to_solid_object = true;
-
- // Light stops.
- current_light = 0;
- }
- else
- {
- // Diminish light
- current_light = diminish_light(current_light);
- }
-
- u8 old_light = n.getLight(LIGHTBANK_DAY, nodemgr);
-
- if(current_light > old_light || remove_light)
- {
- n.setLight(LIGHTBANK_DAY, current_light, nodemgr);
- }
-
- if(diminish_light(current_light) != 0)
- {
- light_sources.insert(pos_relative + pos);
- }
-
- if(current_light == 0 && stopped_to_solid_object)
- {
- if(black_air_left)
- {
- *black_air_left = true;
- }
- }
- }
-
- // Whether or not the block below should see LIGHT_SUN
- bool sunlight_should_go_down = (current_light == LIGHT_SUN);
-
- /*
- If the block below hasn't already been marked invalid:
-
- Check if the node below the block has proper sunlight at top.
- If not, the block below is invalid.
-
- Ignore non-transparent nodes as they always have no light
- */
-
- if(block_below_is_valid)
- {
- MapNode n = getNodeParent(v3s16(x, -1, z), &is_valid_position);
- if (is_valid_position) {
- if(nodemgr->get(n).light_propagates)
- {
- if(n.getLight(LIGHTBANK_DAY, nodemgr) == LIGHT_SUN
- && !sunlight_should_go_down)
- block_below_is_valid = false;
- else if(n.getLight(LIGHTBANK_DAY, nodemgr) != LIGHT_SUN
- && sunlight_should_go_down)
- block_below_is_valid = false;
- }
- }
- else
- {
- /*std::cout<<"InvalidBlockException for bottom block node"
- <<std::endl;*/
- // Just no block below, no need to panic.
- }
- }
- }
- }
-
- return block_below_is_valid;
-}
-
void MapBlock::copyTo(VoxelManipulator &dst)
{
void MapBlock::actuallyUpdateDayNightDiff()
{
- INodeDefManager *nodemgr = m_gamedef->ndef();
+ const NodeDefManager *nodemgr = m_gamedef->ndef();
// Running this function un-expires m_day_night_differs
m_day_night_differs_expired = false;
m_day_night_differs_expired = true;
}
-s16 MapBlock::getGroundLevel(v2s16 p2d)
-{
- if(isDummy())
- return -3;
- try
- {
- s16 y = MAP_BLOCKSIZE-1;
- for(; y>=0; y--)
- {
- MapNode n = getNodeRef(p2d.X, y, p2d.Y);
- if (m_gamedef->ndef()->get(n).walkable) {
- if(y == MAP_BLOCKSIZE-1)
- return -2;
-
- return y;
- }
- }
- return -1;
- }
- catch(InvalidPositionException &e)
- {
- return -3;
- }
-}
-
/*
Serialization
*/
// mapblocks.
static content_t getBlockNodeIdMapping_mapping[USHRT_MAX + 1];
static void getBlockNodeIdMapping(NameIdMapping *nimap, MapNode *nodes,
- INodeDefManager *nodedef)
+ const NodeDefManager *nodedef)
{
memset(getBlockNodeIdMapping_mapping, 0xFF, (USHRT_MAX + 1) * sizeof(content_t));
static void correctBlockNodeIds(const NameIdMapping *nimap, MapNode *nodes,
IGameDef *gamedef)
{
- INodeDefManager *nodedef = gamedef->ndef();
+ const NodeDefManager *nodedef = gamedef->ndef();
// This means the block contains incorrect ids, and we contain
// the information to convert those to names.
// nodedef contains information to convert our names to globally
}
}
-void MapBlock::serialize(std::ostream &os, u8 version, bool disk)
+void MapBlock::serialize(std::ostream &os_compressed, u8 version, bool disk, int compression_level)
{
if(!ser_ver_supported(version))
throw VersionMismatchException("ERROR: MapBlock format not supported");
FATAL_ERROR_IF(version < SER_FMT_VER_LOWEST_WRITE, "Serialisation version error");
+ std::ostringstream os_raw(std::ios_base::binary);
+ std::ostream &os = version >= 29 ? os_raw : os_compressed;
+
// First byte
u8 flags = 0;
if(is_underground)
Bulk node data
*/
NameIdMapping nimap;
- if(disk)
+ SharedBuffer<u8> buf;
+ const u8 content_width = 2;
+ const u8 params_width = 2;
+ if(disk)
{
MapNode *tmp_nodes = new MapNode[nodecount];
- for(u32 i=0; i<nodecount; i++)
- tmp_nodes[i] = data[i];
+ memcpy(tmp_nodes, data, nodecount * sizeof(MapNode));
getBlockNodeIdMapping(&nimap, tmp_nodes, m_gamedef->ndef());
- u8 content_width = 2;
- u8 params_width = 2;
- writeU8(os, content_width);
- writeU8(os, params_width);
- MapNode::serializeBulk(os, version, tmp_nodes, nodecount,
- content_width, params_width, true);
+ buf = MapNode::serializeBulk(version, tmp_nodes, nodecount,
+ content_width, params_width);
delete[] tmp_nodes;
+
+ // write timestamp and node/id mapping first
+ if (version >= 29) {
+ writeU32(os, getTimestamp());
+
+ nimap.serialize(os);
+ }
}
else
{
- u8 content_width = 2;
- u8 params_width = 2;
- writeU8(os, content_width);
- writeU8(os, params_width);
- MapNode::serializeBulk(os, version, data, nodecount,
- content_width, params_width, true);
+ buf = MapNode::serializeBulk(version, data, nodecount,
+ content_width, params_width);
+ }
+
+ writeU8(os, content_width);
+ writeU8(os, params_width);
+ if (version >= 29) {
+ os.write(reinterpret_cast<char*>(*buf), buf.getSize());
+ } else {
+ // prior to 29 node data was compressed individually
+ compress(buf, os, version, compression_level);
}
/*
Node metadata
*/
- std::ostringstream oss(std::ios_base::binary);
- m_node_metadata.serialize(oss, version, disk);
- compressZlib(oss.str(), os);
+ if (version >= 29) {
+ m_node_metadata.serialize(os, version, disk);
+ } else {
+ // use os_raw from above to avoid allocating another stream object
+ m_node_metadata.serialize(os_raw, version, disk);
+ // prior to 29 node data was compressed individually
+ compress(os_raw.str(), os, version, compression_level);
+ }
/*
Data that goes to disk, but not the network
// Static objects
m_static_objects.serialize(os);
- // Timestamp
- writeU32(os, getTimestamp());
+ if(version < 29){
+ // Timestamp
+ writeU32(os, getTimestamp());
- // Write block-specific node definition id mapping
- nimap.serialize(os);
+ // Write block-specific node definition id mapping
+ nimap.serialize(os);
+ }
if(version >= 25){
// Node timers
m_node_timers.serialize(os, version);
}
}
+
+ if (version >= 29) {
+ // now compress the whole thing
+ compress(os_raw.str(), os_compressed, version, compression_level);
+ }
}
void MapBlock::serializeNetworkSpecific(std::ostream &os)
throw SerializationError("ERROR: Not writing dummy block.");
}
- writeU8(os, 1); // version
- writeF1000(os, 0); // deprecated heat
- writeF1000(os, 0); // deprecated humidity
+ writeU8(os, 2); // version
}
-void MapBlock::deSerialize(std::istream &is, u8 version, bool disk)
+void MapBlock::deSerialize(std::istream &in_compressed, u8 version, bool disk)
{
if(!ser_ver_supported(version))
throw VersionMismatchException("ERROR: MapBlock format not supported");
if(version <= 21)
{
- deSerialize_pre22(is, version, disk);
+ deSerialize_pre22(in_compressed, version, disk);
return;
}
+ // Decompress the whole block (version >= 29)
+ std::stringstream in_raw(std::ios_base::binary | std::ios_base::in | std::ios_base::out);
+ if (version >= 29)
+ decompress(in_compressed, in_raw, version);
+ std::istream &is = version >= 29 ? in_raw : in_compressed;
+
u8 flags = readU8(is);
is_underground = (flags & 0x01) != 0;
m_day_night_differs = (flags & 0x02) != 0;
m_lighting_complete = readU16(is);
m_generated = (flags & 0x08) == 0;
- /*
- Bulk node data
- */
+ NameIdMapping nimap;
+ if (disk && version >= 29) {
+ // Timestamp
+ TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
+ <<": Timestamp"<<std::endl);
+ setTimestampNoChangedFlag(readU32(is));
+ m_disk_timestamp = m_timestamp;
+
+ // Node/id mapping
+ TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
+ <<": NameIdMapping"<<std::endl);
+ nimap.deSerialize(is);
+ }
+
TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
<<": Bulk node data"<<std::endl);
u8 content_width = readU8(is);
throw SerializationError("MapBlock::deSerialize(): invalid content_width");
if(params_width != 2)
throw SerializationError("MapBlock::deSerialize(): invalid params_width");
- MapNode::deSerializeBulk(is, version, data, nodecount,
- content_width, params_width, true);
+
+ /*
+ Bulk node data
+ */
+ if (version >= 29) {
+ MapNode::deSerializeBulk(is, version, data, nodecount,
+ content_width, params_width);
+ } else {
+ // use in_raw from above to avoid allocating another stream object
+ decompress(is, in_raw, version);
+ MapNode::deSerializeBulk(in_raw, version, data, nodecount,
+ content_width, params_width);
+ }
/*
NodeMetadata
*/
TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
<<": Node metadata"<<std::endl);
- // Ignore errors
- try {
- std::ostringstream oss(std::ios_base::binary);
- decompressZlib(is, oss);
- std::istringstream iss(oss.str(), std::ios_base::binary);
- if (version >= 23)
- m_node_metadata.deSerialize(iss, m_gamedef->idef());
- else
- content_nodemeta_deserialize_legacy(iss,
- &m_node_metadata, &m_node_timers,
- m_gamedef->idef());
- } catch(SerializationError &e) {
- warningstream<<"MapBlock::deSerialize(): Ignoring an error"
- <<" while deserializing node metadata at ("
- <<PP(getPos())<<": "<<e.what()<<std::endl;
+ if (version >= 29) {
+ m_node_metadata.deSerialize(is, m_gamedef->idef());
+ } else {
+ try {
+ // reuse in_raw
+ in_raw.str("");
+ in_raw.clear();
+ decompress(is, in_raw, version);
+ if (version >= 23)
+ m_node_metadata.deSerialize(in_raw, m_gamedef->idef());
+ else
+ content_nodemeta_deserialize_legacy(in_raw,
+ &m_node_metadata, &m_node_timers,
+ m_gamedef->idef());
+ } catch(SerializationError &e) {
+ warningstream<<"MapBlock::deSerialize(): Ignoring an error"
+ <<" while deserializing node metadata at ("
+ <<PP(getPos())<<": "<<e.what()<<std::endl;
+ }
}
/*
<<": Static objects"<<std::endl);
m_static_objects.deSerialize(is);
- // Timestamp
- TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
- <<": Timestamp"<<std::endl);
- setTimestamp(readU32(is));
- m_disk_timestamp = m_timestamp;
+ if(version < 29) {
+ // Timestamp
+ TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
+ <<": Timestamp"<<std::endl);
+ setTimestampNoChangedFlag(readU32(is));
+ m_disk_timestamp = m_timestamp;
+
+ // Node/id mapping
+ TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
+ <<": NameIdMapping"<<std::endl);
+ nimap.deSerialize(is);
+ }
// Dynamically re-set ids based on node names
- TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
- <<": NameIdMapping"<<std::endl);
- NameIdMapping nimap;
- nimap.deSerialize(is);
correctBlockNodeIds(&nimap, data, m_gamedef);
if(version >= 25){
void MapBlock::deSerializeNetworkSpecific(std::istream &is)
{
try {
- int version = readU8(is);
- //if(version != 1)
- // throw SerializationError("unsupported MapBlock version");
- if(version >= 1) {
- readF1000(is); // deprecated heat
- readF1000(is); // deprecated humidity
- }
- }
- catch(SerializationError &e)
- {
+ readU8(is);
+ //const u8 version = readU8(is);
+ //if (version != 1)
+ //throw SerializationError("unsupported MapBlock version");
+
+ } catch(SerializationError &e) {
warningstream<<"MapBlock::deSerializeNetworkSpecific(): Ignoring an error"
<<": "<<e.what()<<std::endl;
}
// Ignore errors
try {
if (version <= 15) {
- std::string data = deSerializeString(is);
+ std::string data = deSerializeString16(is);
std::istringstream iss(data, std::ios_base::binary);
content_nodemeta_deserialize_legacy(iss,
&m_node_metadata, &m_node_timers,
m_gamedef->idef());
} else {
- //std::string data = deSerializeLongString(is);
+ //std::string data = deSerializeString32(is);
std::ostringstream oss(std::ios_base::binary);
decompressZlib(is, oss);
std::istringstream iss(oss.str(), std::ios_base::binary);
// Timestamp
if (version >= 17) {
- setTimestamp(readU32(is));
+ setTimestampNoChangedFlag(readU32(is));
m_disk_timestamp = m_timestamp;
} else {
- setTimestamp(BLOCK_TIMESTAMP_UNDEFINED);
+ setTimestampNoChangedFlag(BLOCK_TIMESTAMP_UNDEFINED);
}
// Dynamically re-set ids based on node names
// Legacy data changes
// This code has to convert from pre-22 to post-22 format.
- INodeDefManager *nodedef = m_gamedef->ndef();
+ const NodeDefManager *nodedef = m_gamedef->ndef();
for(u32 i=0; i<nodecount; i++)
{
const ContentFeatures &f = nodedef->get(data[i].getContent());
v3s16 p = block->getPos();
char spos[25];
- snprintf(spos, sizeof(spos), "(%2d,%2d,%2d), ", p.X, p.Y, p.Z);
+ porting::mt_snprintf(spos, sizeof(spos), "(%2d,%2d,%2d), ", p.X, p.Y, p.Z);
desc<<spos;
switch(block->getModified())