Add support for map block version 29

This commit is contained in:
sfan5 2021-09-01 23:55:32 +02:00
parent 5c435f6459
commit b0ca3d7066
7 changed files with 163 additions and 37 deletions

@ -1,4 +1,3 @@
#include <stdint.h>
#include <string> #include <string>
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
@ -39,7 +38,7 @@ void BlockDecoder::reset()
m_version = 0; m_version = 0;
m_contentWidth = 0; m_contentWidth = 0;
m_mapData = ustring(); m_mapData.clear();
} }
void BlockDecoder::decode(const ustring &datastr) void BlockDecoder::decode(const ustring &datastr)
@ -49,7 +48,6 @@ void BlockDecoder::decode(const ustring &datastr)
// TODO: bounds checks // TODO: bounds checks
uint8_t version = data[0]; uint8_t version = data[0];
//uint8_t flags = data[1];
if (version < 22) { if (version < 22) {
std::ostringstream oss; std::ostringstream oss;
oss << "Unsupported map version " << (int)version; oss << "Unsupported map version " << (int)version;
@ -57,12 +55,46 @@ void BlockDecoder::decode(const ustring &datastr)
} }
m_version = version; m_version = version;
ustring datastr2;
if (version >= 29) {
// decompress whole block at once
m_zstd_decompressor.setData(data, length, 1);
datastr2 = m_zstd_decompressor.decompress();
data = datastr2.c_str();
length = datastr2.size();
}
size_t dataOffset = 0; size_t dataOffset = 0;
if (version >= 27) if (version >= 29)
dataOffset = 7;
else if (version >= 27)
dataOffset = 4; dataOffset = 4;
else else
dataOffset = 2; dataOffset = 2;
auto decode_mapping = [&] () {
dataOffset++; // mapping version
uint16_t numMappings = readU16(data + dataOffset);
dataOffset += 2;
for (int i = 0; i < numMappings; ++i) {
uint16_t nodeId = readU16(data + dataOffset);
dataOffset += 2;
uint16_t nameLen = readU16(data + dataOffset);
dataOffset += 2;
std::string name(reinterpret_cast<const char *>(data) + dataOffset, nameLen);
if (name == "air")
m_blockAirId = nodeId;
else if (name == "ignore")
m_blockIgnoreId = nodeId;
else
m_nameMap[nodeId] = name;
dataOffset += nameLen;
}
};
if (version >= 29)
decode_mapping();
uint8_t contentWidth = data[dataOffset]; uint8_t contentWidth = data[dataOffset];
dataOffset++; dataOffset++;
uint8_t paramsWidth = data[dataOffset]; uint8_t paramsWidth = data[dataOffset];
@ -73,14 +105,20 @@ void BlockDecoder::decode(const ustring &datastr)
throw std::runtime_error("unsupported map version (paramsWidth)"); throw std::runtime_error("unsupported map version (paramsWidth)");
m_contentWidth = contentWidth; m_contentWidth = contentWidth;
if (version >= 29) {
m_mapData.resize((contentWidth + paramsWidth) * 4096);
m_mapData.assign(data + dataOffset, m_mapData.size());
return; // we have read everything we need and can return early
}
// version < 29
ZlibDecompressor decompressor(data, length); ZlibDecompressor decompressor(data, length);
decompressor.setSeekPos(dataOffset); decompressor.setSeekPos(dataOffset);
m_mapData = decompressor.decompress(); m_mapData = decompressor.decompress();
decompressor.decompress(); // unused metadata decompressor.decompress(); // unused metadata
dataOffset = decompressor.seekPos(); dataOffset = decompressor.seekPos();
// Skip unused data // Skip unused node timers
if (version == 23) if (version == 23)
dataOffset += 1; dataOffset += 1;
if (version == 24) { if (version == 24) {
@ -104,33 +142,7 @@ void BlockDecoder::decode(const ustring &datastr)
dataOffset += 4; // Skip timestamp dataOffset += 4; // Skip timestamp
// Read mapping // Read mapping
{ decode_mapping();
dataOffset++; // mapping version
uint16_t numMappings = readU16(data + dataOffset);
dataOffset += 2;
for (int i = 0; i < numMappings; ++i) {
uint16_t nodeId = readU16(data + dataOffset);
dataOffset += 2;
uint16_t nameLen = readU16(data + dataOffset);
dataOffset += 2;
std::string name(reinterpret_cast<const char *>(data) + dataOffset, nameLen);
if (name == "air")
m_blockAirId = nodeId;
else if (name == "ignore")
m_blockIgnoreId = nodeId;
else
m_nameMap[nodeId] = name;
dataOffset += nameLen;
}
}
// Node timers
if (version >= 25) {
uint8_t timerLength = data[dataOffset++];
uint16_t numTimers = readU16(data + dataOffset);
dataOffset += 2;
dataOffset += numTimers * timerLength;
}
} }
bool BlockDecoder::isEmpty() const bool BlockDecoder::isEmpty() const

@ -43,7 +43,7 @@ if(NOT CUSTOM_DOCDIR STREQUAL "")
message(STATUS "Using DOCDIR=${DOCDIR}") message(STATUS "Using DOCDIR=${DOCDIR}")
endif() endif()
#set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake) set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
# Libraries: gd # Libraries: gd
@ -59,6 +59,10 @@ endif(NOT LIBGD_LIBRARY OR NOT LIBGD_INCLUDE_DIR)
find_package(ZLIB REQUIRED) find_package(ZLIB REQUIRED)
# Libraries: zstd
find_package(Zstd REQUIRED)
# Libraries: sqlite3 # Libraries: sqlite3
find_library(SQLITE3_LIBRARY sqlite3) find_library(SQLITE3_LIBRARY sqlite3)
@ -148,6 +152,7 @@ include_directories(
${SQLITE3_INCLUDE_DIR} ${SQLITE3_INCLUDE_DIR}
${LIBGD_INCLUDE_DIR} ${LIBGD_INCLUDE_DIR}
${ZLIB_INCLUDE_DIR} ${ZLIB_INCLUDE_DIR}
${ZSTD_INCLUDE_DIR}
) )
configure_file( configure_file(
@ -171,6 +176,7 @@ add_executable(minetestmapper
PlayerAttributes.cpp PlayerAttributes.cpp
TileGenerator.cpp TileGenerator.cpp
ZlibDecompressor.cpp ZlibDecompressor.cpp
ZstdDecompressor.cpp
Image.cpp Image.cpp
mapper.cpp mapper.cpp
util.cpp util.cpp
@ -188,6 +194,7 @@ target_link_libraries(
${REDIS_LIBRARY} ${REDIS_LIBRARY}
${LIBGD_LIBRARY} ${LIBGD_LIBRARY}
${ZLIB_LIBRARY} ${ZLIB_LIBRARY}
${ZSTD_LIBRARY}
) )
# Installing & Packaging # Installing & Packaging

58
ZstdDecompressor.cpp Normal file

@ -0,0 +1,58 @@
#include <zstd.h>
#include "ZstdDecompressor.h"
ZstdDecompressor::ZstdDecompressor():
m_data(nullptr),
m_seekPos(0),
m_size(0)
{
m_stream = ZSTD_createDStream();
}
ZstdDecompressor::~ZstdDecompressor()
{
ZSTD_freeDStream(reinterpret_cast<ZSTD_DStream*>(m_stream));
}
void ZstdDecompressor::setData(const u8 *data, size_t size, size_t seekPos)
{
m_data = data;
m_seekPos = seekPos;
m_size = size;
}
std::size_t ZstdDecompressor::seekPos() const
{
return m_seekPos;
}
ustring ZstdDecompressor::decompress()
{
ZSTD_DStream *stream = reinterpret_cast<ZSTD_DStream*>(m_stream);
ZSTD_inBuffer inbuf = { m_data, m_size, m_seekPos };
ustring buffer;
constexpr size_t BUFSIZE = 32 * 1024;
buffer.resize(BUFSIZE);
ZSTD_outBuffer outbuf = { &buffer[0], buffer.size(), 0 };
ZSTD_initDStream(stream);
size_t ret;
do {
ret = ZSTD_decompressStream(stream, &outbuf, &inbuf);
if (outbuf.size == outbuf.pos) {
outbuf.size += BUFSIZE;
buffer.resize(outbuf.size);
outbuf.dst = &buffer[0];
}
} while (ret != 0);
if (ZSTD_isError(ret))
throw DecompressError();
m_seekPos = inbuf.pos;
buffer.resize(outbuf.pos);
return buffer;
}

24
cmake/FindZstd.cmake Normal file

@ -0,0 +1,24 @@
mark_as_advanced(ZSTD_LIBRARY ZSTD_INCLUDE_DIR)
find_path(ZSTD_INCLUDE_DIR NAMES zstd.h)
find_library(ZSTD_LIBRARY NAMES zstd)
if(ZSTD_INCLUDE_DIR AND ZSTD_LIBRARY)
# Check that the API we use exists
include(CheckSymbolExists)
unset(HAVE_ZSTD_INITDSTREAM CACHE)
set(CMAKE_REQUIRED_INCLUDES ${ZSTD_INCLUDE_DIR})
set(CMAKE_REQUIRED_LIBRARIES ${ZSTD_LIBRARY})
check_symbol_exists(ZSTD_initDStream zstd.h HAVE_ZSTD_INITDSTREAM)
unset(CMAKE_REQUIRED_INCLUDES)
unset(CMAKE_REQUIRED_LIBRARIES)
if(NOT HAVE_ZSTD_INITDSTREAM)
unset(ZSTD_INCLUDE_DIR CACHE)
unset(ZSTD_LIBRARY CACHE)
endif()
endif()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Zstd DEFAULT_MSG ZSTD_LIBRARY ZSTD_INCLUDE_DIR)

@ -1,8 +1,9 @@
#pragma once #pragma once
#include <cstdint>
#include <unordered_map> #include <unordered_map>
#include "types.h" #include "types.h"
#include <ZstdDecompressor.h>
class BlockDecoder { class BlockDecoder {
public: public:
@ -17,9 +18,11 @@ public:
private: private:
typedef std::unordered_map<uint16_t, std::string> NameMap; typedef std::unordered_map<uint16_t, std::string> NameMap;
NameMap m_nameMap; NameMap m_nameMap;
int m_blockAirId; uint16_t m_blockAirId, m_blockIgnoreId;
int m_blockIgnoreId;
u8 m_version, m_contentWidth; u8 m_version, m_contentWidth;
ustring m_mapData; ustring m_mapData;
// one instance for performance
ZstdDecompressor m_zstd_decompressor;
}; };

@ -0,0 +1,22 @@
#pragma once
#include <cstdlib>
#include <string>
#include "types.h"
class ZstdDecompressor
{
public:
class DecompressError {};
ZstdDecompressor();
~ZstdDecompressor();
void setData(const u8 *data, size_t size, size_t seekPos);
size_t seekPos() const;
ustring decompress();
private:
void *m_stream; // ZSTD_DStream
const u8 *m_data;
size_t m_seekPos, m_size;
};

@ -1,7 +1,7 @@
#!/bin/bash -e #!/bin/bash -e
install_linux_deps() { install_linux_deps() {
local pkgs=(cmake libgd-dev libsqlite3-dev libleveldb-dev libpq-dev libhiredis-dev) local pkgs=(cmake libgd-dev libsqlite3-dev libleveldb-dev libpq-dev libhiredis-dev libzstd-dev)
sudo apt-get update sudo apt-get update
sudo apt-get install -y --no-install-recommends ${pkgs[@]} "$@" sudo apt-get install -y --no-install-recommends ${pkgs[@]} "$@"