mirror of
https://github.com/minetest/minetestmapper.git
synced 2025-07-05 01:50:21 +02:00
Compare commits
31 Commits
Author | SHA1 | Date | |
---|---|---|---|
42395587b9 | |||
d327a84841 | |||
0d9ed247f9 | |||
b6ad40feb9 | |||
1f471b369b | |||
2f78c39d9c | |||
2ebc3afc7c | |||
e543517eb8 | |||
405951f8f6 | |||
a8f7b47000 | |||
0f36b6e37a | |||
6af6be4632 | |||
7c71138fec | |||
c15adfd325 | |||
75599e8569 | |||
b16f93f183 | |||
976c690357 | |||
7288e60cd2 | |||
cca7072eab | |||
179651e83c | |||
6afe1b78c5 | |||
25d1d43004 | |||
4f7d2a5c83 | |||
21444d00c3 | |||
07c371f68c | |||
95b2f46d7e | |||
bc7d81eb62 | |||
af502f3ac2 | |||
d490cf0a31 | |||
9503f76805 | |||
34d6b413a6 |
15
.travis.yml
15
.travis.yml
@ -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
|
||||||
- libfreeimage-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
142
BlockDecoder.cpp
Normal 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;
|
||||||
|
}
|
@ -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)
|
||||||
@ -21,9 +21,10 @@ if(WIN32)
|
|||||||
set(BINDIR ".")
|
set(BINDIR ".")
|
||||||
set(DOCDIR ".")
|
set(DOCDIR ".")
|
||||||
else()
|
else()
|
||||||
set(SHAREDIR "${CMAKE_INSTALL_PREFIX}/share/minetest") # an extra dir. for just one file doesn't seem useful
|
set(SHAREDIR "${CMAKE_INSTALL_PREFIX}/share/minetest") # reuse Minetest share dir
|
||||||
set(BINDIR "${CMAKE_INSTALL_PREFIX}/bin")
|
set(BINDIR "${CMAKE_INSTALL_PREFIX}/bin")
|
||||||
set(DOCDIR "${CMAKE_INSTALL_PREFIX}/share/doc/${PROJECT_NAME}")
|
set(DOCDIR "${CMAKE_INSTALL_PREFIX}/share/doc/${PROJECT_NAME}")
|
||||||
|
set(MANDIR "${CMAKE_INSTALL_PREFIX}/share/man")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(CUSTOM_SHAREDIR "" CACHE STRING "Directory to install data files into")
|
set(CUSTOM_SHAREDIR "" CACHE STRING "Directory to install data files into")
|
||||||
@ -45,15 +46,15 @@ if(NOT CUSTOM_DOCDIR STREQUAL "")
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
||||||
# Libraries: freeimage
|
# Libraries: gd
|
||||||
|
|
||||||
find_library(FREEIMAGE_LIBRARY freeimage)
|
find_library(LIBGD_LIBRARY gd)
|
||||||
find_path(FREEIMAGE_INCLUDE_DIR FreeImage.h)
|
find_path(LIBGD_INCLUDE_DIR gd.h)
|
||||||
message (STATUS "FreeImage library: ${FREEIMAGE_LIBRARY}")
|
message (STATUS "libgd library: ${LIBGD_LIBRARY}")
|
||||||
message (STATUS "FreeImage headers: ${FREEIMAGE_INCLUDE_DIR}")
|
message (STATUS "libgd headers: ${LIBGD_INCLUDE_DIR}")
|
||||||
if(NOT FREEIMAGE_LIBRARY OR NOT FREEIMAGE_INCLUDE_DIR)
|
if(NOT LIBGD_LIBRARY OR NOT LIBGD_INCLUDE_DIR)
|
||||||
message(FATAL_ERROR "FreeImage not found!")
|
message(FATAL_ERROR "libgd not found!")
|
||||||
endif(NOT FREEIMAGE_LIBRARY OR NOT FREEIMAGE_INCLUDE_DIR)
|
endif(NOT LIBGD_LIBRARY OR NOT LIBGD_INCLUDE_DIR)
|
||||||
|
|
||||||
# Libraries: zlib
|
# Libraries: zlib
|
||||||
|
|
||||||
@ -78,6 +79,40 @@ if(NOT SQLITE3_LIBRARY OR NOT SQLITE3_INCLUDE_DIR)
|
|||||||
message(FATAL_ERROR "sqlite3 not found!")
|
message(FATAL_ERROR "sqlite3 not found!")
|
||||||
endif(NOT SQLITE3_LIBRARY OR NOT SQLITE3_INCLUDE_DIR)
|
endif(NOT SQLITE3_LIBRARY OR NOT SQLITE3_INCLUDE_DIR)
|
||||||
|
|
||||||
|
# Libraries: postgresql
|
||||||
|
|
||||||
|
option(ENABLE_POSTGRESQL "Enable PostgreSQL backend" TRUE)
|
||||||
|
set(USE_POSTGRESQL 0)
|
||||||
|
|
||||||
|
if(ENABLE_POSTGRESQL)
|
||||||
|
find_program(POSTGRESQL_CONFIG_EXECUTABLE pg_config DOC "pg_config")
|
||||||
|
find_library(POSTGRESQL_LIBRARY pq)
|
||||||
|
if(POSTGRESQL_CONFIG_EXECUTABLE)
|
||||||
|
execute_process(COMMAND ${POSTGRESQL_CONFIG_EXECUTABLE} --includedir-server
|
||||||
|
OUTPUT_VARIABLE POSTGRESQL_SERVER_INCLUDE_DIRS
|
||||||
|
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||||
|
execute_process(COMMAND ${POSTGRESQL_CONFIG_EXECUTABLE}
|
||||||
|
OUTPUT_VARIABLE POSTGRESQL_CLIENT_INCLUDE_DIRS
|
||||||
|
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||||
|
# This variable is case sensitive for the cmake PostgreSQL module
|
||||||
|
set(PostgreSQL_ADDITIONAL_SEARCH_PATHS ${POSTGRESQL_SERVER_INCLUDE_DIRS} ${POSTGRESQL_CLIENT_INCLUDE_DIRS})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
find_package("PostgreSQL")
|
||||||
|
|
||||||
|
if(POSTGRESQL_FOUND)
|
||||||
|
set(USE_POSTGRESQL 1)
|
||||||
|
message(STATUS "PostgreSQL backend enabled")
|
||||||
|
# This variable is case sensitive, don't try to change it to POSTGRESQL_INCLUDE_DIR
|
||||||
|
message(STATUS "PostgreSQL includes: ${PostgreSQL_INCLUDE_DIR}")
|
||||||
|
include_directories(${PostgreSQL_INCLUDE_DIR})
|
||||||
|
set(POSTGRESQL_LIBRARY ${PostgreSQL_LIBRARIES})
|
||||||
|
else()
|
||||||
|
message(STATUS "PostgreSQL not found.")
|
||||||
|
set(POSTGRESQL_LIBRARY "")
|
||||||
|
endif()
|
||||||
|
endif(ENABLE_POSTGRESQL)
|
||||||
|
|
||||||
# Libraries: leveldb
|
# Libraries: leveldb
|
||||||
|
|
||||||
set(USE_LEVELDB 0)
|
set(USE_LEVELDB 0)
|
||||||
@ -124,20 +159,21 @@ 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}
|
||||||
${FREEIMAGE_INCLUDE_DIR}
|
${LIBGD_INCLUDE_DIR}
|
||||||
${ZLIB_INCLUDE_DIR}
|
${ZLIB_INCLUDE_DIR}
|
||||||
)
|
)
|
||||||
|
|
||||||
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
|
||||||
@ -148,6 +184,10 @@ set(mapper_SRCS
|
|||||||
db-sqlite3.cpp
|
db-sqlite3.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if(USE_POSTGRESQL)
|
||||||
|
set(mapper_SRCS ${mapper_SRCS} db-postgresql.cpp)
|
||||||
|
endif(USE_POSTGRESQL)
|
||||||
|
|
||||||
if(USE_LEVELDB)
|
if(USE_LEVELDB)
|
||||||
set(mapper_SRCS ${mapper_SRCS} db-leveldb.cpp)
|
set(mapper_SRCS ${mapper_SRCS} db-leveldb.cpp)
|
||||||
endif(USE_LEVELDB)
|
endif(USE_LEVELDB)
|
||||||
@ -163,9 +203,10 @@ add_executable(minetestmapper
|
|||||||
target_link_libraries(
|
target_link_libraries(
|
||||||
minetestmapper
|
minetestmapper
|
||||||
${SQLITE3_LIBRARY}
|
${SQLITE3_LIBRARY}
|
||||||
|
${POSTGRESQL_LIBRARY}
|
||||||
${LEVELDB_LIBRARY}
|
${LEVELDB_LIBRARY}
|
||||||
${REDIS_LIBRARY}
|
${REDIS_LIBRARY}
|
||||||
${FREEIMAGE_LIBRARY}
|
${LIBGD_LIBRARY}
|
||||||
${ZLIB_LIBRARY}
|
${ZLIB_LIBRARY}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -176,6 +217,9 @@ install(FILES "AUTHORS" DESTINATION "${DOCDIR}")
|
|||||||
install(FILES "COPYING" DESTINATION "${DOCDIR}")
|
install(FILES "COPYING" DESTINATION "${DOCDIR}")
|
||||||
install(FILES "README.rst" DESTINATION "${DOCDIR}")
|
install(FILES "README.rst" DESTINATION "${DOCDIR}")
|
||||||
install(FILES "colors.txt" DESTINATION "${SHAREDIR}")
|
install(FILES "colors.txt" DESTINATION "${SHAREDIR}")
|
||||||
|
if(UNIX)
|
||||||
|
install(FILES "minetestmapper.6" DESTINATION "${MANDIR}/man6")
|
||||||
|
endif()
|
||||||
|
|
||||||
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Overview mapper for Minetest")
|
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Overview mapper for Minetest")
|
||||||
set(CPACK_PACKAGE_VERSION_MAJOR ${VERSION_MAJOR})
|
set(CPACK_PACKAGE_VERSION_MAJOR ${VERSION_MAJOR})
|
||||||
|
93
Image.cpp
93
Image.cpp
@ -4,100 +4,121 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
#include <gd.h>
|
||||||
|
#include <gdfontmb.h>
|
||||||
|
|
||||||
#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
|
||||||
|
|
||||||
// ARGB but with inverted alpha
|
// ARGB but with inverted alpha
|
||||||
|
|
||||||
static inline RGBQUAD color2rgbquad(Color c)
|
static inline int color2int(Color c)
|
||||||
{
|
{
|
||||||
RGBQUAD c2;
|
u8 a = 255 - c.a;
|
||||||
c2.rgbRed = c.r;
|
return (a << 24) | (c.r << 16) | (c.g << 8) | c.b;
|
||||||
c2.rgbGreen = c.g;
|
}
|
||||||
c2.rgbBlue = c.b;
|
|
||||||
|
static inline Color int2color(int c)
|
||||||
|
{
|
||||||
|
Color c2;
|
||||||
|
u8 a;
|
||||||
|
c2.b = c & 0xff;
|
||||||
|
c2.g = (c >> 8) & 0xff;
|
||||||
|
c2.r = (c >> 16) & 0xff;
|
||||||
|
a = (c >> 24) & 0xff;
|
||||||
|
c2.a = 255 - a;
|
||||||
return c2;
|
return c2;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline Color rgbquad2color(RGBQUAD c)
|
static inline void check_bounds(int x, int y, int width, int height)
|
||||||
{
|
{
|
||||||
return Color(c.rgbRed, c.rgbGreen, c.rgbBlue);
|
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)
|
||||||
{
|
{
|
||||||
// FIXME: This works because minetestmapper only creates one image
|
m_image = gdImageCreateTrueColor(m_width, m_height);
|
||||||
FreeImage_Initialise();
|
|
||||||
printf("Using FreeImage v%s\n", FreeImage_GetVersion());
|
|
||||||
|
|
||||||
m_image = FreeImage_Allocate(width, height, 24);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Image::~Image()
|
Image::~Image()
|
||||||
{
|
{
|
||||||
FreeImage_Unload(m_image);
|
gdImageDestroy(m_image);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Image::setPixel(int x, int y, const Color &c)
|
void Image::setPixel(int x, int y, const Color &c)
|
||||||
{
|
{
|
||||||
SIZECHECK(x, y);
|
SIZECHECK(x, y);
|
||||||
RGBQUAD col = color2rgbquad(c);
|
m_image->tpixels[y][x] = color2int(c);
|
||||||
FreeImage_SetPixelColor(m_image, x, y, &col);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
return int2color(m_image->tpixels[y][x]);
|
||||||
throw std::out_of_range("sizecheck");
|
|
||||||
#endif
|
|
||||||
RGBQUAD c;
|
|
||||||
FreeImage_GetPixelColor(m_image, x, y, &c);
|
|
||||||
return rgbquad2color(c);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Image::drawLine(int x1, int y1, int x2, int y2, const Color &c)
|
void Image::drawLine(int x1, int y1, int x2, int y2, const Color &c)
|
||||||
{
|
{
|
||||||
SIZECHECK(x1, y1);
|
SIZECHECK(x1, y1);
|
||||||
SIZECHECK(x2, y2);
|
SIZECHECK(x2, y2);
|
||||||
// TODO
|
gdImageLine(m_image, x1, y1, x2, y2, color2int(c));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Image::drawText(int x, int y, const std::string &s, const Color &c)
|
void Image::drawText(int x, int y, const std::string &s, const Color &c)
|
||||||
{
|
{
|
||||||
SIZECHECK(x, y);
|
SIZECHECK(x, y);
|
||||||
// TODO
|
gdImageString(m_image, gdFontGetMediumBold(), x, y, (unsigned char*) s.c_str(), color2int(c));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Image::drawFilledRect(int x, int y, int w, int h, const Color &c)
|
void Image::drawFilledRect(int x, int y, int w, int h, const Color &c)
|
||||||
{
|
{
|
||||||
SIZECHECK(x, y);
|
SIZECHECK(x, y);
|
||||||
SIZECHECK(x + w - 1, y + h - 1);
|
SIZECHECK(x + w - 1, y + h - 1);
|
||||||
RGBQUAD col = color2rgbquad(c);
|
gdImageFilledRectangle(m_image, x, y, x + w - 1, y + h - 1, color2int(c));
|
||||||
for(int xx = 0; xx < w; xx++)
|
|
||||||
for(int yy = 0; yy < h; yy++)
|
|
||||||
FreeImage_SetPixelColor(m_image, x + xx, y + yy, &col);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Image::drawCircle(int x, int y, int diameter, const Color &c)
|
void Image::drawCircle(int x, int y, int diameter, const Color &c)
|
||||||
{
|
{
|
||||||
SIZECHECK(x, y);
|
SIZECHECK(x, y);
|
||||||
// TODO
|
gdImageArc(m_image, x, y, diameter, diameter, 0, 360, color2int(c));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Image::save(const std::string &filename)
|
void Image::save(const std::string &filename)
|
||||||
{
|
{
|
||||||
FreeImage_Save(FIF_PNG, m_image, filename.c_str()); // other formats?
|
#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");
|
||||||
|
if (!f) {
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << "Error opening image file: " << std::strerror(errno);
|
||||||
|
throw std::runtime_error(oss.str());
|
||||||
|
}
|
||||||
|
gdImagePng(m_image, f);
|
||||||
|
fclose(f);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
|
@ -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 */
|
|
||||||
|
|
41
README.rst
41
README.rst
@ -10,15 +10,26 @@ This version is both faster and provides more features than the now deprecated P
|
|||||||
Requirements
|
Requirements
|
||||||
------------
|
------------
|
||||||
|
|
||||||
* FreeImage
|
* libgd
|
||||||
* sqlite3
|
* sqlite3
|
||||||
* leveldb (optional, set ENABLE_LEVELDB=1 in CMake to enable leveldb support)
|
* LevelDB (optional, set ENABLE_LEVELDB=1 in CMake to enable)
|
||||||
* hiredis (optional, set ENABLE_REDIS=1 in CMake to enable redis support)
|
* hiredis library (optional, set ENABLE_REDIS=1 in CMake to enable)
|
||||||
|
* Postgres libraries (optional, set ENABLE_POSTGRES=1 in CMake to enable)
|
||||||
|
|
||||||
e.g. on Debian:
|
e.g. on Debian:
|
||||||
^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
sudo apt-get install libgd-dev libsqlite3-dev libleveldb-dev libhiredis-dev
|
sudo apt-get install libgd-dev libsqlite3-dev libleveldb-dev libhiredis-dev libpq-dev
|
||||||
|
|
||||||
|
Windows
|
||||||
|
^^^^^^^
|
||||||
|
Minetestmapper for Windows can be downloaded here: https://github.com/minetest/minetestmapper/releases
|
||||||
|
|
||||||
|
After extracting the archive, minetestmapper can be invoked from cmd.exe:
|
||||||
|
::
|
||||||
|
|
||||||
|
cd C:\Users\yourname\Desktop\example\path
|
||||||
|
minetestmapper.exe --help
|
||||||
|
|
||||||
Compilation
|
Compilation
|
||||||
-----------
|
-----------
|
||||||
@ -43,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``
|
||||||
@ -76,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*, 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``
|
||||||
|
@ -7,12 +7,16 @@
|
|||||||
#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
|
||||||
|
#include "db-postgresql.h"
|
||||||
|
#endif
|
||||||
#if USE_LEVELDB
|
#if USE_LEVELDB
|
||||||
#include "db-leveldb.h"
|
#include "db-leveldb.h"
|
||||||
#endif
|
#endif
|
||||||
@ -22,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
|
||||||
@ -39,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)
|
||||||
@ -277,12 +270,16 @@ 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();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(backend == "sqlite3")
|
if(backend == "sqlite3")
|
||||||
m_db = new DBSQLite3(input);
|
m_db = new DBSQLite3(input);
|
||||||
|
#if USE_POSTGRESQL
|
||||||
|
else if(backend == "postgresql")
|
||||||
|
m_db = new DBPostgreSQL(input);
|
||||||
|
#endif
|
||||||
#if USE_LEVELDB
|
#if USE_LEVELDB
|
||||||
else if(backend == "leveldb")
|
else if(backend == "leveldb")
|
||||||
m_db = new DBLevelDB(input);
|
m_db = new DBLevelDB(input);
|
||||||
@ -330,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!"
|
||||||
@ -354,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;
|
||||||
@ -363,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
|
||||||
@ -380,163 +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 >= 22)
|
if (m_readPixels.full())
|
||||||
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);
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -545,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];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -584,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);
|
||||||
@ -602,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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -620,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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -690,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,11 +1,14 @@
|
|||||||
==FILE== mods/dumpnodes/init.lua
|
==FILE== mods/dumpnodes/init.lua
|
||||||
local function nd_get_tiles(nd)
|
local function nd_get_tiles(nd)
|
||||||
if nd.tiles then
|
return nd.tiles or nd.tile_images
|
||||||
return nd.tiles
|
end
|
||||||
elseif nd.tile_images then
|
|
||||||
return nd.tile_images
|
local function nd_get_tile(nd, n)
|
||||||
|
local tile = nd_get_tiles(nd)[n]
|
||||||
|
if type(tile) == 'table' then
|
||||||
|
tile = tile.name
|
||||||
end
|
end
|
||||||
return nil
|
return tile
|
||||||
end
|
end
|
||||||
|
|
||||||
local function pairs_s(dict)
|
local function pairs_s(dict)
|
||||||
@ -20,19 +23,19 @@ end
|
|||||||
minetest.register_chatcommand("dumpnodes", {
|
minetest.register_chatcommand("dumpnodes", {
|
||||||
params = "",
|
params = "",
|
||||||
description = "",
|
description = "",
|
||||||
func = function(plname, param)
|
func = function(player, param)
|
||||||
local n = 0
|
local n = 0
|
||||||
local ntbl = {}
|
local ntbl = {}
|
||||||
for _, nn in pairs_s(minetest.registered_nodes) do
|
for _, nn in pairs_s(minetest.registered_nodes) do
|
||||||
local nd = minetest.registered_nodes[nn]
|
local nd = minetest.registered_nodes[nn]
|
||||||
local prefix, name = nn:match('(.*):(.*)')
|
local prefix, name = nn:match('(.*):(.*)')
|
||||||
if prefix == nil or name == nil or prefix == '' or name == '' then
|
if prefix == nil or name == nil then
|
||||||
print("ignored(1): " .. nn)
|
print("ignored(1): " .. nn)
|
||||||
else
|
else
|
||||||
if ntbl[prefix] == nil then
|
if ntbl[prefix] == nil then
|
||||||
ntbl[prefix] = {}
|
ntbl[prefix] = {}
|
||||||
end
|
end
|
||||||
ntbl[prefix][name] = nd
|
ntbl[prefix][name] = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
local out, err = io.open('nodes.txt', 'wb')
|
local out, err = io.open('nodes.txt', 'wb')
|
||||||
@ -40,20 +43,17 @@ minetest.register_chatcommand("dumpnodes", {
|
|||||||
return true, "io.open(): " .. err
|
return true, "io.open(): " .. err
|
||||||
end
|
end
|
||||||
for _, prefix in pairs_s(ntbl) do
|
for _, prefix in pairs_s(ntbl) do
|
||||||
local nodes = ntbl[prefix]
|
|
||||||
out:write('# ' .. prefix .. '\n')
|
out:write('# ' .. prefix .. '\n')
|
||||||
for _, name in pairs_s(nodes) do
|
for _, name in pairs_s(ntbl[prefix]) do
|
||||||
local nd = nodes[name]
|
local nn = prefix .. ":" .. name
|
||||||
if nd.drawtype ~= 'airlike' and nd_get_tiles(nd) ~= nil then
|
local nd = minetest.registered_nodes[nn]
|
||||||
local tl = nd_get_tiles(nd)[1]
|
if nd.drawtype == 'airlike' or nd_get_tiles(nd) == nil then
|
||||||
if type(tl) == 'table' then
|
print("ignored(2): " .. nn)
|
||||||
tl = tl.name
|
|
||||||
end
|
|
||||||
tl = (tl .. '^'):match('(.-)^')
|
|
||||||
out:write(prefix .. ':' .. name .. ' ' .. tl .. '\n')
|
|
||||||
n = n + 1
|
|
||||||
else
|
else
|
||||||
print("ignored(2): " .. prefix .. ':' .. name)
|
local tl = nd_get_tile(nd, 1)
|
||||||
|
tl = (tl .. '^'):match('(.-)^') -- strip modifiers
|
||||||
|
out:write(nn .. ' ' .. tl .. '\n')
|
||||||
|
n = n + 1
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
out:write('\n')
|
out:write('\n')
|
||||||
@ -67,7 +67,7 @@ minetest.register_chatcommand("dumpnodes", {
|
|||||||
import sys
|
import sys
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
def tsum(a, b):
|
def tadd(a, b):
|
||||||
return tuple(sum(e) for e in zip(a, b))
|
return tuple(sum(e) for e in zip(a, b))
|
||||||
|
|
||||||
if len(sys.argv) < 2:
|
if len(sys.argv) < 2:
|
||||||
@ -75,8 +75,7 @@ if len(sys.argv) < 2:
|
|||||||
print("Usage: %s <input>" % sys.argv[0])
|
print("Usage: %s <input>" % sys.argv[0])
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
inp = Image.open(sys.argv[1])
|
inp = Image.open(sys.argv[1]).convert('RGBA')
|
||||||
inp = inp.convert('RGBA')
|
|
||||||
ind = inp.load()
|
ind = inp.load()
|
||||||
|
|
||||||
cl = (0, 0, 0)
|
cl = (0, 0, 0)
|
||||||
@ -84,52 +83,51 @@ counted = 0
|
|||||||
for x in range(inp.size[0]):
|
for x in range(inp.size[0]):
|
||||||
for y in range(inp.size[1]):
|
for y in range(inp.size[1]):
|
||||||
px = ind[x, y]
|
px = ind[x, y]
|
||||||
if px[3] < 128:
|
if px[3] < 128: continue # alpha
|
||||||
continue
|
cl = tadd(cl, px[:3])
|
||||||
cl = tsum(cl, px[:3])
|
counted += 1
|
||||||
counted = counted + 1
|
|
||||||
|
|
||||||
if counted == 0:
|
if counted == 0:
|
||||||
sys.stderr.write("didn't find avg. color for %s\n" % sys.argv[1])
|
sys.stderr.write("did not find avg color for %s\n" % sys.argv[1])
|
||||||
print("0 0 0")
|
print("0 0 0")
|
||||||
exit(0)
|
else:
|
||||||
|
cl = tuple(int(n / counted) for n in cl)
|
||||||
cl = tuple(int(n / counted) for n in cl)
|
print("%d %d %d" % cl)
|
||||||
print("%d %d %d" % cl)
|
|
||||||
==SCRIPT==
|
==SCRIPT==
|
||||||
#!/bin/bash -e
|
#!/bin/bash -e
|
||||||
|
|
||||||
AVGCOLOR_PATH=/path/to/avgcolor.py
|
AVGCOLOR_PATH=/path/to/avgcolor.py
|
||||||
MTGAME_PATH=/path/to/minetest_game
|
GAME_PATH=/path/to/minetest_game
|
||||||
|
MODS_PATH= # path to "mods" folder, only set if you have loaded mods
|
||||||
NODESTXT_PATH=./nodes.txt
|
NODESTXT_PATH=./nodes.txt
|
||||||
COLORSTXT_PATH=./colors.txt
|
COLORSTXT_PATH=./colors.txt
|
||||||
|
|
||||||
while read -r line; do
|
while read -r line; do
|
||||||
set -- junk $line
|
set -- junk $line; shift
|
||||||
shift
|
|
||||||
if [[ -z "$1" || $1 == "#" ]]; then
|
if [[ -z "$1" || $1 == "#" ]]; then
|
||||||
echo $line
|
echo "$line"; continue
|
||||||
continue
|
|
||||||
fi
|
fi
|
||||||
tex=$(find $MTGAME_PATH -type f -name "$2")
|
tex=$(find $GAME_PATH -type f -name "$2")
|
||||||
|
[[ -z "$tex" && -n "$MODS_PATH" ]] && tex=$(find $MODS_PATH -type f -name "$2")
|
||||||
if [ -z "$tex" ]; then
|
if [ -z "$tex" ]; then
|
||||||
echo "skip $1: texture not found" >&2
|
echo "skip $1: texture not found" >&2
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
echo $1 $(python $AVGCOLOR_PATH "$tex")
|
echo "$1" $(python $AVGCOLOR_PATH "$tex")
|
||||||
echo "ok $1" >&2
|
echo "ok $1" >&2
|
||||||
done < $NODESTXT_PATH > $COLORSTXT_PATH
|
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 outputs the usage instructions
|
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
|
||||||
3) Create a world and load dumpnodes & all mods you want to generate colors for
|
3) Create a world and load dumpnodes & all mods you want to generate colors for
|
||||||
4) Execute /dumpnodes ingame
|
4) Execute /dumpnodes ingame
|
||||||
5) Run the script to generate colors.txt (make sure to replace /path/to/... with the actual paths)
|
5) Run the script to generate colors.txt (make sure to adjust the PATH variables at the top)
|
||||||
|
169
colors.txt
169
colors.txt
@ -7,49 +7,75 @@ beds:fancy_bed_top 172 112 103
|
|||||||
# bones
|
# bones
|
||||||
bones:bones 86 86 86
|
bones:bones 86 86 86
|
||||||
|
|
||||||
|
# carts
|
||||||
|
carts:brakerail 129 110 81
|
||||||
|
carts:powerrail 154 136 81
|
||||||
|
carts:rail 143 123 90
|
||||||
|
|
||||||
# default
|
# default
|
||||||
|
default:acacia_bush_leaves 90 124 55
|
||||||
|
default:acacia_bush_sapling 85 118 58
|
||||||
|
default:acacia_bush_stem 84 76 69
|
||||||
default:acacia_leaves 108 147 67
|
default:acacia_leaves 108 147 67
|
||||||
default:acacia_sapling 87 116 61
|
default:acacia_sapling 87 116 61
|
||||||
default:acacia_tree 188 109 90
|
default:acacia_tree 188 109 90
|
||||||
default:acacia_wood 146 60 37
|
default:acacia_wood 146 60 37
|
||||||
default:apple 145 20 9
|
default:apple 145 20 9
|
||||||
default:aspen_leaves 66 89 38
|
default:aspen_leaves 70 104 27
|
||||||
default:aspen_sapling 82 110 43
|
default:aspen_sapling 82 110 43
|
||||||
default:aspen_tree 218 197 166
|
default:aspen_tree 218 197 166
|
||||||
default:aspen_wood 209 198 169
|
default:aspen_wood 209 198 169
|
||||||
default:bookshelf 128 99 55
|
default:bookshelf 128 99 55
|
||||||
default:brick 117 71 69
|
default:brick 117 71 69
|
||||||
default:bronzeblock 185 110 15
|
default:bronzeblock 185 110 15
|
||||||
|
default:bush_leaves 34 52 29
|
||||||
|
default:bush_sapling 65 60 40
|
||||||
|
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_open 140 108 65
|
||||||
default:clay 182 182 182
|
default:clay 182 182 182
|
||||||
default:cloud 255 255 255
|
default:cloud 255 255 255
|
||||||
default:coalblock 57 57 57
|
default:coalblock 57 57 57
|
||||||
default:cobble 88 84 82
|
default:cobble 88 84 82
|
||||||
default:copperblock 192 126 63
|
default:copperblock 192 126 63
|
||||||
default:desert_cobble 146 95 76
|
default:coral_brown 139 104 72
|
||||||
|
default:coral_orange 191 62 12
|
||||||
|
default:coral_skeleton 235 230 214
|
||||||
|
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_block 192 151 94
|
||||||
|
default:desert_sandstone_brick 191 151 94
|
||||||
default:desert_stone 129 79 60
|
default:desert_stone 129 79 60
|
||||||
default:desert_stone_block 130 79 60
|
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
|
||||||
default:dirt_with_snow 223 224 236
|
default:dirt_with_rainforest_litter 74 38 8
|
||||||
|
default:dirt_with_snow 224 225 238
|
||||||
default:dry_grass_1 208 172 87
|
default:dry_grass_1 208 172 87
|
||||||
default:dry_grass_2 210 174 87
|
default:dry_grass_2 210 174 87
|
||||||
default:dry_grass_3 210 174 87
|
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
|
||||||
@ -71,7 +97,11 @@ 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:meselamp 211 213 139
|
default:meselamp 211 213 139
|
||||||
default:mossycobble 86 90 68
|
default:mossycobble 86 90 68
|
||||||
default:obsidian 19 21 24
|
default:obsidian 19 21 24
|
||||||
@ -79,14 +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:rail 143 123 90
|
|
||||||
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
|
||||||
@ -94,8 +124,11 @@ default:sapling 65 59 40
|
|||||||
default:sign_wall_steel 144 144 144
|
default:sign_wall_steel 144 144 144
|
||||||
default:sign_wall_wood 145 101 64
|
default:sign_wall_wood 145 101 64
|
||||||
default:silver_sand 193 191 179
|
default:silver_sand 193 191 179
|
||||||
default:snow 223 224 236
|
default:silver_sandstone 195 192 181
|
||||||
default:snowblock 223 224 236
|
default:silver_sandstone_block 192 190 179
|
||||||
|
default:silver_sandstone_brick 190 188 178
|
||||||
|
default:snow 224 225 238
|
||||||
|
default:snowblock 224 225 238
|
||||||
default:steelblock 194 194 194
|
default:steelblock 194 194 194
|
||||||
default:stone 97 94 93
|
default:stone 97 94 93
|
||||||
default:stone_block 99 96 95
|
default:stone_block 99 96 95
|
||||||
@ -105,8 +138,12 @@ default:stone_with_diamond 97 94 93
|
|||||||
default:stone_with_gold 97 94 93
|
default:stone_with_gold 97 94 93
|
||||||
default:stone_with_iron 97 94 93
|
default:stone_with_iron 97 94 93
|
||||||
default:stone_with_mese 97 94 93
|
default:stone_with_mese 97 94 93
|
||||||
|
default:stone_with_tin 97 94 93
|
||||||
default:stonebrick 99 96 95
|
default:stonebrick 99 96 95
|
||||||
|
default:tinblock 149 149 149
|
||||||
default:torch 120 98 67
|
default:torch 120 98 67
|
||||||
|
default:torch_ceiling 120 98 67
|
||||||
|
default:torch_wall 120 98 67
|
||||||
default:tree 164 131 88
|
default:tree 164 131 88
|
||||||
default:water_flowing 39 66 106 128 224
|
default:water_flowing 39 66 106 128 224
|
||||||
default:water_source 39 66 106 128 224
|
default:water_source 39 66 106 128 224
|
||||||
@ -131,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
|
||||||
|
|
||||||
@ -166,21 +202,22 @@ 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
|
||||||
|
|
||||||
# nyancat
|
|
||||||
nyancat:nyancat 198 111 167
|
|
||||||
nyancat:nyancat_rainbow 123 100 94
|
|
||||||
|
|
||||||
# stairs
|
# stairs
|
||||||
stairs:slab_acacia_wood 146 60 37
|
stairs:slab_acacia_wood 146 60 37
|
||||||
stairs:slab_aspen_wood 209 198 169
|
stairs:slab_aspen_wood 209 198 169
|
||||||
@ -188,11 +225,15 @@ 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_block 192 151 94
|
||||||
|
stairs:slab_desert_sandstone_brick 191 151 94
|
||||||
stairs:slab_desert_stone 129 79 60
|
stairs:slab_desert_stone 129 79 60
|
||||||
stairs:slab_desert_stone_block 130 79 60
|
stairs:slab_desert_stone_block 130 79 60
|
||||||
stairs:slab_desert_stonebrick 129 79 60
|
stairs:slab_desert_stonebrick 129 79 60
|
||||||
stairs:slab_goldblock 230 201 29
|
stairs:slab_goldblock 230 201 29
|
||||||
|
stairs:slab_ice 167 206 247
|
||||||
stairs:slab_junglewood 54 37 11
|
stairs:slab_junglewood 54 37 11
|
||||||
stairs:slab_mossycobble 86 90 68
|
stairs:slab_mossycobble 86 90 68
|
||||||
stairs:slab_obsidian 19 21 24
|
stairs:slab_obsidian 19 21 24
|
||||||
@ -202,11 +243,16 @@ stairs:slab_pine_wood 221 184 128
|
|||||||
stairs:slab_sandstone 197 193 143
|
stairs:slab_sandstone 197 193 143
|
||||||
stairs:slab_sandstone_block 195 190 141
|
stairs:slab_sandstone_block 195 190 141
|
||||||
stairs:slab_sandstonebrick 193 189 140
|
stairs:slab_sandstonebrick 193 189 140
|
||||||
|
stairs:slab_silver_sandstone 195 192 181
|
||||||
|
stairs:slab_silver_sandstone_block 192 190 179
|
||||||
|
stairs:slab_silver_sandstone_brick 190 188 178
|
||||||
|
stairs:slab_snowblock 224 225 238
|
||||||
stairs:slab_steelblock 194 194 194
|
stairs:slab_steelblock 194 194 194
|
||||||
stairs:slab_stone 97 94 93
|
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
|
||||||
@ -214,25 +260,104 @@ 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_block 192 151 94
|
||||||
|
stairs:stair_desert_sandstone_brick 191 151 94
|
||||||
stairs:stair_desert_stone 129 79 60
|
stairs:stair_desert_stone 129 79 60
|
||||||
stairs:stair_desert_stone_block 130 79 60
|
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_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
|
||||||
stairs:stair_sandstonebrick 193 189 140
|
stairs:stair_sandstonebrick 193 189 140
|
||||||
|
stairs:stair_silver_sandstone 195 192 181
|
||||||
|
stairs:stair_silver_sandstone_block 192 190 179
|
||||||
|
stairs:stair_silver_sandstone_brick 190 188 178
|
||||||
|
stairs:stair_snowblock 224 225 238
|
||||||
stairs:stair_steelblock 194 194 194
|
stairs:stair_steelblock 194 194 194
|
||||||
stairs:stair_stone 97 94 93
|
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
|
||||||
@ -249,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
|
||||||
@ -270,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
|
||||||
|
|
||||||
|
168
db-postgresql.cpp
Normal file
168
db-postgresql.cpp
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
#include <stdexcept>
|
||||||
|
#include <iostream>
|
||||||
|
#include <fstream>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include "db-postgresql.h"
|
||||||
|
#include "util.h"
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
|
#define ARRLEN(x) (sizeof(x) / sizeof((x)[0]))
|
||||||
|
|
||||||
|
DBPostgreSQL::DBPostgreSQL(const std::string &mapdir)
|
||||||
|
{
|
||||||
|
std::ifstream ifs((mapdir + "/world.mt").c_str());
|
||||||
|
if(!ifs.good())
|
||||||
|
throw std::runtime_error("Failed to read world.mt");
|
||||||
|
std::string const connect_string = get_setting("pgsql_connection", ifs);
|
||||||
|
ifs.close();
|
||||||
|
db = PQconnectdb(connect_string.c_str());
|
||||||
|
|
||||||
|
if (PQstatus(db) != CONNECTION_OK) {
|
||||||
|
throw std::runtime_error(std::string(
|
||||||
|
"PostgreSQL database error: ") +
|
||||||
|
PQerrorMessage(db)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
prepareStatement(
|
||||||
|
"get_block_pos",
|
||||||
|
"SELECT posX, posY, posZ FROM blocks"
|
||||||
|
);
|
||||||
|
prepareStatement(
|
||||||
|
"get_blocks_z",
|
||||||
|
"SELECT posX, posY, data FROM blocks WHERE posZ = $1::int4"
|
||||||
|
);
|
||||||
|
|
||||||
|
checkResults(PQexec(db, "START TRANSACTION;"));
|
||||||
|
checkResults(PQexec(db, "SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
DBPostgreSQL::~DBPostgreSQL()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
checkResults(PQexec(db, "COMMIT;"));
|
||||||
|
} catch (std::exception& caught) {
|
||||||
|
std::cerr << "could not finalize: " << caught.what() << std::endl;
|
||||||
|
}
|
||||||
|
PQfinish(db);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<BlockPos> DBPostgreSQL::getBlockPos()
|
||||||
|
{
|
||||||
|
std::vector<BlockPos> positions;
|
||||||
|
|
||||||
|
PGresult *results = execPrepared(
|
||||||
|
"get_block_pos", 0,
|
||||||
|
NULL, NULL, NULL, false, false
|
||||||
|
);
|
||||||
|
|
||||||
|
int numrows = PQntuples(results);
|
||||||
|
|
||||||
|
for (int row = 0; row < numrows; ++row)
|
||||||
|
positions.push_back(pg_to_blockpos(results, row, 0));
|
||||||
|
|
||||||
|
PQclear(results);
|
||||||
|
|
||||||
|
return positions;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void DBPostgreSQL::getBlocksOnZ(std::map<int16_t, BlockList> &blocks, int16_t zPos)
|
||||||
|
{
|
||||||
|
int32_t const z = htonl(zPos);
|
||||||
|
|
||||||
|
const void *args[] = { &z };
|
||||||
|
const int argLen[] = { sizeof(z) };
|
||||||
|
const int argFmt[] = { 1 };
|
||||||
|
|
||||||
|
PGresult *results = execPrepared(
|
||||||
|
"get_blocks_z", ARRLEN(args), args,
|
||||||
|
argLen, argFmt, false
|
||||||
|
);
|
||||||
|
|
||||||
|
int numrows = PQntuples(results);
|
||||||
|
|
||||||
|
for (int row = 0; row < numrows; ++row) {
|
||||||
|
BlockPos position;
|
||||||
|
position.x = pg_binary_to_int(results, row, 0);
|
||||||
|
position.y = pg_binary_to_int(results, row, 1);
|
||||||
|
position.z = zPos;
|
||||||
|
Block const b(
|
||||||
|
position,
|
||||||
|
ustring(
|
||||||
|
reinterpret_cast<unsigned char*>(
|
||||||
|
PQgetvalue(results, row, 2)
|
||||||
|
),
|
||||||
|
PQgetlength(results, row, 2)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
blocks[position.x].push_back(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
PQclear(results);
|
||||||
|
}
|
||||||
|
|
||||||
|
PGresult *DBPostgreSQL::checkResults(PGresult *res, bool clear)
|
||||||
|
{
|
||||||
|
ExecStatusType statusType = PQresultStatus(res);
|
||||||
|
|
||||||
|
switch (statusType) {
|
||||||
|
case PGRES_COMMAND_OK:
|
||||||
|
case PGRES_TUPLES_OK:
|
||||||
|
break;
|
||||||
|
case PGRES_FATAL_ERROR:
|
||||||
|
throw std::runtime_error(
|
||||||
|
std::string("PostgreSQL database error: ") +
|
||||||
|
PQresultErrorMessage(res)
|
||||||
|
);
|
||||||
|
default:
|
||||||
|
throw std::runtime_error(
|
||||||
|
"Unhandled PostgreSQL result code"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clear)
|
||||||
|
PQclear(res);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DBPostgreSQL::prepareStatement(const std::string &name, const std::string &sql)
|
||||||
|
{
|
||||||
|
checkResults(PQprepare(db, name.c_str(), sql.c_str(), 0, NULL));
|
||||||
|
}
|
||||||
|
|
||||||
|
PGresult *DBPostgreSQL::execPrepared(
|
||||||
|
const char *stmtName, const int paramsNumber,
|
||||||
|
const void **params,
|
||||||
|
const int *paramsLengths, const int *paramsFormats,
|
||||||
|
bool clear, bool nobinary
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return checkResults(PQexecPrepared(db, stmtName, paramsNumber,
|
||||||
|
(const char* const*) params, paramsLengths, paramsFormats,
|
||||||
|
nobinary ? 1 : 0), clear
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
int DBPostgreSQL::pg_to_int(PGresult *res, int row, int col)
|
||||||
|
{
|
||||||
|
return atoi(PQgetvalue(res, row, col));
|
||||||
|
}
|
||||||
|
|
||||||
|
int DBPostgreSQL::pg_binary_to_int(PGresult *res, int row, int col)
|
||||||
|
{
|
||||||
|
int32_t* raw = reinterpret_cast<int32_t*>(PQgetvalue(res, row, col));
|
||||||
|
return ntohl(*raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
BlockPos DBPostgreSQL::pg_to_blockpos(PGresult *res, int row, int col)
|
||||||
|
{
|
||||||
|
BlockPos result;
|
||||||
|
result.x = pg_to_int(res, row, col);
|
||||||
|
result.y = pg_to_int(res, row, col + 1);
|
||||||
|
result.z = pg_to_int(res, row, col + 2);
|
||||||
|
return result;
|
||||||
|
}
|
31
db-redis.cpp
31
db-redis.cpp
@ -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
35
include/BlockDecoder.h
Normal 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
|
@ -3,12 +3,14 @@
|
|||||||
|
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <FreeImage.h>
|
#include <gd.h>
|
||||||
|
|
||||||
struct Color {
|
struct Color {
|
||||||
Color() : r(0), g(0), b(0), a(0) {};
|
Color() : r(0), g(0), b(0), a(0) {};
|
||||||
Color(u8 r, u8 g, u8 b) : r(r), g(g), b(b), a(255) {};
|
Color(u8 r, u8 g, u8 b) : r(r), g(g), b(b), a(255) {};
|
||||||
Color(u8 r, u8 g, u8 b, u8 a) : r(r), g(g), b(b), a(a) {};
|
Color(u8 r, u8 g, u8 b, u8 a) : r(r), g(g), b(b), a(a) {};
|
||||||
|
inline Color noAlpha() const { return Color(r, g, b); }
|
||||||
|
|
||||||
u8 r, g, b, a;
|
u8 r, g, b, a;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -29,7 +31,7 @@ private:
|
|||||||
Image(const Image&);
|
Image(const Image&);
|
||||||
|
|
||||||
int m_width, m_height;
|
int m_width, m_height;
|
||||||
FIBITMAP *m_image;
|
gdImagePtr m_image;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // IMAGE_HEADER
|
#endif // IMAGE_HEADER
|
30
include/PlayerAttributes.h
Normal file
30
include/PlayerAttributes.h
Normal 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 */
|
||||||
|
|
@ -13,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"
|
||||||
@ -29,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];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -42,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
|
||||||
|
|
||||||
@ -79,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();
|
||||||
@ -87,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:
|
||||||
@ -120,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
|
@ -3,6 +3,7 @@
|
|||||||
#ifndef CMAKE_CONFIG_H
|
#ifndef CMAKE_CONFIG_H
|
||||||
#define CMAKE_CONFIG_H
|
#define CMAKE_CONFIG_H
|
||||||
|
|
||||||
|
#define USE_POSTGRESQL @USE_POSTGRESQL@
|
||||||
#define USE_LEVELDB @USE_LEVELDB@
|
#define USE_LEVELDB @USE_LEVELDB@
|
||||||
#define USE_REDIS @USE_REDIS@
|
#define USE_REDIS @USE_REDIS@
|
||||||
|
|
@ -9,6 +9,7 @@
|
|||||||
#ifdef USE_CMAKE_CONFIG_H
|
#ifdef USE_CMAKE_CONFIG_H
|
||||||
#include "cmake_config.h"
|
#include "cmake_config.h"
|
||||||
#else
|
#else
|
||||||
|
#define USE_POSTGRESQL 0
|
||||||
#define USE_LEVELDB 0
|
#define USE_LEVELDB 0
|
||||||
#define USE_REDIS 0
|
#define USE_REDIS 0
|
||||||
|
|
29
include/db-postgresql.h
Normal file
29
include/db-postgresql.h
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
#ifndef _DB_POSTGRESQL_H
|
||||||
|
#define _DB_POSTGRESQL_H
|
||||||
|
|
||||||
|
#include "db.h"
|
||||||
|
#include <libpq-fe.h>
|
||||||
|
|
||||||
|
class DBPostgreSQL : public DB {
|
||||||
|
public:
|
||||||
|
DBPostgreSQL(const std::string &mapdir);
|
||||||
|
virtual std::vector<BlockPos> getBlockPos();
|
||||||
|
virtual void getBlocksOnZ(std::map<int16_t, BlockList> &blocks, int16_t zPos);
|
||||||
|
virtual ~DBPostgreSQL();
|
||||||
|
protected:
|
||||||
|
PGresult *checkResults(PGresult *res, bool clear = true);
|
||||||
|
void prepareStatement(const std::string &name, const std::string &sql);
|
||||||
|
PGresult *execPrepared(
|
||||||
|
const char *stmtName, const int paramsNumber,
|
||||||
|
const void **params,
|
||||||
|
const int *paramsLengths = NULL, const int *paramsFormats = NULL,
|
||||||
|
bool clear = true, bool nobinary = true
|
||||||
|
);
|
||||||
|
int pg_to_int(PGresult *res, int row, int col);
|
||||||
|
int pg_binary_to_int(PGresult *res, int row, int col);
|
||||||
|
BlockPos pg_to_blockpos(PGresult *res, int row, int col);
|
||||||
|
private:
|
||||||
|
PGconn *db;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // _DB_POSTGRESQL_H
|
18
include/util.h
Normal file
18
include/util.h
Normal 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
|
91
minetestmapper.6
Normal file
91
minetestmapper.6
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
.TH MINETESTMAPPER 6
|
||||||
|
.SH NAME
|
||||||
|
minetestmapper \- generate an overview image of a Minetest map
|
||||||
|
.SH SYNOPSIS
|
||||||
|
.B minetestmapper
|
||||||
|
\fB\-i\fR \fIworld_path\fR
|
||||||
|
\fB\-o\fR \fIoutput_image\fR
|
||||||
|
.PP
|
||||||
|
See additional optional parameters below.
|
||||||
|
.SH DESCRIPTION
|
||||||
|
.B minetestmapper
|
||||||
|
generates an overview image of a minetest map. This is a port of
|
||||||
|
the original minetestmapper.py to C++, that is both faster and
|
||||||
|
provides more funtionality than the deprecated Python script.
|
||||||
|
.SH MANDATORY PARAMETERS
|
||||||
|
.TP
|
||||||
|
.BR \-i " " \fIworld_path\fR
|
||||||
|
Input world path.
|
||||||
|
.TP
|
||||||
|
.BR \-o " " \fIoutput_image\fR
|
||||||
|
Path to output image. (only PNG supported currently)
|
||||||
|
.SH OPTIONAL PARAMETERS
|
||||||
|
.TP
|
||||||
|
.BR \-\-bgcolor " " \fIcolor\fR
|
||||||
|
Background color of image, e.g. "--bgcolor #ffffff"
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BR \-\-scalecolor " " \fIcolor\fR
|
||||||
|
Color of scale, e.g. "--scalecolor #000000"
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BR \-\-playercolor " " \fIcolor\fR
|
||||||
|
Color of player indicators, e.g. "--playercolor #ff0000"
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BR \-\-origincolor " " \fIcolor\fR
|
||||||
|
Color of origin indicator, e.g. "--origincolor #ff0000"
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BR \-\-drawscale
|
||||||
|
Draw tick marks
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BR \-\-drawplayers
|
||||||
|
Draw player indicators
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BR \-\-draworigin
|
||||||
|
Draw origin indicator
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BR \-\-drawalpha
|
||||||
|
Allow nodes to be drawn with transparency
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BR \-\-noshading
|
||||||
|
Don't draw shading on nodes
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BR \-\-min-y " " \fInumber\fR
|
||||||
|
Don't draw nodes below this y value, e.g. "--min-y -25"
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BR \-\-max-y " " \fInumber\fR
|
||||||
|
Don't draw nodes above this y value, e.g. "--max-y 75"
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BR \-\-backend " " \fIbackend\fR
|
||||||
|
Use specific map backend; supported: *sqlite3*, *leveldb*, *redis*, *postgresql*, e.g. "--backend leveldb"
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BR \-\-geometry " " \fIgeometry\fR
|
||||||
|
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"
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BR \-\-zoom " " \fIfactor\fR
|
||||||
|
Zoom the image by using more than one pixel per node, e.g. "--zoom 4"
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BR \-\-colors " " \fIpath\fR
|
||||||
|
Forcefully set path to colors.txt file (it's autodetected otherwise), e.g. "--colors ../minetest/mycolors.txt"
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BR \-\-scales " " \fIedges\fR
|
||||||
|
Draw scales on specified image edges (letters *t b l r* meaning top, bottom, left and right), e.g. "--scales tbr"
|
||||||
|
|
||||||
|
.SH MORE INFORMATION
|
||||||
|
Website: https://github.com/minetest/minetestmapper
|
||||||
|
|
||||||
|
.SH MAN PAGE AUTHOR
|
||||||
|
Daniel Moerner
|
15
util.cpp
15
util.cpp
@ -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
10
util.h
@ -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
|
|
@ -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
|
||||||
|
Reference in New Issue
Block a user