diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a6869db..92af922 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -19,7 +19,7 @@ on: jobs: gcc: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v3 - name: Install deps @@ -41,7 +41,7 @@ jobs: do_functional_test clang: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v3 - name: Install deps diff --git a/CMakeLists.txt b/CMakeLists.txt index 5a179ba..54b670a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -175,6 +175,7 @@ add_executable(minetestmapper BlockDecoder.cpp PixelAttributes.cpp PlayerAttributes.cpp + POIAttributes.cpp TileGenerator.cpp ZlibDecompressor.cpp ZstdDecompressor.cpp diff --git a/Image.cpp b/Image.cpp index c379d3c..1823362 100644 --- a/Image.cpp +++ b/Image.cpp @@ -87,7 +87,8 @@ void Image::drawLine(int x1, int y1, int x2, int y2, const Color &c) void Image::drawText(int x, int y, const std::string &s, const Color &c) { SIZECHECK(x, y); - gdImageString(m_image, gdFontGetMediumBold(), x, y, (unsigned char*) s.c_str(), color2int(c)); + int brect[8]; + gdImageStringFT(m_image, &brect[0], color2int(c), "dejavu/DejaVuSans-Bold;freefont/FreeSansBold;msttcorefonts/Arial", 13., 0., x, y, s.c_str()); } void Image::drawFilledRect(int x, int y, int w, int h, const Color &c) diff --git a/POIAttributes.cpp b/POIAttributes.cpp new file mode 100644 index 0000000..259d62a --- /dev/null +++ b/POIAttributes.cpp @@ -0,0 +1,87 @@ +#include +#include +#include +#include +#include +#include // for usleep +#include + +#include "config.h" +#include "POIAttributes.h" +#include "util.h" + +POIAttributes::POIAttributes(const std::string &worldDir) +{ + std::ifstream ifs(worldDir + "world.mt"); + if (!ifs.good()) + throw std::runtime_error("Failed to read world.mt"); + std::string backend = read_setting_default("mod_storage_backend", ifs, "sqlite3"); + ifs.close(); + + if (backend == "sqlite3") + readSqlite(worldDir + "mod_storage.sqlite"); + else + throw std::runtime_error(std::string("Unknown poi backend: ") + backend); +} + +/**********/ + +#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 POIAttributes::readSqlite(const std::string &db_name) +{ + int result; + sqlite3 *db; + sqlite3_stmt *stmt_get_poi_pos; + + SQLOK(open_v2(db_name.c_str(), &db, SQLITE_OPEN_READONLY | + SQLITE_OPEN_PRIVATECACHE, 0)) + + SQLOK(prepare_v2(db, + "SELECT key, value FROM entries WHERE modname = 'poi'", + -1, &stmt_get_poi_pos, NULL)) + + while ((result = sqlite3_step(stmt_get_poi_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)); + } + + POI poi; + const unsigned char *name_ = sqlite3_column_text(stmt_get_poi_pos, 0); + poi.name = reinterpret_cast(name_); + const char *pos_ = reinterpret_cast(sqlite3_column_text(stmt_get_poi_pos, 1)); + float x, y, z; + int items = sscanf(pos_, "(%f,%f,%f)", &x, &y, &z); + if (items != 3) { + std::cerr << "Failed to parse POI position '" << pos_ << "'" << std::endl; + return; + } + poi.x = x; + poi.y = y; + poi.z = z; + + m_pois.push_back(poi); + } + + sqlite3_finalize(stmt_get_poi_pos); + sqlite3_close(db); +} + +/**********/ + +POIAttributes::POIs::const_iterator POIAttributes::begin() const +{ + return m_pois.cbegin(); +} + +POIAttributes::POIs::const_iterator POIAttributes::end() const +{ + return m_pois.cend(); +} diff --git a/TileGenerator.cpp b/TileGenerator.cpp index a28ae67..9d72606 100644 --- a/TileGenerator.cpp +++ b/TileGenerator.cpp @@ -14,6 +14,7 @@ #include "TileGenerator.h" #include "config.h" #include "PlayerAttributes.h" +#include "POIAttributes.h" #include "BlockDecoder.h" #include "Image.h" #include "util.h" @@ -118,6 +119,7 @@ TileGenerator::TileGenerator(): m_scaleColor(0, 0, 0), m_originColor(255, 0, 0), m_playerColor(255, 0, 0), + m_poiColor(0, 128, 255), m_drawOrigin(false), m_drawPlayers(false), m_drawScale(false), @@ -195,6 +197,11 @@ void TileGenerator::setDrawPlayers(bool drawPlayers) m_drawPlayers = drawPlayers; } +void TileGenerator::setDrawPOIs(bool drawPOIs) +{ + m_drawPOIs = drawPOIs; +} + void TileGenerator::setDrawScale(bool drawScale) { m_drawScale = drawScale; @@ -314,6 +321,9 @@ void TileGenerator::generate(const std::string &input_path, const std::string &o if (m_drawPlayers) { renderPlayers(input_path); } + if (m_drawPOIs) { + renderPOIs(input_path); + } writeImage(output); printUnknown(); } @@ -848,6 +858,27 @@ void TileGenerator::renderPlayers(const std::string &input_path) } } +void TileGenerator::renderPOIs(const std::string &input_path) +{ + std::string input = input_path; + if (input.back() != PATH_SEPARATOR) + input += PATH_SEPARATOR; + + POIAttributes pois(input); + for (auto &poi : pois) { + if (poi.x < m_xMin * 16 || poi.x > m_xMax * 16 || + poi.z < m_zMin * 16 || poi.z > m_zMax * 16) + continue; + if (poi.y < m_yMin || poi.y > m_yMax) + continue; + int imageX = getImageX(poi.x, true), + imageY = getImageY(poi.z, true); + + m_image->drawCircle(imageX, imageY, 9, m_poiColor); + m_image->drawText(imageX + 7, imageY, poi.name, m_poiColor); + } +} + void TileGenerator::writeImage(const std::string &output) { m_image->save(output); diff --git a/include/POIAttributes.h b/include/POIAttributes.h new file mode 100644 index 0000000..6c20bcd --- /dev/null +++ b/include/POIAttributes.h @@ -0,0 +1,26 @@ +#pragma once + +#include +#include + +struct POI +{ + std::string name; + float x, y, z; +}; + +class POIAttributes +{ +public: + typedef std::list POIs; + + POIAttributes(const std::string &worldDir); + POIs::const_iterator begin() const; + POIs::const_iterator end() const; + +private: + void readFiles(const std::string &poisPath); + void readSqlite(const std::string &db_name); + + POIs m_pois; +}; diff --git a/include/TileGenerator.h b/include/TileGenerator.h index 0f1c8f7..9b7b2de 100644 --- a/include/TileGenerator.h +++ b/include/TileGenerator.h @@ -79,6 +79,7 @@ class TileGenerator void setDrawPlayers(bool drawPlayers); void setDrawScale(bool drawScale); void setDrawAlpha(bool drawAlpha); + void setDrawPOIs(bool drawPOIs); void setShading(bool shading); void setGeometry(int x, int y, int w, int h); void setMinY(int y); @@ -109,6 +110,7 @@ class TileGenerator void renderScale(); void renderOrigin(); void renderPlayers(const std::string &inputPath); + void renderPOIs(const std::string &inputPath); void writeImage(const std::string &output); void printUnknown(); void reportProgress(size_t count); @@ -121,10 +123,12 @@ class TileGenerator Color m_scaleColor; Color m_originColor; Color m_playerColor; + Color m_poiColor; bool m_drawOrigin; bool m_drawPlayers; bool m_drawScale; bool m_drawAlpha; + bool m_drawPOIs; bool m_shading; bool m_dontWriteEmpty; std::string m_backend; diff --git a/mapper.cpp b/mapper.cpp index da44d84..7af0f3c 100644 --- a/mapper.cpp +++ b/mapper.cpp @@ -24,6 +24,7 @@ static void usage() {"--drawplayers", ""}, {"--draworigin", ""}, {"--drawalpha", ""}, + {"--drawpois", ""}, {"--noshading", ""}, {"--noemptyimage", ""}, {"--min-y", ""}, @@ -108,6 +109,7 @@ int main(int argc, char *argv[]) {"drawplayers", no_argument, 0, 'P'}, {"drawscale", no_argument, 0, 'S'}, {"drawalpha", no_argument, 0, 'e'}, + {"drawpois", no_argument, 0, 'I'}, {"noshading", no_argument, 0, 'H'}, {"backend", required_argument, 0, 'd'}, {"geometry", required_argument, 0, 'g'}, @@ -170,6 +172,9 @@ int main(int argc, char *argv[]) case 'e': generator.setDrawAlpha(true); break; + case 'I': + generator.setDrawPOIs(true); + break; case 'E': onlyPrintExtent = true; break; diff --git a/minetestmapper.6 b/minetestmapper.6 index c7ced36..a14ea57 100644 --- a/minetestmapper.6 +++ b/minetestmapper.6 @@ -52,6 +52,10 @@ Draw origin indicator .BR \-\-drawalpha Allow nodes to be drawn with transparency +.TP +.BR \-\-drawpois +Draw POIs + .TP .BR \-\-noshading Don't draw shading on nodes