21 Commits

Author SHA1 Message Date
42395587b9 Fix travis 2018-03-25 16:47:23 +02:00
d327a84841 Update README 2018-03-25 16:42:45 +02:00
0d9ed247f9 Finish refactor 2018-03-25 16:25:41 +02:00
b6ad40feb9 sqlite3 player backend support
fixes #47
2018-03-25 16:03:11 +02:00
1f471b369b Refactoring (3) 2018-03-25 15:44:30 +02:00
2f78c39d9c Refactoring (2) 2018-03-25 15:19:48 +02:00
2ebc3afc7c Refactoring (1) 2018-03-25 14:32:11 +02:00
e543517eb8 Move include files 2018-03-25 14:12:39 +02:00
405951f8f6 Update travis build process 2018-03-25 13:58:18 +02:00
a8f7b47000 Update colors.txt 2018-03-25 13:40:19 +02:00
0f36b6e37a Fix subtly broken libgd version check 2018-03-24 18:40:21 +01:00
6af6be4632 Re-add support for outdated libgd 2018-03-24 16:56:17 +01:00
7c71138fec Minor refactoring (2)
Also tunes --drawalpha to hide shadows in deep water better.
2018-03-24 15:24:02 +01:00
c15adfd325 Minor refactoring 2018-03-24 14:54:45 +01:00
75599e8569 Support all image formats offered by libgd 2018-03-24 14:16:56 +01:00
b16f93f183 Properly disable debugging code in Release builds 2018-03-24 14:05:34 +01:00
976c690357 Improve exception messages for out-of-bounds image access 2018-03-24 14:05:25 +01:00
7288e60cd2 Fix scales being drawn outside of image
closes #54
2018-03-24 13:58:39 +01:00
cca7072eab put color values in quotes
the command line doesn't seem to like the "#" char, see https://forum.minetest.net/viewtopic.php?p=182380#p182380
2018-02-01 14:30:36 +01:00
179651e83c Fix failing travis build 2017-09-03 14:36:46 +02:00
6afe1b78c5 Add support for Redis via UNIX domain sockets (#46) 2017-09-03 14:27:59 +02:00
29 changed files with 668 additions and 392 deletions

View File

@ -3,22 +3,19 @@ compiler:
- gcc - gcc
- clang - clang
sudo: false sudo: false
dist: trusty
addons: addons:
apt: apt:
sources: sources:
- llvm-toolchain-precise-3.8 - llvm-toolchain-trusty-5.0
- ubuntu-toolchain-r-test - ubuntu-toolchain-r-test
packages: packages:
- cmake - cmake
- libgd2-noxpm-dev - libgd2-dev
- libsqlite3-dev - libsqlite3-dev
- p7zip - libleveldb-dev
- g++-6 - g++-7
- clang-3.8 - clang-5.0
before_install:
- # Nothing ever works correctly with precise, use a custom libleveldb build
- wget http://minetest.kitsunemimi.pw/libleveldb-1.18-ubuntu12.04.7z
- 7zr x -olibleveldb libleveldb-1.18-ubuntu12.04.7z
script: ./util/travis/script.sh script: ./util/travis/script.sh
notifications: notifications:
email: false email: false

142
BlockDecoder.cpp Normal file
View File

@ -0,0 +1,142 @@
#include <stdint.h>
#include <string>
#include <iostream>
#include <sstream>
#include "BlockDecoder.h"
#include "ZlibDecompressor.h"
static inline uint16_t readU16(const unsigned char *data)
{
return data[0] << 8 | data[1];
}
static int readBlockContent(const unsigned char *mapData, u8 version, unsigned int datapos)
{
if (version >= 24) {
size_t index = datapos << 1;
return (mapData[index] << 8) | mapData[index + 1];
} else if (version >= 20) {
if (mapData[datapos] <= 0x80)
return mapData[datapos];
else
return (int(mapData[datapos]) << 4) | (int(mapData[datapos + 0x2000]) >> 4);
}
std::ostringstream oss;
oss << "Unsupported map version " << version;
throw std::runtime_error(oss.str());
}
BlockDecoder::BlockDecoder()
{
reset();
}
void BlockDecoder::reset()
{
m_blockAirId = -1;
m_blockIgnoreId = -1;
m_nameMap.clear();
m_version = 0;
m_mapData = ustring();
}
void BlockDecoder::decode(const ustring &datastr)
{
const unsigned char *data = datastr.c_str();
size_t length = datastr.length();
// TODO: bounds checks
uint8_t version = data[0];
//uint8_t flags = data[1];
m_version = version;
size_t dataOffset = 0;
if (version >= 27)
dataOffset = 6;
else if (version >= 22)
dataOffset = 4;
else
dataOffset = 2;
ZlibDecompressor decompressor(data, length);
decompressor.setSeekPos(dataOffset);
m_mapData = decompressor.decompress();
decompressor.decompress(); // unused metadata
dataOffset = decompressor.seekPos();
// Skip unused data
if (version <= 21)
dataOffset += 2;
if (version == 23)
dataOffset += 1;
if (version == 24) {
uint8_t ver = data[dataOffset++];
if (ver == 1) {
uint16_t num = readU16(data + dataOffset);
dataOffset += 2;
dataOffset += 10 * num;
}
}
// Skip unused static objects
dataOffset++; // Skip static object version
int staticObjectCount = readU16(data + dataOffset);
dataOffset += 2;
for (int i = 0; i < staticObjectCount; ++i) {
dataOffset += 13;
uint16_t dataSize = readU16(data + dataOffset);
dataOffset += dataSize + 2;
}
dataOffset += 4; // Skip timestamp
// Read mapping
if (version >= 22) {
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) {
dataOffset++;
uint16_t numTimers = readU16(data + dataOffset);
dataOffset += 2;
dataOffset += numTimers * 10;
}
}
bool BlockDecoder::isEmpty() const
{
// only contains ignore and air nodes?
return m_nameMap.empty();
}
std::string BlockDecoder::getNode(u8 x, u8 y, u8 z) const
{
unsigned int position = x + (y << 4) + (z << 8);
int content = readBlockContent(m_mapData.c_str(), m_version, position);
if (content == m_blockAirId || content == m_blockIgnoreId)
return "";
NameMap::const_iterator it = m_nameMap.find(content);
if (it == m_nameMap.end()) {
std::cerr << "Skipping node with invalid ID." << std::endl;
return "";
}
return it->second;
}

View File

@ -13,7 +13,7 @@ if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build." FORCE) set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build." FORCE)
endif() endif()
set(CMAKE_CXX_FLAGS_RELEASE "-O3 -Wall") set(CMAKE_CXX_FLAGS_RELEASE "-O3 -Wall -DNDEBUG")
set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g2 -Wall") set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g2 -Wall")
if(WIN32) if(WIN32)
@ -159,7 +159,7 @@ endif(ENABLE_REDIS)
include_directories( include_directories(
"${PROJECT_BINARY_DIR}" "${PROJECT_BINARY_DIR}"
"${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/include"
"${CMAKE_CURRENT_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}"
${SQLITE3_INCLUDE_DIR} ${SQLITE3_INCLUDE_DIR}
${LIBGD_INCLUDE_DIR} ${LIBGD_INCLUDE_DIR}
@ -167,12 +167,13 @@ include_directories(
) )
configure_file( configure_file(
"${PROJECT_SOURCE_DIR}/cmake_config.h.in" "${PROJECT_SOURCE_DIR}/include/cmake_config.h.in"
"${PROJECT_BINARY_DIR}/cmake_config.h" "${PROJECT_BINARY_DIR}/cmake_config.h"
) )
add_definitions ( -DUSE_CMAKE_CONFIG_H ) add_definitions ( -DUSE_CMAKE_CONFIG_H )
set(mapper_SRCS set(mapper_SRCS
BlockDecoder.cpp
PixelAttributes.cpp PixelAttributes.cpp
PlayerAttributes.cpp PlayerAttributes.cpp
TileGenerator.cpp TileGenerator.cpp

View File

@ -10,12 +10,7 @@
#include "Image.h" #include "Image.h"
#ifndef NDEBUG #ifndef NDEBUG
#define SIZECHECK(x, y) do { \ #define SIZECHECK(x, y) check_bounds((x), (y), m_width, m_height)
if((x) < 0 || (x) >= m_width) \
throw std::out_of_range("sizecheck x"); \
if((y) < 0 || (y) >= m_height) \
throw std::out_of_range("sizecheck y"); \
} while(0)
#else #else
#define SIZECHECK(x, y) do {} while(0) #define SIZECHECK(x, y) do {} while(0)
#endif #endif
@ -40,6 +35,22 @@ static inline Color int2color(int c)
return c2; return c2;
} }
static inline void check_bounds(int x, int y, int width, int height)
{
if(x < 0 || x >= width) {
std::ostringstream oss;
oss << "Access outside image bounds (x), 0 < "
<< x << " < " << width << " is false.";
throw std::out_of_range(oss.str());
}
if(y < 0 || y >= height) {
std::ostringstream oss;
oss << "Access outside image bounds (y), 0 < "
<< y << " < " << height << " is false.";
throw std::out_of_range(oss.str());
}
}
Image::Image(int width, int height) : Image::Image(int width, int height) :
m_width(width), m_height(height), m_image(NULL) m_width(width), m_height(height), m_image(NULL)
@ -60,10 +71,7 @@ void Image::setPixel(int x, int y, const Color &c)
Color Image::getPixel(int x, int y) Color Image::getPixel(int x, int y)
{ {
#ifndef NDEBUG SIZECHECK(x, y);
if(x < 0 || x > m_width || y < 0 || y > m_height)
throw std::out_of_range("sizecheck");
#endif
return int2color(m_image->tpixels[y][x]); return int2color(m_image->tpixels[y][x]);
} }
@ -95,12 +103,22 @@ void Image::drawCircle(int x, int y, int diameter, const Color &c)
void Image::save(const std::string &filename) void Image::save(const std::string &filename)
{ {
#if (GD_MAJOR_VERSION == 2 && GD_MINOR_VERSION == 1 && GD_RELEASE_VERSION >= 1) || (GD_MAJOR_VERSION == 2 && GD_MINOR_VERSION > 1) || GD_MAJOR_VERSION > 2
const char *f = filename.c_str();
if (gdSupportsFileType(f, 1) == GD_FALSE)
throw std::runtime_error("Image format not supported by gd");
if (gdImageFile(m_image, f) == GD_FALSE)
throw std::runtime_error("Error saving image");
#else
if (filename.compare(filename.length() - 4, 4, ".png") != 0)
throw std::runtime_error("Only PNG is supported");
FILE *f = fopen(filename.c_str(), "wb"); FILE *f = fopen(filename.c_str(), "wb");
if(!f) { if (!f) {
std::ostringstream oss; std::ostringstream oss;
oss << "Error writing image file: " << std::strerror(errno); oss << "Error opening image file: " << std::strerror(errno);
throw std::runtime_error(oss.str()); throw std::runtime_error(oss.str());
} }
gdImagePng(m_image, f); // other formats? gdImagePng(m_image, f);
fclose(f); fclose(f);
#endif
} }

View File

@ -1,66 +1,126 @@
/*
* =====================================================================
* Version: 1.0
* Created: 01.09.2012 14:38:05
* Author: Miroslav Bendík
* Company: LinuxOS.sk
* =====================================================================
*/
#include <dirent.h>
#include <fstream> #include <fstream>
#include <sstream> #include <sstream>
#include <stdexcept>
#include <dirent.h>
#include <unistd.h> // for usleep
#include <sqlite3.h>
#include "config.h" #include "config.h"
#include "PlayerAttributes.h" #include "PlayerAttributes.h"
#include "util.h"
using namespace std; using namespace std;
PlayerAttributes::PlayerAttributes(const std::string &sourceDirectory) PlayerAttributes::PlayerAttributes(const std::string &worldDir)
{ {
std::ifstream ifs((worldDir + "world.mt").c_str());
if (!ifs.good())
throw std::runtime_error("Failed to read world.mt");
std::string backend = read_setting_default("player_backend", ifs, "files");
ifs.close();
string playersPath = sourceDirectory + "players"; if (backend == "files")
readFiles(worldDir + "players");
else if (backend == "sqlite3")
readSqlite(worldDir + "players.sqlite");
else
throw std::runtime_error(((std::string) "Unknown player backend: ") + backend);
}
void PlayerAttributes::readFiles(const std::string &playersPath)
{
DIR *dir; DIR *dir;
dir = opendir (playersPath.c_str()); dir = opendir (playersPath.c_str());
if (dir == NULL) { if (dir == NULL)
return; return;
}
struct dirent *ent; struct dirent *ent;
while ((ent = readdir (dir)) != NULL) { while ((ent = readdir (dir)) != NULL) {
if (ent->d_name[0] == '.') { if (ent->d_name[0] == '.')
continue; continue;
}
string path = playersPath + PATH_SEPARATOR + ent->d_name; string path = playersPath + PATH_SEPARATOR + ent->d_name;
ifstream in(path.c_str());
if(!in.good())
continue;
string name, position;
name = read_setting("name", in);
in.seekg(0);
position = read_setting("position", in);
ifstream in;
in.open(path.c_str(), ifstream::in);
string buffer;
string name;
string position;
while (getline(in, buffer)) {
if (buffer.find("name = ") == 0) {
name = buffer.substr(7);
}
else if (buffer.find("position = ") == 0) {
position = buffer.substr(12, buffer.length() - 13);
}
}
char comma;
Player player; Player player;
istringstream positionStream(position, istringstream::in); istringstream iss(position);
positionStream >> player.x; char tmp;
positionStream >> comma; iss >> tmp; // '('
positionStream >> player.y; iss >> player.x;
positionStream >> comma; iss >> tmp; // ','
positionStream >> player.z; iss >> player.y;
iss >> tmp; // ','
iss >> player.z;
iss >> tmp; // ')'
if(tmp != ')')
continue;
player.name = name; player.name = name;
player.x /= 10.0;
player.y /= 10.0;
player.z /= 10.0;
m_players.push_back(player); m_players.push_back(player);
} }
closedir(dir); closedir(dir);
} }
/**********/
#define SQLRES(f, good) \
result = (sqlite3_##f); \
if (result != good) { \
throw std::runtime_error(sqlite3_errmsg(db));\
}
#define SQLOK(f) SQLRES(f, SQLITE_OK)
void PlayerAttributes::readSqlite(const std::string &db_name)
{
int result;
sqlite3 *db;
sqlite3_stmt *stmt_get_player_pos;
SQLOK(open_v2(db_name.c_str(), &db, SQLITE_OPEN_READONLY |
SQLITE_OPEN_PRIVATECACHE, 0))
SQLOK(prepare_v2(db,
"SELECT name, posX, posY, posZ FROM player",
-1, &stmt_get_player_pos, NULL))
while ((result = sqlite3_step(stmt_get_player_pos)) != SQLITE_DONE) {
if (result == SQLITE_BUSY) { // Wait some time and try again
usleep(10000);
} else if (result != SQLITE_ROW) {
throw std::runtime_error(sqlite3_errmsg(db));
}
Player player;
const unsigned char *name_ = sqlite3_column_text(stmt_get_player_pos, 0);
player.name = std::string(reinterpret_cast<const char*>(name_));
player.x = sqlite3_column_double(stmt_get_player_pos, 1);
player.y = sqlite3_column_double(stmt_get_player_pos, 2);
player.z = sqlite3_column_double(stmt_get_player_pos, 3);
player.x /= 10.0;
player.y /= 10.0;
player.z /= 10.0;
m_players.push_back(player);
}
sqlite3_finalize(stmt_get_player_pos);
sqlite3_close(db);
}
/**********/
PlayerAttributes::Players::iterator PlayerAttributes::begin() PlayerAttributes::Players::iterator PlayerAttributes::begin()
{ {
return m_players.begin(); return m_players.begin();

View File

@ -1,38 +0,0 @@
/*
* =====================================================================
* Version: 1.0
* Created: 01.09.2012 14:38:08
* Author: Miroslav Bendík
* Company: LinuxOS.sk
* =====================================================================
*/
#ifndef PLAYERATTRIBUTES_H_D7THWFVV
#define PLAYERATTRIBUTES_H_D7THWFVV
#include <list>
#include <string>
struct Player
{
std::string name;
double x;
double y;
double z;
}; /* ----- end of struct Player ----- */
class PlayerAttributes
{
public:
typedef std::list<Player> Players;
PlayerAttributes(const std::string &sourceDirectory);
Players::iterator begin();
Players::iterator end();
private:
Players m_players;
}; /* ----- end of class PlayerAttributes ----- */
#endif /* end of include guard: PLAYERATTRIBUTES_H_D7THWFVV */

View File

@ -12,8 +12,8 @@ Requirements
* libgd * libgd
* sqlite3 * sqlite3
* leveldb (optional, set ENABLE_LEVELDB=1 in CMake to enable) * LevelDB (optional, set ENABLE_LEVELDB=1 in CMake to enable)
* hiredis (optional, set ENABLE_REDIS=1 in CMake to enable) * hiredis library (optional, set ENABLE_REDIS=1 in CMake to enable)
* Postgres libraries (optional, set ENABLE_POSTGRES=1 in CMake to enable) * Postgres libraries (optional, set ENABLE_POSTGRES=1 in CMake to enable)
e.g. on Debian: e.g. on Debian:
@ -25,7 +25,7 @@ Windows
^^^^^^^ ^^^^^^^
Minetestmapper for Windows can be downloaded here: https://github.com/minetest/minetestmapper/releases Minetestmapper for Windows can be downloaded here: https://github.com/minetest/minetestmapper/releases
After extracting the archive somewhere minetestmapper will be available from cmd.exe: After extracting the archive, minetestmapper can be invoked from cmd.exe:
:: ::
cd C:\Users\yourname\Desktop\example\path cd C:\Users\yourname\Desktop\example\path
@ -54,28 +54,28 @@ Parameters
^^^^^^^^^^ ^^^^^^^^^^
bgcolor: bgcolor:
Background color of image, e.g. ``--bgcolor #ffffff`` Background color of image, e.g. ``--bgcolor '#ffffff'``
scalecolor: scalecolor:
Color of scale, e.g. ``--scalecolor #000000`` Color of scale marks and text, e.g. ``--scalecolor '#000000'``
playercolor: playercolor:
Color of player indicators, e.g. ``--playercolor #ff0000`` Color of player indicators, e.g. ``--playercolor '#ff0000'``
origincolor: origincolor:
Color of origin indicator, e.g. ``--origincolor #ff0000`` Color of origin indicator, e.g. ``--origincolor '#ff0000'``
drawscale: drawscale:
Draw tick marks, ``--drawscale`` Draw scale(s) with tick marks and numbers, ``--drawscale``
drawplayers: drawplayers:
Draw player indicators, ``--drawplayers`` Draw player indicators with name, ``--drawplayers``
draworigin: draworigin:
Draw origin indicator, ``--draworigin`` Draw origin indicator, ``--draworigin``
drawalpha: drawalpha:
Allow nodes to be drawn with transparency, ``--drawalpha`` Allow nodes to be drawn with transparency (e.g. water), ``--drawalpha``
noshading: noshading:
Don't draw shading on nodes, ``--noshading`` Don't draw shading on nodes, ``--noshading``
@ -87,16 +87,16 @@ max-y:
Don't draw nodes above this y value, e.g. ``--max-y 75`` Don't draw nodes above this y value, e.g. ``--max-y 75``
backend: backend:
Use specific map backend; supported: *sqlite3*, *leveldb*, *redis*, *postgresql*, e.g. ``--backend leveldb`` Override auto-detected map backend; supported: *sqlite3*, *leveldb*, *redis*, *postgresql*, e.g. ``--backend leveldb``
geometry: geometry:
Limit area to specific geometry (*x:y+w+h* where x and y specify the lower left corner), e.g. ``--geometry -800:-800+1600+1600`` Limit area to specific geometry (*x:z+w+h* where x and z specify the lower left corner), e.g. ``--geometry -800:-800+1600+1600``
zoom: zoom:
"Zoom" the image by using more than one pixel per node, e.g. ``--zoom 4`` Apply zoom to drawn nodes by enlarging them to n*n squares, e.g. ``--zoom 4``
colors: colors:
Forcefully set path to colors.txt file (it's autodetected otherwise), e.g. ``--colors ../minetest/mycolors.txt`` Override auto-detected path to colors.txt, e.g. ``--colors ../minetest/mycolors.txt``
scales: scales:
Draw scales on specified image edges (letters *t b l r* meaning top, bottom, left and right), e.g. ``--scales tbr`` Draw scales on specified image edges (letters *t b l r* meaning top, bottom, left and right), e.g. ``--scales tbr``

View File

@ -7,10 +7,11 @@
#include <stdexcept> #include <stdexcept>
#include <cstring> #include <cstring>
#include <vector> #include <vector>
#include "TileGenerator.h"
#include "config.h" #include "config.h"
#include "PlayerAttributes.h" #include "PlayerAttributes.h"
#include "TileGenerator.h" #include "BlockDecoder.h"
#include "ZlibDecompressor.h"
#include "util.h" #include "util.h"
#include "db-sqlite3.h" #include "db-sqlite3.h"
#if USE_POSTGRESQL #if USE_POSTGRESQL
@ -25,9 +26,16 @@
using namespace std; using namespace std;
static inline uint16_t readU16(const unsigned char *data) template<typename T>
static inline T mymax(T a, T b)
{ {
return data[0] << 8 | data[1]; return (a > b) ? a : b;
}
template<typename T>
static inline T mymin(T a, T b)
{
return (a > b) ? b : a;
} }
// rounds n (away from 0) to a multiple of f while preserving the sign of n // rounds n (away from 0) to a multiple of f while preserving the sign of n
@ -42,27 +50,9 @@ static int round_multiple_nosign(int n, int f)
return sign * (abs_n + f - (abs_n % f)); return sign * (abs_n + f - (abs_n % f));
} }
static int readBlockContent(const unsigned char *mapData, int version, int datapos) static inline unsigned int colorSafeBounds (int channel)
{ {
if (version >= 24) { return mymin(mymax(channel, 0), 255);
size_t index = datapos << 1;
return (mapData[index] << 8) | mapData[index + 1];
} else if (version >= 20) {
if (mapData[datapos] <= 0x80)
return mapData[datapos];
else
return (int(mapData[datapos]) << 4) | (int(mapData[datapos + 0x2000]) >> 4);
}
std::ostringstream oss;
oss << "Unsupported map version " << version;
throw std::runtime_error(oss.str());
}
static inline int colorSafeBounds(int color)
{
color = (color > 255) ? 255 : color;
color = (color < 0) ? 0 : color;
return color;
} }
static Color mixColors(Color a, Color b) static Color mixColors(Color a, Color b)
@ -280,7 +270,7 @@ void TileGenerator::openDb(const std::string &input)
std::ifstream ifs((input + "/world.mt").c_str()); std::ifstream ifs((input + "/world.mt").c_str());
if(!ifs.good()) if(!ifs.good())
throw std::runtime_error("Failed to read world.mt"); throw std::runtime_error("Failed to read world.mt");
backend = get_setting("backend", ifs); backend = read_setting("backend", ifs);
ifs.close(); ifs.close();
} }
@ -337,19 +327,21 @@ void TileGenerator::loadBlocks()
void TileGenerator::createImage() void TileGenerator::createImage()
{ {
const int scale_d = 40; // pixels reserved for a scale
if(!m_drawScale)
m_scales = 0;
m_mapWidth = (m_xMax - m_xMin + 1) * 16; m_mapWidth = (m_xMax - m_xMin + 1) * 16;
m_mapHeight = (m_zMax - m_zMin + 1) * 16; m_mapHeight = (m_zMax - m_zMin + 1) * 16;
if(m_drawScale) { m_xBorder = (m_scales & SCALE_LEFT) ? scale_d : 0;
m_xBorder = (m_scales & SCALE_LEFT) ? 40 : 0; m_yBorder = (m_scales & SCALE_TOP) ? scale_d : 0;
m_yBorder = (m_scales & SCALE_TOP) ? 40 : 0;
}
m_blockPixelAttributes.setWidth(m_mapWidth); m_blockPixelAttributes.setWidth(m_mapWidth);
int image_width, image_height; int image_width, image_height;
image_width = (m_mapWidth * m_zoom) + m_xBorder; image_width = (m_mapWidth * m_zoom) + m_xBorder;
image_width += m_drawScale && (m_scales & SCALE_RIGHT) ? 40 : 0; image_width += (m_scales & SCALE_RIGHT) ? scale_d : 0;
image_height = (m_mapHeight * m_zoom) + m_yBorder; image_height = (m_mapHeight * m_zoom) + m_yBorder;
image_height += m_drawScale && (m_scales & SCALE_BOTTOM) ? 40 : 0; image_height += (m_scales & SCALE_BOTTOM) ? scale_d : 0;
if(image_width > 4096 || image_height > 4096) if(image_width > 4096 || image_height > 4096)
std::cerr << "Warning: The width or height of the image to be created exceeds 4096 pixels!" std::cerr << "Warning: The width or height of the image to be created exceeds 4096 pixels!"
@ -361,6 +353,7 @@ void TileGenerator::createImage()
void TileGenerator::renderMap() void TileGenerator::renderMap()
{ {
BlockDecoder blk;
std::list<int> zlist = getZValueList(); std::list<int> zlist = getZValueList();
for (std::list<int>::iterator zPosition = zlist.begin(); zPosition != zlist.end(); ++zPosition) { for (std::list<int>::iterator zPosition = zlist.begin(); zPosition != zlist.end(); ++zPosition) {
int zPos = *zPosition; int zPos = *zPosition;
@ -370,10 +363,8 @@ void TileGenerator::renderMap()
if (position->second != zPos) if (position->second != zPos)
continue; continue;
for (int i = 0; i < 16; ++i) { m_readPixels.reset();
m_readPixels[i] = 0; m_readInfo.reset();
m_readInfo[i] = 0;
}
for (int i = 0; i < 16; i++) { for (int i = 0; i < 16; i++) {
for (int j = 0; j < 16; j++) { for (int j = 0; j < 16; j++) {
m_color[i][j] = m_bgColor; // This will be drawn by renderMapBlockBottom() for y-rows with only 'air', 'ignore' or unknown nodes if --drawalpha is used m_color[i][j] = m_bgColor; // This will be drawn by renderMapBlockBottom() for y-rows with only 'air', 'ignore' or unknown nodes if --drawalpha is used
@ -387,165 +378,72 @@ void TileGenerator::renderMap()
const BlockList &blockStack = blocks[xPos]; const BlockList &blockStack = blocks[xPos];
for (BlockList::const_iterator it = blockStack.begin(); it != blockStack.end(); ++it) { for (BlockList::const_iterator it = blockStack.begin(); it != blockStack.end(); ++it) {
const BlockPos &pos = it->first; const BlockPos &pos = it->first;
const unsigned char *data = it->second.c_str();
size_t length = it->second.length();
uint8_t version = data[0]; blk.reset();
//uint8_t flags = data[1]; blk.decode(it->second);
if (blk.isEmpty())
continue;
renderMapBlock(blk, pos);
size_t dataOffset = 0; // Exit out if all pixels for this MapBlock are covered
if (version >= 27) if (m_readPixels.full())
dataOffset = 6;
else if (version >= 22)
dataOffset = 4;
else
dataOffset = 2;
ZlibDecompressor decompressor(data, length);
decompressor.setSeekPos(dataOffset);
ustring mapData = decompressor.decompress();
ustring mapMetadata = decompressor.decompress();
dataOffset = decompressor.seekPos();
// Skip unused data
if (version <= 21)
dataOffset += 2;
if (version == 23)
dataOffset += 1;
if (version == 24) {
uint8_t ver = data[dataOffset++];
if (ver == 1) {
uint16_t num = readU16(data + dataOffset);
dataOffset += 2;
dataOffset += 10 * num;
}
}
// Skip unused static objects
dataOffset++; // Skip static object version
int staticObjectCount = readU16(data + dataOffset);
dataOffset += 2;
for (int i = 0; i < staticObjectCount; ++i) {
dataOffset += 13;
uint16_t dataSize = readU16(data + dataOffset);
dataOffset += dataSize + 2;
}
dataOffset += 4; // Skip timestamp
m_blockAirId = -1;
m_blockIgnoreId = -1;
m_nameMap.clear();
// Read mapping
if (version >= 22) {
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;
string name = string(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;
}
// Skip block if made of only air or ignore nodes
if (m_nameMap.empty())
continue;
}
// Node timers
if (version >= 25) {
dataOffset++;
uint16_t numTimers = readU16(data + dataOffset);
dataOffset += 2;
dataOffset += numTimers * 10;
}
renderMapBlock(mapData, pos, version);
bool allRead = true;
for (int i = 0; i < 16; ++i) {
if (m_readPixels[i] != 0xffff)
allRead = false;
}
if (allRead)
break; break;
} }
bool allRead = true; if (!m_readPixels.full())
for (int i = 0; i < 16; ++i) {
if (m_readPixels[i] != 0xffff)
allRead = false;
}
if (!allRead)
renderMapBlockBottom(blockStack.begin()->first); renderMapBlockBottom(blockStack.begin()->first);
} }
if(m_shading) if (m_shading)
renderShading(zPos); renderShading(zPos);
} }
} }
void TileGenerator::renderMapBlock(const ustring &mapBlock, const BlockPos &pos, int version) void TileGenerator::renderMapBlock(const BlockDecoder &blk, const BlockPos &pos)
{ {
int xBegin = (pos.x - m_xMin) * 16; int xBegin = (pos.x - m_xMin) * 16;
int zBegin = (m_zMax - pos.z) * 16; int zBegin = (m_zMax - pos.z) * 16;
const unsigned char *mapData = mapBlock.c_str();
int minY = (pos.y * 16 > m_yMin) ? 0 : m_yMin - pos.y * 16; int minY = (pos.y * 16 > m_yMin) ? 0 : m_yMin - pos.y * 16;
int maxY = (pos.y * 16 < m_yMax) ? 15 : m_yMax - pos.y * 16; int maxY = (pos.y * 16 < m_yMax) ? 15 : m_yMax - pos.y * 16;
for (int z = 0; z < 16; ++z) { for (int z = 0; z < 16; ++z) {
int imageY = zBegin + 15 - z; int imageY = zBegin + 15 - z;
for (int x = 0; x < 16; ++x) { for (int x = 0; x < 16; ++x) {
if (m_readPixels[z] & (1 << x)) if (m_readPixels.get(x, z))
continue; continue;
int imageX = xBegin + x; int imageX = xBegin + x;
for (int y = maxY; y >= minY; --y) { for (int y = maxY; y >= minY; --y) {
int position = x + (y << 4) + (z << 8); string name = blk.getNode(x, y, z);
int content = readBlockContent(mapData, version, position); if (name == "")
if (content == m_blockAirId || content == m_blockIgnoreId)
continue; continue;
NameMap::iterator blockName = m_nameMap.find(content); ColorMap::const_iterator it = m_colorMap.find(name);
if (blockName == m_nameMap.end()) { if (it == m_colorMap.end()) {
std::cerr << "Skipping node with invalid ID." << std::endl;
continue;
}
const string &name = blockName->second;
ColorMap::const_iterator color = m_colorMap.find(name);
if (color != m_colorMap.end()) {
const Color c = color->second.to_color();
if (m_drawAlpha) {
// mix with previous color (unless first visible time)
if (m_color[z][x].a == 0)
m_color[z][x] = c;
else
m_color[z][x] = mixColors(m_color[z][x], c);
if(m_color[z][x].a == 0xff) {
// color is opaque at this depth (no point continuing)
setZoomed(imageX, imageY, m_color[z][x]);
m_readPixels[z] |= (1 << x);
m_blockPixelAttributes.attribute(15 - z, xBegin + x).thickness = m_thickness[z][x];
} else {
// near thickness value to thickness of current node
m_thickness[z][x] = (m_thickness[z][x] + color->second.t) / 2.0;
continue;
}
} else {
setZoomed(imageX, imageY, c.noAlpha());
m_readPixels[z] |= (1 << x);
}
if(!(m_readInfo[z] & (1 << x))) {
m_blockPixelAttributes.attribute(15 - z, xBegin + x).height = pos.y * 16 + y;
m_readInfo[z] |= (1 << x);
}
} else {
m_unknownNodes.insert(name); m_unknownNodes.insert(name);
continue; continue;
} }
const Color c = it->second.to_color();
if (m_drawAlpha) {
if (m_color[z][x].a == 0)
m_color[z][x] = c; // first visible time, no color mixing
else
m_color[z][x] = mixColors(m_color[z][x], c);
if(m_color[z][x].a < 0xff) {
// near thickness value to thickness of current node
m_thickness[z][x] = (m_thickness[z][x] + it->second.t) / 2.0;
continue;
}
// color became opaque, draw it
setZoomed(imageX, imageY, m_color[z][x]);
m_blockPixelAttributes.attribute(15 - z, xBegin + x).thickness = m_thickness[z][x];
} else {
setZoomed(imageX, imageY, c.noAlpha());
}
m_readPixels.set(x, z);
// do this afterwards so we can record height values
// inside transparent nodes (water) too
if (!m_readInfo.get(x, z)) {
m_blockPixelAttributes.attribute(15 - z, xBegin + x).height = pos.y * 16 + y;
m_readInfo.set(x, z);
}
break; break;
} }
} }
@ -554,21 +452,22 @@ void TileGenerator::renderMapBlock(const ustring &mapBlock, const BlockPos &pos,
void TileGenerator::renderMapBlockBottom(const BlockPos &pos) void TileGenerator::renderMapBlockBottom(const BlockPos &pos)
{ {
if (!m_drawAlpha)
return; // "missing" pixels can only happen with --drawalpha
int xBegin = (pos.x - m_xMin) * 16; int xBegin = (pos.x - m_xMin) * 16;
int zBegin = (m_zMax - pos.z) * 16; int zBegin = (m_zMax - pos.z) * 16;
for (int z = 0; z < 16; ++z) { for (int z = 0; z < 16; ++z) {
int imageY = zBegin + 15 - z; int imageY = zBegin + 15 - z;
for (int x = 0; x < 16; ++x) { for (int x = 0; x < 16; ++x) {
if (m_readPixels[z] & (1 << x)) if (m_readPixels.get(x, z))
continue; continue;
int imageX = xBegin + x; int imageX = xBegin + x;
if (m_drawAlpha) { // set color since it wasn't done in renderMapBlock()
// set color in case it wasn't done in renderMapBlock() setZoomed(imageX, imageY, m_color[z][x]);
setZoomed(imageX, imageY, m_color[z][x]); m_readPixels.set(x, z);
m_readPixels[z] |= (1 << x); m_blockPixelAttributes.attribute(15 - z, xBegin + x).thickness = m_thickness[z][x];
m_blockPixelAttributes.attribute(15 - z, xBegin + x).thickness = m_thickness[z][x];
}
} }
} }
} }
@ -593,11 +492,11 @@ void TileGenerator::renderShading(int zPos)
int y1 = m_blockPixelAttributes.attribute(z, x - 1).height; int y1 = m_blockPixelAttributes.attribute(z, x - 1).height;
int y2 = m_blockPixelAttributes.attribute(z - 1, x).height; int y2 = m_blockPixelAttributes.attribute(z - 1, x).height;
int d = ((y - y1) + (y - y2)) * 12; int d = ((y - y1) + (y - y2)) * 12;
if (d > 36) if (m_drawAlpha) { // less visible shadow with increasing "thickness"
d = 36; double t = m_blockPixelAttributes.attribute(z, x).thickness * 1.2;
// more thickness -> less visible shadows: t=0 -> 100% visible, t=255 -> 0% visible d *= 1.0 - mymin(t, 255.0) / 255.0;
if (m_drawAlpha) }
d = d * ((0xFF - m_blockPixelAttributes.attribute(z, x).thickness) / 255.0); d = mymin(d, 36);
Color c = m_image->getPixel(getImageX(x), getImageY(imageY)); Color c = m_image->getPixel(getImageX(x), getImageY(imageY));
c.r = colorSafeBounds(c.r + d); c.r = colorSafeBounds(c.r + d);
@ -611,15 +510,19 @@ void TileGenerator::renderShading(int zPos)
void TileGenerator::renderScale() void TileGenerator::renderScale()
{ {
const int scale_d = 40; // see createImage()
if (m_scales & SCALE_TOP) { if (m_scales & SCALE_TOP) {
m_image->drawText(24, 0, "X", m_scaleColor); m_image->drawText(24, 0, "X", m_scaleColor);
for (int i = (m_xMin / 4) * 4; i <= m_xMax; i += 4) { for (int i = (m_xMin / 4) * 4; i <= m_xMax; i += 4) {
stringstream buf; stringstream buf;
buf << i * 16; buf << i * 16;
int xPos = (m_xMin * -16 + i * 16)*m_zoom + m_xBorder; int xPos = getImageX(i * 16, true);
m_image->drawText(xPos + 2, 0, buf.str(), m_scaleColor); if (xPos >= 0) {
m_image->drawLine(xPos, 0, xPos, m_yBorder - 1, m_scaleColor); m_image->drawText(xPos + 2, 0, buf.str(), m_scaleColor);
m_image->drawLine(xPos, 0, xPos, m_yBorder - 1, m_scaleColor);
}
} }
} }
@ -629,53 +532,72 @@ void TileGenerator::renderScale()
stringstream buf; stringstream buf;
buf << i * 16; buf << i * 16;
int yPos = (m_mapHeight - 1 - (i * 16 - m_zMin * 16))*m_zoom + m_yBorder; int yPos = getImageY(i * 16 + 1, true);
m_image->drawText(2, yPos, buf.str(), m_scaleColor); if (yPos >= 0) {
m_image->drawLine(0, yPos, m_xBorder - 1, yPos, m_scaleColor); m_image->drawText(2, yPos, buf.str(), m_scaleColor);
m_image->drawLine(0, yPos, m_xBorder - 1, yPos, m_scaleColor);
}
} }
} }
if (m_scales & SCALE_BOTTOM) { if (m_scales & SCALE_BOTTOM) {
int xPos = m_xBorder + m_mapWidth*m_zoom - 24 - 8,
yPos = m_yBorder + m_mapHeight*m_zoom + scale_d - 12;
m_image->drawText(xPos, yPos, "X", m_scaleColor);
for (int i = (m_xMin / 4) * 4; i <= m_xMax; i += 4) { for (int i = (m_xMin / 4) * 4; i <= m_xMax; i += 4) {
stringstream buf; stringstream buf;
buf << i * 16; buf << i * 16;
int xPos = (m_xMin * -16 + i * 16)*m_zoom + m_xBorder; xPos = getImageX(i * 16, true);
int yPos = m_yBorder + m_mapHeight*m_zoom; yPos = m_yBorder + m_mapHeight*m_zoom;
m_image->drawText(xPos + 2, yPos, buf.str(), m_scaleColor); if (xPos >= 0) {
m_image->drawLine(xPos, yPos, xPos, yPos + 39, m_scaleColor); m_image->drawText(xPos + 2, yPos, buf.str(), m_scaleColor);
m_image->drawLine(xPos, yPos, xPos, yPos + 39, m_scaleColor);
}
} }
} }
if (m_scales & SCALE_RIGHT) { if (m_scales & SCALE_RIGHT) {
int xPos = m_xBorder + m_mapWidth*m_zoom + scale_d - 2 - 8,
yPos = m_yBorder + m_mapHeight*m_zoom - 24 - 12;
m_image->drawText(xPos, yPos, "Z", m_scaleColor);
for (int i = (m_zMax / 4) * 4; i >= m_zMin; i -= 4) { for (int i = (m_zMax / 4) * 4; i >= m_zMin; i -= 4) {
stringstream buf; stringstream buf;
buf << i * 16; buf << i * 16;
int xPos = m_xBorder + m_mapWidth*m_zoom; xPos = m_xBorder + m_mapWidth*m_zoom;
int yPos = (m_mapHeight - 1 - (i * 16 - m_zMin * 16))*m_zoom + m_yBorder; yPos = getImageY(i * 16 + 1, true);
m_image->drawText(xPos + 2, yPos, buf.str(), m_scaleColor); if (yPos >= 0) {
m_image->drawLine(xPos, yPos, xPos + 39, yPos, m_scaleColor); m_image->drawText(xPos + 2, yPos, buf.str(), m_scaleColor);
m_image->drawLine(xPos, yPos, xPos + 39, yPos, m_scaleColor);
}
} }
} }
} }
void TileGenerator::renderOrigin() void TileGenerator::renderOrigin()
{ {
int imageX = (-m_xMin * 16)*m_zoom + m_xBorder; if (m_xMin > 0 || m_xMax < 0 ||
int imageY = (m_mapHeight - m_zMin * -16)*m_zoom + m_yBorder; m_zMin > 0 || m_zMax < 0)
m_image->drawCircle(imageX, imageY, 12, m_originColor); return;
m_image->drawCircle(getImageX(0, true), getImageY(0, true), 12, m_originColor);
} }
void TileGenerator::renderPlayers(const std::string &inputPath) void TileGenerator::renderPlayers(const std::string &inputPath)
{ {
PlayerAttributes players(inputPath); PlayerAttributes players(inputPath);
for (PlayerAttributes::Players::iterator player = players.begin(); player != players.end(); ++player) { for (PlayerAttributes::Players::iterator player = players.begin(); player != players.end(); ++player) {
int imageX = (player->x / 10 - m_xMin * 16)*m_zoom + m_xBorder; if (player->x < m_xMin * 16 || player->x > m_xMax * 16 ||
int imageY = (m_mapHeight - (player->z / 10 - m_zMin * 16))*m_zoom + m_yBorder; player->z < m_zMin * 16 || player->z > m_zMax * 16)
continue;
if (player->y < m_yMin || player->y > m_yMax)
continue;
int imageX = getImageX(player->x, true),
imageY = getImageY(player->z, true);
m_image->drawCircle(imageX, imageY, 5, m_playerColor); m_image->drawFilledRect(imageX - 1, imageY, 3, 1, m_playerColor);
m_image->drawText(imageX + 2, imageY + 2, player->name, m_playerColor); m_image->drawFilledRect(imageX, imageY - 1, 1, 3, m_playerColor);
m_image->drawText(imageX + 2, imageY, player->name, m_playerColor);
} }
} }
@ -699,21 +621,24 @@ void TileGenerator::writeImage(const std::string &output)
void TileGenerator::printUnknown() void TileGenerator::printUnknown()
{ {
if (m_unknownNodes.size() > 0) { if (m_unknownNodes.size() == 0)
std::cerr << "Unknown nodes:" << std::endl; return;
for (NameSet::iterator node = m_unknownNodes.begin(); node != m_unknownNodes.end(); ++node) { std::cerr << "Unknown nodes:" << std::endl;
std::cerr << *node << std::endl; for (NameSet::iterator node = m_unknownNodes.begin(); node != m_unknownNodes.end(); ++node)
} std::cerr << "\t" << *node << std::endl;
}
} }
inline int TileGenerator::getImageX(int val) const inline int TileGenerator::getImageX(int val, bool absolute) const
{ {
if (absolute)
val = (val - m_xMin * 16);
return (m_zoom*val) + m_xBorder; return (m_zoom*val) + m_xBorder;
} }
inline int TileGenerator::getImageY(int val) const inline int TileGenerator::getImageY(int val, bool absolute) const
{ {
if (absolute)
val = m_mapHeight - (val - m_zMin * 16); // Z axis is flipped on image
return (m_zoom*val) + m_yBorder; return (m_zoom*val) + m_yBorder;
} }

View File

@ -118,11 +118,13 @@ done < $NODESTXT_PATH > $COLORSTXT_PATH
# Use nicer colors for water and lava: # Use nicer colors for water and lava:
sed -re 's/^default:((river_)?water_(flowing|source)) [0-9 ]+$/default:\1 39 66 106 128 224/g' $COLORSTXT_PATH -i sed -re 's/^default:((river_)?water_(flowing|source)) [0-9 ]+$/default:\1 39 66 106 128 224/g' $COLORSTXT_PATH -i
sed -re 's/^default:(lava_(flowing|source)) [0-9 ]+$/default:\1 255 100 0/g' $COLORSTXT_PATH -i sed -re 's/^default:(lava_(flowing|source)) [0-9 ]+$/default:\1 255 100 0/g' $COLORSTXT_PATH -i
# Add transparency to glass nodes: # Add transparency to glass nodes and xpanes:
sed -re 's/^default:(.*glass) ([0-9 ]+)$/default:\1 \2 64 16/g' $COLORSTXT_PATH -i sed -re 's/^default:(.*glass) ([0-9 ]+)$/default:\1 \2 64 16/g' $COLORSTXT_PATH -i
sed -re 's/^doors:(.*glass[^ ]*) ([0-9 ]+)$/doors:\1 \2 64 16/g' $COLORSTXT_PATH -i sed -re 's/^doors:(.*glass[^ ]*) ([0-9 ]+)$/doors:\1 \2 64 16/g' $COLORSTXT_PATH -i
# Fix xpanes color: sed -re 's/^xpanes:(.*(pane|bar)[^ ]*) ([0-9 ]+)$/xpanes:\1 \3 64 16/g' $COLORSTXT_PATH -i
sed -re 's/^xpanes:((pane|bar)(_flat)?) [0-9 ]+$/xpanes:\1 194 194 227 64 16/g' $COLORSTXT_PATH -i # Delete some usually hidden nodes:
sed '/^doors:hidden /d' $COLORSTXT_PATH -i
sed '/^fireflies:firefly /d' $COLORSTXT_PATH -i
==INSTRUCTIONS== ==INSTRUCTIONS==
1) Make sure avgcolors.py works (outputs the usage instructions when run) 1) Make sure avgcolors.py works (outputs the usage instructions when run)
2) Add the dumpnodes mod to Minetest 2) Add the dumpnodes mod to Minetest

View File

@ -32,6 +32,7 @@ default:bush_leaves 34 52 29
default:bush_sapling 65 60 40 default:bush_sapling 65 60 40
default:bush_stem 45 33 23 default:bush_stem 45 33 23
default:cactus 52 116 15 default:cactus 52 116 15
default:cave_ice 167 206 247
default:chest 140 108 65 default:chest 140 108 65
default:chest_locked 140 108 65 default:chest_locked 140 108 65
default:chest_locked_open 140 108 65 default:chest_locked_open 140 108 65
@ -44,7 +45,7 @@ default:copperblock 192 126 63
default:coral_brown 139 104 72 default:coral_brown 139 104 72
default:coral_orange 191 62 12 default:coral_orange 191 62 12
default:coral_skeleton 235 230 214 default:coral_skeleton 235 230 214
default:desert_cobble 146 95 76 default:desert_cobble 107 65 49
default:desert_sand 206 165 98 default:desert_sand 206 165 98
default:desert_sandstone 195 152 92 default:desert_sandstone 195 152 92
default:desert_sandstone_block 192 151 94 default:desert_sandstone_block 192 151 94
@ -54,6 +55,7 @@ default:desert_stone_block 130 79 60
default:desert_stonebrick 129 79 60 default:desert_stonebrick 129 79 60
default:diamondblock 135 217 223 default:diamondblock 135 217 223
default:dirt 95 64 39 default:dirt 95 64 39
default:dirt_with_coniferous_litter 107 88 68
default:dirt_with_dry_grass 187 148 77 default:dirt_with_dry_grass 187 148 77
default:dirt_with_grass 66 112 31 default:dirt_with_grass 66 112 31
default:dirt_with_grass_footsteps 66 112 31 default:dirt_with_grass_footsteps 66 112 31
@ -65,11 +67,15 @@ default:dry_grass_3 210 174 87
default:dry_grass_4 211 175 88 default:dry_grass_4 211 175 88
default:dry_grass_5 213 178 92 default:dry_grass_5 213 178 92
default:dry_shrub 101 66 17 default:dry_shrub 101 66 17
default:emergent_jungle_sapling 48 39 15
default:fence_acacia_wood 147 60 38 default:fence_acacia_wood 147 60 38
default:fence_aspen_wood 210 199 170 default:fence_aspen_wood 210 199 170
default:fence_junglewood 54 37 11 default:fence_junglewood 54 37 11
default:fence_pine_wood 221 184 129 default:fence_pine_wood 221 184 129
default:fence_wood 129 100 55 default:fence_wood 129 100 55
default:fern_1 81 114 48
default:fern_2 86 118 50
default:fern_3 88 121 52
default:furnace 100 96 94 default:furnace 100 96 94
default:furnace_active 100 96 94 default:furnace_active 100 96 94
default:glass 194 194 227 64 16 default:glass 194 194 227 64 16
@ -91,6 +97,9 @@ default:ladder_wood 123 90 34
default:lava_flowing 255 100 0 default:lava_flowing 255 100 0
default:lava_source 255 100 0 default:lava_source 255 100 0
default:leaves 34 52 29 default:leaves 34 52 29
default:marram_grass_1 96 133 89
default:marram_grass_2 87 126 83
default:marram_grass_3 85 125 82
default:mese 220 220 0 default:mese 220 220 0
default:mese_post_light 131 102 57 default:mese_post_light 131 102 57
default:meselamp 211 213 139 default:meselamp 211 213 139
@ -100,13 +109,14 @@ default:obsidian_block 20 22 25
default:obsidian_glass 19 21 23 64 16 default:obsidian_glass 19 21 23 64 16
default:obsidianbrick 20 22 24 default:obsidianbrick 20 22 24
default:papyrus 94 132 33 default:papyrus 94 132 33
default:pine_needles 13 36 21 default:pine_needles 11 48 18
default:pine_sapling 27 48 25 default:pine_sapling 27 48 25
default:pine_tree 182 155 124 default:pine_tree 182 155 124
default:pine_wood 221 184 128 default:pine_wood 221 184 128
default:river_water_flowing 39 66 106 128 224 default:river_water_flowing 39 66 106 128 224
default:river_water_source 39 66 106 128 224 default:river_water_source 39 66 106 128 224
default:sand 214 207 158 default:sand 214 207 158
default:sand_with_kelp 214 207 158
default:sandstone 197 193 143 default:sandstone 197 193 143
default:sandstone_block 195 190 141 default:sandstone_block 195 190 141
default:sandstonebrick 193 189 140 default:sandstonebrick 193 189 140
@ -158,9 +168,8 @@ doors:gate_pine_wood_closed 221 184 128
doors:gate_pine_wood_open 221 184 128 doors:gate_pine_wood_open 221 184 128
doors:gate_wood_closed 128 99 55 doors:gate_wood_closed 128 99 55
doors:gate_wood_open 128 99 55 doors:gate_wood_open 128 99 55
doors:hidden 0 0 0
doors:trapdoor 128 99 50 doors:trapdoor 128 99 50
doors:trapdoor_open 225 217 206 doors:trapdoor_open 136 106 59
doors:trapdoor_steel 199 199 199 doors:trapdoor_steel 199 199 199
doors:trapdoor_steel_open 206 206 206 doors:trapdoor_steel_open 206 206 206
@ -193,14 +202,19 @@ farming:wheat_8 186 161 35
fire:basic_flame 218 120 36 fire:basic_flame 218 120 36
fire:permanent_flame 218 120 36 fire:permanent_flame 218 120 36
# fireflies
fireflies:firefly_bottle 190 191 198
# flowers # flowers
flowers:chrysanthemum_green 101 140 39
flowers:dandelion_white 178 176 140 flowers:dandelion_white 178 176 140
flowers:dandelion_yellow 178 148 24 flowers:dandelion_yellow 194 159 17
flowers:geranium 72 87 155 flowers:geranium 72 87 155
flowers:mushroom_brown 106 79 72 flowers:mushroom_brown 106 79 72
flowers:mushroom_red 192 85 85 flowers:mushroom_red 192 85 85
flowers:rose 118 48 24 flowers:rose 118 48 24
flowers:tulip 130 99 36 flowers:tulip 130 99 36
flowers:tulip_black 72 103 63
flowers:viola 106 60 159 flowers:viola 106 60 159
flowers:waterlily 102 158 61 flowers:waterlily 102 158 61
@ -211,7 +225,7 @@ stairs:slab_brick 117 71 69
stairs:slab_bronzeblock 185 110 15 stairs:slab_bronzeblock 185 110 15
stairs:slab_cobble 88 84 82 stairs:slab_cobble 88 84 82
stairs:slab_copperblock 192 126 63 stairs:slab_copperblock 192 126 63
stairs:slab_desert_cobble 146 95 76 stairs:slab_desert_cobble 107 65 49
stairs:slab_desert_sandstone 195 152 92 stairs:slab_desert_sandstone 195 152 92
stairs:slab_desert_sandstone_block 192 151 94 stairs:slab_desert_sandstone_block 192 151 94
stairs:slab_desert_sandstone_brick 191 151 94 stairs:slab_desert_sandstone_brick 191 151 94
@ -238,6 +252,7 @@ stairs:slab_stone 97 94 93
stairs:slab_stone_block 99 96 95 stairs:slab_stone_block 99 96 95
stairs:slab_stonebrick 99 96 95 stairs:slab_stonebrick 99 96 95
stairs:slab_straw 211 182 67 stairs:slab_straw 211 182 67
stairs:slab_tinblock 149 149 149
stairs:slab_wood 128 99 55 stairs:slab_wood 128 99 55
stairs:stair_acacia_wood 146 60 37 stairs:stair_acacia_wood 146 60 37
stairs:stair_aspen_wood 209 198 169 stairs:stair_aspen_wood 209 198 169
@ -245,7 +260,7 @@ stairs:stair_brick 117 71 69
stairs:stair_bronzeblock 185 110 15 stairs:stair_bronzeblock 185 110 15
stairs:stair_cobble 88 84 82 stairs:stair_cobble 88 84 82
stairs:stair_copperblock 192 126 63 stairs:stair_copperblock 192 126 63
stairs:stair_desert_cobble 146 95 76 stairs:stair_desert_cobble 107 65 49
stairs:stair_desert_sandstone 195 152 92 stairs:stair_desert_sandstone 195 152 92
stairs:stair_desert_sandstone_block 192 151 94 stairs:stair_desert_sandstone_block 192 151 94
stairs:stair_desert_sandstone_brick 191 151 94 stairs:stair_desert_sandstone_brick 191 151 94
@ -254,11 +269,81 @@ stairs:stair_desert_stone_block 130 79 60
stairs:stair_desert_stonebrick 129 79 60 stairs:stair_desert_stonebrick 129 79 60
stairs:stair_goldblock 230 201 29 stairs:stair_goldblock 230 201 29
stairs:stair_ice 167 206 247 stairs:stair_ice 167 206 247
stairs:stair_inner_acacia_wood 146 60 37
stairs:stair_inner_aspen_wood 209 198 169
stairs:stair_inner_brick 117 71 69
stairs:stair_inner_bronzeblock 185 110 15
stairs:stair_inner_cobble 88 84 82
stairs:stair_inner_copperblock 192 126 63
stairs:stair_inner_desert_cobble 107 65 49
stairs:stair_inner_desert_sandstone 195 152 92
stairs:stair_inner_desert_sandstone_block 192 151 94
stairs:stair_inner_desert_sandstone_brick 191 151 94
stairs:stair_inner_desert_stone 129 79 60
stairs:stair_inner_desert_stone_block 130 79 60
stairs:stair_inner_desert_stonebrick 129 79 60
stairs:stair_inner_goldblock 230 201 29
stairs:stair_inner_ice 167 206 247
stairs:stair_inner_junglewood 54 37 11
stairs:stair_inner_mossycobble 86 90 68
stairs:stair_inner_obsidian 19 21 24
stairs:stair_inner_obsidian_block 20 22 25
stairs:stair_inner_obsidianbrick 20 22 24
stairs:stair_inner_pine_wood 221 184 128
stairs:stair_inner_sandstone 197 193 143
stairs:stair_inner_sandstone_block 195 190 141
stairs:stair_inner_sandstonebrick 193 189 140
stairs:stair_inner_silver_sandstone 195 192 181
stairs:stair_inner_silver_sandstone_block 192 190 179
stairs:stair_inner_silver_sandstone_brick 190 188 178
stairs:stair_inner_snowblock 224 225 238
stairs:stair_inner_steelblock 194 194 194
stairs:stair_inner_stone 97 94 93
stairs:stair_inner_stone_block 99 96 95
stairs:stair_inner_stonebrick 99 96 95
stairs:stair_inner_straw 211 182 67
stairs:stair_inner_tinblock 149 149 149
stairs:stair_inner_wood 128 99 55
stairs:stair_junglewood 54 37 11 stairs:stair_junglewood 54 37 11
stairs:stair_mossycobble 86 90 68 stairs:stair_mossycobble 86 90 68
stairs:stair_obsidian 19 21 24 stairs:stair_obsidian 19 21 24
stairs:stair_obsidian_block 20 22 25 stairs:stair_obsidian_block 20 22 25
stairs:stair_obsidianbrick 20 22 24 stairs:stair_obsidianbrick 20 22 24
stairs:stair_outer_acacia_wood 146 60 37
stairs:stair_outer_aspen_wood 209 198 169
stairs:stair_outer_brick 117 71 69
stairs:stair_outer_bronzeblock 185 110 15
stairs:stair_outer_cobble 88 84 82
stairs:stair_outer_copperblock 192 126 63
stairs:stair_outer_desert_cobble 107 65 49
stairs:stair_outer_desert_sandstone 195 152 92
stairs:stair_outer_desert_sandstone_block 192 151 94
stairs:stair_outer_desert_sandstone_brick 191 151 94
stairs:stair_outer_desert_stone 129 79 60
stairs:stair_outer_desert_stone_block 130 79 60
stairs:stair_outer_desert_stonebrick 129 79 60
stairs:stair_outer_goldblock 230 201 29
stairs:stair_outer_ice 167 206 247
stairs:stair_outer_junglewood 54 37 11
stairs:stair_outer_mossycobble 86 90 68
stairs:stair_outer_obsidian 19 21 24
stairs:stair_outer_obsidian_block 20 22 25
stairs:stair_outer_obsidianbrick 20 22 24
stairs:stair_outer_pine_wood 221 184 128
stairs:stair_outer_sandstone 197 193 143
stairs:stair_outer_sandstone_block 195 190 141
stairs:stair_outer_sandstonebrick 193 189 140
stairs:stair_outer_silver_sandstone 195 192 181
stairs:stair_outer_silver_sandstone_block 192 190 179
stairs:stair_outer_silver_sandstone_brick 190 188 178
stairs:stair_outer_snowblock 224 225 238
stairs:stair_outer_steelblock 194 194 194
stairs:stair_outer_stone 97 94 93
stairs:stair_outer_stone_block 99 96 95
stairs:stair_outer_stonebrick 99 96 95
stairs:stair_outer_straw 211 182 67
stairs:stair_outer_tinblock 149 149 149
stairs:stair_outer_wood 128 99 55
stairs:stair_pine_wood 221 184 128 stairs:stair_pine_wood 221 184 128
stairs:stair_sandstone 197 193 143 stairs:stair_sandstone 197 193 143
stairs:stair_sandstone_block 195 190 141 stairs:stair_sandstone_block 195 190 141
@ -272,6 +357,7 @@ stairs:stair_stone 97 94 93
stairs:stair_stone_block 99 96 95 stairs:stair_stone_block 99 96 95
stairs:stair_stonebrick 99 96 95 stairs:stair_stonebrick 99 96 95
stairs:stair_straw 211 182 67 stairs:stair_straw 211 182 67
stairs:stair_tinblock 149 149 149
stairs:stair_wood 128 99 55 stairs:stair_wood 128 99 55
# tnt # tnt
@ -288,7 +374,7 @@ vessels:steel_bottle 193 192 191
# walls # walls
walls:cobble 88 84 82 walls:cobble 88 84 82
walls:desertcobble 146 95 76 walls:desertcobble 107 65 49
walls:mossycobble 86 90 68 walls:mossycobble 86 90 68
# wool # wool
@ -309,8 +395,10 @@ wool:white 220 220 220
wool:yellow 254 225 15 wool:yellow 254 225 15
# xpanes # xpanes
xpanes:bar 194 194 227 64 16 xpanes:bar 114 114 114 64 16
xpanes:bar_flat 194 194 227 64 16 xpanes:bar_flat 114 114 114 64 16
xpanes:pane 194 194 227 64 16 xpanes:obsidian_pane 16 16 17 64 16
xpanes:pane_flat 194 194 227 64 16 xpanes:obsidian_pane_flat 16 16 17 64 16
xpanes:pane 192 192 227 64 16
xpanes:pane_flat 192 192 227 64 16

View File

@ -28,35 +28,24 @@ static inline std::string i64tos(int64_t i)
return os.str(); return os.str();
} }
std::string get_setting_default(std::string name, std::istream &is, const std::string def)
{
try {
return get_setting(name, is);
} catch(std::runtime_error e) {
return def;
}
}
DBRedis::DBRedis(const std::string &mapdir) DBRedis::DBRedis(const std::string &mapdir)
{ {
std::ifstream ifs((mapdir + "/world.mt").c_str()); std::ifstream ifs((mapdir + "/world.mt").c_str());
if(!ifs.good()) if(!ifs.good())
throw std::runtime_error("Failed to read world.mt"); throw std::runtime_error("Failed to read world.mt");
std::string tmp; std::string tmp;
try {
tmp = get_setting("redis_address", ifs); tmp = read_setting("redis_address", ifs);
ifs.seekg(0); ifs.seekg(0);
hash = get_setting("redis_hash", ifs); hash = read_setting("redis_hash", ifs);
ifs.seekg(0); ifs.seekg(0);
} catch(std::runtime_error e) {
throw std::runtime_error("Set redis_address and redis_hash in world.mt to use the redis backend");
}
const char *addr = tmp.c_str(); const char *addr = tmp.c_str();
int port = stoi64(get_setting_default("redis_port", ifs, "6379")); int port = stoi64(read_setting_default("redis_port", ifs, "6379"));
ctx = redisConnect(addr, port); ctx = tmp.find('/') != std::string::npos ? redisConnectUnix(addr) : redisConnect(addr, port);
if(!ctx) if(!ctx) {
throw std::runtime_error("Cannot allocate redis context"); throw std::runtime_error("Cannot allocate redis context");
else if(ctx->err) { } else if(ctx->err) {
std::string err = std::string("Connection error: ") + ctx->errstr; std::string err = std::string("Connection error: ") + ctx->errstr;
redisFree(ctx); redisFree(ctx);
throw std::runtime_error(err); throw std::runtime_error(err);

35
include/BlockDecoder.h Normal file
View File

@ -0,0 +1,35 @@
#ifndef BLOCKDECODER_H
#define BLOCKDECODER_H
#if __cplusplus >= 201103L
#include <unordered_map>
#else
#include <map>
#endif
#include "types.h"
class BlockDecoder {
public:
BlockDecoder();
void reset();
void decode(const ustring &data);
bool isEmpty() const;
std::string getNode(u8 x, u8 y, u8 z) const; // returns "" for air, ignore and invalid nodes
private:
#if __cplusplus >= 201103L
typedef std::unordered_map<int, std::string> NameMap;
#else
typedef std::map<int, std::string> NameMap;
#endif
NameMap m_nameMap;
int m_blockAirId;
int m_blockIgnoreId;
u8 m_version;
ustring m_mapData;
};
#endif // BLOCKDECODER_H

View File

@ -0,0 +1,30 @@
#ifndef PLAYERATTRIBUTES_H_D7THWFVV
#define PLAYERATTRIBUTES_H_D7THWFVV
#include <list>
#include <string>
struct Player
{
std::string name;
double x, y, z;
};
class PlayerAttributes
{
public:
typedef std::list<Player> Players;
PlayerAttributes(const std::string &worldDir);
Players::iterator begin();
Players::iterator end();
private:
void readFiles(const std::string &playersPath);
void readSqlite(const std::string &db_name);
Players m_players;
};
#endif /* end of include guard: PLAYERATTRIBUTES_H_D7THWFVV */

View File

@ -1,7 +1,6 @@
#ifndef TILEGENERATOR_HEADER #ifndef TILEGENERATOR_HEADER
#define TILEGENERATOR_HEADER #define TILEGENERATOR_HEADER
#include <gd.h>
#include <iosfwd> #include <iosfwd>
#include <list> #include <list>
#include <config.h> #include <config.h>
@ -14,7 +13,9 @@
#endif #endif
#include <stdint.h> #include <stdint.h>
#include <string> #include <string>
#include "PixelAttributes.h" #include "PixelAttributes.h"
#include "BlockDecoder.h"
#include "Image.h" #include "Image.h"
#include "db.h" #include "db.h"
#include "types.h" #include "types.h"
@ -30,11 +31,29 @@ struct ColorEntry {
ColorEntry(): r(0), g(0), b(0), a(0), t(0) {}; ColorEntry(): r(0), g(0), b(0), a(0), t(0) {};
ColorEntry(uint8_t r, uint8_t g, uint8_t b, uint8_t a, uint8_t t): r(r), g(g), b(b), a(a), t(t) {}; ColorEntry(uint8_t r, uint8_t g, uint8_t b, uint8_t a, uint8_t t): r(r), g(g), b(b), a(a), t(t) {};
inline Color to_color() const { return Color(r, g, b, a); } inline Color to_color() const { return Color(r, g, b, a); }
uint8_t r; uint8_t r, g, b, a, t;
uint8_t g; };
uint8_t b;
uint8_t a; struct BitmapThing { // 16x16 bitmap
uint8_t t; inline void reset() {
for (int i = 0; i < 16; ++i)
val[i] = 0;
}
inline bool full() const {
for (int i = 0; i < 16; ++i) {
if (val[i] != 0xffff)
return false;
}
return true;
}
inline void set(unsigned int x, unsigned int z) {
val[z] |= (1 << x);
}
inline bool get(unsigned int x, unsigned int z) {
return !!(val[z] & (1 << x));
}
uint16_t val[16];
}; };
@ -43,11 +62,9 @@ class TileGenerator
private: private:
#if __cplusplus >= 201103L #if __cplusplus >= 201103L
typedef std::unordered_map<std::string, ColorEntry> ColorMap; typedef std::unordered_map<std::string, ColorEntry> ColorMap;
typedef std::unordered_map<int, std::string> NameMap;
typedef std::unordered_set<std::string> NameSet; typedef std::unordered_set<std::string> NameSet;
#else #else
typedef std::map<std::string, ColorEntry> ColorMap; typedef std::map<std::string, ColorEntry> ColorMap;
typedef std::map<int, std::string> NameMap;
typedef std::set<std::string> NameSet; typedef std::set<std::string> NameSet;
#endif #endif
@ -80,7 +97,7 @@ private:
void createImage(); void createImage();
void renderMap(); void renderMap();
std::list<int> getZValueList() const; std::list<int> getZValueList() const;
void renderMapBlock(const ustring &mapBlock, const BlockPos &pos, int version); void renderMapBlock(const BlockDecoder &blk, const BlockPos &pos);
void renderMapBlockBottom(const BlockPos &pos); void renderMapBlockBottom(const BlockPos &pos);
void renderShading(int zPos); void renderShading(int zPos);
void renderScale(); void renderScale();
@ -88,8 +105,8 @@ private:
void renderPlayers(const std::string &inputPath); void renderPlayers(const std::string &inputPath);
void writeImage(const std::string &output); void writeImage(const std::string &output);
void printUnknown(); void printUnknown();
int getImageX(int val) const; int getImageX(int val, bool absolute=false) const;
int getImageY(int val) const; int getImageY(int val, bool absolute=false) const;
void setZoomed(int x, int y, Color color); void setZoomed(int x, int y, Color color);
private: private:
@ -121,16 +138,13 @@ private:
int m_mapWidth; int m_mapWidth;
int m_mapHeight; int m_mapHeight;
std::list<std::pair<int, int> > m_positions; std::list<std::pair<int, int> > m_positions;
NameMap m_nameMap;
ColorMap m_colorMap; ColorMap m_colorMap;
uint16_t m_readPixels[16]; BitmapThing m_readPixels;
uint16_t m_readInfo[16]; BitmapThing m_readInfo;
NameSet m_unknownNodes; NameSet m_unknownNodes;
Color m_color[16][16]; Color m_color[16][16];
uint8_t m_thickness[16][16]; uint8_t m_thickness[16][16];
int m_blockAirId;
int m_blockIgnoreId;
int m_zoom; int m_zoom;
uint m_scales; uint m_scales;
}; // class TileGenerator }; // class TileGenerator

View File

18
include/util.h Normal file
View File

@ -0,0 +1,18 @@
#ifndef UTIL_H
#define UTIL_H
#include <string>
#include <fstream>
std::string read_setting(const std::string &name, std::istream &is);
inline std::string read_setting_default(const std::string &name, std::istream &is, const std::string &def)
{
try {
return read_setting(name, is);
} catch(std::runtime_error e) {
return def;
}
}
#endif // UTIL_H

View File

@ -1,3 +1,6 @@
#include <stdexcept>
#include <sstream>
#include "util.h" #include "util.h"
inline std::string trim(const std::string &s) inline std::string trim(const std::string &s)
@ -23,11 +26,15 @@ inline std::string trim(const std::string &s)
return s.substr(front, back - front); return s.substr(front, back - front);
} }
#define EOFCHECK() \ #define EOFCHECK() do { \
if(is.eof()) \ if (is.eof()) { \
throw std::runtime_error(((std::string) "Setting '") + name + "' not found"); std::ostringstream oss; \
oss << "Setting '" << name << "' not found."; \
throw std::runtime_error(oss.str()); \
} \
} while(0)
std::string get_setting(std::string name, std::istream &is) std::string read_setting(const std::string &name, std::istream &is)
{ {
char c; char c;
char s[256]; char s[256];

10
util.h
View File

@ -1,10 +0,0 @@
#ifndef _UTIL_H
#define _UTIL_H
#include <string>
#include <stdexcept>
#include <fstream>
std::string get_setting(std::string name, std::istream &is);
#endif // _UTIL_H

View File

@ -1,7 +1,7 @@
#!/bin/bash -e #!/bin/bash -e
CXX=g++-6 CXX=g++-7
[ $CC == "clang" ] && CXX=clang-3.8 [ $CC == "clang" ] && CXX=clang++-5.0
export CXX export CXX
@ -10,8 +10,6 @@ cd travisbuild
cmake \ cmake \
-DENABLE_LEVELDB=1 \ -DENABLE_LEVELDB=1 \
-DLEVELDB_LIBRARY=../libleveldb/lib/libleveldb.so \
-DLEVELDB_INCLUDE_DIR=../libleveldb/include \
.. ..
make -j2 make -j2