New upstream snapshot.
Debian Janitor
2 years ago
0 | name: build | |
1 | ||
2 | # build on c/cpp changes or workflow changes | |
3 | on: | |
4 | push: | |
5 | paths: | |
6 | - '**.[ch]' | |
7 | - '**.cpp' | |
8 | - '**/CMakeLists.txt' | |
9 | - '.github/workflows/**.yml' | |
10 | pull_request: | |
11 | paths: | |
12 | - '**.[ch]' | |
13 | - '**.cpp' | |
14 | - '**/CMakeLists.txt' | |
15 | - '.github/workflows/**.yml' | |
16 | ||
17 | jobs: | |
18 | gcc: | |
19 | runs-on: ubuntu-20.04 | |
20 | steps: | |
21 | - uses: actions/checkout@v2 | |
22 | - name: Install deps | |
23 | run: | | |
24 | source util/ci/script.sh | |
25 | install_linux_deps | |
26 | ||
27 | - name: Build | |
28 | run: | | |
29 | source util/ci/script.sh | |
30 | run_build | |
31 | env: | |
32 | CC: gcc | |
33 | CXX: g++ | |
34 | ||
35 | - name: Test | |
36 | run: | | |
37 | source util/ci/script.sh | |
38 | do_functional_test | |
39 | ||
40 | clang: | |
41 | runs-on: ubuntu-20.04 | |
42 | steps: | |
43 | - uses: actions/checkout@v2 | |
44 | - name: Install deps | |
45 | run: | | |
46 | source util/ci/script.sh | |
47 | install_linux_deps | |
48 | ||
49 | - name: Build | |
50 | run: | | |
51 | source util/ci/script.sh | |
52 | run_build | |
53 | env: | |
54 | CC: clang | |
55 | CXX: clang++ | |
56 | ||
57 | - name: Test | |
58 | run: | | |
59 | source util/ci/script.sh | |
60 | do_functional_test |
0 | colors.txt | |
0 | *~ | |
1 | ||
1 | 2 | minetestmapper |
2 | 3 | minetestmapper.exe |
4 | colors.txt | |
5 | ||
3 | 6 | CMakeCache.txt |
4 | 7 | CMakeFiles/ |
5 | CPack* | |
8 | CPack*.cmake | |
9 | _CPack_Packages/ | |
10 | install_manifest.txt | |
6 | 11 | Makefile |
7 | 12 | cmake_install.cmake |
8 | 13 | cmake_config.h |
9 | *~ |
0 | language: cpp | |
1 | compiler: | |
2 | - gcc | |
3 | - clang | |
4 | dist: bionic | |
5 | addons: | |
6 | apt: | |
7 | packages: | |
8 | - cmake | |
9 | - libgd-dev | |
10 | - libsqlite3-dev | |
11 | - libleveldb-dev | |
12 | - libpq-dev | |
13 | - postgresql-server-dev-all | |
14 | script: ./util/travis/script.sh | |
15 | notifications: | |
16 | email: false | |
17 | matrix: | |
18 | fast_finish: true |
0 | #include <stdint.h> | |
1 | 0 | #include <string> |
2 | 1 | #include <iostream> |
3 | 2 | #include <sstream> |
10 | 9 | return data[0] << 8 | data[1]; |
11 | 10 | } |
12 | 11 | |
13 | static int readBlockContent(const unsigned char *mapData, u8 version, unsigned int datapos) | |
12 | static inline uint16_t readBlockContent(const unsigned char *mapData, | |
13 | u8 contentWidth, unsigned int datapos) | |
14 | 14 | { |
15 | if (version >= 24) { | |
15 | if (contentWidth == 2) { | |
16 | 16 | size_t index = datapos << 1; |
17 | 17 | return (mapData[index] << 8) | mapData[index + 1]; |
18 | } else if (version >= 20) { | |
19 | if (mapData[datapos] <= 0x80) | |
20 | return mapData[datapos]; | |
18 | } else { | |
19 | u8 param = mapData[datapos]; | |
20 | if (param <= 0x7f) | |
21 | return param; | |
21 | 22 | else |
22 | return (int(mapData[datapos]) << 4) | (int(mapData[datapos + 0x2000]) >> 4); | |
23 | return (param << 4) | (mapData[datapos + 0x2000] >> 4); | |
23 | 24 | } |
24 | std::ostringstream oss; | |
25 | oss << "Unsupported map version " << version; | |
26 | throw std::runtime_error(oss.str()); | |
27 | 25 | } |
28 | 26 | |
29 | 27 | BlockDecoder::BlockDecoder() |
38 | 36 | m_nameMap.clear(); |
39 | 37 | |
40 | 38 | m_version = 0; |
41 | m_mapData = ustring(); | |
39 | m_contentWidth = 0; | |
40 | m_mapData.clear(); | |
42 | 41 | } |
43 | 42 | |
44 | 43 | void BlockDecoder::decode(const ustring &datastr) |
48 | 47 | // TODO: bounds checks |
49 | 48 | |
50 | 49 | uint8_t version = data[0]; |
51 | //uint8_t flags = data[1]; | |
50 | if (version < 22) { | |
51 | std::ostringstream oss; | |
52 | oss << "Unsupported map version " << (int)version; | |
53 | throw std::runtime_error(oss.str()); | |
54 | } | |
52 | 55 | m_version = version; |
53 | 56 | |
57 | ustring datastr2; | |
58 | if (version >= 29) { | |
59 | // decompress whole block at once | |
60 | m_zstd_decompressor.setData(data, length, 1); | |
61 | datastr2 = m_zstd_decompressor.decompress(); | |
62 | data = datastr2.c_str(); | |
63 | length = datastr2.size(); | |
64 | } | |
65 | ||
54 | 66 | size_t dataOffset = 0; |
55 | if (version >= 27) | |
56 | dataOffset = 6; | |
57 | else if (version >= 22) | |
67 | if (version >= 29) | |
68 | dataOffset = 7; | |
69 | else if (version >= 27) | |
58 | 70 | dataOffset = 4; |
59 | 71 | else |
60 | 72 | dataOffset = 2; |
61 | 73 | |
74 | auto decode_mapping = [&] () { | |
75 | dataOffset++; // mapping version | |
76 | uint16_t numMappings = readU16(data + dataOffset); | |
77 | dataOffset += 2; | |
78 | for (int i = 0; i < numMappings; ++i) { | |
79 | uint16_t nodeId = readU16(data + dataOffset); | |
80 | dataOffset += 2; | |
81 | uint16_t nameLen = readU16(data + dataOffset); | |
82 | dataOffset += 2; | |
83 | std::string name(reinterpret_cast<const char *>(data) + dataOffset, nameLen); | |
84 | if (name == "air") | |
85 | m_blockAirId = nodeId; | |
86 | else if (name == "ignore") | |
87 | m_blockIgnoreId = nodeId; | |
88 | else | |
89 | m_nameMap[nodeId] = name; | |
90 | dataOffset += nameLen; | |
91 | } | |
92 | }; | |
93 | ||
94 | if (version >= 29) | |
95 | decode_mapping(); | |
96 | ||
97 | uint8_t contentWidth = data[dataOffset]; | |
98 | dataOffset++; | |
99 | uint8_t paramsWidth = data[dataOffset]; | |
100 | dataOffset++; | |
101 | if (contentWidth != 1 && contentWidth != 2) | |
102 | throw std::runtime_error("unsupported map version (contentWidth)"); | |
103 | if (paramsWidth != 2) | |
104 | throw std::runtime_error("unsupported map version (paramsWidth)"); | |
105 | m_contentWidth = contentWidth; | |
106 | ||
107 | if (version >= 29) { | |
108 | m_mapData.resize((contentWidth + paramsWidth) * 4096); | |
109 | m_mapData.assign(data + dataOffset, m_mapData.size()); | |
110 | return; // we have read everything we need and can return early | |
111 | } | |
112 | ||
113 | // version < 29 | |
62 | 114 | ZlibDecompressor decompressor(data, length); |
63 | 115 | decompressor.setSeekPos(dataOffset); |
64 | 116 | m_mapData = decompressor.decompress(); |
65 | 117 | decompressor.decompress(); // unused metadata |
66 | 118 | dataOffset = decompressor.seekPos(); |
67 | 119 | |
68 | // Skip unused data | |
69 | if (version <= 21) | |
70 | dataOffset += 2; | |
120 | // Skip unused node timers | |
71 | 121 | if (version == 23) |
72 | 122 | dataOffset += 1; |
73 | 123 | if (version == 24) { |
91 | 141 | dataOffset += 4; // Skip timestamp |
92 | 142 | |
93 | 143 | // Read mapping |
94 | if (version >= 22) { | |
95 | dataOffset++; // mapping version | |
96 | uint16_t numMappings = readU16(data + dataOffset); | |
97 | dataOffset += 2; | |
98 | for (int i = 0; i < numMappings; ++i) { | |
99 | uint16_t nodeId = readU16(data + dataOffset); | |
100 | dataOffset += 2; | |
101 | uint16_t nameLen = readU16(data + dataOffset); | |
102 | dataOffset += 2; | |
103 | std::string name(reinterpret_cast<const char *>(data) + dataOffset, nameLen); | |
104 | if (name == "air") | |
105 | m_blockAirId = nodeId; | |
106 | else if (name == "ignore") | |
107 | m_blockIgnoreId = nodeId; | |
108 | else | |
109 | m_nameMap[nodeId] = name; | |
110 | dataOffset += nameLen; | |
111 | } | |
112 | } | |
113 | ||
114 | // Node timers | |
115 | if (version >= 25) { | |
116 | dataOffset++; | |
117 | uint16_t numTimers = readU16(data + dataOffset); | |
118 | dataOffset += 2; | |
119 | dataOffset += numTimers * 10; | |
120 | } | |
144 | decode_mapping(); | |
121 | 145 | } |
122 | 146 | |
123 | 147 | bool BlockDecoder::isEmpty() const |
126 | 150 | return m_nameMap.empty(); |
127 | 151 | } |
128 | 152 | |
129 | std::string BlockDecoder::getNode(u8 x, u8 y, u8 z) const | |
153 | const static std::string empty; | |
154 | ||
155 | const std::string &BlockDecoder::getNode(u8 x, u8 y, u8 z) const | |
130 | 156 | { |
131 | 157 | unsigned int position = x + (y << 4) + (z << 8); |
132 | int content = readBlockContent(m_mapData.c_str(), m_version, position); | |
158 | uint16_t content = readBlockContent(m_mapData.c_str(), m_contentWidth, position); | |
133 | 159 | if (content == m_blockAirId || content == m_blockIgnoreId) |
134 | return ""; | |
160 | return empty; | |
135 | 161 | NameMap::const_iterator it = m_nameMap.find(content); |
136 | 162 | if (it == m_nameMap.end()) { |
137 | 163 | std::cerr << "Skipping node with invalid ID." << std::endl; |
138 | return ""; | |
164 | return empty; | |
139 | 165 | } |
140 | 166 | return it->second; |
141 | 167 | } |
0 | project(minetestmapper CXX) | |
1 | cmake_minimum_required(VERSION 2.6) | |
2 | cmake_policy(SET CMP0003 NEW) | |
3 | ||
4 | set(VERSION_MAJOR 1) | |
5 | set(VERSION_MINOR 0) | |
6 | set(VERSION_STRING "${VERSION_MAJOR}.${VERSION_MINOR}") | |
0 | cmake_minimum_required(VERSION 3.5) | |
1 | ||
2 | project(minetestmapper | |
3 | VERSION 1.0 | |
4 | LANGUAGES CXX | |
5 | ) | |
7 | 6 | |
8 | 7 | # Stuff & Paths |
9 | 8 | |
12 | 11 | endif() |
13 | 12 | |
14 | 13 | set(CMAKE_CXX_STANDARD 11) |
15 | set(CMAKE_CXX_FLAGS_RELEASE "-O3 -Wall -DNDEBUG") | |
16 | set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g2 -Wall") | |
14 | set(CMAKE_CXX_STANDARD_REQUIRED ON) | |
17 | 15 | |
18 | 16 | if(WIN32) |
19 | 17 | set(SHAREDIR ".") |
20 | 18 | set(BINDIR ".") |
21 | 19 | set(DOCDIR ".") |
22 | 20 | else() |
23 | set(SHAREDIR "${CMAKE_INSTALL_PREFIX}/share/minetest") # reuse Minetest share dir | |
24 | set(BINDIR "${CMAKE_INSTALL_PREFIX}/bin") | |
25 | set(DOCDIR "${CMAKE_INSTALL_PREFIX}/share/doc/${PROJECT_NAME}") | |
26 | set(MANDIR "${CMAKE_INSTALL_PREFIX}/share/man") | |
21 | set(SHAREDIR "share/minetest") # reuse Minetest share dir | |
22 | set(BINDIR "bin") | |
23 | set(DOCDIR "share/doc/${PROJECT_NAME}") | |
24 | set(MANDIR "share/man") | |
27 | 25 | endif() |
28 | 26 | |
29 | 27 | set(CUSTOM_SHAREDIR "" CACHE STRING "Directory to install data files into") |
44 | 42 | message(STATUS "Using DOCDIR=${DOCDIR}") |
45 | 43 | endif() |
46 | 44 | |
47 | #set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake) | |
48 | find_package(PkgConfig) | |
49 | include(FindPackageHandleStandardArgs) | |
45 | set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake) | |
50 | 46 | |
51 | 47 | # Libraries: gd |
52 | 48 | |
60 | 56 | |
61 | 57 | # Libraries: zlib |
62 | 58 | |
63 | find_library(ZLIB_LIBRARY z) | |
64 | find_path(ZLIB_INCLUDE_DIR zlib.h) | |
65 | message (STATUS "zlib library: ${ZLIB_LIBRARY}") | |
66 | message (STATUS "zlib headers: ${ZLIB_INCLUDE_DIR}") | |
67 | if(NOT ZLIB_LIBRARY OR NOT ZLIB_INCLUDE_DIR) | |
68 | message(FATAL_ERROR "zlib not found!") | |
69 | endif(NOT ZLIB_LIBRARY OR NOT ZLIB_INCLUDE_DIR) | |
59 | find_package(ZLIB REQUIRED) | |
60 | ||
61 | # Libraries: zstd | |
62 | ||
63 | find_package(Zstd REQUIRED) | |
70 | 64 | |
71 | 65 | # Libraries: sqlite3 |
72 | 66 | |
73 | 67 | find_library(SQLITE3_LIBRARY sqlite3) |
74 | find_path(SQLITE3_INCLUDE_DIR zlib.h) | |
68 | find_path(SQLITE3_INCLUDE_DIR sqlite3.h) | |
75 | 69 | message (STATUS "sqlite3 library: ${SQLITE3_LIBRARY}") |
76 | 70 | message (STATUS "sqlite3 headers: ${SQLITE3_INCLUDE_DIR}") |
77 | 71 | if(NOT SQLITE3_LIBRARY OR NOT SQLITE3_INCLUDE_DIR) |
84 | 78 | set(USE_POSTGRESQL FALSE) |
85 | 79 | |
86 | 80 | if(ENABLE_POSTGRESQL) |
87 | find_package("PostgreSQL") | |
81 | if(CMAKE_VERSION VERSION_LESS "3.20") | |
82 | find_package(PostgreSQL QUIET) | |
83 | # Before CMake 3.20 FindPostgreSQL.cmake always looked for server includes | |
84 | # but we don't need them, so continue anyway if only those are missing. | |
85 | if(PostgreSQL_INCLUDE_DIR AND PostgreSQL_LIBRARY) | |
86 | set(PostgreSQL_FOUND TRUE) | |
87 | set(PostgreSQL_INCLUDE_DIRS ${PostgreSQL_INCLUDE_DIR}) | |
88 | endif() | |
89 | else() | |
90 | find_package(PostgreSQL) | |
91 | endif() | |
88 | 92 | |
89 | 93 | if(PostgreSQL_FOUND) |
90 | 94 | set(USE_POSTGRESQL TRUE) |
147 | 151 | ${SQLITE3_INCLUDE_DIR} |
148 | 152 | ${LIBGD_INCLUDE_DIR} |
149 | 153 | ${ZLIB_INCLUDE_DIR} |
154 | ${ZSTD_INCLUDE_DIR} | |
150 | 155 | ) |
151 | 156 | |
152 | 157 | configure_file( |
153 | 158 | "${PROJECT_SOURCE_DIR}/include/cmake_config.h.in" |
154 | 159 | "${PROJECT_BINARY_DIR}/cmake_config.h" |
155 | 160 | ) |
156 | add_definitions ( -DUSE_CMAKE_CONFIG_H ) | |
157 | ||
158 | set(mapper_SRCS | |
161 | add_definitions(-DUSE_CMAKE_CONFIG_H) | |
162 | ||
163 | if(CMAKE_CXX_COMPILER_ID MATCHES "^(GNU|Clang|AppleClang)$") | |
164 | set(CMAKE_CXX_FLAGS_RELEASE "-O2") | |
165 | set(CMAKE_CXX_FLAGS_DEBUG "-Og -g2") | |
166 | add_compile_options(-Wall -pipe) | |
167 | endif() | |
168 | if(CMAKE_BUILD_TYPE STREQUAL "Release") | |
169 | add_definitions(-DNDEBUG) | |
170 | endif() | |
171 | ||
172 | add_executable(minetestmapper | |
159 | 173 | BlockDecoder.cpp |
160 | 174 | PixelAttributes.cpp |
161 | 175 | PlayerAttributes.cpp |
162 | 176 | TileGenerator.cpp |
163 | 177 | ZlibDecompressor.cpp |
178 | ZstdDecompressor.cpp | |
164 | 179 | Image.cpp |
165 | 180 | mapper.cpp |
166 | 181 | util.cpp |
167 | 182 | db-sqlite3.cpp |
168 | ) | |
169 | ||
170 | if(USE_POSTGRESQL) | |
171 | set(mapper_SRCS ${mapper_SRCS} db-postgresql.cpp) | |
172 | endif(USE_POSTGRESQL) | |
173 | ||
174 | if(USE_LEVELDB) | |
175 | set(mapper_SRCS ${mapper_SRCS} db-leveldb.cpp) | |
176 | endif(USE_LEVELDB) | |
177 | ||
178 | if(USE_REDIS) | |
179 | set(mapper_SRCS ${mapper_SRCS} db-redis.cpp) | |
180 | endif(USE_REDIS) | |
181 | ||
182 | add_executable(minetestmapper | |
183 | ${mapper_SRCS} | |
183 | $<$<BOOL:${USE_POSTGRESQL}>:db-postgresql.cpp> | |
184 | $<$<BOOL:${USE_LEVELDB}>:db-leveldb.cpp> | |
185 | $<$<BOOL:${USE_REDIS}>:db-redis.cpp> | |
184 | 186 | ) |
185 | 187 | |
186 | 188 | target_link_libraries( |
191 | 193 | ${REDIS_LIBRARY} |
192 | 194 | ${LIBGD_LIBRARY} |
193 | 195 | ${ZLIB_LIBRARY} |
196 | ${ZSTD_LIBRARY} | |
194 | 197 | ) |
195 | 198 | |
196 | 199 | # Installing & Packaging |
205 | 208 | endif() |
206 | 209 | |
207 | 210 | set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Overview mapper for Minetest") |
208 | set(CPACK_PACKAGE_VERSION_MAJOR ${VERSION_MAJOR}) | |
209 | set(CPACK_PACKAGE_VERSION_MINOR ${VERSION_MINOR}) | |
210 | 211 | set(CPACK_PACKAGE_VENDOR "celeron55") |
211 | 212 | set(CPACK_PACKAGE_CONTACT "Perttu Ahola <celeron55@gmail.com>") |
212 | 213 | |
213 | 214 | if(WIN32) |
214 | set(CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}-${VERSION_STRING}-win32") | |
215 | set(CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}-${PROJECT_VERSION}-win32") | |
215 | 216 | set(CPACK_GENERATOR ZIP) |
216 | 217 | else() |
217 | set(CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}-${VERSION_STRING}-linux") | |
218 | set(CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}-${PROJECT_VERSION}-linux") | |
218 | 219 | set(CPACK_GENERATOR TGZ) |
219 | 220 | set(CPACK_SOURCE_GENERATOR TGZ) |
220 | 221 | endif() |
16 | 16 | |
17 | 17 | // ARGB but with inverted alpha |
18 | 18 | |
19 | static inline int color2int(Color c) | |
19 | static inline int color2int(const Color &c) | |
20 | 20 | { |
21 | 21 | u8 a = (255 - c.a) * gdAlphaMax / 255; |
22 | 22 | return (a << 24) | (c.r << 16) | (c.g << 8) | c.b; |
34 | 34 | return c2; |
35 | 35 | } |
36 | 36 | |
37 | #ifndef NDEBUG | |
37 | 38 | static inline void check_bounds(int x, int y, int width, int height) |
38 | 39 | { |
39 | 40 | if(x < 0 || x >= width) { |
49 | 50 | throw std::out_of_range(oss.str()); |
50 | 51 | } |
51 | 52 | } |
53 | #endif | |
52 | 54 | |
53 | 55 | |
54 | 56 | Image::Image(int width, int height) : |
55 | 57 | m_width(width), m_height(height), m_image(NULL) |
56 | 58 | { |
59 | SIZECHECK(0, 0); | |
57 | 60 | m_image = gdImageCreateTrueColor(m_width, m_height); |
58 | 61 | } |
59 | 62 |
0 | /* | |
1 | * ===================================================================== | |
2 | * Version: 1.0 | |
3 | * Created: 25.08.2012 10:55:27 | |
4 | * Author: Miroslav BendÃk | |
5 | * Company: LinuxOS.sk | |
6 | * ===================================================================== | |
7 | */ | |
0 | #include <cstring> | |
8 | 1 | |
9 | 2 | #include "PixelAttributes.h" |
10 | #include <cstring> | |
11 | 3 | |
12 | 4 | PixelAttributes::PixelAttributes(): |
13 | 5 | m_width(0) |
8 | 8 | #include "PlayerAttributes.h" |
9 | 9 | #include "util.h" |
10 | 10 | |
11 | using namespace std; | |
12 | ||
13 | 11 | PlayerAttributes::PlayerAttributes(const std::string &worldDir) |
14 | 12 | { |
15 | std::ifstream ifs((worldDir + "world.mt").c_str()); | |
13 | std::ifstream ifs(worldDir + "world.mt"); | |
16 | 14 | if (!ifs.good()) |
17 | 15 | throw std::runtime_error("Failed to read world.mt"); |
18 | 16 | std::string backend = read_setting_default("player_backend", ifs, "files"); |
38 | 36 | if (ent->d_name[0] == '.') |
39 | 37 | continue; |
40 | 38 | |
41 | string path = playersPath + PATH_SEPARATOR + ent->d_name; | |
42 | ifstream in(path.c_str()); | |
39 | std::string path = playersPath + PATH_SEPARATOR + ent->d_name; | |
40 | std::ifstream in(path); | |
43 | 41 | if(!in.good()) |
44 | 42 | continue; |
45 | 43 | |
46 | string name, position; | |
44 | std::string name, position; | |
47 | 45 | name = read_setting("name", in); |
48 | 46 | in.seekg(0); |
49 | 47 | position = read_setting("position", in); |
50 | 48 | |
51 | 49 | Player player; |
52 | istringstream iss(position); | |
50 | std::istringstream iss(position); | |
53 | 51 | char tmp; |
54 | 52 | iss >> tmp; // '(' |
55 | 53 | iss >> player.x; |
120 | 118 | |
121 | 119 | /**********/ |
122 | 120 | |
123 | PlayerAttributes::Players::iterator PlayerAttributes::begin() | |
121 | PlayerAttributes::Players::const_iterator PlayerAttributes::begin() const | |
124 | 122 | { |
125 | return m_players.begin(); | |
123 | return m_players.cbegin(); | |
126 | 124 | } |
127 | 125 | |
128 | PlayerAttributes::Players::iterator PlayerAttributes::end() | |
126 | PlayerAttributes::Players::const_iterator PlayerAttributes::end() const | |
129 | 127 | { |
130 | return m_players.end(); | |
128 | return m_players.cend(); | |
131 | 129 | } |
132 | 130 |
0 | 0 | Minetest Mapper C++ |
1 | 1 | =================== |
2 | 2 | |
3 | .. image:: https://travis-ci.org/minetest/minetestmapper.svg?branch=master | |
4 | :target: https://travis-ci.org/minetest/minetestmapper | |
3 | .. image:: https://github.com/minetest/minetestmapper/workflows/build/badge.svg | |
4 | :target: https://github.com/minetest/minetestmapper/actions/workflows/build.yml | |
5 | 5 | |
6 | A port of minetestmapper.py to C++ from https://github.com/minetest/minetest/tree/master/util. | |
7 | This version is both faster and provides more features than the now deprecated Python script. | |
6 | Minetestmapper generates an overview image from a Minetest map. | |
7 | ||
8 | A port of minetestmapper.py to C++ from https://github.com/minetest/minetest/tree/0.4.17/util. | |
9 | This version is both faster and provides more features than the now obsolete Python script. | |
10 | ||
11 | Minetestmapper ships with a colors.txt file for Minetest Game, if you use a different game or have | |
12 | many mods installed you should generate a matching colors.txt for better results. | |
13 | The `generate_colorstxt.py script | |
14 | <./util/generate_colorstxt.py>`_ in the util folder exists for this purpose, detailed instructions can be found within. | |
8 | 15 | |
9 | 16 | Requirements |
10 | 17 | ------------ |
11 | 18 | |
12 | 19 | * libgd |
13 | 20 | * sqlite3 |
14 | * LevelDB (optional, set ENABLE_LEVELDB=1 in CMake to enable) | |
15 | * hiredis library (optional, set ENABLE_REDIS=1 in CMake to enable) | |
16 | * Postgres libraries (optional, set ENABLE_POSTGRES=1 in CMake to enable) | |
21 | * LevelDB (optional) | |
22 | * hiredis (optional) | |
23 | * Postgres libraries (optional) | |
17 | 24 | |
18 | e.g. on Debian: | |
19 | ^^^^^^^^^^^^^^^ | |
25 | on Debian/Ubuntu: | |
26 | ^^^^^^^^^^^^^^^^^ | |
20 | 27 | |
21 | sudo apt-get install libgd-dev libsqlite3-dev libleveldb-dev libhiredis-dev libpq-dev | |
28 | ``sudo apt install libgd-dev libsqlite3-dev libleveldb-dev libhiredis-dev libpq-dev`` | |
22 | 29 | |
23 | Windows | |
24 | ^^^^^^^ | |
25 | Minetestmapper for Windows can be downloaded here: https://github.com/minetest/minetestmapper/releases | |
30 | on openSUSE: | |
31 | ^^^^^^^^^^^^ | |
26 | 32 | |
27 | After extracting the archive, minetestmapper can be invoked from cmd.exe: | |
33 | ``sudo zypper install gd-devel sqlite3-devel leveldb-devel hiredis-devel postgresql-devel`` | |
34 | ||
35 | for Windows: | |
36 | ^^^^^^^^^^^^ | |
37 | Minetestmapper for Windows can be downloaded `from the Releases section | |
38 | <https://github.com/minetest/minetestmapper/releases>`_. | |
39 | ||
40 | After extracting the archive, it can be invoked from cmd.exe: | |
28 | 41 | :: |
29 | 42 | |
30 | 43 | cd C:\Users\yourname\Desktop\example\path |
36 | 49 | :: |
37 | 50 | |
38 | 51 | cmake . -DENABLE_LEVELDB=1 |
39 | make -j2 | |
52 | make -j$(nproc) | |
40 | 53 | |
41 | 54 | Usage |
42 | 55 | ----- |
12 | 12 | #include "config.h" |
13 | 13 | #include "PlayerAttributes.h" |
14 | 14 | #include "BlockDecoder.h" |
15 | #include "Image.h" | |
15 | 16 | #include "util.h" |
17 | ||
16 | 18 | #include "db-sqlite3.h" |
17 | 19 | #if USE_POSTGRESQL |
18 | 20 | #include "db-postgresql.h" |
23 | 25 | #if USE_REDIS |
24 | 26 | #include "db-redis.h" |
25 | 27 | #endif |
26 | ||
27 | using namespace std; | |
28 | 28 | |
29 | 29 | template<typename T> |
30 | 30 | static inline T mymax(T a, T b) |
53 | 53 | static inline unsigned int colorSafeBounds (int channel) |
54 | 54 | { |
55 | 55 | return mymin(mymax(channel, 0), 255); |
56 | } | |
57 | ||
58 | static Color parseColor(const std::string &color) | |
59 | { | |
60 | if (color.length() != 7) | |
61 | throw std::runtime_error("Color needs to be 7 characters long"); | |
62 | if (color[0] != '#') | |
63 | throw std::runtime_error("Color needs to begin with #"); | |
64 | unsigned long col = strtoul(color.c_str() + 1, NULL, 16); | |
65 | u8 b, g, r; | |
66 | b = col & 0xff; | |
67 | g = (col >> 8) & 0xff; | |
68 | r = (col >> 16) & 0xff; | |
69 | return Color(r, g, b); | |
56 | 70 | } |
57 | 71 | |
58 | 72 | static Color mixColors(Color a, Color b) |
97 | 111 | m_geomY2(2048), |
98 | 112 | m_exhaustiveSearch(EXH_AUTO), |
99 | 113 | m_zoom(1), |
100 | m_scales(SCALE_LEFT | SCALE_TOP) | |
114 | m_scales(SCALE_LEFT | SCALE_TOP), | |
115 | m_progressMax(0), | |
116 | m_progressLast(-1) | |
101 | 117 | { |
102 | 118 | } |
103 | 119 | |
136 | 152 | void TileGenerator::setScales(uint flags) |
137 | 153 | { |
138 | 154 | m_scales = flags; |
139 | } | |
140 | ||
141 | Color TileGenerator::parseColor(const std::string &color) | |
142 | { | |
143 | Color parsed; | |
144 | if (color.length() != 7) | |
145 | throw std::runtime_error("Color needs to be 7 characters long"); | |
146 | if (color[0] != '#') | |
147 | throw std::runtime_error("Color needs to begin with #"); | |
148 | unsigned long col = strtoul(color.c_str() + 1, NULL, 16); | |
149 | parsed.b = col & 0xff; | |
150 | parsed.g = (col >> 8) & 0xff; | |
151 | parsed.r = (col >> 16) & 0xff; | |
152 | parsed.a = 255; | |
153 | return parsed; | |
154 | 155 | } |
155 | 156 | |
156 | 157 | void TileGenerator::setDrawOrigin(bool drawOrigin) |
213 | 214 | |
214 | 215 | void TileGenerator::parseColorsFile(const std::string &fileName) |
215 | 216 | { |
216 | ifstream in; | |
217 | in.open(fileName.c_str(), ifstream::in); | |
218 | if (!in.is_open()) | |
217 | std::ifstream in(fileName); | |
218 | if (!in.good()) | |
219 | 219 | throw std::runtime_error("Specified colors file could not be found"); |
220 | 220 | parseColorsStream(in); |
221 | 221 | } |
222 | 222 | |
223 | 223 | void TileGenerator::printGeometry(const std::string &input) |
224 | 224 | { |
225 | string input_path = input; | |
225 | std::string input_path = input; | |
226 | 226 | if (input_path[input.length() - 1] != PATH_SEPARATOR) { |
227 | 227 | input_path += PATH_SEPARATOR; |
228 | 228 | } |
248 | 248 | |
249 | 249 | void TileGenerator::generate(const std::string &input, const std::string &output) |
250 | 250 | { |
251 | string input_path = input; | |
251 | std::string input_path = input; | |
252 | 252 | if (input_path[input.length() - 1] != PATH_SEPARATOR) { |
253 | 253 | input_path += PATH_SEPARATOR; |
254 | 254 | } |
304 | 304 | std::cerr << "Failed to parse color entry '" << line << "'" << std::endl; |
305 | 305 | continue; |
306 | 306 | } |
307 | ||
308 | ColorEntry color(r, g, b, a, t); | |
309 | m_colorMap[name] = color; | |
307 | ||
308 | m_colorMap[name] = ColorEntry(r, g, b, a, t); | |
310 | 309 | } |
311 | 310 | } |
312 | 311 | |
329 | 328 | void TileGenerator::openDb(const std::string &input) |
330 | 329 | { |
331 | 330 | std::string backend = m_backend; |
332 | if(backend == "") { | |
333 | std::ifstream ifs((input + "/world.mt").c_str()); | |
331 | if (backend == "") { | |
332 | std::ifstream ifs(input + "/world.mt"); | |
334 | 333 | if(!ifs.good()) |
335 | throw std::runtime_error("Failed to read world.mt"); | |
336 | backend = read_setting("backend", ifs); | |
334 | throw std::runtime_error("Failed to open world.mt"); | |
335 | backend = read_setting_default("backend", ifs, "sqlite3"); | |
337 | 336 | ifs.close(); |
338 | 337 | } |
339 | 338 | |
356 | 355 | |
357 | 356 | // Determine how we're going to traverse the database (heuristic) |
358 | 357 | if (m_exhaustiveSearch == EXH_AUTO) { |
359 | using u64 = uint64_t; | |
360 | u64 y_range = (m_yMax / 16 + 1) - (m_yMin / 16); | |
361 | u64 blocks = (u64)(m_geomX2 - m_geomX) * y_range * (u64)(m_geomY2 - m_geomY); | |
358 | size_t y_range = (m_yMax / 16 + 1) - (m_yMin / 16); | |
359 | size_t blocks = (m_geomX2 - m_geomX) * y_range * (m_geomY2 - m_geomY); | |
362 | 360 | #ifndef NDEBUG |
363 | std::cout << "Heuristic parameters:" | |
361 | std::cerr << "Heuristic parameters:" | |
364 | 362 | << " preferRangeQueries()=" << m_db->preferRangeQueries() |
365 | 363 | << " y_range=" << y_range << " blocks=" << blocks << std::endl; |
366 | 364 | #endif |
417 | 415 | m_positions[pos.z].emplace(pos.x); |
418 | 416 | } |
419 | 417 | |
420 | #ifndef NDEBUG | |
421 | 418 | int count = 0; |
422 | 419 | for (const auto &it : m_positions) |
423 | 420 | count += it.second.size(); |
424 | std::cout << "Loaded " << count | |
421 | m_progressMax = count; | |
422 | #ifndef NDEBUG | |
423 | std::cerr << "Loaded " << count | |
425 | 424 | << " positions (across Z: " << m_positions.size() << ") for rendering" << std::endl; |
426 | 425 | #endif |
427 | 426 | } |
475 | 474 | { |
476 | 475 | BlockDecoder blk; |
477 | 476 | const int16_t yMax = m_yMax / 16 + 1; |
477 | size_t count = 0; | |
478 | 478 | |
479 | 479 | auto renderSingle = [&] (int16_t xPos, int16_t zPos, BlockList &blockStack) { |
480 | 480 | m_readPixels.reset(); |
521 | 521 | blockStack.sort(); |
522 | 522 | |
523 | 523 | renderSingle(xPos, zPos, blockStack); |
524 | reportProgress(count++); | |
524 | 525 | } |
525 | 526 | postRenderRow(zPos); |
526 | 527 | } |
545 | 546 | blockStack.sort(); |
546 | 547 | |
547 | 548 | renderSingle(xPos, zPos, blockStack); |
549 | reportProgress(count++); | |
548 | 550 | } |
549 | 551 | postRenderRow(zPos); |
550 | 552 | } |
551 | 553 | } else if (m_exhaustiveSearch == EXH_FULL) { |
554 | const size_t span_y = yMax - (m_yMin / 16); | |
555 | m_progressMax = (m_geomX2 - m_geomX) * span_y * (m_geomY2 - m_geomY); | |
552 | 556 | #ifndef NDEBUG |
553 | 557 | std::cerr << "Exhaustively searching " |
554 | << (m_geomX2 - m_geomX) << "x" << (yMax - (m_yMin / 16)) << "x" | |
558 | << (m_geomX2 - m_geomX) << "x" << span_y << "x" | |
555 | 559 | << (m_geomY2 - m_geomY) << " blocks" << std::endl; |
556 | 560 | #endif |
561 | ||
557 | 562 | std::vector<BlockPos> positions; |
558 | positions.reserve(yMax - (m_yMin / 16)); | |
563 | positions.reserve(span_y); | |
559 | 564 | for (int16_t zPos = m_geomY2 - 1; zPos >= m_geomY; zPos--) { |
560 | 565 | for (int16_t xPos = m_geomX2 - 1; xPos >= m_geomX; xPos--) { |
561 | 566 | positions.clear(); |
567 | 572 | blockStack.sort(); |
568 | 573 | |
569 | 574 | renderSingle(xPos, zPos, blockStack); |
575 | reportProgress(count++); | |
570 | 576 | } |
571 | 577 | postRenderRow(zPos); |
572 | 578 | } |
573 | 579 | } |
580 | ||
581 | reportProgress(m_progressMax); | |
574 | 582 | } |
575 | 583 | |
576 | 584 | void TileGenerator::renderMapBlock(const BlockDecoder &blk, const BlockPos &pos) |
585 | 593 | if (m_readPixels.get(x, z)) |
586 | 594 | continue; |
587 | 595 | int imageX = xBegin + x; |
596 | auto &attr = m_blockPixelAttributes.attribute(15 - z, xBegin + x); | |
588 | 597 | |
589 | 598 | for (int y = maxY; y >= minY; --y) { |
590 | string name = blk.getNode(x, y, z); | |
591 | if (name == "") | |
599 | const std::string &name = blk.getNode(x, y, z); | |
600 | if (name.empty()) | |
592 | 601 | continue; |
593 | 602 | ColorMap::const_iterator it = m_colorMap.find(name); |
594 | 603 | if (it == m_colorMap.end()) { |
595 | 604 | m_unknownNodes.insert(name); |
596 | 605 | continue; |
597 | 606 | } |
598 | const Color c = it->second.to_color(); | |
607 | ||
608 | Color c = it->second.toColor(); | |
609 | if (c.a == 0) | |
610 | continue; // node is fully invisible | |
599 | 611 | if (m_drawAlpha) { |
600 | if (m_color[z][x].a == 0) | |
601 | m_color[z][x] = c; // first visible time, no color mixing | |
602 | else | |
603 | m_color[z][x] = mixColors(m_color[z][x], c); | |
604 | if(m_color[z][x].a < 0xff) { | |
605 | // near thickness value to thickness of current node | |
606 | m_thickness[z][x] = (m_thickness[z][x] + it->second.t) / 2.0; | |
612 | if (m_color[z][x].a != 0) | |
613 | c = mixColors(m_color[z][x], c); | |
614 | if (c.a < 255) { | |
615 | // remember color and near thickness value | |
616 | m_color[z][x] = c; | |
617 | m_thickness[z][x] = (m_thickness[z][x] + it->second.t) / 2; | |
607 | 618 | continue; |
608 | 619 | } |
609 | 620 | // color became opaque, draw it |
610 | setZoomed(imageX, imageY, m_color[z][x]); | |
611 | m_blockPixelAttributes.attribute(15 - z, xBegin + x).thickness = m_thickness[z][x]; | |
621 | setZoomed(imageX, imageY, c); | |
622 | attr.thickness = m_thickness[z][x]; | |
612 | 623 | } else { |
613 | setZoomed(imageX, imageY, c.noAlpha()); | |
624 | c.a = 255; | |
625 | setZoomed(imageX, imageY, c); | |
614 | 626 | } |
615 | 627 | m_readPixels.set(x, z); |
616 | 628 | |
617 | 629 | // do this afterwards so we can record height values |
618 | 630 | // inside transparent nodes (water) too |
619 | 631 | if (!m_readInfo.get(x, z)) { |
620 | m_blockPixelAttributes.attribute(15 - z, xBegin + x).height = pos.y * 16 + y; | |
632 | attr.height = pos.y * 16 + y; | |
621 | 633 | m_readInfo.set(x, z); |
622 | 634 | } |
623 | 635 | break; |
639 | 651 | if (m_readPixels.get(x, z)) |
640 | 652 | continue; |
641 | 653 | int imageX = xBegin + x; |
654 | auto &attr = m_blockPixelAttributes.attribute(15 - z, xBegin + x); | |
642 | 655 | |
643 | 656 | // set color since it wasn't done in renderMapBlock() |
644 | 657 | setZoomed(imageX, imageY, m_color[z][x]); |
645 | 658 | m_readPixels.set(x, z); |
646 | m_blockPixelAttributes.attribute(15 - z, xBegin + x).thickness = m_thickness[z][x]; | |
659 | attr.thickness = m_thickness[z][x]; | |
647 | 660 | } |
648 | 661 | } |
649 | 662 | } |
650 | 663 | |
651 | 664 | void TileGenerator::renderShading(int zPos) |
652 | 665 | { |
666 | auto &a = m_blockPixelAttributes; | |
653 | 667 | int zBegin = (m_zMax - zPos) * 16; |
654 | 668 | for (int z = 0; z < 16; ++z) { |
655 | 669 | int imageY = zBegin + z; |
657 | 671 | continue; |
658 | 672 | for (int x = 0; x < m_mapWidth; ++x) { |
659 | 673 | if( |
660 | !m_blockPixelAttributes.attribute(z, x).valid_height() || | |
661 | !m_blockPixelAttributes.attribute(z, x - 1).valid_height() || | |
662 | !m_blockPixelAttributes.attribute(z - 1, x).valid_height() | |
674 | !a.attribute(z, x).valid_height() || | |
675 | !a.attribute(z, x - 1).valid_height() || | |
676 | !a.attribute(z - 1, x).valid_height() | |
663 | 677 | ) |
664 | 678 | continue; |
665 | 679 | |
666 | 680 | // calculate shadow to apply |
667 | int y = m_blockPixelAttributes.attribute(z, x).height; | |
668 | int y1 = m_blockPixelAttributes.attribute(z, x - 1).height; | |
669 | int y2 = m_blockPixelAttributes.attribute(z - 1, x).height; | |
681 | int y = a.attribute(z, x).height; | |
682 | int y1 = a.attribute(z, x - 1).height; | |
683 | int y2 = a.attribute(z - 1, x).height; | |
670 | 684 | int d = ((y - y1) + (y - y2)) * 12; |
685 | ||
671 | 686 | if (m_drawAlpha) { // less visible shadow with increasing "thickness" |
672 | double t = m_blockPixelAttributes.attribute(z, x).thickness * 1.2; | |
673 | d *= 1.0 - mymin(t, 255.0) / 255.0; | |
674 | } | |
687 | float t = a.attribute(z, x).thickness * 1.2f; | |
688 | t = mymin(t, 255.0f); | |
689 | d *= 1.0f - t / 255.0f; | |
690 | } | |
691 | ||
675 | 692 | d = mymin(d, 36); |
676 | 693 | |
694 | // apply shadow/light by just adding to it pixel values | |
677 | 695 | Color c = m_image->getPixel(getImageX(x), getImageY(imageY)); |
678 | 696 | c.r = colorSafeBounds(c.r + d); |
679 | 697 | c.g = colorSafeBounds(c.g + d); |
681 | 699 | setZoomed(x, imageY, c); |
682 | 700 | } |
683 | 701 | } |
684 | m_blockPixelAttributes.scroll(); | |
702 | a.scroll(); | |
685 | 703 | } |
686 | 704 | |
687 | 705 | void TileGenerator::renderScale() |
793 | 811 | std::cerr << "\t" << node << std::endl; |
794 | 812 | } |
795 | 813 | |
814 | void TileGenerator::reportProgress(size_t count) | |
815 | { | |
816 | if (!m_progressMax) | |
817 | return; | |
818 | int percent = count / static_cast<float>(m_progressMax) * 100; | |
819 | if (percent == m_progressLast) | |
820 | return; | |
821 | m_progressLast = percent; | |
822 | ||
823 | // Print a nice-looking ASCII progress bar | |
824 | char bar[51] = {0}; | |
825 | memset(bar, ' ', 50); | |
826 | int i = 0, j = percent; | |
827 | for (; j >= 2; j -= 2) | |
828 | bar[i++] = '='; | |
829 | if (j) | |
830 | bar[i++] = '-'; | |
831 | std::cout << "[" << bar << "] " << percent << "% " << (percent == 100 ? "\n" : "\r"); | |
832 | std::cout.flush(); | |
833 | } | |
834 | ||
796 | 835 | inline int TileGenerator::getImageX(int val, bool absolute) const |
797 | 836 | { |
798 | 837 | if (absolute) |
0 | /* | |
1 | * ===================================================================== | |
2 | * Version: 1.0 | |
3 | * Created: 18.09.2012 10:20:47 | |
4 | * Author: Miroslav BendÃk | |
5 | * Company: LinuxOS.sk | |
6 | * ===================================================================== | |
7 | */ | |
8 | ||
9 | 0 | #include <zlib.h> |
10 | 1 | #include <stdint.h> |
11 | 2 | #include "ZlibDecompressor.h" |
37 | 28 | const std::size_t size = m_size - m_seekPos; |
38 | 29 | |
39 | 30 | ustring buffer; |
40 | const size_t BUFSIZE = 128 * 1024; | |
41 | uint8_t temp_buffer[BUFSIZE]; | |
31 | constexpr size_t BUFSIZE = 128 * 1024; | |
32 | unsigned char temp_buffer[BUFSIZE]; | |
42 | 33 | |
43 | 34 | z_stream strm; |
44 | 35 | strm.zalloc = Z_NULL; |
47 | 38 | strm.next_in = Z_NULL; |
48 | 39 | strm.avail_in = size; |
49 | 40 | |
50 | if (inflateInit(&strm) != Z_OK) { | |
41 | if (inflateInit(&strm) != Z_OK) | |
51 | 42 | throw DecompressError(); |
52 | } | |
53 | 43 | |
54 | 44 | strm.next_in = const_cast<unsigned char *>(data); |
55 | 45 | int ret = 0; |
57 | 47 | strm.avail_out = BUFSIZE; |
58 | 48 | strm.next_out = temp_buffer; |
59 | 49 | ret = inflate(&strm, Z_NO_FLUSH); |
60 | buffer += ustring(reinterpret_cast<unsigned char *>(temp_buffer), BUFSIZE - strm.avail_out); | |
50 | buffer.append(temp_buffer, BUFSIZE - strm.avail_out); | |
61 | 51 | } while (ret == Z_OK); |
62 | if (ret != Z_STREAM_END) { | |
52 | if (ret != Z_STREAM_END) | |
63 | 53 | throw DecompressError(); |
64 | } | |
54 | ||
65 | 55 | m_seekPos += strm.next_in - data; |
66 | 56 | (void)inflateEnd(&strm); |
67 | 57 |
0 | #include <zstd.h> | |
1 | #include "ZstdDecompressor.h" | |
2 | ||
3 | ZstdDecompressor::ZstdDecompressor(): | |
4 | m_data(nullptr), | |
5 | m_seekPos(0), | |
6 | m_size(0) | |
7 | { | |
8 | m_stream = ZSTD_createDStream(); | |
9 | } | |
10 | ||
11 | ZstdDecompressor::~ZstdDecompressor() | |
12 | { | |
13 | ZSTD_freeDStream(reinterpret_cast<ZSTD_DStream*>(m_stream)); | |
14 | } | |
15 | ||
16 | void ZstdDecompressor::setData(const u8 *data, size_t size, size_t seekPos) | |
17 | { | |
18 | m_data = data; | |
19 | m_seekPos = seekPos; | |
20 | m_size = size; | |
21 | } | |
22 | ||
23 | std::size_t ZstdDecompressor::seekPos() const | |
24 | { | |
25 | return m_seekPos; | |
26 | } | |
27 | ||
28 | ustring ZstdDecompressor::decompress() | |
29 | { | |
30 | ZSTD_DStream *stream = reinterpret_cast<ZSTD_DStream*>(m_stream); | |
31 | ZSTD_inBuffer inbuf = { m_data, m_size, m_seekPos }; | |
32 | ||
33 | ustring buffer; | |
34 | constexpr size_t BUFSIZE = 32 * 1024; | |
35 | ||
36 | buffer.resize(BUFSIZE); | |
37 | ZSTD_outBuffer outbuf = { &buffer[0], buffer.size(), 0 }; | |
38 | ||
39 | ZSTD_initDStream(stream); | |
40 | ||
41 | size_t ret; | |
42 | do { | |
43 | ret = ZSTD_decompressStream(stream, &outbuf, &inbuf); | |
44 | if (outbuf.size == outbuf.pos) { | |
45 | outbuf.size += BUFSIZE; | |
46 | buffer.resize(outbuf.size); | |
47 | outbuf.dst = &buffer[0]; | |
48 | } | |
49 | if (ret && ZSTD_isError(ret)) | |
50 | throw DecompressError(); | |
51 | } while (ret != 0); | |
52 | ||
53 | m_seekPos = inbuf.pos; | |
54 | buffer.resize(outbuf.pos); | |
55 | ||
56 | return buffer; | |
57 | } |
0 | ==FILE== mods/dumpnodes/init.lua | |
1 | local function nd_get_tiles(nd) | |
2 | return nd.tiles or nd.tile_images | |
3 | end | |
4 | ||
5 | local function nd_get_tile(nd, n) | |
6 | local tile = nd_get_tiles(nd)[n] | |
7 | if type(tile) == 'table' then | |
8 | tile = tile.name | |
9 | end | |
10 | return tile | |
11 | end | |
12 | ||
13 | local function pairs_s(dict) | |
14 | local keys = {} | |
15 | for k in pairs(dict) do | |
16 | table.insert(keys, k) | |
17 | end | |
18 | table.sort(keys) | |
19 | return ipairs(keys) | |
20 | end | |
21 | ||
22 | minetest.register_chatcommand("dumpnodes", { | |
23 | params = "", | |
24 | description = "", | |
25 | func = function(player, param) | |
26 | local n = 0 | |
27 | local ntbl = {} | |
28 | for _, nn in pairs_s(minetest.registered_nodes) do | |
29 | local nd = minetest.registered_nodes[nn] | |
30 | local prefix, name = nn:match('(.*):(.*)') | |
31 | if prefix == nil or name == nil then | |
32 | print("ignored(1): " .. nn) | |
33 | else | |
34 | if ntbl[prefix] == nil then | |
35 | ntbl[prefix] = {} | |
36 | end | |
37 | ntbl[prefix][name] = true | |
38 | end | |
39 | end | |
40 | local out, err = io.open('nodes.txt', 'wb') | |
41 | if not out then | |
42 | return true, "io.open(): " .. err | |
43 | end | |
44 | for _, prefix in pairs_s(ntbl) do | |
45 | out:write('# ' .. prefix .. '\n') | |
46 | for _, name in pairs_s(ntbl[prefix]) do | |
47 | local nn = prefix .. ":" .. name | |
48 | local nd = minetest.registered_nodes[nn] | |
49 | if nd.drawtype == 'airlike' or nd_get_tiles(nd) == nil then | |
50 | print("ignored(2): " .. nn) | |
51 | else | |
52 | local tl = nd_get_tile(nd, 1) | |
53 | tl = (tl .. '^'):match('(.-)^') -- strip modifiers | |
54 | out:write(nn .. ' ' .. tl .. '\n') | |
55 | n = n + 1 | |
56 | end | |
57 | end | |
58 | out:write('\n') | |
59 | end | |
60 | out:close() | |
61 | return true, n .. " nodes dumped." | |
62 | end, | |
63 | }) | |
64 | ==FILE== avgcolor.py | |
65 | #!/usr/bin/env python | |
66 | import sys | |
67 | from math import sqrt | |
68 | from PIL import Image | |
69 | ||
70 | if len(sys.argv) < 2: | |
71 | print("Prints average color (RGB) of input image") | |
72 | print("Usage: %s <input>" % sys.argv[0]) | |
73 | exit(1) | |
74 | ||
75 | inp = Image.open(sys.argv[1]).convert('RGBA') | |
76 | ind = inp.load() | |
77 | ||
78 | cl = ([], [], []) | |
79 | for x in range(inp.size[0]): | |
80 | for y in range(inp.size[1]): | |
81 | px = ind[x, y] | |
82 | if px[3] < 128: continue # alpha | |
83 | cl[0].append(px[0]**2) | |
84 | cl[1].append(px[1]**2) | |
85 | cl[2].append(px[2]**2) | |
86 | ||
87 | if len(cl[0]) == 0: | |
88 | print("Didn't find average color for %s" % sys.argv[1], file=sys.stderr) | |
89 | print("0 0 0") | |
90 | else: | |
91 | cl = tuple(sqrt(sum(x)/len(x)) for x in cl) | |
92 | print("%d %d %d" % cl) | |
93 | ==SCRIPT== | |
94 | #!/bin/bash -e | |
95 | AVGCOLOR_PATH=/path/to/avgcolor.py | |
96 | GAME_PATH=/path/to/minetest_game | |
97 | MODS_PATH= # path to "mods" folder, only set if you have loaded mods | |
98 | NODESTXT_PATH=./nodes.txt | |
99 | COLORSTXT_PATH=./colors.txt | |
100 | ||
101 | while read -r line; do | |
102 | set -- junk $line; shift | |
103 | if [[ -z "$1" || $1 == "#" ]]; then | |
104 | echo "$line"; continue | |
105 | fi | |
106 | tex=$(find $GAME_PATH -type f -name "$2") | |
107 | [[ -z "$tex" && -n "$MODS_PATH" ]] && tex=$(find $MODS_PATH -type f -name "$2") | |
108 | if [ -z "$tex" ]; then | |
109 | echo "skip $1: texture not found" >&2 | |
110 | continue | |
111 | fi | |
112 | echo "$1" $(python $AVGCOLOR_PATH "$tex") | |
113 | echo "ok $1" >&2 | |
114 | done < $NODESTXT_PATH > $COLORSTXT_PATH | |
115 | # Use nicer colors for water and lava: | |
116 | sed -re 's/^default:((river_)?water_(flowing|source)) [0-9 ]+$/default:\1 39 66 106 128 224/g' $COLORSTXT_PATH -i | |
117 | sed -re 's/^default:(lava_(flowing|source)) [0-9 ]+$/default:\1 255 100 0/g' $COLORSTXT_PATH -i | |
118 | # Add transparency to glass nodes and xpanes: | |
119 | sed -re 's/^default:(.*glass) ([0-9 ]+)$/default:\1 \2 64 16/g' $COLORSTXT_PATH -i | |
120 | sed -re 's/^doors:(.*glass[^ ]*) ([0-9 ]+)$/doors:\1 \2 64 16/g' $COLORSTXT_PATH -i | |
121 | sed -re 's/^xpanes:(.*(pane|bar)[^ ]*) ([0-9 ]+)$/xpanes:\1 \3 64 16/g' $COLORSTXT_PATH -i | |
122 | # Delete some usually hidden nodes: | |
123 | sed '/^doors:hidden /d' $COLORSTXT_PATH -i | |
124 | sed '/^fireflies:firefly /d' $COLORSTXT_PATH -i | |
125 | sed '/^butterflies:butterfly_/d' $COLORSTXT_PATH -i | |
126 | ==INSTRUCTIONS== | |
127 | 1) Make sure avgcolors.py works (outputs the usage instructions when run) | |
128 | 2) Add the dumpnodes mod to Minetest | |
129 | 3) Create a world and load dumpnodes & all mods you want to generate colors for | |
130 | 4) Execute /dumpnodes ingame | |
131 | 5) Run the script to generate colors.txt (make sure to adjust the PATH variables at the top) |
0 | mark_as_advanced(ZSTD_LIBRARY ZSTD_INCLUDE_DIR) | |
1 | ||
2 | find_path(ZSTD_INCLUDE_DIR NAMES zstd.h) | |
3 | ||
4 | find_library(ZSTD_LIBRARY NAMES zstd) | |
5 | ||
6 | if(ZSTD_INCLUDE_DIR AND ZSTD_LIBRARY) | |
7 | # Check that the API we use exists | |
8 | include(CheckSymbolExists) | |
9 | unset(HAVE_ZSTD_INITDSTREAM CACHE) | |
10 | set(CMAKE_REQUIRED_INCLUDES ${ZSTD_INCLUDE_DIR}) | |
11 | set(CMAKE_REQUIRED_LIBRARIES ${ZSTD_LIBRARY}) | |
12 | check_symbol_exists(ZSTD_initDStream zstd.h HAVE_ZSTD_INITDSTREAM) | |
13 | unset(CMAKE_REQUIRED_INCLUDES) | |
14 | unset(CMAKE_REQUIRED_LIBRARIES) | |
15 | ||
16 | if(NOT HAVE_ZSTD_INITDSTREAM) | |
17 | unset(ZSTD_INCLUDE_DIR CACHE) | |
18 | unset(ZSTD_LIBRARY CACHE) | |
19 | endif() | |
20 | endif() | |
21 | ||
22 | include(FindPackageHandleStandardArgs) | |
23 | find_package_handle_standard_args(Zstd DEFAULT_MSG ZSTD_LIBRARY ZSTD_INCLUDE_DIR) |
0 | minetestmapper (20200328+git20210911.1.0198897-1) UNRELEASED; urgency=low | |
1 | ||
2 | * New upstream snapshot. | |
3 | ||
4 | -- Debian Janitor <janitor@jelmer.uk> Tue, 09 Nov 2021 12:02:16 -0000 | |
5 | ||
0 | 6 | minetestmapper (20200328-1) unstable; urgency=medium |
1 | 7 | |
2 | 8 | * New upstream release. |
0 | #ifndef BLOCKDECODER_H | |
1 | #define BLOCKDECODER_H | |
0 | #pragma once | |
2 | 1 | |
2 | #include <cstdint> | |
3 | 3 | #include <unordered_map> |
4 | ||
5 | 4 | #include "types.h" |
5 | #include <ZstdDecompressor.h> | |
6 | 6 | |
7 | 7 | class BlockDecoder { |
8 | 8 | public: |
11 | 11 | void reset(); |
12 | 12 | void decode(const ustring &data); |
13 | 13 | bool isEmpty() const; |
14 | std::string getNode(u8 x, u8 y, u8 z) const; // returns "" for air, ignore and invalid nodes | |
14 | // returns "" for air, ignore and invalid nodes | |
15 | const std::string &getNode(u8 x, u8 y, u8 z) const; | |
15 | 16 | |
16 | 17 | private: |
17 | typedef std::unordered_map<int, std::string> NameMap; | |
18 | typedef std::unordered_map<uint16_t, std::string> NameMap; | |
18 | 19 | NameMap m_nameMap; |
19 | int m_blockAirId; | |
20 | int m_blockIgnoreId; | |
20 | uint16_t m_blockAirId, m_blockIgnoreId; | |
21 | 21 | |
22 | u8 m_version; | |
22 | u8 m_version, m_contentWidth; | |
23 | 23 | ustring m_mapData; |
24 | ||
25 | // one instance for performance | |
26 | ZstdDecompressor m_zstd_decompressor; | |
24 | 27 | }; |
25 | ||
26 | #endif // BLOCKDECODER_H |
0 | #ifndef IMAGE_HEADER | |
1 | #define IMAGE_HEADER | |
0 | #pragma once | |
2 | 1 | |
3 | 2 | #include "types.h" |
4 | 3 | #include <string> |
8 | 7 | Color() : r(0), g(0), b(0), a(0) {}; |
9 | 8 | Color(u8 r, u8 g, u8 b) : r(r), g(g), b(b), a(255) {}; |
10 | 9 | Color(u8 r, u8 g, u8 b, u8 a) : r(r), g(g), b(b), a(a) {}; |
11 | inline Color noAlpha() const { return Color(r, g, b); } | |
12 | 10 | |
13 | 11 | u8 r, g, b, a; |
14 | 12 | }; |
17 | 15 | public: |
18 | 16 | Image(int width, int height); |
19 | 17 | ~Image(); |
18 | ||
19 | Image(const Image&) = delete; | |
20 | Image& operator=(const Image&) = delete; | |
20 | 21 | |
21 | 22 | void setPixel(int x, int y, const Color &c); |
22 | 23 | Color getPixel(int x, int y); |
27 | 28 | void save(const std::string &filename); |
28 | 29 | |
29 | 30 | private: |
30 | Image(const Image&); | |
31 | ||
32 | 31 | int m_width, m_height; |
33 | 32 | gdImagePtr m_image; |
34 | 33 | }; |
35 | ||
36 | #endif // IMAGE_HEADER |
0 | /* | |
1 | * ===================================================================== | |
2 | * Version: 1.0 | |
3 | * Created: 25.08.2012 10:55:29 | |
4 | * Author: Miroslav BendÃk | |
5 | * Company: LinuxOS.sk | |
6 | * ===================================================================== | |
7 | */ | |
0 | #pragma once | |
8 | 1 | |
9 | #ifndef PIXELATTRIBUTES_H_ADZ35GYF | |
10 | #define PIXELATTRIBUTES_H_ADZ35GYF | |
11 | ||
12 | #include <limits> | |
13 | #include <stdint.h> | |
2 | #include <climits> | |
3 | #include <cstdint> | |
14 | 4 | #include "config.h" |
15 | 5 | |
16 | 6 | struct PixelAttribute { |
17 | PixelAttribute(): height(std::numeric_limits<int>::min()), thickness(0) {}; | |
18 | int height; | |
7 | PixelAttribute(): height(INT16_MIN), thickness(0) {}; | |
8 | int16_t height; | |
19 | 9 | uint8_t thickness; |
20 | 10 | inline bool valid_height() { |
21 | return height != std::numeric_limits<int>::min(); | |
11 | return height != INT16_MIN; | |
22 | 12 | } |
23 | 13 | }; |
24 | 14 | |
29 | 19 | virtual ~PixelAttributes(); |
30 | 20 | void setWidth(int width); |
31 | 21 | void scroll(); |
32 | inline PixelAttribute &attribute(int z, int x) { return m_pixelAttributes[z + 1][x + 1]; }; | |
22 | inline PixelAttribute &attribute(int z, int x) { | |
23 | return m_pixelAttributes[z + 1][x + 1]; | |
24 | }; | |
33 | 25 | |
34 | 26 | private: |
35 | 27 | void freeAttributes(); |
44 | 36 | PixelAttribute *m_pixelAttributes[BLOCK_SIZE + 2]; // 1px gradient + empty |
45 | 37 | int m_width; |
46 | 38 | }; |
47 | ||
48 | #endif /* end of include guard: PIXELATTRIBUTES_H_ADZ35GYF */ | |
49 |
0 | #ifndef PLAYERATTRIBUTES_H_D7THWFVV | |
1 | #define PLAYERATTRIBUTES_H_D7THWFVV | |
0 | #pragma once | |
2 | 1 | |
3 | 2 | #include <list> |
4 | 3 | #include <string> |
15 | 14 | typedef std::list<Player> Players; |
16 | 15 | |
17 | 16 | PlayerAttributes(const std::string &worldDir); |
18 | Players::iterator begin(); | |
19 | Players::iterator end(); | |
17 | Players::const_iterator begin() const; | |
18 | Players::const_iterator end() const; | |
20 | 19 | |
21 | 20 | private: |
22 | 21 | void readFiles(const std::string &playersPath); |
24 | 23 | |
25 | 24 | Players m_players; |
26 | 25 | }; |
27 | ||
28 | #endif /* end of include guard: PLAYERATTRIBUTES_H_D7THWFVV */ | |
29 |
0 | #ifndef TILEGENERATOR_HEADER | |
1 | #define TILEGENERATOR_HEADER | |
0 | #pragma once | |
2 | 1 | |
3 | #include <iosfwd> | |
2 | #include <iostream> | |
4 | 3 | #include <map> |
5 | 4 | #include <set> |
6 | #include <config.h> | |
7 | 5 | #include <unordered_map> |
8 | 6 | #include <unordered_set> |
9 | #include <stdint.h> | |
7 | #include <cstdint> | |
10 | 8 | #include <string> |
11 | 9 | |
12 | 10 | #include "PixelAttributes.h" |
13 | #include "BlockDecoder.h" | |
14 | 11 | #include "Image.h" |
15 | 12 | #include "db.h" |
16 | 13 | #include "types.h" |
14 | ||
15 | class BlockDecoder; | |
16 | class Image; | |
17 | 17 | |
18 | 18 | enum { |
19 | 19 | SCALE_TOP = (1 << 0), |
30 | 30 | }; |
31 | 31 | |
32 | 32 | struct ColorEntry { |
33 | ColorEntry(): r(0), g(0), b(0), a(0), t(0) {}; | |
34 | 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) {}; | |
35 | inline Color to_color() const { return Color(r, g, b, a); } | |
36 | uint8_t r, g, b, a, t; | |
33 | ColorEntry() : r(0), g(0), b(0), a(0), t(0) {}; | |
34 | ColorEntry(uint8_t r, uint8_t g, uint8_t b, uint8_t a, uint8_t t) : | |
35 | r(r), g(g), b(b), a(a), t(t) {}; | |
36 | inline Color toColor() const { return Color(r, g, b, a); } | |
37 | uint8_t r, g, b, a; // Red, Green, Blue, Alpha | |
38 | uint8_t t; // "thickness" value | |
37 | 39 | }; |
38 | 40 | |
39 | 41 | struct BitmapThing { // 16x16 bitmap |
72 | 74 | void setScaleColor(const std::string &scaleColor); |
73 | 75 | void setOriginColor(const std::string &originColor); |
74 | 76 | void setPlayerColor(const std::string &playerColor); |
75 | Color parseColor(const std::string &color); | |
76 | 77 | void setDrawOrigin(bool drawOrigin); |
77 | 78 | void setDrawPlayers(bool drawPlayers); |
78 | 79 | void setDrawScale(bool drawScale); |
107 | 108 | void renderPlayers(const std::string &inputPath); |
108 | 109 | void writeImage(const std::string &output); |
109 | 110 | void printUnknown(); |
111 | void reportProgress(size_t count); | |
110 | 112 | int getImageX(int val, bool absolute=false) const; |
111 | 113 | int getImageY(int val, bool absolute=false) const; |
112 | 114 | void setZoomed(int x, int y, Color color); |
141 | 143 | int16_t m_geomY; /* Y in terms of rendered image, Z in the world */ |
142 | 144 | int16_t m_geomX2; |
143 | 145 | int16_t m_geomY2; |
144 | /* */ | |
146 | ||
145 | 147 | int m_mapWidth; |
146 | 148 | int m_mapHeight; |
147 | 149 | int m_exhaustiveSearch; |
155 | 157 | |
156 | 158 | int m_zoom; |
157 | 159 | uint m_scales; |
160 | ||
161 | size_t m_progressMax; | |
162 | int m_progressLast; // percentage | |
158 | 163 | }; // class TileGenerator |
159 | ||
160 | #endif // TILEGENERATOR_HEADER |
0 | /* | |
1 | * ===================================================================== | |
2 | * Version: 1.0 | |
3 | * Created: 18.09.2012 10:20:51 | |
4 | * Author: Miroslav BendÃk | |
5 | * Company: LinuxOS.sk | |
6 | * ===================================================================== | |
7 | */ | |
8 | ||
9 | #ifndef ZLIBDECOMPRESSOR_H_ZQL1PN8Q | |
10 | #define ZLIBDECOMPRESSOR_H_ZQL1PN8Q | |
0 | #pragma once | |
11 | 1 | |
12 | 2 | #include <cstdlib> |
13 | 3 | #include <string> |
30 | 20 | const unsigned char *m_data; |
31 | 21 | std::size_t m_seekPos; |
32 | 22 | std::size_t m_size; |
33 | }; /* ----- end of class ZlibDecompressor ----- */ | |
34 | ||
35 | #endif /* end of include guard: ZLIBDECOMPRESSOR_H_ZQL1PN8Q */ | |
36 | ||
23 | }; |
0 | #pragma once | |
1 | ||
2 | #include <cstdlib> | |
3 | #include <string> | |
4 | #include "types.h" | |
5 | ||
6 | class ZstdDecompressor | |
7 | { | |
8 | public: | |
9 | class DecompressError {}; | |
10 | ||
11 | ZstdDecompressor(); | |
12 | ~ZstdDecompressor(); | |
13 | void setData(const u8 *data, size_t size, size_t seekPos); | |
14 | size_t seekPos() const; | |
15 | ustring decompress(); | |
16 | ||
17 | private: | |
18 | void *m_stream; // ZSTD_DStream | |
19 | const u8 *m_data; | |
20 | size_t m_seekPos, m_size; | |
21 | }; |
0 | #ifndef DB_LEVELDB_HEADER | |
1 | #define DB_LEVELDB_HEADER | |
0 | #pragma once | |
2 | 1 | |
3 | 2 | #include "db.h" |
4 | 3 | #include <unordered_map> |
26 | 25 | std::unordered_map<int16_t, std::vector<pos2d>> posCache; |
27 | 26 | leveldb::DB *db; |
28 | 27 | }; |
29 | ||
30 | #endif // DB_LEVELDB_HEADER |
0 | #ifndef _DB_POSTGRESQL_H | |
1 | #define _DB_POSTGRESQL_H | |
0 | #pragma once | |
2 | 1 | |
3 | 2 | #include "db.h" |
4 | 3 | #include <libpq-fe.h> |
30 | 29 | private: |
31 | 30 | PGconn *db; |
32 | 31 | }; |
33 | ||
34 | #endif // _DB_POSTGRESQL_H |
0 | #ifndef DB_REDIS_HEADER | |
1 | #define DB_REDIS_HEADER | |
0 | #pragma once | |
2 | 1 | |
3 | 2 | #include "db.h" |
4 | 3 | #include <unordered_map> |
32 | 31 | redisContext *ctx; |
33 | 32 | std::string hash; |
34 | 33 | }; |
35 | ||
36 | #endif // DB_REDIS_HEADER |
0 | #ifndef _DB_SQLITE3_H | |
1 | #define _DB_SQLITE3_H | |
0 | #pragma once | |
2 | 1 | |
3 | 2 | #include "db.h" |
4 | 3 | #include <unordered_map> |
31 | 30 | int16_t blockCachedZ = -10000; |
32 | 31 | std::unordered_map<int16_t, BlockList> blockCache; // indexed by X |
33 | 32 | }; |
34 | ||
35 | #endif // _DB_SQLITE3_H |
0 | #ifndef DB_HEADER | |
1 | #define DB_HEADER | |
0 | #pragma once | |
2 | 1 | |
3 | #include <stdint.h> | |
4 | #include <map> | |
2 | #include <cstdint> | |
5 | 3 | #include <list> |
6 | 4 | #include <vector> |
7 | 5 | #include <utility> |
121 | 119 | * End black magic * |
122 | 120 | *******************/ |
123 | 121 | |
124 | #endif // DB_HEADER |
0 | #ifndef UTIL_H | |
1 | #define UTIL_H | |
0 | #pragma once | |
2 | 1 | |
3 | 2 | #include <string> |
4 | 3 | #include <fstream> |
5 | 4 | |
6 | 5 | std::string read_setting(const std::string &name, std::istream &is); |
7 | 6 | |
8 | inline std::string read_setting_default(const std::string &name, std::istream &is, const std::string &def) | |
9 | { | |
10 | try { | |
11 | return read_setting(name, is); | |
12 | } catch(const std::runtime_error &e) { | |
13 | return def; | |
14 | } | |
15 | } | |
16 | ||
17 | #endif // UTIL_H | |
7 | std::string read_setting_default(const std::string &name, std::istream &is, | |
8 | const std::string &def); |
7 | 7 | #include <string> |
8 | 8 | #include <sstream> |
9 | 9 | #include <stdexcept> |
10 | #include "cmake_config.h" | |
10 | #include "config.h" | |
11 | 11 | #include "TileGenerator.h" |
12 | 12 | |
13 | 13 | static void usage() |
57 | 57 | |
58 | 58 | static bool file_exists(const std::string &path) |
59 | 59 | { |
60 | std::ifstream ifs(path.c_str()); | |
60 | std::ifstream ifs(path); | |
61 | 61 | return ifs.is_open(); |
62 | 62 | } |
63 | 63 | |
255 | 255 | generator.parseColorsFile(colors); |
256 | 256 | generator.generate(input, output); |
257 | 257 | |
258 | } catch(std::runtime_error &e) { | |
258 | } catch (const std::runtime_error &e) { | |
259 | 259 | std::cerr << "Exception: " << e.what() << std::endl; |
260 | 260 | return 1; |
261 | 261 | } |
0 | #!/bin/bash -e | |
1 | ||
2 | [ -z "$CXX" ] && exit 255 | |
3 | export CC=false # don't need it actually | |
4 | ||
5 | variant=win32 | |
6 | [[ "$(basename "$CXX")" == "x86_64-"* ]] && variant=win64 | |
7 | ||
8 | ####### | |
9 | # this expects unpacked libraries similar to what Minetest's buildbot uses | |
10 | # $extradlls will typically point to the DLLs for libgcc, libstdc++ and libpng | |
11 | libgd_dir= | |
12 | zlib_dir= | |
13 | zstd_dir= | |
14 | sqlite_dir= | |
15 | leveldb_dir= | |
16 | extradlls=() | |
17 | ####### | |
18 | ||
19 | [ -f ./CMakeLists.txt ] || exit 1 | |
20 | ||
21 | cmake -S . -B build \ | |
22 | -DCMAKE_SYSTEM_NAME=Windows \ | |
23 | -DCMAKE_EXE_LINKER_FLAGS="-s" \ | |
24 | \ | |
25 | -DENABLE_LEVELDB=1 \ | |
26 | \ | |
27 | -DLEVELDB_INCLUDE_DIR=$leveldb_dir/include \ | |
28 | -DLEVELDB_LIBRARY=$leveldb_dir/lib/libleveldb.dll.a \ | |
29 | -DLIBGD_INCLUDE_DIR=$libgd_dir/include \ | |
30 | -DLIBGD_LIBRARY=$libgd_dir/lib/libgd.dll.a \ | |
31 | -DSQLITE3_INCLUDE_DIR=$sqlite_dir/include \ | |
32 | -DSQLITE3_LIBRARY=$sqlite_dir/lib/libsqlite3.dll.a \ | |
33 | -DZLIB_INCLUDE_DIR=$zlib_dir/include \ | |
34 | -DZLIB_LIBRARY=$zlib_dir/lib/libz.dll.a \ | |
35 | -DZSTD_INCLUDE_DIR=$zstd_dir/include \ | |
36 | -DZSTD_LIBRARY=$zstd_dir/lib/libzstd.dll.a \ | |
37 | ||
38 | make -C build -j4 | |
39 | ||
40 | mkdir pack | |
41 | cp -p \ | |
42 | AUTHORS colors.txt COPYING README.rst \ | |
43 | build/minetestmapper.exe \ | |
44 | $leveldb_dir/bin/libleveldb.dll \ | |
45 | $libgd_dir/bin/libgd*.dll \ | |
46 | $sqlite_dir/bin/libsqlite*.dll \ | |
47 | $zlib_dir/bin/zlib1.dll \ | |
48 | $zstd_dir/bin/libzstd.dll \ | |
49 | "${extradlls[@]}" \ | |
50 | pack/ | |
51 | zipfile=$PWD/minetestmapper-$variant.zip | |
52 | (cd pack; zip -9r "$zipfile" *) | |
53 | ||
54 | rm -rf build pack | |
55 | echo "Done." |
0 | #!/bin/bash -e | |
1 | ||
2 | ####### | |
3 | # this expects an env similar to what minetest's buildbot uses | |
4 | # extradll_path will typically contain libgcc, libstdc++ and libpng | |
5 | toolchain_file= | |
6 | toolchain_file64= | |
7 | libgd_dir= | |
8 | libgd_dir64= | |
9 | zlib_dir= | |
10 | zlib_dir64= | |
11 | sqlite_dir= | |
12 | sqlite_dir64= | |
13 | leveldb_dir= | |
14 | leveldb_dir64= | |
15 | extradll_path= | |
16 | extradll_path64= | |
17 | ####### | |
18 | ||
19 | [ -f ./CMakeLists.txt ] || exit 1 | |
20 | ||
21 | if [ "$1" == "32" ]; then | |
22 | : | |
23 | elif [ "$1" == "64" ]; then | |
24 | toolchain_file=$toolchain_file64 | |
25 | libgd_dir=$libgd_dir64 | |
26 | zlib_dir=$zlib_dir64 | |
27 | sqlite_dir=$sqlite_dir64 | |
28 | leveldb_dir=$leveldb_dir64 | |
29 | extradll_path=$extradll_path64 | |
30 | else | |
31 | echo "Usage: $0 <32 / 64>" | |
32 | exit 1 | |
33 | fi | |
34 | ||
35 | cmake . \ | |
36 | -DCMAKE_INSTALL_PREFIX=/tmp \ | |
37 | -DCMAKE_TOOLCHAIN_FILE=$toolchain_file \ | |
38 | -DCMAKE_EXE_LINKER_FLAGS="-s" \ | |
39 | \ | |
40 | -DENABLE_LEVELDB=1 \ | |
41 | \ | |
42 | -DLIBGD_INCLUDE_DIR=$libgd_dir/include \ | |
43 | -DLIBGD_LIBRARY=$libgd_dir/lib/libgd.dll.a \ | |
44 | \ | |
45 | -DZLIB_INCLUDE_DIR=$zlib_dir/include \ | |
46 | -DZLIB_LIBRARY=$zlib_dir/lib/libz.dll.a \ | |
47 | \ | |
48 | -DSQLITE3_INCLUDE_DIR=$sqlite_dir/include \ | |
49 | -DSQLITE3_LIBRARY=$sqlite_dir/lib/libsqlite3.dll.a \ | |
50 | \ | |
51 | -DLEVELDB_INCLUDE_DIR=$leveldb_dir/include \ | |
52 | -DLEVELDB_LIBRARY=$leveldb_dir/lib/libleveldb.dll.a | |
53 | ||
54 | make -j4 | |
55 | ||
56 | mkdir pack | |
57 | cp -p \ | |
58 | AUTHORS colors.txt COPYING README.rst \ | |
59 | minetestmapper.exe \ | |
60 | $libgd_dir/bin/libgd-3.dll \ | |
61 | $zlib_dir/bin/zlib1.dll \ | |
62 | $sqlite_dir/bin/libsqlite3-0.dll \ | |
63 | $leveldb_dir/bin/libleveldb.dll \ | |
64 | $extradll_path/*.dll \ | |
65 | pack/ | |
66 | zipfile=minetestmapper-win$1.zip | |
67 | (cd pack; zip -9r ../$zipfile *) | |
68 | ||
69 | make clean | |
70 | rm -r pack CMakeCache.txt | |
71 | ||
72 | echo "Done." |
0 | #!/bin/bash -e | |
1 | ||
2 | install_linux_deps() { | |
3 | local pkgs=(cmake libgd-dev libsqlite3-dev libleveldb-dev libpq-dev libhiredis-dev libzstd-dev) | |
4 | ||
5 | sudo apt-get update | |
6 | sudo apt-get install -y --no-install-recommends ${pkgs[@]} "$@" | |
7 | } | |
8 | ||
9 | run_build() { | |
10 | cmake . -DCMAKE_BUILD_TYPE=Debug \ | |
11 | -DENABLE_LEVELDB=1 -DENABLE_POSTGRESQL=1 -DENABLE_REDIS=1 | |
12 | ||
13 | make -j2 | |
14 | } | |
15 | ||
16 | do_functional_test() { | |
17 | mkdir testmap | |
18 | echo "backend = sqlite3" >testmap/world.mt | |
19 | sqlite3 testmap/map.sqlite <<END | |
20 | CREATE TABLE blocks(pos INT,data BLOB); | |
21 | INSERT INTO blocks(pos, data) VALUES(0, x'$(cat util/ci/test_block)'); | |
22 | END | |
23 | ||
24 | ./minetestmapper --noemptyimage -i ./testmap -o map.png | |
25 | file map.png | |
26 | } |
0 | 1b00ffff020278daedd4c1090000080331dd7f691710faf12589235cb12ae870fca6bffefaebafbffefaebafbffefaebbff7b708fdf1ffd11ffdd11ffdd11ffd01000000000000003836d59f010578da63000000010001000000ffffffff000002000000036169720001000d64656661756c743a73746f6e650a0000 |
0 | local function get_tile(tiles, n) | |
1 | local tile = tiles[n] | |
2 | if type(tile) == 'table' then | |
3 | return tile.name | |
4 | end | |
5 | return tile | |
6 | end | |
7 | ||
8 | local function pairs_s(dict) | |
9 | local keys = {} | |
10 | for k in pairs(dict) do | |
11 | keys[#keys+1] = k | |
12 | end | |
13 | table.sort(keys) | |
14 | return ipairs(keys) | |
15 | end | |
16 | ||
17 | minetest.register_chatcommand("dumpnodes", { | |
18 | description = "Dump node and texture list for use with minetestmapper", | |
19 | func = function() | |
20 | local ntbl = {} | |
21 | for _, nn in pairs_s(minetest.registered_nodes) do | |
22 | local prefix, name = nn:match('(.*):(.*)') | |
23 | if prefix == nil or name == nil then | |
24 | print("ignored(1): " .. nn) | |
25 | else | |
26 | if ntbl[prefix] == nil then | |
27 | ntbl[prefix] = {} | |
28 | end | |
29 | ntbl[prefix][name] = true | |
30 | end | |
31 | end | |
32 | local out, err = io.open(minetest.get_worldpath() .. "/nodes.txt", 'wb') | |
33 | if not out then | |
34 | return true, err | |
35 | end | |
36 | local n = 0 | |
37 | for _, prefix in pairs_s(ntbl) do | |
38 | out:write('# ' .. prefix .. '\n') | |
39 | for _, name in pairs_s(ntbl[prefix]) do | |
40 | local nn = prefix .. ":" .. name | |
41 | local nd = minetest.registered_nodes[nn] | |
42 | local tiles = nd.tiles or nd.tile_images | |
43 | if tiles == nil or nd.drawtype == 'airlike' then | |
44 | print("ignored(2): " .. nn) | |
45 | else | |
46 | local tex = get_tile(tiles, 1) | |
47 | tex = (tex .. '^'):match('%(*(.-)%)*^') -- strip modifiers | |
48 | if tex:find("[combine", 1, true) then | |
49 | tex = tex:match('.-=([^:]-)') -- extract first texture | |
50 | end | |
51 | out:write(nn .. ' ' .. tex .. '\n') | |
52 | n = n + 1 | |
53 | end | |
54 | end | |
55 | out:write('\n') | |
56 | end | |
57 | out:close() | |
58 | return true, n .. " nodes dumped." | |
59 | end, | |
60 | }) |
0 | #!/usr/bin/env python3 | |
1 | import sys | |
2 | import os.path | |
3 | import getopt | |
4 | import re | |
5 | from math import sqrt | |
6 | try: | |
7 | from PIL import Image | |
8 | except: | |
9 | print("Could not load image routines, install PIL ('pillow' on pypi)!", file=sys.stderr) | |
10 | exit(1) | |
11 | ||
12 | ############ | |
13 | ############ | |
14 | # Instructions for generating a colors.txt file for custom games and/or mods: | |
15 | # 1) Add the dumpnodes mod to a Minetest world with the chosen game and mods enabled. | |
16 | # 2) Join ingame and run the /dumpnodes chat command. | |
17 | # 3) Run this script and poin it to the installation path of the game using -g, | |
18 | # the path(s) where mods are stored using -m and the nodes.txt in your world folder. | |
19 | # Example command line: | |
20 | # ./util/generate_colorstxt.py --game /usr/share/minetest/games/minetest_game \ | |
21 | # -m ~/.minetest/mods ~/.minetest/worlds/my_world/nodes.txt | |
22 | # 4) Copy the resulting colors.txt file to your world folder or to any other places | |
23 | # and use it with minetestmapper's --colors option. | |
24 | ########### | |
25 | ########### | |
26 | ||
27 | # minimal sed syntax, s|match|replace| and /match/d supported | |
28 | REPLACEMENTS = [ | |
29 | # Delete some nodes that are usually hidden | |
30 | r'/^fireflies:firefly /d', | |
31 | r'/^butterflies:butterfly_/d', | |
32 | # Nicer colors for water and lava | |
33 | r's/^(default:(river_)?water_(flowing|source)) [0-9 ]+$/\1 39 66 106 128 224/', | |
34 | r's/^(default:lava_(flowing|source)) [0-9 ]+$/\1 255 100 0/', | |
35 | # Transparency for glass nodes and panes | |
36 | r's/^(default:.*glass) ([0-9 ]+)$/\1 \2 64 16/', | |
37 | r's/^(doors:.*glass[^ ]*) ([0-9 ]+)$/\1 \2 64 16/', | |
38 | r's/^(xpanes:.*(pane|bar)[^ ]*) ([0-9 ]+)$/\1 \3 64 16/', | |
39 | ] | |
40 | ||
41 | def usage(): | |
42 | print("Usage: generate_colorstxt.py [options] [input file] [output file]") | |
43 | print("If not specified the input file defaults to ./nodes.txt and the output file to ./colors.txt") | |
44 | print(" -g / --game <folder>\t\tSet path to the game (for textures), required") | |
45 | print(" -m / --mods <folder>\t\tAdd search path for mod textures") | |
46 | print(" --replace <file>\t\tLoad replacements from file (ADVANCED)") | |
47 | ||
48 | def collect_files(path): | |
49 | dirs = [] | |
50 | with os.scandir(path) as it: | |
51 | for entry in it: | |
52 | if entry.name[0] == '.': continue | |
53 | if entry.is_dir(): | |
54 | dirs.append(entry.path) | |
55 | continue | |
56 | if entry.is_file() and '.' in entry.name: | |
57 | if entry.name not in textures.keys(): | |
58 | textures[entry.name] = entry.path | |
59 | for path2 in dirs: | |
60 | collect_files(path2) | |
61 | ||
62 | def average_color(filename): | |
63 | inp = Image.open(filename).convert('RGBA') | |
64 | data = inp.load() | |
65 | ||
66 | c0, c1, c2 = [], [], [] | |
67 | for x in range(inp.size[0]): | |
68 | for y in range(inp.size[1]): | |
69 | px = data[x, y] | |
70 | if px[3] < 128: continue # alpha | |
71 | c0.append(px[0]**2) | |
72 | c1.append(px[1]**2) | |
73 | c2.append(px[2]**2) | |
74 | ||
75 | if len(c0) == 0: | |
76 | print(f"didn't find color for '{os.path.basename(filename)}'", file=sys.stderr) | |
77 | return "0 0 0" | |
78 | c0 = sqrt(sum(c0) / len(c0)) | |
79 | c1 = sqrt(sum(c1) / len(c1)) | |
80 | c2 = sqrt(sum(c2) / len(c2)) | |
81 | return "%d %d %d" % (c0, c1, c2) | |
82 | ||
83 | def apply_sed(line, exprs): | |
84 | for expr in exprs: | |
85 | if expr[0] == '/': | |
86 | if not expr.endswith("/d"): raise ValueError() | |
87 | if re.search(expr[1:-2], line): | |
88 | return '' | |
89 | elif expr[0] == 's': | |
90 | expr = expr.split(expr[1]) | |
91 | if len(expr) != 4 or expr[3] != '': raise ValueError() | |
92 | line = re.sub(expr[1], expr[2], line) | |
93 | else: | |
94 | raise ValueError() | |
95 | return line | |
96 | # | |
97 | ||
98 | try: | |
99 | opts, args = getopt.getopt(sys.argv[1:], "hg:m:", ["help", "game=", "mods=", "replace="]) | |
100 | except getopt.GetoptError as e: | |
101 | print(str(e)) | |
102 | exit(1) | |
103 | if ('-h', '') in opts or ('--help', '') in opts: | |
104 | usage() | |
105 | exit(0) | |
106 | ||
107 | input_file = "./nodes.txt" | |
108 | output_file = "./colors.txt" | |
109 | texturepaths = [] | |
110 | ||
111 | try: | |
112 | gamepath = next(o[1] for o in opts if o[0] in ('-g', '--game')) | |
113 | if not os.path.isdir(os.path.join(gamepath, "mods")): | |
114 | print(f"'{gamepath}' doesn't exist or does not contain a game.", file=sys.stderr) | |
115 | exit(1) | |
116 | texturepaths.append(os.path.join(gamepath, "mods")) | |
117 | except StopIteration: | |
118 | print("No game path set but one is required. (see --help)", file=sys.stderr) | |
119 | exit(1) | |
120 | ||
121 | try: | |
122 | tmp = next(o[1] for o in opts if o[0] == "--replace") | |
123 | REPLACEMENTS.clear() | |
124 | with open(tmp, 'r') as f: | |
125 | for line in f: | |
126 | if not line or line[0] == '#': continue | |
127 | REPLACEMENTS.append(line.strip()) | |
128 | except StopIteration: | |
129 | pass | |
130 | ||
131 | for o in opts: | |
132 | if o[0] not in ('-m', '--mods'): continue | |
133 | if not os.path.isdir(o[1]): | |
134 | print(f"Given path '{o[1]}' does not exist.'", file=sys.stderr) | |
135 | exit(1) | |
136 | texturepaths.append(o[1]) | |
137 | ||
138 | if len(args) > 2: | |
139 | print("Too many arguments.", file=sys.stderr) | |
140 | exit(1) | |
141 | if len(args) > 1: | |
142 | output_file = args[1] | |
143 | if len(args) > 0: | |
144 | input_file = args[0] | |
145 | ||
146 | if not os.path.exists(input_file) or os.path.isdir(input_file): | |
147 | print(f"Input file '{input_file}' does not exist.", file=sys.stderr) | |
148 | exit(1) | |
149 | ||
150 | # | |
151 | ||
152 | print(f"Collecting textures from {len(texturepaths)} path(s)... ", end="", flush=True) | |
153 | textures = {} | |
154 | for path in texturepaths: | |
155 | collect_files(path) | |
156 | print("done") | |
157 | ||
158 | print("Processing nodes...") | |
159 | fin = open(input_file, 'r') | |
160 | fout = open(output_file, 'w') | |
161 | n = 0 | |
162 | for line in fin: | |
163 | line = line.rstrip('\r\n') | |
164 | if not line or line[0] == '#': | |
165 | fout.write(line + '\n') | |
166 | continue | |
167 | node, tex = line.split(" ") | |
168 | if not tex or tex == "blank.png": | |
169 | continue | |
170 | elif tex not in textures.keys(): | |
171 | print(f"skip {node} texture not found") | |
172 | continue | |
173 | color = average_color(textures[tex]) | |
174 | line = f"{node} {color}" | |
175 | #print(f"ok {node}") | |
176 | line = apply_sed(line, REPLACEMENTS) | |
177 | if line: | |
178 | fout.write(line + '\n') | |
179 | n += 1 | |
180 | fin.close() | |
181 | fout.close() | |
182 | print(f"Done, {n} entries written.") |
0 | #!/bin/bash -e | |
1 | mkdir -p travisbuild | |
2 | cd travisbuild | |
3 | ||
4 | cmake .. \ | |
5 | -DENABLE_LEVELDB=1 | |
6 | ||
7 | make -j2 |
42 | 42 | oss << "Setting '" << name << "' not found"; |
43 | 43 | throw std::runtime_error(oss.str()); |
44 | 44 | } |
45 | ||
46 | std::string read_setting_default(const std::string &name, std::istream &is, | |
47 | const std::string &def) | |
48 | { | |
49 | try { | |
50 | return read_setting(name, is); | |
51 | } catch(const std::runtime_error &e) { | |
52 | return def; | |
53 | } | |
54 | } |