From 143401451c457da5079b2970fe260acea45bd85a Mon Sep 17 00:00:00 2001 From: Loic Blot Date: Sat, 14 May 2016 12:23:15 +0200 Subject: DB::loadBlock copy removal & DB backend cleanup * Remove the copy from db::loadBlock by using a pointer to the destination * cleanup db backend, the child backend doesn't have to set their functions as virtual --- src/database-dummy.cpp | 11 +++++++---- src/database-dummy.h | 8 ++++---- src/database-leveldb.cpp | 7 ++----- src/database-leveldb.h | 8 ++++---- src/database-redis.cpp | 11 +++++++---- src/database-redis.h | 12 ++++++------ src/database-sqlite3.cpp | 11 ++++------- src/database-sqlite3.h | 16 ++++++++-------- src/database.h | 2 +- src/main.cpp | 3 ++- src/map.cpp | 3 +-- 11 files changed, 46 insertions(+), 46 deletions(-) (limited to 'src') diff --git a/src/database-dummy.cpp b/src/database-dummy.cpp index b38db1fb9..ef2148f70 100644 --- a/src/database-dummy.cpp +++ b/src/database-dummy.cpp @@ -30,13 +30,16 @@ bool Database_Dummy::saveBlock(const v3s16 &pos, const std::string &data) return true; } -std::string Database_Dummy::loadBlock(const v3s16 &pos) +void Database_Dummy::loadBlock(const v3s16 &pos, std::string *block) { s64 i = getBlockAsInteger(pos); std::map::iterator it = m_database.find(i); - if (it == m_database.end()) - return ""; - return it->second; + if (it == m_database.end()) { + *block = ""; + return; + } + + *block = it->second; } bool Database_Dummy::deleteBlock(const v3s16 &pos) diff --git a/src/database-dummy.h b/src/database-dummy.h index 0cf56928e..72100edd0 100644 --- a/src/database-dummy.h +++ b/src/database-dummy.h @@ -28,10 +28,10 @@ with this program; if not, write to the Free Software Foundation, Inc., class Database_Dummy : public Database { public: - virtual bool saveBlock(const v3s16 &pos, const std::string &data); - virtual std::string loadBlock(const v3s16 &pos); - virtual bool deleteBlock(const v3s16 &pos); - virtual void listAllLoadableBlocks(std::vector &dst); + bool saveBlock(const v3s16 &pos, const std::string &data); + void loadBlock(const v3s16 &pos, std::string *block); + bool deleteBlock(const v3s16 &pos); + void listAllLoadableBlocks(std::vector &dst); private: std::map m_database; diff --git a/src/database-leveldb.cpp b/src/database-leveldb.cpp index acd0fd1eb..f46f82b98 100644 --- a/src/database-leveldb.cpp +++ b/src/database-leveldb.cpp @@ -65,16 +65,13 @@ bool Database_LevelDB::saveBlock(const v3s16 &pos, const std::string &data) return true; } -std::string Database_LevelDB::loadBlock(const v3s16 &pos) +void Database_LevelDB::loadBlock(const v3s16 &pos, std::string *block) { std::string datastr; leveldb::Status status = m_database->Get(leveldb::ReadOptions(), i64tos(getBlockAsInteger(pos)), &datastr); - if(status.ok()) - return datastr; - else - return ""; + *block = (status.ok()) ? datastr : ""; } bool Database_LevelDB::deleteBlock(const v3s16 &pos) diff --git a/src/database-leveldb.h b/src/database-leveldb.h index 4afe2fdc7..3993db0c3 100644 --- a/src/database-leveldb.h +++ b/src/database-leveldb.h @@ -34,10 +34,10 @@ public: Database_LevelDB(const std::string &savedir); ~Database_LevelDB(); - virtual bool saveBlock(const v3s16 &pos, const std::string &data); - virtual std::string loadBlock(const v3s16 &pos); - virtual bool deleteBlock(const v3s16 &pos); - virtual void listAllLoadableBlocks(std::vector &dst); + bool saveBlock(const v3s16 &pos, const std::string &data); + void loadBlock(const v3s16 &pos, std::string *block); + bool deleteBlock(const v3s16 &pos); + void listAllLoadableBlocks(std::vector &dst); private: leveldb::DB *m_database; diff --git a/src/database-redis.cpp b/src/database-redis.cpp index 498e9d39a..01427b6f9 100644 --- a/src/database-redis.cpp +++ b/src/database-redis.cpp @@ -101,7 +101,7 @@ bool Database_Redis::saveBlock(const v3s16 &pos, const std::string &data) return true; } -std::string Database_Redis::loadBlock(const v3s16 &pos) +void Database_Redis::loadBlock(const v3s16 &pos, std::string *block) { std::string tmp = i64tos(getBlockAsInteger(pos)); redisReply *reply = static_cast(redisCommand(ctx, @@ -111,12 +111,13 @@ std::string Database_Redis::loadBlock(const v3s16 &pos) throw FileNotGoodException(std::string( "Redis command 'HGET %s %s' failed: ") + ctx->errstr); } + switch (reply->type) { case REDIS_REPLY_STRING: { - std::string str(reply->str, reply->len); + *block = std::string(reply->str, reply->len); // std::string copies the memory so this won't cause any problems freeReplyObject(reply); - return str; + return; } case REDIS_REPLY_ERROR: { std::string errstr(reply->str, reply->len); @@ -127,11 +128,13 @@ std::string Database_Redis::loadBlock(const v3s16 &pos) "Redis command 'HGET %s %s' errored: ") + errstr); } case REDIS_REPLY_NIL: { + *block = ""; // block not found in database freeReplyObject(reply); - return ""; + return; } } + errorstream << "loadBlock: loading block " << PP(pos) << " returned invalid reply type " << reply->type << ": " << std::string(reply->str, reply->len) << std::endl; diff --git a/src/database-redis.h b/src/database-redis.h index 45e702c83..3addaa20a 100644 --- a/src/database-redis.h +++ b/src/database-redis.h @@ -36,13 +36,13 @@ public: Database_Redis(Settings &conf); ~Database_Redis(); - virtual void beginSave(); - virtual void endSave(); + void beginSave(); + void endSave(); - virtual bool saveBlock(const v3s16 &pos, const std::string &data); - virtual std::string loadBlock(const v3s16 &pos); - virtual bool deleteBlock(const v3s16 &pos); - virtual void listAllLoadableBlocks(std::vector &dst); + bool saveBlock(const v3s16 &pos, const std::string &data); + void loadBlock(const v3s16 &pos, std::string *block); + bool deleteBlock(const v3s16 &pos); + void listAllLoadableBlocks(std::vector &dst); private: redisContext *ctx; diff --git a/src/database-sqlite3.cpp b/src/database-sqlite3.cpp index 56f937bf2..2a23e2ea0 100644 --- a/src/database-sqlite3.cpp +++ b/src/database-sqlite3.cpp @@ -237,7 +237,7 @@ bool Database_SQLite3::saveBlock(const v3s16 &pos, const std::string &data) return true; } -std::string Database_SQLite3::loadBlock(const v3s16 &pos) +void Database_SQLite3::loadBlock(const v3s16 &pos, std::string *block) { verifyDatabase(); @@ -245,20 +245,17 @@ std::string Database_SQLite3::loadBlock(const v3s16 &pos) if (sqlite3_step(m_stmt_read) != SQLITE_ROW) { sqlite3_reset(m_stmt_read); - return ""; + return; } + const char *data = (const char *) sqlite3_column_blob(m_stmt_read, 0); size_t len = sqlite3_column_bytes(m_stmt_read, 0); - std::string s; - if (data) - s = std::string(data, len); + *block = (data) ? std::string(data, len) : ""; sqlite3_step(m_stmt_read); // We should never get more than 1 row, so ok to reset sqlite3_reset(m_stmt_read); - - return s; } void Database_SQLite3::createDatabase() diff --git a/src/database-sqlite3.h b/src/database-sqlite3.h index 04a1825d9..debbc9d8b 100644 --- a/src/database-sqlite3.h +++ b/src/database-sqlite3.h @@ -31,16 +31,16 @@ class Database_SQLite3 : public Database { public: Database_SQLite3(const std::string &savedir); + ~Database_SQLite3(); - virtual void beginSave(); - virtual void endSave(); + void beginSave(); + void endSave(); - virtual bool saveBlock(const v3s16 &pos, const std::string &data); - virtual std::string loadBlock(const v3s16 &pos); - virtual bool deleteBlock(const v3s16 &pos); - virtual void listAllLoadableBlocks(std::vector &dst); - virtual bool initialized() const { return m_initialized; } - ~Database_SQLite3(); + bool saveBlock(const v3s16 &pos, const std::string &data); + void loadBlock(const v3s16 &pos, std::string *block); + bool deleteBlock(const v3s16 &pos); + void listAllLoadableBlocks(std::vector &dst); + bool initialized() const { return m_initialized; } private: // Open the database diff --git a/src/database.h b/src/database.h index cee7b6fd9..0cf75232f 100644 --- a/src/database.h +++ b/src/database.h @@ -38,7 +38,7 @@ public: virtual void endSave() {} virtual bool saveBlock(const v3s16 &pos, const std::string &data) = 0; - virtual std::string loadBlock(const v3s16 &pos) = 0; + virtual void loadBlock(const v3s16 &pos, std::string *block) = 0; virtual bool deleteBlock(const v3s16 &pos) = 0; static s64 getBlockAsInteger(const v3s16 &pos); diff --git a/src/main.cpp b/src/main.cpp index 1b95a9f1c..f63b6a986 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -948,7 +948,8 @@ static bool migrate_database(const GameParams &game_params, const Settings &cmd_ for (std::vector::const_iterator it = blocks.begin(); it != blocks.end(); ++it) { if (kill) return false; - const std::string &data = old_db->loadBlock(*it); + std::string data; + old_db->loadBlock(*it, &data); if (!data.empty()) { new_db->saveBlock(*it, data); } else { diff --git a/src/map.cpp b/src/map.cpp index 66fabaf87..848d2ef13 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -3442,8 +3442,7 @@ MapBlock* ServerMap::loadBlock(v3s16 blockpos) v2s16 p2d(blockpos.X, blockpos.Z); std::string ret; - - ret = dbase->loadBlock(blockpos); + dbase->loadBlock(blockpos, &ret); if (ret != "") { loadBlock(&ret, blockpos, createSector(p2d), false); return getBlockNoCreateNoEx(blockpos); -- cgit v1.2.3 From 8ba6d9f227398d2004d1fe7ae095f5e342dc6d7b Mon Sep 17 00:00:00 2001 From: nerzhul Date: Tue, 17 May 2016 09:59:02 +0200 Subject: Implement DatabaseException for databases --- src/database-leveldb.cpp | 2 +- src/database-redis.cpp | 20 ++++++++++---------- src/database-sqlite3.cpp | 2 +- src/exceptions.h | 5 +++++ 4 files changed, 17 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/database-leveldb.cpp b/src/database-leveldb.cpp index f46f82b98..4a4904c6a 100644 --- a/src/database-leveldb.cpp +++ b/src/database-leveldb.cpp @@ -33,7 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #define ENSURE_STATUS_OK(s) \ if (!(s).ok()) { \ - throw FileNotGoodException(std::string("LevelDB error: ") + \ + throw DatabaseException(std::string("LevelDB error: ") + \ (s).ToString()); \ } diff --git a/src/database-redis.cpp b/src/database-redis.cpp index 01427b6f9..3bcedad9b 100644 --- a/src/database-redis.cpp +++ b/src/database-redis.cpp @@ -46,11 +46,11 @@ Database_Redis::Database_Redis(Settings &conf) int port = conf.exists("redis_port") ? conf.getU16("redis_port") : 6379; ctx = redisConnect(addr, port); if (!ctx) { - throw FileNotGoodException("Cannot allocate redis context"); + throw DatabaseException("Cannot allocate redis context"); } else if (ctx->err) { std::string err = std::string("Connection error: ") + ctx->errstr; redisFree(ctx); - throw FileNotGoodException(err); + throw DatabaseException(err); } } @@ -62,7 +62,7 @@ Database_Redis::~Database_Redis() void Database_Redis::beginSave() { redisReply *reply = static_cast(redisCommand(ctx, "MULTI")); if (!reply) { - throw FileNotGoodException(std::string( + throw DatabaseException(std::string( "Redis command 'MULTI' failed: ") + ctx->errstr); } freeReplyObject(reply); @@ -71,7 +71,7 @@ void Database_Redis::beginSave() { void Database_Redis::endSave() { redisReply *reply = static_cast(redisCommand(ctx, "EXEC")); if (!reply) { - throw FileNotGoodException(std::string( + throw DatabaseException(std::string( "Redis command 'EXEC' failed: ") + ctx->errstr); } freeReplyObject(reply); @@ -108,7 +108,7 @@ void Database_Redis::loadBlock(const v3s16 &pos, std::string *block) "HGET %s %s", hash.c_str(), tmp.c_str())); if (!reply) { - throw FileNotGoodException(std::string( + throw DatabaseException(std::string( "Redis command 'HGET %s %s' failed: ") + ctx->errstr); } @@ -124,7 +124,7 @@ void Database_Redis::loadBlock(const v3s16 &pos, std::string *block) freeReplyObject(reply); errorstream << "loadBlock: loading block " << PP(pos) << " failed: " << errstr << std::endl; - throw FileNotGoodException(std::string( + throw DatabaseException(std::string( "Redis command 'HGET %s %s' errored: ") + errstr); } case REDIS_REPLY_NIL: { @@ -139,7 +139,7 @@ void Database_Redis::loadBlock(const v3s16 &pos, std::string *block) << " returned invalid reply type " << reply->type << ": " << std::string(reply->str, reply->len) << std::endl; freeReplyObject(reply); - throw FileNotGoodException(std::string( + throw DatabaseException(std::string( "Redis command 'HGET %s %s' gave invalid reply.")); } @@ -150,7 +150,7 @@ bool Database_Redis::deleteBlock(const v3s16 &pos) redisReply *reply = static_cast(redisCommand(ctx, "HDEL %s %s", hash.c_str(), tmp.c_str())); if (!reply) { - throw FileNotGoodException(std::string( + throw DatabaseException(std::string( "Redis command 'HDEL %s %s' failed: ") + ctx->errstr); } else if (reply->type == REDIS_REPLY_ERROR) { warningstream << "deleteBlock: deleting block " << PP(pos) @@ -167,7 +167,7 @@ void Database_Redis::listAllLoadableBlocks(std::vector &dst) { redisReply *reply = static_cast(redisCommand(ctx, "HKEYS %s", hash.c_str())); if (!reply) { - throw FileNotGoodException(std::string( + throw DatabaseException(std::string( "Redis command 'HKEYS %s' failed: ") + ctx->errstr); } switch (reply->type) { @@ -179,7 +179,7 @@ void Database_Redis::listAllLoadableBlocks(std::vector &dst) } break; case REDIS_REPLY_ERROR: - throw FileNotGoodException(std::string( + throw DatabaseException(std::string( "Failed to get keys from database: ") + std::string(reply->str, reply->len)); } diff --git a/src/database-sqlite3.cpp b/src/database-sqlite3.cpp index 2a23e2ea0..07df976d4 100644 --- a/src/database-sqlite3.cpp +++ b/src/database-sqlite3.cpp @@ -47,7 +47,7 @@ SQLite format specification: #define SQLRES(s, r, m) \ if ((s) != (r)) { \ - throw FileNotGoodException(std::string(m) + ": " +\ + throw DatabaseException(std::string(m) + ": " +\ sqlite3_errmsg(m_database)); \ } #define SQLOK(s, m) SQLRES(s, SQLITE_OK, m) diff --git a/src/exceptions.h b/src/exceptions.h index 4f18f70d9..67a2d0df6 100644 --- a/src/exceptions.h +++ b/src/exceptions.h @@ -65,6 +65,11 @@ public: FileNotGoodException(const std::string &s): BaseException(s) {} }; +class DatabaseException : public BaseException { +public: + DatabaseException(const std::string &s): BaseException(s) {} +}; + class SerializationError : public BaseException { public: SerializationError(const std::string &s): BaseException(s) {} -- cgit v1.2.3 From 88acda02567d586820867059718cac551dc7a58b Mon Sep 17 00:00:00 2001 From: Craig Robbins Date: Fri, 20 May 2016 21:18:59 +1000 Subject: Fix tooltip height for versions of irrlicht < 1.8.2 Version 1.8.2 of irrlicht changed the way that IGUIStaticText::getTextHeight() works and since that release properly deals with newlines. From irrlicht changes.txt for 1.8.2, "IGUIStaticText::getTextHeight returns now the correct height for texts with newlines even WordWrap is not set." --- src/guiFormSpecMenu.cpp | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src') diff --git a/src/guiFormSpecMenu.cpp b/src/guiFormSpecMenu.cpp index 2bf06c1d6..b0215a84a 100644 --- a/src/guiFormSpecMenu.cpp +++ b/src/guiFormSpecMenu.cpp @@ -2265,7 +2265,11 @@ void GUIFormSpecMenu::drawList(const ListDrawSpec &s, int phase, this->bringToFront(m_tooltip_element); m_tooltip_element->setText(tooltip_text.c_str()); s32 tooltip_width = m_tooltip_element->getTextWidth() + m_btn_height; +#if IRRLICHT_VERSION_MAJOR >= 1 && IRRLICHT_VERSION_MINOR >= 8 && IRRLICHT_VERSION_REVISION >= 2 s32 tooltip_height = m_tooltip_element->getTextHeight() + 5; +#else + s32 tooltip_height = m_tooltip_element->getTextHeight() * tt_rows.size() + 5; +#endif v2u32 screenSize = driver->getScreenSize(); int tooltip_offset_x = m_btn_height; int tooltip_offset_y = m_btn_height; -- cgit v1.2.3 From ce42ff9cf74ebb8d4b68bc78c95e90ea3db02b78 Mon Sep 17 00:00:00 2001 From: Loic Blot Date: Sat, 14 May 2016 11:00:42 +0200 Subject: Implement a PostgreSQL backend --- README.txt | 5 +- src/CMakeLists.txt | 37 ++++++ src/cmake_config.h.in | 1 + src/database-postgresql.cpp | 286 ++++++++++++++++++++++++++++++++++++++++++ src/database-postgresql.h | 95 ++++++++++++++ src/map.cpp | 7 ++ util/travis/before_install.sh | 4 +- 7 files changed, 433 insertions(+), 2 deletions(-) create mode 100644 src/database-postgresql.cpp create mode 100644 src/database-postgresql.h (limited to 'src') diff --git a/README.txt b/README.txt index 9ca9b331e..bd2dadebb 100644 --- a/README.txt +++ b/README.txt @@ -169,7 +169,8 @@ ENABLE_CURSES - Build with (n)curses; Enables a server side terminal (comm ENABLE_FREETYPE - Build with FreeType2; Allows using TTF fonts ENABLE_GETTEXT - Build with Gettext; Allows using translations ENABLE_GLES - Search for Open GLES headers & libraries and use them -ENABLE_LEVELDB - Build with LevelDB; Enables use of LevelDB map backend (faster than SQLite3) +ENABLE_LEVELDB - Build with LevelDB; Enables use of LevelDB map backend +ENABLE_POSTGRESQL - Build with libpq; Enables use of PostgreSQL map backend (PostgreSQL 9.5 or greater required) ENABLE_REDIS - Build with libhiredis; Enables use of Redis map backend ENABLE_SPATIAL - Build with LibSpatial; Speeds up AreaStores ENABLE_SOUND - Build with OpenAL, libogg & libvorbis; in-game Sounds @@ -203,6 +204,8 @@ IRRLICHT_LIBRARY - Path to libIrrlicht.a/libIrrlicht.so/libIrrlic LEVELDB_INCLUDE_DIR - Only when building with LevelDB; directory that contains db.h LEVELDB_LIBRARY - Only when building with LevelDB; path to libleveldb.a/libleveldb.so/libleveldb.dll.a LEVELDB_DLL - Only when building with LevelDB on Windows; path to libleveldb.dll +POSTGRESQL_INCLUDE_DIR - Only when building with PostgreSQL; directory that contains libpq-fe.h +POSTGRESQL_LIBRARY - Only when building with PostgreSQL; path to libpq.a/libpq.so REDIS_INCLUDE_DIR - Only when building with Redis; directory that contains hiredis.h REDIS_LIBRARY - Only when building with Redis; path to libhiredis.a/libhiredis.so SPATIAL_INCLUDE_DIR - Only when building with LibSpatial; directory that contains spatialindex/SpatialIndex.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index feca199c1..ea1564eeb 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -189,6 +189,36 @@ if(ENABLE_CURSES) endif() endif(ENABLE_CURSES) +option(ENABLE_POSTGRESQL "Enable PostgreSQL backend" TRUE) +set(USE_POSTGRESQL FALSE) + +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 TRUE) + 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}) + else() + message(STATUS "PostgreSQL not found!") + endif() +endif(ENABLE_POSTGRESQL) + option(ENABLE_LEVELDB "Enable LevelDB backend" TRUE) set(USE_LEVELDB FALSE) @@ -361,6 +391,7 @@ set(common_SRCS craftdef.cpp database-dummy.cpp database-leveldb.cpp + database-postgresql.cpp database-redis.cpp database-sqlite3.cpp database.cpp @@ -592,6 +623,9 @@ if(BUILD_CLIENT) if (USE_CURSES) target_link_libraries(${PROJECT_NAME} ${CURSES_LIBRARIES}) endif() + if (USE_POSTGRESQL) + target_link_libraries(${PROJECT_NAME} ${POSTGRESQL_LIBRARY}) + endif() if (USE_LEVELDB) target_link_libraries(${PROJECT_NAME} ${LEVELDB_LIBRARY}) endif() @@ -622,6 +656,9 @@ if(BUILD_SERVER) if (USE_CURSES) target_link_libraries(${PROJECT_NAME}server ${CURSES_LIBRARIES}) endif() + if (USE_POSTGRESQL) + target_link_libraries(${PROJECT_NAME}server ${POSTGRESQL_LIBRARY}) + endif() if (USE_LEVELDB) target_link_libraries(${PROJECT_NAME}server ${LEVELDB_LIBRARY}) endif() diff --git a/src/cmake_config.h.in b/src/cmake_config.h.in index 018532d13..50f34a0b8 100644 --- a/src/cmake_config.h.in +++ b/src/cmake_config.h.in @@ -22,6 +22,7 @@ #cmakedefine01 USE_CURSES #cmakedefine01 USE_LEVELDB #cmakedefine01 USE_LUAJIT +#cmakedefine01 USE_POSTGRESQL #cmakedefine01 USE_SPATIAL #cmakedefine01 USE_SYSTEM_GMP #cmakedefine01 USE_REDIS diff --git a/src/database-postgresql.cpp b/src/database-postgresql.cpp new file mode 100644 index 000000000..3b6b42aea --- /dev/null +++ b/src/database-postgresql.cpp @@ -0,0 +1,286 @@ +/* +Copyright (C) 2016 Loic Blot + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "config.h" + +#if USE_POSTGRESQL + +#include "database-postgresql.h" + +#ifdef _WIN32 + #ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN + #endif + // Without this some of the network functions are not found on mingw + #ifndef _WIN32_WINNT + #define _WIN32_WINNT 0x0501 + #endif + #include + #include +#else +#include +#endif + +#include "log.h" +#include "exceptions.h" +#include "settings.h" + +Database_PostgreSQL::Database_PostgreSQL(const Settings &conf) : + m_connect_string(""), + m_conn(NULL), + m_pgversion(0) +{ + if (!conf.getNoEx("pgsql_connection", m_connect_string)) { + throw SettingNotFoundException( + "Set pgsql_connection string in world.mt to " + "use the postgresql backend\n" + "Notes:\n" + "pgsql_connection has the following form: \n" + "\tpgsql_connection = host=127.0.0.1 port=5432 user=mt_user " + "password=mt_password dbname=minetest_world\n" + "mt_user should have CREATE TABLE, INSERT, SELECT, UPDATE and " + "DELETE rights on the database.\n" + "Don't create mt_user as a SUPERUSER!"); + } + + connectToDatabase(); +} + +Database_PostgreSQL::~Database_PostgreSQL() +{ + PQfinish(m_conn); +} + +void Database_PostgreSQL::connectToDatabase() +{ + m_conn = PQconnectdb(m_connect_string.c_str()); + + if (PQstatus(m_conn) != CONNECTION_OK) { + throw DatabaseException(std::string( + "PostgreSQL database error: ") + + PQerrorMessage(m_conn)); + } + + m_pgversion = PQserverVersion(m_conn); + + /* + * We are using UPSERT feature from PostgreSQL 9.5 + * to have the better performance, + * set the minimum version to 90500 + */ + if (m_pgversion < 90500) { + throw DatabaseException("PostgreSQL database error: " + "Server version 9.5 or greater required."); + } + + infostream << "PostgreSQL Database: Version " << m_pgversion + << " Connection made." << std::endl; + + createDatabase(); + initStatements(); +} + +void Database_PostgreSQL::verifyDatabase() +{ + if (PQstatus(m_conn) == CONNECTION_OK) + return; + + PQreset(m_conn); + ping(); +} + +void Database_PostgreSQL::ping() +{ + if (PQping(m_connect_string.c_str()) != PQPING_OK) { + throw DatabaseException(std::string( + "PostgreSQL database error: ") + + PQerrorMessage(m_conn)); + } +} + +bool Database_PostgreSQL::initialized() const +{ + return (PQstatus(m_conn) == CONNECTION_OK); +} + +void Database_PostgreSQL::initStatements() +{ + prepareStatement("read_block", + "SELECT data FROM blocks " + "WHERE posX = $1::int4 AND posY = $2::int4 AND " + "posZ = $3::int4"); + + prepareStatement("write_block", + "INSERT INTO blocks (posX, posY, posZ, data) VALUES " + "($1::int4, $2::int4, $3::int4, $4::bytea) " + "ON CONFLICT ON CONSTRAINT blocks_pkey DO " + "UPDATE SET data = $4::bytea"); + + prepareStatement("delete_block", "DELETE FROM blocks WHERE " + "posX = $1::int4 AND posY = $2::int4 AND posZ = $3::int4"); + + prepareStatement("list_all_loadable_blocks", + "SELECT posX, posY, posZ FROM blocks"); +} + +PGresult *Database_PostgreSQL::checkResults(PGresult *result, bool clear) +{ + ExecStatusType statusType = PQresultStatus(result); + + switch (statusType) { + case PGRES_COMMAND_OK: + case PGRES_TUPLES_OK: + break; + case PGRES_FATAL_ERROR: + default: + throw DatabaseException( + std::string("PostgreSQL database error: ") + + PQresultErrorMessage(result)); + } + + if (clear) + PQclear(result); + + return result; +} + +void Database_PostgreSQL::createDatabase() +{ + PGresult *result = checkResults(PQexec(m_conn, + "SELECT relname FROM pg_class WHERE relname='blocks';"), + false); + + // If table doesn't exist, create it + if (!PQntuples(result)) { + static const char* dbcreate_sql = "CREATE TABLE blocks (" + "posX INT NOT NULL," + "posY INT NOT NULL," + "posZ INT NOT NULL," + "data BYTEA," + "PRIMARY KEY (posX,posY,posZ)" + ");"; + checkResults(PQexec(m_conn, dbcreate_sql)); + } + + PQclear(result); + + infostream << "PostgreSQL: Game Database was inited." << std::endl; +} + + +void Database_PostgreSQL::beginSave() +{ + verifyDatabase(); + checkResults(PQexec(m_conn, "BEGIN;")); +} + +void Database_PostgreSQL::endSave() +{ + checkResults(PQexec(m_conn, "COMMIT;")); +} + +bool Database_PostgreSQL::saveBlock(const v3s16 &pos, + const std::string &data) +{ + // Verify if we don't overflow the platform integer with the mapblock size + if (data.size() > INT_MAX) { + errorstream << "Database_PostgreSQL::saveBlock: Data truncation! " + << "data.size() over 0xFFFF (== " << data.size() + << ")" << std::endl; + return false; + } + + verifyDatabase(); + + s32 x, y, z; + x = htonl(pos.X); + y = htonl(pos.Y); + z = htonl(pos.Z); + + const void *args[] = { &x, &y, &z, data.c_str() }; + const int argLen[] = { + sizeof(x), sizeof(y), sizeof(z), (int)data.size() + }; + const int argFmt[] = { 1, 1, 1, 1 }; + + execPrepared("write_block", ARRLEN(args), args, argLen, argFmt); + return true; +} + +void Database_PostgreSQL::loadBlock(const v3s16 &pos, + std::string *block) +{ + verifyDatabase(); + + s32 x, y, z; + x = htonl(pos.X); + y = htonl(pos.Y); + z = htonl(pos.Z); + + const void *args[] = { &x, &y, &z }; + const int argLen[] = { sizeof(x), sizeof(y), sizeof(z) }; + const int argFmt[] = { 1, 1, 1 }; + + PGresult *results = execPrepared("read_block", ARRLEN(args), args, + argLen, argFmt, false); + + *block = ""; + + if (PQntuples(results)) { + *block = std::string(PQgetvalue(results, 0, 0), + PQgetlength(results, 0, 0)); + } + + PQclear(results); +} + +bool Database_PostgreSQL::deleteBlock(const v3s16 &pos) +{ + verifyDatabase(); + + s32 x, y, z; + x = htonl(pos.X); + y = htonl(pos.Y); + z = htonl(pos.Z); + + const void *args[] = { &x, &y, &z }; + const int argLen[] = { sizeof(x), sizeof(y), sizeof(z) }; + const int argFmt[] = { 1, 1, 1 }; + + execPrepared("read_block", ARRLEN(args), args, argLen, argFmt); + + return true; +} + +void Database_PostgreSQL::listAllLoadableBlocks(std::vector &dst) +{ + verifyDatabase(); + + PGresult *results = execPrepared("list_all_loadable_blocks", 0, + NULL, NULL, NULL, false, false); + + int numrows = PQntuples(results); + + for (int row = 0; row < numrows; ++row) { + dst.push_back(pg_to_v3s16(results, 0, 0)); + } + + PQclear(results); +} + +#endif // USE_POSTGRESQL diff --git a/src/database-postgresql.h b/src/database-postgresql.h new file mode 100644 index 000000000..1cfa544e3 --- /dev/null +++ b/src/database-postgresql.h @@ -0,0 +1,95 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef DATABASE_POSTGRESQL_HEADER +#define DATABASE_POSTGRESQL_HEADER + +#include +#include +#include "database.h" +#include "util/basic_macros.h" + +class Settings; + +class Database_PostgreSQL : public Database +{ +public: + Database_PostgreSQL(const Settings &conf); + ~Database_PostgreSQL(); + + void beginSave(); + void endSave(); + + bool saveBlock(const v3s16 &pos, const std::string &data); + void loadBlock(const v3s16 &pos, std::string *block); + bool deleteBlock(const v3s16 &pos); + void listAllLoadableBlocks(std::vector &dst); + bool initialized() const; + +private: + // Database initialization + void connectToDatabase(); + void initStatements(); + void createDatabase(); + + inline void prepareStatement(const std::string &name, const std::string &sql) + { + checkResults(PQprepare(m_conn, name.c_str(), sql.c_str(), 0, NULL)); + } + + // Database connectivity checks + void ping(); + void verifyDatabase(); + + // Database usage + PGresult *checkResults(PGresult *res, bool clear = true); + + inline 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) + { + return checkResults(PQexecPrepared(m_conn, stmtName, paramsNumber, + (const char* const*) params, paramsLengths, paramsFormats, + nobinary ? 1 : 0), clear); + } + + // Conversion helpers + inline int pg_to_int(PGresult *res, int row, int col) + { + return atoi(PQgetvalue(res, row, col)); + } + + inline v3s16 pg_to_v3s16(PGresult *res, int row, int col) + { + return v3s16( + pg_to_int(res, row, col), + pg_to_int(res, row, col + 1), + pg_to_int(res, row, col + 2) + ); + } + + // Attributes + std::string m_connect_string; + PGconn *m_conn; + int m_pgversion; +}; + +#endif + diff --git a/src/map.cpp b/src/map.cpp index 848d2ef13..03daf4fa8 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -50,6 +50,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #if USE_REDIS #include "database-redis.h" #endif +#if USE_POSTGRESQL +#include "database-postgresql.h" +#endif #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")" @@ -3240,6 +3243,10 @@ Database *ServerMap::createDatabase( else if (name == "redis") return new Database_Redis(conf); #endif + #if USE_POSTGRESQL + else if (name == "postgresql") + return new Database_PostgreSQL(conf); + #endif else throw BaseException(std::string("Database backend ") + name + " not supported."); } diff --git a/util/travis/before_install.sh b/util/travis/before_install.sh index 58dc42b17..70037389b 100755 --- a/util/travis/before_install.sh +++ b/util/travis/before_install.sh @@ -17,13 +17,15 @@ if [[ $PLATFORM == "Unix" ]]; then if [[ $TRAVIS_OS_NAME == "linux" ]]; then sudo apt-get install libirrlicht-dev cmake libbz2-dev libpng12-dev \ libjpeg-dev libxxf86vm-dev libgl1-mesa-dev libsqlite3-dev \ - libhiredis-dev libogg-dev libgmp-dev libvorbis-dev libopenal-dev gettext + libhiredis-dev libogg-dev libgmp-dev libvorbis-dev libopenal-dev \ + gettext libpq-dev postgresql-server-dev-all # Linking to LevelDB is broken, use a custom build wget http://minetest.kitsunemimi.pw/libleveldb-1.18-ubuntu12.04.7z sudo 7z x -o/usr libleveldb-1.18-ubuntu12.04.7z else brew update brew install freetype gettext hiredis irrlicht jpeg leveldb libogg libvorbis luajit + brew upgrade postgresql fi elif [[ $PLATFORM == "Win32" ]]; then wget http://minetest.kitsunemimi.pw/mingw_w64_i686_ubuntu12.04_4.9.1.7z -O mingw.7z -- cgit v1.2.3 From f64a6259b2b5e79103cfd4bc151304e6da39bca9 Mon Sep 17 00:00:00 2001 From: Loic Blot Date: Sun, 22 May 2016 13:31:41 +0200 Subject: Fix a m_camera not used warning fix pointed by clang --- src/content_cao.cpp | 3 +-- src/content_cao.h | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/content_cao.cpp b/src/content_cao.cpp index d701e4f72..35ab1c508 100644 --- a/src/content_cao.cpp +++ b/src/content_cao.cpp @@ -546,7 +546,6 @@ GenericCAO::GenericCAO(IGameDef *gamedef, ClientEnvironment *env): // m_smgr(NULL), m_irr(NULL), - m_camera(NULL), m_gamedef(NULL), m_selection_box(-BS/3.,-BS/3.,-BS/3., BS/3.,BS/3.,BS/3.), m_meshnode(NULL), @@ -804,7 +803,7 @@ void GenericCAO::removeFromScene(bool permanent) } } -void GenericCAO::addToScene(scene::ISceneManager *smgr, +void GenericCAO::addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc, IrrlichtDevice *irr) { m_smgr = smgr; diff --git a/src/content_cao.h b/src/content_cao.h index a166ff494..bf99fd3ba 100644 --- a/src/content_cao.h +++ b/src/content_cao.h @@ -68,7 +68,6 @@ private: // scene::ISceneManager *m_smgr; IrrlichtDevice *m_irr; - Camera* m_camera; IGameDef *m_gamedef; aabb3f m_selection_box; scene::IMeshSceneNode *m_meshnode; @@ -206,7 +205,7 @@ public: float time_from_last_punch=1000000); std::string debugInfoText(); - + std::string infoText() { return m_prop.infotext; -- cgit v1.2.3 From 423d8c1b0d3841f6ce9756ab0d6b2e10408fc89b Mon Sep 17 00:00:00 2001 From: est31 Date: Wed, 18 May 2016 06:18:08 +0200 Subject: Tolerate packet reordering in the early init process Fixes a bug where packet reordering made the server give the client two peer ids instead of one. This in turn confused reliable packet sending and made connecting to the server fail. The client usually sends three packets at init: one "dummy" packet consisting of two 0 bytes, and the init packet as well as its legacy counterpart. The last one can be turned off since commit af30183124d40a969040d7de4b3a487feec466e4, but this is of lower relevance for the bug. The relevant part here is that network packet reorder (which is a normal occurence) can make the packets reach the server in different order. If reorder puts the dummy packet further behind, the following would happen before the patch: 1. The server will get one of the init packets on channel 1 and assign the client a peer id, as the packet will have zero as peer id. 2. The server sends a CONTROLTYPE_SET_PEER_ID packet to inform the client of the peer id. 3. The next packet from the client will contain the peer id set by the server. 4. The server sets the m_has_sent_with_id member for the client's peer structure to true. 5. Now the dummy packet arrives. It has a peer id of zero, therefore the server searches whether it already has a peer id for the address the packet was sent from. The search fails because m_has_sent_with_id was set to true and the server only searched for peers with m_has_sent_with_id set to false. 6. In a working setup, the server would assign the dummy packet to the correct peer id. However the server instead now assigns a second peer id and peer structure to the peer, and assign the packet to that new peer. 7. In order to inform the peer of its peer id, the server sends a CONTROLTYPE_SET_PEER_ID command packet, reliably, to the peer. This packet uses the new peer id. 8. The client sends an ack to that packet, not with the new peer id but with the peer id sent in 2. 9. This packet reaches the server, but it drops the ACK as the peer id does not map to any un-ACK-ed packets with that seqnum. The same time, the server still waits for an ACK with the new peer id, which of course won't come. This causes the server to periodically re-try sending that packet, and the client ACKing it each time. Steps 7-9 cause annoyances and erroneous output, but don't cause the connection failure itself. The actual mistake that causes the connection failure happens in 6: The server does not assign the dummy packet to the correct peer, but to a newly created one. Therefore, all further packets sent by the client on channel 0 are now buffered by the server as it waits for the dummy packet to reach the peer, which of course doesn't happen as the server assigned that packet to the second peer it created for the client. This makes the connection code indefinitely buffer the TOSERVER_CLIENT_READY packet, not passing it to higher level code, which stalls the continuation of the further init process indefinitely and causes the actual bug. Maybe this can be caused by reordered init packets as well, the only studied case was where network has reliably reordered the dummy packet to get sent after the init packets. The patch fixes the bug by not ignoring peers where m_has_sent_with_id has been set anymore. The other changes of the patch are just cleanups of unused methods and fields and additional explanatory comments. One could think of alternate ways to fix the bug: * The client could simply take the new peer id and continue communicating with that. This is however worse than the fix as it requires the peer id set command to be sent reliably (which currently happens, but it cant be changed anymore). Also, such a change would require both server and client to be patched in order for the bug to be fixed, as right now the client ignores peer id set commands after the peer id is different from PEER_ID_INEXISTENT and the server requires modification too to change the peer id internally. And, most importantly, right now we guarantee higher level server code that the peer id for a certain peer does not change. This guarantee would have to be broken, and it would require much larger changes to the server than this patch means. * One could stop sending the dummy packet. One may be unsure whether this is a good idea, as the meaning of the dummy packet is not known (it might be there for something important), and as it is possible that the init packets may cause this problem as well (although it may be possible too that they can't cause this). Thanks to @auouymous who had originally reported this bug and who has helped patiently in finding its cause. --- src/network/connection.cpp | 13 ++++--------- src/network/connection.h | 19 ++++--------------- 2 files changed, 8 insertions(+), 24 deletions(-) (limited to 'src') diff --git a/src/network/connection.cpp b/src/network/connection.cpp index f7452d8e4..02c0aa165 100644 --- a/src/network/connection.cpp +++ b/src/network/connection.cpp @@ -2167,12 +2167,12 @@ void ConnectionReceiveThread::receive() throw InvalidIncomingDataException("Channel doesn't exist"); } - /* preserve original peer_id for later usage */ - u16 packet_peer_id = peer_id; - /* Try to identify peer by sender address (may happen on join) */ if (peer_id == PEER_ID_INEXISTENT) { peer_id = m_connection->lookupPeer(sender); + // We do not have to remind the peer of its + // peer id as the CONTROLTYPE_SET_PEER_ID + // command was sent reliably. } /* The peer was not found in our lists. Add it. */ @@ -2214,11 +2214,6 @@ void ConnectionReceiveThread::receive() } } - - /* mark peer as seen with id */ - if (!(packet_peer_id == PEER_ID_INEXISTENT)) - peer->setSentWithID(); - peer->ResetTimeout(); Channel *channel = 0; @@ -2756,7 +2751,7 @@ u16 Connection::lookupPeer(Address& sender) for(; j != m_peers.end(); ++j) { Peer *peer = j->second; - if (peer->isActive()) + if (peer->isPendingDeletion()) continue; Address tocheck; diff --git a/src/network/connection.h b/src/network/connection.h index fe2c9819d..5ee53b9d4 100644 --- a/src/network/connection.h +++ b/src/network/connection.h @@ -663,8 +663,7 @@ class Peer { m_last_rtt(-1.0), m_usage(0), m_timeout_counter(0.0), - m_last_timeout_check(porting::getTimeMs()), - m_has_sent_with_id(false) + m_last_timeout_check(porting::getTimeMs()) { m_rtt.avg_rtt = -1.0; m_rtt.jitter_avg = -1.0; @@ -687,21 +686,16 @@ class Peer { virtual void PutReliableSendCommand(ConnectionCommand &c, unsigned int max_packet_size) {}; - virtual bool isActive() { return false; }; - virtual bool getAddress(MTProtocols type, Address& toset) = 0; + bool isPendingDeletion() + { MutexAutoLock lock(m_exclusive_access_mutex); return m_pending_deletion; }; + void ResetTimeout() {MutexAutoLock lock(m_exclusive_access_mutex); m_timeout_counter=0.0; }; bool isTimedOut(float timeout); - void setSentWithID() - { MutexAutoLock lock(m_exclusive_access_mutex); m_has_sent_with_id = true; }; - - bool hasSentWithID() - { MutexAutoLock lock(m_exclusive_access_mutex); return m_has_sent_with_id; }; - unsigned int m_increment_packets_remaining; unsigned int m_increment_bytes_remaining; @@ -776,8 +770,6 @@ class Peer { float m_timeout_counter; u32 m_last_timeout_check; - - bool m_has_sent_with_id; }; class UDPPeer : public Peer @@ -795,9 +787,6 @@ public: void PutReliableSendCommand(ConnectionCommand &c, unsigned int max_packet_size); - bool isActive() - { return ((hasSentWithID()) && (!m_pending_deletion)); }; - bool getAddress(MTProtocols type, Address& toset); void setNonLegacyPeer(); -- cgit v1.2.3 From 725edc78b214f8ffa5494ed846755083295c3feb Mon Sep 17 00:00:00 2001 From: Ekdohibs Date: Sun, 22 May 2016 20:33:06 +0200 Subject: Move updateTextures and fillTileAttribs to ContentFeatures --- src/nodedef.cpp | 534 +++++++++++++++++++++++++++++--------------------------- src/nodedef.h | 29 +++ 2 files changed, 302 insertions(+), 261 deletions(-) (limited to 'src') diff --git a/src/nodedef.cpp b/src/nodedef.cpp index 3a2cb00b1..1691289c2 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -247,6 +247,28 @@ static void deSerializeSimpleSoundSpec(SimpleSoundSpec &ss, std::istream &is) ss.gain = readF1000(is); } +void TextureSettings::readSettings() +{ + connected_glass = g_settings->getBool("connected_glass"); + opaque_water = g_settings->getBool("opaque_water"); + bool enable_shaders = g_settings->getBool("enable_shaders"); + bool enable_bumpmapping = g_settings->getBool("enable_bumpmapping"); + bool enable_parallax_occlusion = g_settings->getBool("enable_parallax_occlusion"); + enable_mesh_cache = g_settings->getBool("enable_mesh_cache"); + enable_minimap = g_settings->getBool("enable_minimap"); + std::string leaves_style_str = g_settings->get("leaves_style"); + + use_normal_texture = enable_shaders && + (enable_bumpmapping || enable_parallax_occlusion); + if (leaves_style_str == "fancy") { + leaves_style = LEAVES_FANCY; + } else if (leaves_style_str == "simple") { + leaves_style = LEAVES_SIMPLE; + } else { + leaves_style = LEAVES_OPAQUE; + } +} + /* ContentFeatures */ @@ -486,6 +508,254 @@ void ContentFeatures::deSerialize(std::istream &is) }catch(SerializationError &e) {}; } +#ifndef SERVER +void ContentFeatures::fillTileAttribs(ITextureSource *tsrc, TileSpec *tile, + TileDef *tiledef, u32 shader_id, bool use_normal_texture, + bool backface_culling, u8 alpha, u8 material_type) +{ + tile->shader_id = shader_id; + tile->texture = tsrc->getTextureForMesh(tiledef->name, &tile->texture_id); + tile->alpha = alpha; + tile->material_type = material_type; + + // Normal texture and shader flags texture + if (use_normal_texture) { + tile->normal_texture = tsrc->getNormalTexture(tiledef->name); + } + tile->flags_texture = tsrc->getShaderFlagsTexture(tile->normal_texture ? true : false); + + // Material flags + tile->material_flags = 0; + if (backface_culling) + tile->material_flags |= MATERIAL_FLAG_BACKFACE_CULLING; + if (tiledef->animation.type == TAT_VERTICAL_FRAMES) + tile->material_flags |= MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES; + if (tiledef->tileable_horizontal) + tile->material_flags |= MATERIAL_FLAG_TILEABLE_HORIZONTAL; + if (tiledef->tileable_vertical) + tile->material_flags |= MATERIAL_FLAG_TILEABLE_VERTICAL; + + // Animation parameters + int frame_count = 1; + if (tile->material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES) { + // Get texture size to determine frame count by aspect ratio + v2u32 size = tile->texture->getOriginalSize(); + int frame_height = (float)size.X / + (float)tiledef->animation.aspect_w * + (float)tiledef->animation.aspect_h; + frame_count = size.Y / frame_height; + int frame_length_ms = 1000.0 * tiledef->animation.length / frame_count; + tile->animation_frame_count = frame_count; + tile->animation_frame_length_ms = frame_length_ms; + } + + if (frame_count == 1) { + tile->material_flags &= ~MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES; + } else { + std::ostringstream os(std::ios::binary); + tile->frames.resize(frame_count); + + for (int i = 0; i < frame_count; i++) { + + FrameSpec frame; + + os.str(""); + os << tiledef->name << "^[verticalframe:" + << frame_count << ":" << i; + + frame.texture = tsrc->getTextureForMesh(os.str(), &frame.texture_id); + if (tile->normal_texture) + frame.normal_texture = tsrc->getNormalTexture(os.str()); + frame.flags_texture = tile->flags_texture; + tile->frames[i] = frame; + } + } +} +#endif + +#ifndef SERVER +void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc, + scene::ISceneManager *smgr, scene::IMeshManipulator *meshmanip, + IGameDef *gamedef, const TextureSettings &tsettings) +{ + // minimap pixel color - the average color of a texture + if (tsettings.enable_minimap && tiledef[0].name != "") + minimap_color = tsrc->getTextureAverageColor(tiledef[0].name); + + // Figure out the actual tiles to use + TileDef tdef[6]; + for (u32 j = 0; j < 6; j++) { + tdef[j] = tiledef[j]; + if (tdef[j].name == "") + tdef[j].name = "unknown_node.png"; + } + + bool is_liquid = false; + bool is_water_surface = false; + + u8 material_type = (alpha == 255) ? + TILE_MATERIAL_BASIC : TILE_MATERIAL_ALPHA; + + switch (drawtype) { + default: + case NDT_NORMAL: + solidness = 2; + break; + case NDT_AIRLIKE: + solidness = 0; + break; + case NDT_LIQUID: + assert(liquid_type == LIQUID_SOURCE); + if (tsettings.opaque_water) + alpha = 255; + solidness = 1; + is_liquid = true; + break; + case NDT_FLOWINGLIQUID: + assert(liquid_type == LIQUID_FLOWING); + solidness = 0; + if (tsettings.opaque_water) + alpha = 255; + is_liquid = true; + break; + case NDT_GLASSLIKE: + solidness = 0; + visual_solidness = 1; + break; + case NDT_GLASSLIKE_FRAMED: + solidness = 0; + visual_solidness = 1; + break; + case NDT_GLASSLIKE_FRAMED_OPTIONAL: + solidness = 0; + visual_solidness = 1; + drawtype = tsettings.connected_glass ? NDT_GLASSLIKE_FRAMED : NDT_GLASSLIKE; + break; + case NDT_ALLFACES: + solidness = 0; + visual_solidness = 1; + break; + case NDT_ALLFACES_OPTIONAL: + if (tsettings.leaves_style == LEAVES_FANCY) { + drawtype = NDT_ALLFACES; + solidness = 0; + visual_solidness = 1; + } else if (tsettings.leaves_style == LEAVES_SIMPLE) { + for (u32 j = 0; j < 6; j++) { + if (tiledef_special[j].name != "") + tdef[j].name = tiledef_special[j].name; + } + drawtype = NDT_GLASSLIKE; + solidness = 0; + visual_solidness = 1; + } else { + drawtype = NDT_NORMAL; + solidness = 2; + for (u32 i = 0; i < 6; i++) + tdef[i].name += std::string("^[noalpha"); + } + if (waving == 1) + material_type = TILE_MATERIAL_WAVING_LEAVES; + break; + case NDT_PLANTLIKE: + solidness = 0; + if (waving == 1) + material_type = TILE_MATERIAL_WAVING_PLANTS; + break; + case NDT_FIRELIKE: + solidness = 0; + break; + case NDT_MESH: + solidness = 0; + break; + case NDT_TORCHLIKE: + case NDT_SIGNLIKE: + case NDT_FENCELIKE: + case NDT_RAILLIKE: + case NDT_NODEBOX: + solidness = 0; + break; + } + + if (is_liquid) { + material_type = (alpha == 255) ? + TILE_MATERIAL_LIQUID_OPAQUE : TILE_MATERIAL_LIQUID_TRANSPARENT; + if (name == "default:water_source") + is_water_surface = true; + } + + u32 tile_shader[6]; + for (u16 j = 0; j < 6; j++) { + tile_shader[j] = shdsrc->getShader("nodes_shader", + material_type, drawtype); + } + + if (is_water_surface) { + tile_shader[0] = shdsrc->getShader("water_surface_shader", + material_type, drawtype); + } + + // Tiles (fill in f->tiles[]) + for (u16 j = 0; j < 6; j++) { + fillTileAttribs(tsrc, &tiles[j], &tdef[j], tile_shader[j], + tsettings.use_normal_texture, + tiledef[j].backface_culling, alpha, material_type); + } + + // Special tiles (fill in f->special_tiles[]) + for (u16 j = 0; j < CF_SPECIAL_COUNT; j++) { + fillTileAttribs(tsrc, &special_tiles[j], &tiledef_special[j], + tile_shader[j], tsettings.use_normal_texture, + tiledef_special[j].backface_culling, alpha, material_type); + } + + if ((drawtype == NDT_MESH) && (mesh != "")) { + // Meshnode drawtype + // Read the mesh and apply scale + mesh_ptr[0] = gamedef->getMesh(mesh); + if (mesh_ptr[0]){ + v3f scale = v3f(1.0, 1.0, 1.0) * BS * visual_scale; + scaleMesh(mesh_ptr[0], scale); + recalculateBoundingBox(mesh_ptr[0]); + meshmanip->recalculateNormals(mesh_ptr[0], true, false); + } + } else if ((drawtype == NDT_NODEBOX) && + ((node_box.type == NODEBOX_REGULAR) || + (node_box.type == NODEBOX_FIXED)) && + (!node_box.fixed.empty())) { + //Convert regular nodebox nodes to meshnodes + //Change the drawtype and apply scale + drawtype = NDT_MESH; + mesh_ptr[0] = convertNodeboxesToMesh(node_box.fixed); + v3f scale = v3f(1.0, 1.0, 1.0) * visual_scale; + scaleMesh(mesh_ptr[0], scale); + recalculateBoundingBox(mesh_ptr[0]); + meshmanip->recalculateNormals(mesh_ptr[0], true, false); + } + + //Cache 6dfacedir and wallmounted rotated clones of meshes + if (tsettings.enable_mesh_cache && mesh_ptr[0] && (param_type_2 == CPT2_FACEDIR)) { + for (u16 j = 1; j < 24; j++) { + mesh_ptr[j] = cloneMesh(mesh_ptr[0]); + rotateMeshBy6dFacedir(mesh_ptr[j], j); + recalculateBoundingBox(mesh_ptr[j]); + meshmanip->recalculateNormals(mesh_ptr[j], true, false); + } + } else if (tsettings.enable_mesh_cache && mesh_ptr[0] && (param_type_2 == CPT2_WALLMOUNTED)) { + static const u8 wm_to_6d[6] = {20, 0, 16+1, 12+3, 8, 4+2}; + for (u16 j = 1; j < 6; j++) { + mesh_ptr[j] = cloneMesh(mesh_ptr[0]); + rotateMeshBy6dFacedir(mesh_ptr[j], wm_to_6d[j]); + recalculateBoundingBox(mesh_ptr[j]); + meshmanip->recalculateNormals(mesh_ptr[j], true, false); + } + rotateMeshBy6dFacedir(mesh_ptr[0], wm_to_6d[0]); + recalculateBoundingBox(mesh_ptr[0]); + meshmanip->recalculateNormals(mesh_ptr[0], true, false); + } +} +#endif + /* CNodeDefManager */ @@ -525,11 +795,6 @@ public: private: void addNameIdMapping(content_t i, std::string name); -#ifndef SERVER - void fillTileAttribs(ITextureSource *tsrc, TileSpec *tile, TileDef *tiledef, - u32 shader_id, bool use_normal_texture, bool backface_culling, - u8 alpha, u8 material_type); -#endif // Features indexed by id std::vector m_content_features; @@ -890,271 +1155,18 @@ void CNodeDefManager::updateTextures(IGameDef *gamedef, IShaderSource *shdsrc = gamedef->getShaderSource(); scene::ISceneManager* smgr = gamedef->getSceneManager(); scene::IMeshManipulator* meshmanip = smgr->getMeshManipulator(); - - bool connected_glass = g_settings->getBool("connected_glass"); - bool opaque_water = g_settings->getBool("opaque_water"); - bool enable_shaders = g_settings->getBool("enable_shaders"); - bool enable_bumpmapping = g_settings->getBool("enable_bumpmapping"); - bool enable_parallax_occlusion = g_settings->getBool("enable_parallax_occlusion"); - bool enable_mesh_cache = g_settings->getBool("enable_mesh_cache"); - bool enable_minimap = g_settings->getBool("enable_minimap"); - std::string leaves_style = g_settings->get("leaves_style"); - - bool use_normal_texture = enable_shaders && - (enable_bumpmapping || enable_parallax_occlusion); + TextureSettings tsettings; + tsettings.readSettings(); u32 size = m_content_features.size(); for (u32 i = 0; i < size; i++) { - ContentFeatures *f = &m_content_features[i]; - - // minimap pixel color - the average color of a texture - if (enable_minimap && f->tiledef[0].name != "") - f->minimap_color = tsrc->getTextureAverageColor(f->tiledef[0].name); - - // Figure out the actual tiles to use - TileDef tiledef[6]; - for (u32 j = 0; j < 6; j++) { - tiledef[j] = f->tiledef[j]; - if (tiledef[j].name == "") - tiledef[j].name = "unknown_node.png"; - } - - bool is_liquid = false; - bool is_water_surface = false; - - u8 material_type = (f->alpha == 255) ? - TILE_MATERIAL_BASIC : TILE_MATERIAL_ALPHA; - - switch (f->drawtype) { - default: - case NDT_NORMAL: - f->solidness = 2; - break; - case NDT_AIRLIKE: - f->solidness = 0; - break; - case NDT_LIQUID: - assert(f->liquid_type == LIQUID_SOURCE); - if (opaque_water) - f->alpha = 255; - f->solidness = 1; - is_liquid = true; - break; - case NDT_FLOWINGLIQUID: - assert(f->liquid_type == LIQUID_FLOWING); - f->solidness = 0; - if (opaque_water) - f->alpha = 255; - is_liquid = true; - break; - case NDT_GLASSLIKE: - f->solidness = 0; - f->visual_solidness = 1; - break; - case NDT_GLASSLIKE_FRAMED: - f->solidness = 0; - f->visual_solidness = 1; - break; - case NDT_GLASSLIKE_FRAMED_OPTIONAL: - f->solidness = 0; - f->visual_solidness = 1; - f->drawtype = connected_glass ? NDT_GLASSLIKE_FRAMED : NDT_GLASSLIKE; - break; - case NDT_ALLFACES: - f->solidness = 0; - f->visual_solidness = 1; - break; - case NDT_ALLFACES_OPTIONAL: - if (leaves_style == "fancy") { - f->drawtype = NDT_ALLFACES; - f->solidness = 0; - f->visual_solidness = 1; - } else if (leaves_style == "simple") { - for (u32 j = 0; j < 6; j++) { - if (f->tiledef_special[j].name != "") - tiledef[j].name = f->tiledef_special[j].name; - } - f->drawtype = NDT_GLASSLIKE; - f->solidness = 0; - f->visual_solidness = 1; - } else { - f->drawtype = NDT_NORMAL; - f->solidness = 2; - for (u32 i = 0; i < 6; i++) - tiledef[i].name += std::string("^[noalpha"); - } - if (f->waving == 1) - material_type = TILE_MATERIAL_WAVING_LEAVES; - break; - case NDT_PLANTLIKE: - f->solidness = 0; - if (f->waving == 1) - material_type = TILE_MATERIAL_WAVING_PLANTS; - break; - case NDT_FIRELIKE: - f->solidness = 0; - break; - case NDT_MESH: - f->solidness = 0; - break; - case NDT_TORCHLIKE: - case NDT_SIGNLIKE: - case NDT_FENCELIKE: - case NDT_RAILLIKE: - case NDT_NODEBOX: - f->solidness = 0; - break; - } - - if (is_liquid) { - material_type = (f->alpha == 255) ? - TILE_MATERIAL_LIQUID_OPAQUE : TILE_MATERIAL_LIQUID_TRANSPARENT; - if (f->name == "default:water_source") - is_water_surface = true; - } - - u32 tile_shader[6]; - for (u16 j = 0; j < 6; j++) { - tile_shader[j] = shdsrc->getShader("nodes_shader", - material_type, f->drawtype); - } - - if (is_water_surface) { - tile_shader[0] = shdsrc->getShader("water_surface_shader", - material_type, f->drawtype); - } - - // Tiles (fill in f->tiles[]) - for (u16 j = 0; j < 6; j++) { - fillTileAttribs(tsrc, &f->tiles[j], &tiledef[j], tile_shader[j], - use_normal_texture, f->tiledef[j].backface_culling, f->alpha, material_type); - } - - // Special tiles (fill in f->special_tiles[]) - for (u16 j = 0; j < CF_SPECIAL_COUNT; j++) { - fillTileAttribs(tsrc, &f->special_tiles[j], &f->tiledef_special[j], - tile_shader[j], use_normal_texture, - f->tiledef_special[j].backface_culling, f->alpha, material_type); - } - - if ((f->drawtype == NDT_MESH) && (f->mesh != "")) { - // Meshnode drawtype - // Read the mesh and apply scale - f->mesh_ptr[0] = gamedef->getMesh(f->mesh); - if (f->mesh_ptr[0]){ - v3f scale = v3f(1.0, 1.0, 1.0) * BS * f->visual_scale; - scaleMesh(f->mesh_ptr[0], scale); - recalculateBoundingBox(f->mesh_ptr[0]); - meshmanip->recalculateNormals(f->mesh_ptr[0], true, false); - } - } else if ((f->drawtype == NDT_NODEBOX) && - ((f->node_box.type == NODEBOX_REGULAR) || - (f->node_box.type == NODEBOX_FIXED)) && - (!f->node_box.fixed.empty())) { - //Convert regular nodebox nodes to meshnodes - //Change the drawtype and apply scale - f->drawtype = NDT_MESH; - f->mesh_ptr[0] = convertNodeboxesToMesh(f->node_box.fixed); - v3f scale = v3f(1.0, 1.0, 1.0) * f->visual_scale; - scaleMesh(f->mesh_ptr[0], scale); - recalculateBoundingBox(f->mesh_ptr[0]); - meshmanip->recalculateNormals(f->mesh_ptr[0], true, false); - } - - //Cache 6dfacedir and wallmounted rotated clones of meshes - if (enable_mesh_cache && f->mesh_ptr[0] && (f->param_type_2 == CPT2_FACEDIR)) { - for (u16 j = 1; j < 24; j++) { - f->mesh_ptr[j] = cloneMesh(f->mesh_ptr[0]); - rotateMeshBy6dFacedir(f->mesh_ptr[j], j); - recalculateBoundingBox(f->mesh_ptr[j]); - meshmanip->recalculateNormals(f->mesh_ptr[j], true, false); - } - } else if (enable_mesh_cache && f->mesh_ptr[0] && (f->param_type_2 == CPT2_WALLMOUNTED)) { - static const u8 wm_to_6d[6] = {20, 0, 16+1, 12+3, 8, 4+2}; - for (u16 j = 1; j < 6; j++) { - f->mesh_ptr[j] = cloneMesh(f->mesh_ptr[0]); - rotateMeshBy6dFacedir(f->mesh_ptr[j], wm_to_6d[j]); - recalculateBoundingBox(f->mesh_ptr[j]); - meshmanip->recalculateNormals(f->mesh_ptr[j], true, false); - } - rotateMeshBy6dFacedir(f->mesh_ptr[0], wm_to_6d[0]); - recalculateBoundingBox(f->mesh_ptr[0]); - meshmanip->recalculateNormals(f->mesh_ptr[0], true, false); - } - + m_content_features[i].updateTextures(tsrc, shdsrc, smgr, meshmanip, gamedef, tsettings); progress_callback(progress_callback_args, i, size); } #endif } - -#ifndef SERVER -void CNodeDefManager::fillTileAttribs(ITextureSource *tsrc, TileSpec *tile, - TileDef *tiledef, u32 shader_id, bool use_normal_texture, - bool backface_culling, u8 alpha, u8 material_type) -{ - tile->shader_id = shader_id; - tile->texture = tsrc->getTextureForMesh(tiledef->name, &tile->texture_id); - tile->alpha = alpha; - tile->material_type = material_type; - - // Normal texture and shader flags texture - if (use_normal_texture) { - tile->normal_texture = tsrc->getNormalTexture(tiledef->name); - } - tile->flags_texture = tsrc->getShaderFlagsTexture(tile->normal_texture ? true : false); - - // Material flags - tile->material_flags = 0; - if (backface_culling) - tile->material_flags |= MATERIAL_FLAG_BACKFACE_CULLING; - if (tiledef->animation.type == TAT_VERTICAL_FRAMES) - tile->material_flags |= MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES; - if (tiledef->tileable_horizontal) - tile->material_flags |= MATERIAL_FLAG_TILEABLE_HORIZONTAL; - if (tiledef->tileable_vertical) - tile->material_flags |= MATERIAL_FLAG_TILEABLE_VERTICAL; - - // Animation parameters - int frame_count = 1; - if (tile->material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES) { - // Get texture size to determine frame count by aspect ratio - v2u32 size = tile->texture->getOriginalSize(); - int frame_height = (float)size.X / - (float)tiledef->animation.aspect_w * - (float)tiledef->animation.aspect_h; - frame_count = size.Y / frame_height; - int frame_length_ms = 1000.0 * tiledef->animation.length / frame_count; - tile->animation_frame_count = frame_count; - tile->animation_frame_length_ms = frame_length_ms; - } - - if (frame_count == 1) { - tile->material_flags &= ~MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES; - } else { - std::ostringstream os(std::ios::binary); - tile->frames.resize(frame_count); - - for (int i = 0; i < frame_count; i++) { - - FrameSpec frame; - - os.str(""); - os << tiledef->name << "^[verticalframe:" - << frame_count << ":" << i; - - frame.texture = tsrc->getTextureForMesh(os.str(), &frame.texture_id); - if (tile->normal_texture) - frame.normal_texture = tsrc->getNormalTexture(os.str()); - frame.flags_texture = tile->flags_texture; - tile->frames[i] = frame; - } - } -} -#endif - - void CNodeDefManager::serialize(std::ostream &os, u16 protocol_version) const { writeU8(os, 1); // version diff --git a/src/nodedef.h b/src/nodedef.h index 58d0faffa..1c2f792d8 100644 --- a/src/nodedef.h +++ b/src/nodedef.h @@ -112,6 +112,26 @@ struct NodeBox struct MapNode; class NodeMetadata; +enum LeavesStyle { + LEAVES_FANCY, + LEAVES_SIMPLE, + LEAVES_OPAQUE, +}; + +class TextureSettings { +public: + LeavesStyle leaves_style; + bool opaque_water; + bool connected_glass; + bool use_normal_texture; + bool enable_mesh_cache; + bool enable_minimap; + + TextureSettings() {} + + void readSettings(); +}; + enum NodeDrawType { NDT_NORMAL, // A basic solid block @@ -304,6 +324,15 @@ struct ContentFeatures if(!isLiquid() || !f.isLiquid()) return false; return (liquid_alternative_flowing == f.liquid_alternative_flowing); } + +#ifndef SERVER + void fillTileAttribs(ITextureSource *tsrc, TileSpec *tile, TileDef *tiledef, + u32 shader_id, bool use_normal_texture, bool backface_culling, + u8 alpha, u8 material_type); + void updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc, + scene::ISceneManager *smgr, scene::IMeshManipulator *meshmanip, + IGameDef *gamedef, const TextureSettings &tsettings); +#endif }; class INodeDefManager { -- cgit v1.2.3 From 22f78ea38e415c40ef445786b77788a40f4ddac3 Mon Sep 17 00:00:00 2001 From: Craig Robbins Date: Tue, 24 May 2016 00:27:11 +1000 Subject: Fix irrlicht version checking macro for tooltip_height calculation --- src/guiFormSpecMenu.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/guiFormSpecMenu.cpp b/src/guiFormSpecMenu.cpp index b0215a84a..99b1153c1 100644 --- a/src/guiFormSpecMenu.cpp +++ b/src/guiFormSpecMenu.cpp @@ -2265,10 +2265,10 @@ void GUIFormSpecMenu::drawList(const ListDrawSpec &s, int phase, this->bringToFront(m_tooltip_element); m_tooltip_element->setText(tooltip_text.c_str()); s32 tooltip_width = m_tooltip_element->getTextWidth() + m_btn_height; -#if IRRLICHT_VERSION_MAJOR >= 1 && IRRLICHT_VERSION_MINOR >= 8 && IRRLICHT_VERSION_REVISION >= 2 - s32 tooltip_height = m_tooltip_element->getTextHeight() + 5; -#else +#if IRRLICHT_VERSION_MAJOR <= 1 && IRRLICHT_VERSION_MINOR <= 8 && IRRLICHT_VERSION_REVISION < 2 s32 tooltip_height = m_tooltip_element->getTextHeight() * tt_rows.size() + 5; +#else + s32 tooltip_height = m_tooltip_element->getTextHeight() + 5; #endif v2u32 screenSize = driver->getScreenSize(); int tooltip_offset_x = m_btn_height; -- cgit v1.2.3 From ef100f12a1346f7bfd2a05dc606cb22e97179c02 Mon Sep 17 00:00:00 2001 From: ShadowNinja Date: Fri, 12 Feb 2016 18:39:03 -0500 Subject: Fix rollback.txt migration Broken by b1965ac20922e3722392114bd63a22b403dcbe98. This also prepares the begin and commit statements only once. --- src/rollback.cpp | 41 +++++++++++++++++++++++++++-------------- src/rollback.h | 2 +- 2 files changed, 28 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/rollback.cpp b/src/rollback.cpp index 2367c3a21..ac4324cab 100644 --- a/src/rollback.cpp +++ b/src/rollback.cpp @@ -93,10 +93,10 @@ RollbackManager::RollbackManager(const std::string & world_path, std::string migrating_flag = txt_filename + ".migrating"; database_path = world_path + DIR_DELIM "rollback.sqlite"; - initDatabase(); + bool created = initDatabase(); - if (fs::PathExists(txt_filename) && (fs::PathExists(migrating_flag) || - !fs::PathExists(database_path))) { + if (fs::PathExists(txt_filename) && (created || + fs::PathExists(migrating_flag))) { std::ofstream of(migrating_flag.c_str()); of.close(); migrate(txt_filename); @@ -250,15 +250,15 @@ bool RollbackManager::createTables() } -void RollbackManager::initDatabase() +bool RollbackManager::initDatabase() { verbosestream << "RollbackManager: Database connection setup" << std::endl; - bool needsCreate = !fs::PathExists(database_path); + bool needs_create = !fs::PathExists(database_path); SQLOK(sqlite3_open_v2(database_path.c_str(), &db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL)); - if (needsCreate) { + if (needs_create) { createTables(); } @@ -374,6 +374,8 @@ void RollbackManager::initDatabase() ); } SQLOK(sqlite3_reset(stmt_knownNode_select)); + + return needs_create; } @@ -672,23 +674,27 @@ void RollbackManager::migrate(const std::string & file_path) std::streampos file_size = fh.tellg(); - if (file_size > 10) { + if (file_size < 10) { errorstream << "Empty rollback log." << std::endl; return; } fh.seekg(0); + sqlite3_stmt *stmt_begin; + sqlite3_stmt *stmt_commit; + SQLOK(sqlite3_prepare_v2(db, "BEGIN", -1, &stmt_begin, NULL)); + SQLOK(sqlite3_prepare_v2(db, "COMMIT", -1, &stmt_commit, NULL)); + std::string bit; int i = 0; - int id = 1; time_t start = time(0); time_t t = start; - sqlite3_exec(db, "BEGIN", NULL, NULL, NULL); + SQLRES(sqlite3_step(stmt_begin), SQLITE_DONE); + sqlite3_reset(stmt_begin); do { ActionRow row; - - row.id = id; + row.id = 0; // Get the timestamp std::getline(fh, bit, ' '); @@ -758,17 +764,24 @@ void RollbackManager::migrate(const std::string & file_path) ++i; if (time(0) - t >= 1) { - sqlite3_exec(db, "COMMIT", NULL, NULL, NULL); + SQLRES(sqlite3_step(stmt_commit), SQLITE_DONE); + sqlite3_reset(stmt_commit); t = time(0); std::cout << " Done: " << static_cast((static_cast(fh.tellg()) / static_cast(file_size)) * 100) << "%" << " Speed: " << i / (t - start) << "/second \r" << std::flush; - sqlite3_exec(db, "BEGIN", NULL, NULL, NULL); + SQLRES(sqlite3_step(stmt_begin), SQLITE_DONE); + sqlite3_reset(stmt_begin); } } while (fh.good()); + SQLRES(sqlite3_step(stmt_commit), SQLITE_DONE); + sqlite3_reset(stmt_commit); + + SQLOK(sqlite3_finalize(stmt_begin)); + SQLOK(sqlite3_finalize(stmt_commit)); std::cout - << " Done: 100% " << std::endl + << " Done: 100% " << std::endl << "Now you can delete the old rollback.txt file." << std::endl; } diff --git a/src/rollback.h b/src/rollback.h index c57e38ab0..a05ef8b78 100644 --- a/src/rollback.h +++ b/src/rollback.h @@ -61,7 +61,7 @@ private: const char * getActorName(const int id); const char * getNodeName(const int id); bool createTables(); - void initDatabase(); + bool initDatabase(); bool registerRow(const ActionRow & row); const std::list actionRowsFromSelect(sqlite3_stmt * stmt); ActionRow actionRowFromRollbackAction(const RollbackAction & action); -- cgit v1.2.3 From fa6b21a15b415cd82dce6896b94a5341b7dd76f0 Mon Sep 17 00:00:00 2001 From: est31 Date: Sat, 14 May 2016 16:25:57 +0200 Subject: Tell irrlicht if we handle a key or not. We can remove the function in MtNativeActivity now as it serves precisely that purpose: to tell irrlicht that we handled the esc key. TODO for later: * Perhaps try to find a more performant container than KeyList --- .../net.minetest.minetest/MtNativeActivity.java | 5 --- src/client/inputhandler.h | 37 +++++++++++++++++++--- src/game.cpp | 23 +++++++++++++- src/game.h | 3 ++ 4 files changed, 57 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/build/android/src/main/java/net.minetest.minetest/MtNativeActivity.java b/build/android/src/main/java/net.minetest.minetest/MtNativeActivity.java index 3173a71f4..159521a50 100644 --- a/build/android/src/main/java/net.minetest.minetest/MtNativeActivity.java +++ b/build/android/src/main/java/net.minetest.minetest/MtNativeActivity.java @@ -19,11 +19,6 @@ public class MtNativeActivity extends NativeActivity { public void onDestroy() { super.onDestroy(); } - - @Override - public void onBackPressed() { - } - public void copyAssets() { Intent intent = new Intent(this, MinetestAssetCopy.class); diff --git a/src/client/inputhandler.h b/src/client/inputhandler.h index 69e4b25fa..73e507fdc 100644 --- a/src/client/inputhandler.h +++ b/src/client/inputhandler.h @@ -42,11 +42,15 @@ public: // Remember whether each key is down or up if (event.EventType == irr::EET_KEY_INPUT_EVENT) { - if (event.KeyInput.PressedDown) { - keyIsDown.set(event.KeyInput); - keyWasDown.set(event.KeyInput); - } else { - keyIsDown.unset(event.KeyInput); + const KeyPress &keyCode = event.KeyInput; + if (keysListenedFor[keyCode]) { + if (event.KeyInput.PressedDown) { + keyIsDown.set(keyCode); + keyWasDown.set(keyCode); + } else { + keyIsDown.unset(keyCode); + } + return true; } } @@ -116,6 +120,15 @@ public: return b; } + void listenForKey(const KeyPress &keyCode) + { + keysListenedFor.set(keyCode); + } + void dontListenForKeys() + { + keysListenedFor.clear(); + } + s32 getMouseWheel() { s32 a = mouse_wheel; @@ -168,6 +181,12 @@ private: KeyList keyIsDown; // Whether a key has been pressed or not KeyList keyWasDown; + // List of keys we listen for + // TODO perhaps the type of this is not really + // performant as KeyList is designed for few but + // often changing keys, and keysListenedFor is expected + // to change seldomly but contain lots of keys. + KeyList keysListenedFor; }; @@ -192,6 +211,14 @@ public: { return m_receiver->WasKeyDown(keyCode); } + virtual void listenForKey(const KeyPress &keyCode) + { + m_receiver->listenForKey(keyCode); + } + virtual void dontListenForKeys() + { + m_receiver->dontListenForKeys(); + } virtual v2s32 getMousePos() { if (m_device->getCursorControl()) { diff --git a/src/game.cpp b/src/game.cpp index 23f261cfd..c5211a042 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -1297,7 +1297,11 @@ static void updateChat(Client &client, f32 dtime, bool show_debug, */ struct KeyCache { - KeyCache() { populate(); } + KeyCache() + { + handler = NULL; + populate(); + } enum { // Player movement @@ -1349,6 +1353,7 @@ struct KeyCache { void populate(); KeyPress key[KEYMAP_INTERNAL_ENUM_COUNT]; + InputHandler *handler; }; void KeyCache::populate() @@ -1399,6 +1404,19 @@ void KeyCache::populate() key[KEYMAP_ID_QUICKTUNE_DEC] = getKeySetting("keymap_quicktune_dec"); key[KEYMAP_ID_DEBUG_STACKS] = getKeySetting("keymap_print_debug_stacks"); + + if (handler) { + // First clear all keys, then re-add the ones we listen for + handler->dontListenForKeys(); + for (size_t i = 0; i < KEYMAP_INTERNAL_ENUM_COUNT; i++) { + handler->listenForKey(key[i]); + } + handler->listenForKey(EscapeKey); + handler->listenForKey(CancelKey); + for (size_t i = 0; i < 10; i++) { + handler->listenForKey(NumberKey[i]); + } + } } @@ -1829,6 +1847,9 @@ bool Game::startup(bool *kill, this->chat_backend = chat_backend; this->simple_singleplayer_mode = simple_singleplayer_mode; + keycache.handler = input; + keycache.populate(); + driver = device->getVideoDriver(); smgr = device->getSceneManager(); diff --git a/src/game.h b/src/game.h index e1f4e9346..5465ecdc6 100644 --- a/src/game.h +++ b/src/game.h @@ -110,6 +110,9 @@ public: virtual bool isKeyDown(const KeyPress &keyCode) = 0; virtual bool wasKeyDown(const KeyPress &keyCode) = 0; + virtual void listenForKey(const KeyPress &keyCode) {} + virtual void dontListenForKeys() {} + virtual v2s32 getMousePos() = 0; virtual void setMousePos(s32 x, s32 y) = 0; -- cgit v1.2.3 From 76f485647983ebd7eb4c3abbca0869d13f76920b Mon Sep 17 00:00:00 2001 From: kwolekr Date: Thu, 28 Apr 2016 03:43:09 -0400 Subject: Move biome calculation to BiomeGen BiomeGen defines an interface that, given a set of BiomeParams, computes biomes for a given area using the algorithm implemented by that specific BiomeGen. This abstracts away the old system where each mapgen supplied the noises required for biome generation. --- src/emerge.cpp | 2 - src/mapgen.cpp | 38 +++++---- src/mapgen.h | 21 +++-- src/mapgen_flat.cpp | 48 ++++-------- src/mapgen_flat.h | 2 +- src/mapgen_fractal.cpp | 48 ++++-------- src/mapgen_fractal.h | 7 +- src/mapgen_v5.cpp | 46 +++-------- src/mapgen_v5.h | 7 +- src/mapgen_v7.cpp | 61 ++++----------- src/mapgen_v7.h | 8 +- src/mapgen_valleys.cpp | 70 +++++++---------- src/mapgen_valleys.h | 11 +-- src/mg_biome.cpp | 169 +++++++++++++++++++++++++++++++--------- src/mg_biome.h | 155 ++++++++++++++++++++++++++++++++++-- src/script/lua_api/l_mapgen.cpp | 16 ++-- 16 files changed, 413 insertions(+), 296 deletions(-) (limited to 'src') diff --git a/src/emerge.cpp b/src/emerge.cpp index 93e8f2b30..a2a10a177 100644 --- a/src/emerge.cpp +++ b/src/emerge.cpp @@ -181,8 +181,6 @@ EmergeManager::~EmergeManager() delete oremgr; delete decomgr; delete schemmgr; - - delete params.sparams; } diff --git a/src/mapgen.cpp b/src/mapgen.cpp index b3c9380a0..e269bf454 100644 --- a/src/mapgen.cpp +++ b/src/mapgen.cpp @@ -76,10 +76,9 @@ Mapgen::Mapgen() vm = NULL; ndef = NULL; - heightmap = NULL; + biomegen = NULL; biomemap = NULL; - heatmap = NULL; - humidmap = NULL; + heightmap = NULL; } @@ -94,11 +93,10 @@ Mapgen::Mapgen(int mapgenid, MapgenParams *params, EmergeManager *emerge) : csize = v3s16(1, 1, 1) * (params->chunksize * MAP_BLOCKSIZE); vm = NULL; - ndef = NULL; - heightmap = NULL; + ndef = emerge->ndef; + biomegen = NULL; biomemap = NULL; - heatmap = NULL; - humidmap = NULL; + heightmap = NULL; } @@ -444,6 +442,14 @@ void GenerateNotifier::getEvents( //// MapgenParams //// + +MapgenParams::~MapgenParams() +{ + delete bparams; + delete sparams; +} + + void MapgenParams::load(const Settings &settings) { std::string seed_str; @@ -458,10 +464,13 @@ void MapgenParams::load(const Settings &settings) settings.getS16NoEx("water_level", water_level); settings.getS16NoEx("chunksize", chunksize); settings.getFlagStrNoEx("mg_flags", flags, flagdesc_mapgen); - settings.getNoiseParams("mg_biome_np_heat", np_biome_heat); - settings.getNoiseParams("mg_biome_np_heat_blend", np_biome_heat_blend); - settings.getNoiseParams("mg_biome_np_humidity", np_biome_humidity); - settings.getNoiseParams("mg_biome_np_humidity_blend", np_biome_humidity_blend); + + delete bparams; + bparams = BiomeManager::createBiomeParams(BIOMEGEN_ORIGINAL); + if (bparams) { + bparams->readParams(&settings); + bparams->seed = seed; + } delete sparams; MapgenFactory *mgfactory = EmergeManager::getMapgenFactory(mg_name); @@ -479,10 +488,9 @@ void MapgenParams::save(Settings &settings) const settings.setS16("water_level", water_level); settings.setS16("chunksize", chunksize); settings.setFlagStr("mg_flags", flags, flagdesc_mapgen, U32_MAX); - settings.setNoiseParams("mg_biome_np_heat", np_biome_heat); - settings.setNoiseParams("mg_biome_np_heat_blend", np_biome_heat_blend); - settings.setNoiseParams("mg_biome_np_humidity", np_biome_humidity); - settings.setNoiseParams("mg_biome_np_humidity_blend", np_biome_humidity_blend); + + if (bparams) + bparams->writeParams(&settings); if (sparams) sparams->writeParams(&settings); diff --git a/src/mapgen.h b/src/mapgen.h index abc3d2e89..554ec6084 100644 --- a/src/mapgen.h +++ b/src/mapgen.h @@ -44,6 +44,8 @@ extern FlagDesc flagdesc_mapgen[]; extern FlagDesc flagdesc_gennotify[]; class Biome; +class BiomeGen; +struct BiomeParams; class EmergeManager; class MapBlock; class VoxelManipulator; @@ -115,11 +117,7 @@ struct MapgenParams { s16 water_level; u32 flags; - NoiseParams np_biome_heat; - NoiseParams np_biome_heat_blend; - NoiseParams np_biome_humidity; - NoiseParams np_biome_humidity_blend; - + BiomeParams *bparams; MapgenSpecificParams *sparams; MapgenParams() : @@ -128,12 +126,12 @@ struct MapgenParams { seed(0), water_level(1), flags(MG_CAVES | MG_LIGHT | MG_DECORATIONS), - np_biome_heat(NoiseParams(50, 50, v3f(750.0, 750.0, 750.0), 5349, 3, 0.5, 2.0)), - np_biome_heat_blend(NoiseParams(0, 1.5, v3f(8.0, 8.0, 8.0), 13, 2, 1.0, 2.0)), - np_biome_humidity(NoiseParams(50, 50, v3f(750.0, 750.0, 750.0), 842, 3, 0.5, 2.0)), - np_biome_humidity_blend(NoiseParams(0, 1.5, v3f(8.0, 8.0, 8.0), 90003, 2, 1.0, 2.0)), + bparams(NULL), sparams(NULL) - {} + { + } + + virtual ~MapgenParams(); void load(const Settings &settings); void save(Settings &settings) const; @@ -153,10 +151,9 @@ public: u32 blockseed; s16 *heightmap; u8 *biomemap; - float *heatmap; - float *humidmap; v3s16 csize; + BiomeGen *biomegen; GenerateNotifier gennotify; Mapgen(); diff --git a/src/mapgen_flat.cpp b/src/mapgen_flat.cpp index 4669f1716..cb9aa9a8b 100644 --- a/src/mapgen_flat.cpp +++ b/src/mapgen_flat.cpp @@ -61,10 +61,7 @@ MapgenFlat::MapgenFlat(int mapgenid, MapgenParams *params, EmergeManager *emerge // 1-down overgeneration this->zstride_1d = csize.X * (csize.Y + 1); - this->biomemap = new u8[csize.X * csize.Z]; - this->heightmap = new s16[csize.X * csize.Z]; - this->heatmap = NULL; - this->humidmap = NULL; + this->heightmap = new s16[csize.X * csize.Z]; MapgenFlatParams *sp = (MapgenFlatParams *)params->sparams; @@ -86,15 +83,12 @@ MapgenFlat::MapgenFlat(int mapgenid, MapgenParams *params, EmergeManager *emerge noise_cave1 = new Noise(&sp->np_cave1, seed, csize.X, csize.Y + 1, csize.Z); noise_cave2 = new Noise(&sp->np_cave2, seed, csize.X, csize.Y + 1, csize.Z); - //// Biome noise - noise_heat = new Noise(¶ms->np_biome_heat, seed, csize.X, csize.Z); - noise_humidity = new Noise(¶ms->np_biome_humidity, seed, csize.X, csize.Z); - noise_heat_blend = new Noise(¶ms->np_biome_heat_blend, seed, csize.X, csize.Z); - noise_humidity_blend = new Noise(¶ms->np_biome_humidity_blend, seed, csize.X, csize.Z); + //// Initialize biome generator + biomegen = emerge->biomemgr->createBiomeGen( + BIOMEGEN_ORIGINAL, params->bparams, csize); + biomemap = biomegen->biomemap; //// Resolve nodes to be used - INodeDefManager *ndef = emerge->ndef; - c_stone = ndef->getId("mapgen_stone"); c_water_source = ndef->getId("mapgen_water_source"); c_lava_source = ndef->getId("mapgen_lava_source"); @@ -128,13 +122,9 @@ MapgenFlat::~MapgenFlat() delete noise_cave1; delete noise_cave2; - delete noise_heat; - delete noise_humidity; - delete noise_heat_blend; - delete noise_humidity_blend; + delete biomegen; delete[] heightmap; - delete[] biomemap; } @@ -252,12 +242,10 @@ void MapgenFlat::makeChunk(BlockMakeData *data) // Create heightmap updateHeightmap(node_min, node_max); - // Create biomemap at heightmap surface - bmgr->calcBiomes(csize.X, csize.Z, noise_heat->result, - noise_humidity->result, heightmap, biomemap); - - // Actually place the biome-specific nodes - MgStoneType stone_type = generateBiomes(noise_heat->result, noise_humidity->result); + // Init biome generator, place biome-specific nodes, and build biomemap + biomegen->calcBiomeNoise(node_min); + biomegen->getBiomes(heightmap); + MgStoneType stone_type = generateBiomes(); if (flags & MG_CAVES) generateCaves(stone_surface_max_y); @@ -343,18 +331,7 @@ void MapgenFlat::calculateNoise() // only if solid terrain is present in mapchunk noise_filler_depth->perlinMap2D(x, z); - noise_heat->perlinMap2D(x, z); - noise_humidity->perlinMap2D(x, z); - noise_heat_blend->perlinMap2D(x, z); - noise_humidity_blend->perlinMap2D(x, z); - - for (s32 i = 0; i < csize.X * csize.Z; i++) { - noise_heat->result[i] += noise_heat_blend->result[i]; - noise_humidity->result[i] += noise_humidity_blend->result[i]; - } - heatmap = noise_heat->result; - humidmap = noise_humidity->result; //printf("calculateNoise: %dus\n", t.stop()); } @@ -406,7 +383,7 @@ s16 MapgenFlat::generateTerrain() } -MgStoneType MapgenFlat::generateBiomes(float *heat_map, float *humidity_map) +MgStoneType MapgenFlat::generateBiomes() { v3s16 em = vm->m_area.getExtent(); u32 index = 0; @@ -443,7 +420,8 @@ MgStoneType MapgenFlat::generateBiomes(float *heat_map, float *humidity_map) // 3. When stone or water is detected but biome has not yet been calculated. if ((c == c_stone && (air_above || water_above || !biome)) || (c == c_water_source && (air_above || !biome))) { - biome = bmgr->getBiome(heat_map[index], humidity_map[index], y); + biome = biomegen->getBiomeAtIndex(index, y); + depth_top = biome->depth_top; base_filler = MYMAX(depth_top + biome->depth_filler + noise_filler_depth->result[index], 0); diff --git a/src/mapgen_flat.h b/src/mapgen_flat.h index 8aed09be5..53fd66a67 100644 --- a/src/mapgen_flat.h +++ b/src/mapgen_flat.h @@ -104,7 +104,7 @@ public: int getSpawnLevelAtPoint(v2s16 p); void calculateNoise(); s16 generateTerrain(); - MgStoneType generateBiomes(float *heat_map, float *humidity_map); + MgStoneType generateBiomes(); void dustTopNodes(); void generateCaves(s16 max_stone_y); }; diff --git a/src/mapgen_fractal.cpp b/src/mapgen_fractal.cpp index e2e29f875..9671a2143 100644 --- a/src/mapgen_fractal.cpp +++ b/src/mapgen_fractal.cpp @@ -59,10 +59,7 @@ MapgenFractal::MapgenFractal(int mapgenid, MapgenParams *params, EmergeManager * // 1-down overgeneration this->zstride_1d = csize.X * (csize.Y + 1); - this->biomemap = new u8[csize.X * csize.Z]; this->heightmap = new s16[csize.X * csize.Z]; - this->heatmap = NULL; - this->humidmap = NULL; MapgenFractalParams *sp = (MapgenFractalParams *)params->sparams; @@ -87,18 +84,15 @@ MapgenFractal::MapgenFractal(int mapgenid, MapgenParams *params, EmergeManager * noise_cave1 = new Noise(&sp->np_cave1, seed, csize.X, csize.Y + 1, csize.Z); noise_cave2 = new Noise(&sp->np_cave2, seed, csize.X, csize.Y + 1, csize.Z); - //// Biome noise - noise_heat = new Noise(¶ms->np_biome_heat, seed, csize.X, csize.Z); - noise_humidity = new Noise(¶ms->np_biome_humidity, seed, csize.X, csize.Z); - noise_heat_blend = new Noise(¶ms->np_biome_heat_blend, seed, csize.X, csize.Z); - noise_humidity_blend = new Noise(¶ms->np_biome_humidity_blend, seed, csize.X, csize.Z); + //// Initialize biome generator + biomegen = emerge->biomemgr->createBiomeGen( + BIOMEGEN_ORIGINAL, params->bparams, csize); + biomemap = biomegen->biomemap; this->formula = fractal / 2 + fractal % 2; this->julia = fractal % 2 == 0; //// Resolve nodes to be used - INodeDefManager *ndef = emerge->ndef; - c_stone = ndef->getId("mapgen_stone"); c_water_source = ndef->getId("mapgen_water_source"); c_lava_source = ndef->getId("mapgen_lava_source"); @@ -132,13 +126,9 @@ MapgenFractal::~MapgenFractal() delete noise_cave1; delete noise_cave2; - delete noise_heat; - delete noise_humidity; - delete noise_heat_blend; - delete noise_humidity_blend; + delete biomegen; delete[] heightmap; - delete[] biomemap; } @@ -217,7 +207,7 @@ int MapgenFractal::getSpawnLevelAtPoint(v2s16 p) s16 search_start = MYMAX(seabed_level, water_level + 1); if (seabed_level > water_level) solid_below = true; - + for (s16 y = search_start; y <= search_start + 128; y++) { if (getFractalAtPoint(p.X, y, p.Y)) { // Fractal node solid_below = true; @@ -268,12 +258,10 @@ void MapgenFractal::makeChunk(BlockMakeData *data) // Create heightmap updateHeightmap(node_min, node_max); - // Create biomemap at heightmap surface - bmgr->calcBiomes(csize.X, csize.Z, noise_heat->result, - noise_humidity->result, heightmap, biomemap); - - // Actually place the biome-specific nodes - MgStoneType stone_type = generateBiomes(noise_heat->result, noise_humidity->result); + // Init biome generator, place biome-specific nodes, and build biomemap + biomegen->calcBiomeNoise(node_min); + biomegen->getBiomes(heightmap); + MgStoneType stone_type = generateBiomes(); if (flags & MG_CAVES) generateCaves(stone_surface_max_y); @@ -358,18 +346,7 @@ void MapgenFractal::calculateNoise() // only if solid terrain is present in mapchunk noise_filler_depth->perlinMap2D(x, z); - noise_heat->perlinMap2D(x, z); - noise_humidity->perlinMap2D(x, z); - noise_heat_blend->perlinMap2D(x, z); - noise_humidity_blend->perlinMap2D(x, z); - - for (s32 i = 0; i < csize.X * csize.Z; i++) { - noise_heat->result[i] += noise_heat_blend->result[i]; - noise_humidity->result[i] += noise_humidity_blend->result[i]; - } - heatmap = noise_heat->result; - humidmap = noise_humidity->result; //printf("calculateNoise: %dus\n", t.stop()); } @@ -530,7 +507,7 @@ s16 MapgenFractal::generateTerrain() } -MgStoneType MapgenFractal::generateBiomes(float *heat_map, float *humidity_map) +MgStoneType MapgenFractal::generateBiomes() { v3s16 em = vm->m_area.getExtent(); u32 index = 0; @@ -567,7 +544,8 @@ MgStoneType MapgenFractal::generateBiomes(float *heat_map, float *humidity_map) // 3. When stone or water is detected but biome has not yet been calculated. if ((c == c_stone && (air_above || water_above || !biome)) || (c == c_water_source && (air_above || !biome))) { - biome = bmgr->getBiome(heat_map[index], humidity_map[index], y); + biome = biomegen->getBiomeAtIndex(index, y); + depth_top = biome->depth_top; base_filler = MYMAX(depth_top + biome->depth_filler + noise_filler_depth->result[index], 0); diff --git a/src/mapgen_fractal.h b/src/mapgen_fractal.h index dd96045e2..a0e51b2c6 100644 --- a/src/mapgen_fractal.h +++ b/src/mapgen_fractal.h @@ -88,11 +88,6 @@ public: Noise *noise_cave1; Noise *noise_cave2; - Noise *noise_heat; - Noise *noise_humidity; - Noise *noise_heat_blend; - Noise *noise_humidity_blend; - content_t c_stone; content_t c_water_source; content_t c_lava_source; @@ -114,7 +109,7 @@ public: void calculateNoise(); bool getFractalAtPoint(s16 x, s16 y, s16 z); s16 generateTerrain(); - MgStoneType generateBiomes(float *heat_map, float *humidity_map); + MgStoneType generateBiomes(); void dustTopNodes(); void generateCaves(s16 max_stone_y); }; diff --git a/src/mapgen_v5.cpp b/src/mapgen_v5.cpp index b98acb928..829a1f432 100644 --- a/src/mapgen_v5.cpp +++ b/src/mapgen_v5.cpp @@ -57,10 +57,7 @@ MapgenV5::MapgenV5(int mapgenid, MapgenParams *params, EmergeManager *emerge) // 1-down overgeneration this->zstride_1d = csize.X * (csize.Y + 1); - this->biomemap = new u8[csize.X * csize.Z]; this->heightmap = new s16[csize.X * csize.Z]; - this->heatmap = NULL; - this->humidmap = NULL; MapgenV5Params *sp = (MapgenV5Params *)params->sparams; @@ -79,15 +76,12 @@ MapgenV5::MapgenV5(int mapgenid, MapgenParams *params, EmergeManager *emerge) noise_cave1 = new Noise(&sp->np_cave1, seed, csize.X, csize.Y + 1, csize.Z); noise_cave2 = new Noise(&sp->np_cave2, seed, csize.X, csize.Y + 1, csize.Z); - // Biome noise - noise_heat = new Noise(¶ms->np_biome_heat, seed, csize.X, csize.Z); - noise_humidity = new Noise(¶ms->np_biome_humidity, seed, csize.X, csize.Z); - noise_heat_blend = new Noise(¶ms->np_biome_heat_blend, seed, csize.X, csize.Z); - noise_humidity_blend = new Noise(¶ms->np_biome_humidity_blend, seed, csize.X, csize.Z); + //// Initialize biome generator + biomegen = emerge->biomemgr->createBiomeGen( + BIOMEGEN_ORIGINAL, params->bparams, csize); + biomemap = biomegen->biomemap; //// Resolve nodes to be used - INodeDefManager *ndef = emerge->ndef; - c_stone = ndef->getId("mapgen_stone"); c_water_source = ndef->getId("mapgen_water_source"); c_lava_source = ndef->getId("mapgen_lava_source"); @@ -123,13 +117,9 @@ MapgenV5::~MapgenV5() delete noise_cave2; delete noise_ground; - delete noise_heat; - delete noise_humidity; - delete noise_heat_blend; - delete noise_humidity_blend; + delete biomegen; delete[] heightmap; - delete[] biomemap; } @@ -248,12 +238,10 @@ void MapgenV5::makeChunk(BlockMakeData *data) // Create heightmap updateHeightmap(node_min, node_max); - // Create biomemap at heightmap surface - bmgr->calcBiomes(csize.X, csize.Z, noise_heat->result, - noise_humidity->result, heightmap, biomemap); - - // Actually place the biome-specific nodes - MgStoneType stone_type = generateBiomes(noise_heat->result, noise_humidity->result); + // Init biome generator, place biome-specific nodes, and build biomemap + biomegen->calcBiomeNoise(node_min); + biomegen->getBiomes(heightmap); + MgStoneType stone_type = generateBiomes(); // Generate caves if ((flags & MG_CAVES) && (stone_surface_max_y >= node_min.Y)) @@ -343,18 +331,7 @@ void MapgenV5::calculateNoise() // only if solid terrain is present in mapchunk noise_filler_depth->perlinMap2D(x, z); - noise_heat->perlinMap2D(x, z); - noise_humidity->perlinMap2D(x, z); - noise_heat_blend->perlinMap2D(x, z); - noise_humidity_blend->perlinMap2D(x, z); - - for (s32 i = 0; i < csize.X * csize.Z; i++) { - noise_heat->result[i] += noise_heat_blend->result[i]; - noise_humidity->result[i] += noise_humidity_blend->result[i]; - } - heatmap = noise_heat->result; - humidmap = noise_humidity->result; //printf("calculateNoise: %dus\n", t.stop()); } @@ -416,7 +393,7 @@ int MapgenV5::generateBaseTerrain() } -MgStoneType MapgenV5::generateBiomes(float *heat_map, float *humidity_map) +MgStoneType MapgenV5::generateBiomes() { v3s16 em = vm->m_area.getExtent(); u32 index = 0; @@ -452,7 +429,8 @@ MgStoneType MapgenV5::generateBiomes(float *heat_map, float *humidity_map) // 3. When stone or water is detected but biome has not yet been calculated. if ((c == c_stone && (air_above || water_above || !biome)) || (c == c_water_source && (air_above || !biome))) { - biome = bmgr->getBiome(heat_map[index], humidity_map[index], y); + biome = biomegen->getBiomeAtIndex(index, y); + depth_top = biome->depth_top; base_filler = MYMAX(depth_top + biome->depth_filler + noise_filler_depth->result[index], 0); diff --git a/src/mapgen_v5.h b/src/mapgen_v5.h index fd2f7f4d8..eef3da617 100644 --- a/src/mapgen_v5.h +++ b/src/mapgen_v5.h @@ -70,11 +70,6 @@ public: Noise *noise_cave2; Noise *noise_ground; - Noise *noise_heat; - Noise *noise_humidity; - Noise *noise_heat_blend; - Noise *noise_humidity_blend; - content_t c_stone; content_t c_water_source; content_t c_lava_source; @@ -95,7 +90,7 @@ public: int getSpawnLevelAtPoint(v2s16 p); void calculateNoise(); int generateBaseTerrain(); - MgStoneType generateBiomes(float *heat_map, float *humidity_map); + MgStoneType generateBiomes(); void generateCaves(int max_stone_y); void dustTopNodes(); }; diff --git a/src/mapgen_v7.cpp b/src/mapgen_v7.cpp index 9fb65f577..fddb34682 100644 --- a/src/mapgen_v7.cpp +++ b/src/mapgen_v7.cpp @@ -64,10 +64,7 @@ MapgenV7::MapgenV7(int mapgenid, MapgenParams *params, EmergeManager *emerge) // 1-down overgeneration this->zstride_1d = csize.X * (csize.Y + 1); - this->biomemap = new u8[csize.X * csize.Z]; this->heightmap = new s16[csize.X * csize.Z]; - this->heatmap = NULL; - this->humidmap = NULL; this->ridge_heightmap = new s16[csize.X * csize.Z]; MapgenV7Params *sp = (MapgenV7Params *)params->sparams; @@ -92,15 +89,13 @@ MapgenV7::MapgenV7(int mapgenid, MapgenParams *params, EmergeManager *emerge) noise_cave1 = new Noise(&sp->np_cave1, seed, csize.X, csize.Y + 1, csize.Z); noise_cave2 = new Noise(&sp->np_cave2, seed, csize.X, csize.Y + 1, csize.Z); - //// Biome noise - noise_heat = new Noise(¶ms->np_biome_heat, seed, csize.X, csize.Z); - noise_humidity = new Noise(¶ms->np_biome_humidity, seed, csize.X, csize.Z); - noise_heat_blend = new Noise(¶ms->np_biome_heat_blend, seed, csize.X, csize.Z); - noise_humidity_blend = new Noise(¶ms->np_biome_humidity_blend, seed, csize.X, csize.Z); + // TODO(hmmmm): should we have a way to disable biomemanager biomes? + //// Initialize biome generator + biomegen = emerge->biomemgr->createBiomeGen( + BIOMEGEN_ORIGINAL, params->bparams, csize); + biomemap = biomegen->biomemap; //// Resolve nodes to be used - INodeDefManager *ndef = emerge->ndef; - c_stone = ndef->getId("mapgen_stone"); c_water_source = ndef->getId("mapgen_water_source"); c_lava_source = ndef->getId("mapgen_lava_source"); @@ -141,14 +136,10 @@ MapgenV7::~MapgenV7() delete noise_cave1; delete noise_cave2; - delete noise_heat; - delete noise_humidity; - delete noise_heat_blend; - delete noise_humidity_blend; + delete biomegen; delete[] ridge_heightmap; delete[] heightmap; - delete[] biomemap; } @@ -279,12 +270,10 @@ void MapgenV7::makeChunk(BlockMakeData *data) // Update heightmap to include mountain terrain updateHeightmap(node_min, node_max); - // Create biomemap at heightmap surface - bmgr->calcBiomes(csize.X, csize.Z, noise_heat->result, - noise_humidity->result, heightmap, biomemap); - - // Actually place the biome-specific nodes - MgStoneType stone_type = generateBiomes(noise_heat->result, noise_humidity->result); + // Init biome generator, place biome-specific nodes, and build biomemap + biomegen->calcBiomeNoise(node_min); + biomegen->getBiomes(heightmap); + MgStoneType stone_type = generateBiomes(); if (flags & MG_CAVES) generateCaves(stone_surface_max_y); @@ -384,35 +373,10 @@ void MapgenV7::calculateNoise() // Cave noises are calculated in generateCaves() // only if solid terrain is present in mapchunk - noise_filler_depth->perlinMap2D(x, z); - noise_heat->perlinMap2D(x, z); - noise_humidity->perlinMap2D(x, z); - noise_heat_blend->perlinMap2D(x, z); - noise_humidity_blend->perlinMap2D(x, z); - - for (s32 i = 0; i < csize.X * csize.Z; i++) { - noise_heat->result[i] += noise_heat_blend->result[i]; - noise_humidity->result[i] += noise_humidity_blend->result[i]; - } - - heatmap = noise_heat->result; - humidmap = noise_humidity->result; //printf("calculateNoise: %dus\n", t.stop()); } -Biome *MapgenV7::getBiomeAtPoint(v3s16 p) -{ - float heat = NoisePerlin2D(&noise_heat->np, p.X, p.Z, seed) + - NoisePerlin2D(&noise_heat_blend->np, p.X, p.Z, seed); - float humidity = NoisePerlin2D(&noise_humidity->np, p.X, p.Z, seed) + - NoisePerlin2D(&noise_humidity_blend->np, p.X, p.Z, seed); - s16 groundlevel = baseTerrainLevelAtPoint(p.X, p.Z); - - return bmgr->getBiome(heat, humidity, groundlevel); -} - - float MapgenV7::baseTerrainLevelAtPoint(s16 x, s16 z) { float hselect = NoisePerlin2D(&noise_height_select->np, x, z, seed); @@ -553,7 +517,7 @@ void MapgenV7::generateRidgeTerrain() } -MgStoneType MapgenV7::generateBiomes(float *heat_map, float *humidity_map) +MgStoneType MapgenV7::generateBiomes() { v3s16 em = vm->m_area.getExtent(); u32 index = 0; @@ -589,7 +553,8 @@ MgStoneType MapgenV7::generateBiomes(float *heat_map, float *humidity_map) // 3. When stone or water is detected but biome has not yet been calculated. if ((c == c_stone && (air_above || water_above || !biome)) || (c == c_water_source && (air_above || !biome))) { - biome = bmgr->getBiome(heat_map[index], humidity_map[index], y); + biome = biomegen->getBiomeAtIndex(index, y); + depth_top = biome->depth_top; base_filler = MYMAX(depth_top + biome->depth_filler + noise_filler_depth->result[index], 0); diff --git a/src/mapgen_v7.h b/src/mapgen_v7.h index c25220646..a8dd9986d 100644 --- a/src/mapgen_v7.h +++ b/src/mapgen_v7.h @@ -84,11 +84,6 @@ public: Noise *noise_cave1; Noise *noise_cave2; - Noise *noise_heat; - Noise *noise_humidity; - Noise *noise_heat_blend; - Noise *noise_humidity_blend; - content_t c_stone; content_t c_water_source; content_t c_lava_source; @@ -107,7 +102,6 @@ public: virtual void makeChunk(BlockMakeData *data); int getSpawnLevelAtPoint(v2s16 p); - Biome *getBiomeAtPoint(v3s16 p); float baseTerrainLevelAtPoint(s16 x, s16 z); float baseTerrainLevelFromMap(int index); @@ -119,7 +113,7 @@ public: int generateTerrain(); void generateRidgeTerrain(); - MgStoneType generateBiomes(float *heat_map, float *humidity_map); + MgStoneType generateBiomes(); void dustTopNodes(); void generateCaves(s16 max_stone_y); diff --git a/src/mapgen_valleys.cpp b/src/mapgen_valleys.cpp index 0ec5409cb..44083d9d3 100644 --- a/src/mapgen_valleys.cpp +++ b/src/mapgen_valleys.cpp @@ -68,7 +68,7 @@ MapgenValleys::MapgenValleys(int mapgenid, MapgenParams *params, EmergeManager * : Mapgen(mapgenid, params, emerge) { this->m_emerge = emerge; - this->bmgr = emerge->biomemgr; + this->bmgr = emerge->biomemgr; //// amount of elements to skip for the next index //// for noise/height/biome maps (not vmanip) @@ -77,15 +77,13 @@ MapgenValleys::MapgenValleys(int mapgenid, MapgenParams *params, EmergeManager * // 1-down overgeneration this->zstride_1d = csize.X * (csize.Y + 1); - this->biomemap = new u8[csize.X * csize.Z]; this->heightmap = new s16[csize.X * csize.Z]; - this->heatmap = NULL; - this->humidmap = NULL; this->map_gen_limit = MYMIN(MAX_MAP_GENERATION_LIMIT, g_settings->getU16("map_generation_limit")); MapgenValleysParams *sp = (MapgenValleysParams *)params->sparams; + BiomeParamsOriginal *bp = (BiomeParamsOriginal *)params->bparams; this->spflags = sp->spflags; this->altitude_chill = sp->altitude_chill; @@ -113,15 +111,16 @@ MapgenValleys::MapgenValleys(int mapgenid, MapgenParams *params, EmergeManager * noise_cave2 = new Noise(&sp->np_cave2, seed, csize.X, csize.Y + 1, csize.Z); noise_massive_caves = new Noise(&sp->np_massive_caves, seed, csize.X, csize.Y + 1, csize.Z); - //// Biome noise - noise_heat_blend = new Noise(¶ms->np_biome_heat_blend, seed, csize.X, csize.Z); - noise_heat = new Noise(¶ms->np_biome_heat, seed, csize.X, csize.Z); - noise_humidity_blend = new Noise(¶ms->np_biome_humidity_blend, seed, csize.X, csize.Z); - noise_humidity = new Noise(¶ms->np_biome_humidity, seed, csize.X, csize.Z); + //// Initialize biome generator + // NOTE: valleys mapgen can only use BiomeGenOriginal + biomegen = emerge->biomemgr->createBiomeGen( + BIOMEGEN_ORIGINAL, params->bparams, csize); + biomemap = biomegen->biomemap; + m_bgen = (BiomeGenOriginal *)biomegen; this->humid_rivers = (spflags & MGVALLEYS_HUMID_RIVERS); this->use_altitude_chill = (spflags & MGVALLEYS_ALT_CHILL); - this->humidity_adjust = params->np_biome_humidity.offset - 50.f; + this->humidity_adjust = bp->np_humidity.offset - 50.f; // a small chance of overflows if the settings are very high this->cave_water_max_height = water_level + MYMAX(0, water_features_lim - 4) * 50; @@ -130,8 +129,6 @@ MapgenValleys::MapgenValleys(int mapgenid, MapgenParams *params, EmergeManager * tcave_cache = new float[csize.Y + 2]; //// Resolve nodes to be used - INodeDefManager *ndef = emerge->ndef; - c_cobble = ndef->getId("mapgen_cobble"); c_desert_stone = ndef->getId("mapgen_desert_stone"); c_dirt = ndef->getId("mapgen_dirt"); @@ -174,12 +171,8 @@ MapgenValleys::~MapgenValleys() delete noise_valley_depth; delete noise_valley_profile; - delete noise_heat; - delete noise_heat_blend; - delete noise_humidity; - delete noise_humidity_blend; + delete biomegen; - delete[] biomemap; delete[] heightmap; delete[] tcave_cache; } @@ -293,14 +286,19 @@ void MapgenValleys::makeChunk(BlockMakeData *data) // Generate noise maps and base terrain height. calculateNoise(); + // Generate biome noises. Note this must be executed strictly before + // generateTerrain, because generateTerrain depends on intermediate + // biome-related noises. + biomegen->calcBiomeNoise(node_min); + // Generate base terrain with initial heightmaps s16 stone_surface_max_y = generateTerrain(); - // Create biomemap at heightmap surface - bmgr->calcBiomes(csize.X, csize.Z, heatmap, humidmap, heightmap, biomemap); + // Build biomemap + biomegen->getBiomes(heightmap); - // Actually place the biome-specific nodes - MgStoneType stone_type = generateBiomes(heatmap, humidmap); + // Place biome-specific nodes + MgStoneType stone_type = generateBiomes(); // Cave creation. if (flags & MG_CAVES) @@ -391,10 +389,6 @@ void MapgenValleys::calculateNoise() //TimeTaker tcn("actualNoise"); noise_filler_depth->perlinMap2D(x, z); - noise_heat_blend->perlinMap2D(x, z); - noise_heat->perlinMap2D(x, z); - noise_humidity_blend->perlinMap2D(x, z); - noise_humidity->perlinMap2D(x, z); noise_inter_valley_slope->perlinMap2D(x, z); noise_rivers->perlinMap2D(x, z); noise_terrain_height->perlinMap2D(x, z); @@ -418,9 +412,8 @@ void MapgenValleys::calculateNoise() } for (s32 index = 0; index < csize.X * csize.Z; index++) { - noise_heat->result[index] += noise_heat_blend->result[index] + heat_offset; - noise_humidity->result[index] *= humidity_scale; - noise_humidity->result[index] += noise_humidity_blend->result[index]; + m_bgen->heatmap[index] += heat_offset; + m_bgen->humidmap[index] *= humidity_scale; } TerrainNoise tn; @@ -450,9 +443,6 @@ void MapgenValleys::calculateNoise() float mount = terrainLevelFromNoise(&tn); noise_terrain_height->result[index] = mount; } - - heatmap = noise_heat->result; - humidmap = noise_humidity->result; } @@ -596,7 +586,7 @@ int MapgenValleys::generateTerrain() float river_y = noise_rivers->result[index_2d]; float surface_y = noise_terrain_height->result[index_2d]; float slope = noise_inter_valley_slope->result[index_2d]; - float t_heat = noise_heat->result[index_2d]; + float t_heat = m_bgen->heatmap[index_2d]; heightmap[index_2d] = -MAX_MAP_GENERATION_LIMIT; @@ -610,7 +600,7 @@ int MapgenValleys::generateTerrain() t_heat -= alt_to_heat * MYMAX(surface_y, river_y) / altitude_chill; // If humidity is low or heat is high, lower the water table. - float delta = noise_humidity->result[index_2d] - 50.f; + float delta = m_bgen->humidmap[index_2d] - 50.f; if (delta < 0.f) { float t_evap = (t_heat - 32.f) / evaporation; river_y += delta * MYMAX(t_evap, 0.08f); @@ -672,7 +662,7 @@ int MapgenValleys::generateTerrain() // Use base ground (water table) in a riverbed, to // avoid an unnatural rise in humidity. float t_alt = MYMAX(noise_rivers->result[index_2d], (float)heightmap[index_2d]); - float humid = noise_humidity->result[index_2d]; + float humid = m_bgen->humidmap[index_2d]; float water_depth = (t_alt - river_y) / humidity_dropoff; humid *= 1.f + pow(0.5f, MYMAX(water_depth, 1.f)); @@ -683,7 +673,7 @@ int MapgenValleys::generateTerrain() if (t_alt > 0.f) humid -= alt_to_humid * t_alt / altitude_chill; - noise_humidity->result[index_2d] = humid; + m_bgen->humidmap[index_2d] = humid; } // Assign the heat adjusted by any changed altitudes. @@ -693,9 +683,9 @@ int MapgenValleys::generateTerrain() float t_alt = MYMAX(noise_rivers->result[index_2d], (float)heightmap[index_2d]); if (humid_rivers && heightmap[index_2d] == (s16)myround(surface_y)) // The altitude hasn't changed. Use the first result. - noise_heat->result[index_2d] = t_heat; + m_bgen->heatmap[index_2d] = t_heat; else if (t_alt > 0.f) - noise_heat->result[index_2d] -= alt_to_heat * t_alt / altitude_chill; + m_bgen->heatmap[index_2d] -= alt_to_heat * t_alt / altitude_chill; } } @@ -703,7 +693,7 @@ int MapgenValleys::generateTerrain() } -MgStoneType MapgenValleys::generateBiomes(float *heat_map, float *humidity_map) +MgStoneType MapgenValleys::generateBiomes() { v3s16 em = vm->m_area.getExtent(); u32 index = 0; @@ -739,9 +729,9 @@ MgStoneType MapgenValleys::generateBiomes(float *heat_map, float *humidity_map) // 3. When stone or water is detected but biome has not yet been calculated. if ((c == c_stone && (air_above || water_above || !biome)) || ((c == c_water_source || c == c_river_water_source) - && (air_above || !biome))) { + && (air_above || !biome))) { // Both heat and humidity have already been adjusted for altitude. - biome = bmgr->getBiome(heat_map[index], humidity_map[index], y); + biome = biomegen->getBiomeAtIndex(index, y); depth_top = biome->depth_top; base_filler = MYMAX(depth_top diff --git a/src/mapgen_valleys.h b/src/mapgen_valleys.h index 5224ea54b..0e268c1cf 100644 --- a/src/mapgen_valleys.h +++ b/src/mapgen_valleys.h @@ -39,6 +39,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #define MYCUBE(x) (x) * (x) * (x) class BiomeManager; +class BiomeGenOriginal; // Global profiler //class Profiler; @@ -98,6 +99,7 @@ public: private: EmergeManager *m_emerge; BiomeManager *bmgr; + BiomeGenOriginal *m_bgen; int ystride; int zstride; @@ -136,11 +138,6 @@ private: Noise *noise_valley_depth; Noise *noise_valley_profile; - Noise *noise_heat; - Noise *noise_heat_blend; - Noise *noise_humidity; - Noise *noise_humidity_blend; - content_t c_cobble; content_t c_desert_stone; content_t c_dirt; @@ -164,9 +161,7 @@ private: float terrainLevelFromNoise(TerrainNoise *tn); float adjustedTerrainLevelFromNoise(TerrainNoise *tn); - float humidityByTerrain(float humidity_base, float mount, float rivers, float valley); - - MgStoneType generateBiomes(float *heat_map, float *humidity_map); + MgStoneType generateBiomes(); void dustTopNodes(); void generateCaves(s16 max_stone_y); diff --git a/src/mg_biome.cpp b/src/mg_biome.cpp index 9ab8d06cc..dac0f7acc 100644 --- a/src/mg_biome.cpp +++ b/src/mg_biome.cpp @@ -27,6 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/numeric.h" #include "util/mathconstants.h" #include "porting.h" +#include "settings.h" /////////////////////////////////////////////////////////////////////////////// @@ -63,33 +64,152 @@ BiomeManager::BiomeManager(IGameDef *gamedef) : } - BiomeManager::~BiomeManager() { - //if (biomecache) - // delete[] biomecache; } +void BiomeManager::clear() +{ + EmergeManager *emerge = m_gamedef->getEmergeManager(); + + // Remove all dangling references in Decorations + DecorationManager *decomgr = emerge->decomgr; + for (size_t i = 0; i != decomgr->getNumObjects(); i++) { + Decoration *deco = (Decoration *)decomgr->getRaw(i); + deco->biomes.clear(); + } + + // Don't delete the first biome + for (size_t i = 1; i < m_objects.size(); i++) + delete (Biome *)m_objects[i]; + + m_objects.resize(1); +} + +//////////////////////////////////////////////////////////////////////////////// + + +void BiomeParamsOriginal::readParams(const Settings *settings) +{ + settings->getNoiseParams("mg_biome_np_heat", np_heat); + settings->getNoiseParams("mg_biome_np_heat_blend", np_heat_blend); + settings->getNoiseParams("mg_biome_np_humidity", np_humidity); + settings->getNoiseParams("mg_biome_np_humidity_blend", np_humidity_blend); +} + + +void BiomeParamsOriginal::writeParams(Settings *settings) const +{ + settings->setNoiseParams("mg_biome_np_heat", np_heat); + settings->setNoiseParams("mg_biome_np_heat_blend", np_heat_blend); + settings->setNoiseParams("mg_biome_np_humidity", np_humidity); + settings->setNoiseParams("mg_biome_np_humidity_blend", np_humidity_blend); +} + + +//////////////////////////////////////////////////////////////////////////////// -// just a PoC, obviously needs optimization later on (precalculate this) -void BiomeManager::calcBiomes(s16 sx, s16 sy, float *heat_map, - float *humidity_map, s16 *height_map, u8 *biomeid_map) +BiomeGenOriginal::BiomeGenOriginal(BiomeManager *biomemgr, + BiomeParamsOriginal *params, v3s16 chunksize) { - for (s32 i = 0; i != sx * sy; i++) { - Biome *biome = getBiome(heat_map[i], humidity_map[i], height_map[i]); - biomeid_map[i] = biome->index; + m_bmgr = biomemgr; + m_params = params; + m_csize = chunksize; + + noise_heat = new Noise(¶ms->np_heat, + params->seed, m_csize.X, m_csize.Z); + noise_humidity = new Noise(¶ms->np_humidity, + params->seed, m_csize.X, m_csize.Z); + noise_heat_blend = new Noise(¶ms->np_heat_blend, + params->seed, m_csize.X, m_csize.Z); + noise_humidity_blend = new Noise(¶ms->np_humidity_blend, + params->seed, m_csize.X, m_csize.Z); + + heatmap = noise_heat->result; + humidmap = noise_humidity->result; + biomemap = new u8[m_csize.X * m_csize.Z]; +} + +BiomeGenOriginal::~BiomeGenOriginal() +{ + delete []biomemap; + + delete noise_heat; + delete noise_humidity; + delete noise_heat_blend; + delete noise_humidity_blend; +} + + +Biome *BiomeGenOriginal::calcBiomeAtPoint(v3s16 pos) const +{ + float heat = + NoisePerlin2D(&m_params->np_heat, pos.X, pos.Z, m_params->seed) + + NoisePerlin2D(&m_params->np_heat_blend, pos.X, pos.Z, m_params->seed); + float humidity = + NoisePerlin2D(&m_params->np_humidity, pos.X, pos.Z, m_params->seed) + + NoisePerlin2D(&m_params->np_humidity_blend, pos.X, pos.Z, m_params->seed); + + return calcBiomeFromNoise(heat, humidity, pos.Y); +} + + +void BiomeGenOriginal::calcBiomeNoise(v3s16 pmin) +{ + m_pmin = pmin; + + noise_heat->perlinMap2D(pmin.X, pmin.Z); + noise_humidity->perlinMap2D(pmin.X, pmin.Z); + noise_heat_blend->perlinMap2D(pmin.X, pmin.Z); + noise_humidity_blend->perlinMap2D(pmin.X, pmin.Z); + + for (s32 i = 0; i < m_csize.X * m_csize.Z; i++) { + noise_heat->result[i] += noise_heat_blend->result[i]; + noise_humidity->result[i] += noise_humidity_blend->result[i]; + } +} + + +u8 *BiomeGenOriginal::getBiomes(s16 *heightmap) +{ + for (s32 i = 0; i != m_csize.X * m_csize.Z; i++) { + Biome *biome = calcBiomeFromNoise( + noise_heat->result[i], + noise_humidity->result[i], + heightmap[i]); + + biomemap[i] = biome->index; } + + return biomemap; } -Biome *BiomeManager::getBiome(float heat, float humidity, s16 y) +Biome *BiomeGenOriginal::getBiomeAtPoint(v3s16 pos) const +{ + return getBiomeAtIndex( + (pos.Z - m_pmin.Z) * m_csize.X + (pos.X - m_pmin.X), + pos.Y); +} + + +Biome *BiomeGenOriginal::getBiomeAtIndex(size_t index, s16 y) const +{ + return calcBiomeFromNoise( + noise_heat->result[index], + noise_humidity->result[index], + y); +} + + +Biome *BiomeGenOriginal::calcBiomeFromNoise(float heat, float humidity, s16 y) const { Biome *b, *biome_closest = NULL; float dist_min = FLT_MAX; - for (size_t i = 1; i < m_objects.size(); i++) { - b = (Biome *)m_objects[i]; + for (size_t i = 1; i < m_bmgr->getNumObjects(); i++) { + b = (Biome *)m_bmgr->getRaw(i); if (!b || y > b->y_max || y < b->y_min) continue; @@ -103,32 +223,11 @@ Biome *BiomeManager::getBiome(float heat, float humidity, s16 y) } } - return biome_closest ? biome_closest : (Biome *)m_objects[0]; + return biome_closest ? biome_closest : (Biome *)m_bmgr->getRaw(BIOME_NONE); } -void BiomeManager::clear() -{ - EmergeManager *emerge = m_gamedef->getEmergeManager(); - - // Remove all dangling references in Decorations - DecorationManager *decomgr = emerge->decomgr; - for (size_t i = 0; i != decomgr->getNumObjects(); i++) { - Decoration *deco = (Decoration *)decomgr->getRaw(i); - deco->biomes.clear(); - } - - // Don't delete the first biome - for (size_t i = 1; i < m_objects.size(); i++) { - Biome *b = (Biome *)m_objects[i]; - delete b; - } - - m_objects.resize(1); -} - - -/////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// void Biome::resolveNodeNames() { diff --git a/src/mg_biome.h b/src/mg_biome.h index 8d519f808..e9378fd79 100644 --- a/src/mg_biome.h +++ b/src/mg_biome.h @@ -22,6 +22,16 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "objdef.h" #include "nodedef.h" +#include "noise.h" + +class Settings; +class BiomeManager; + +//// +//// Biome +//// + +#define BIOME_NONE ((u8)0) enum BiomeType { @@ -56,10 +66,122 @@ public: virtual void resolveNodeNames(); }; -class BiomeManager : public ObjDefManager { + +//// +//// BiomeGen +//// + +enum BiomeGenType { + BIOMEGEN_ORIGINAL, +}; + +struct BiomeParams { + virtual void readParams(const Settings *settings) = 0; + virtual void writeParams(Settings *settings) const = 0; + virtual ~BiomeParams() {} + + int seed; +}; + +class BiomeGen { +public: + virtual ~BiomeGen() {} + virtual BiomeGenType getType() const = 0; + + // Calculates the biome at the exact position provided. This function can + // be called at any time, but may be less efficient than the latter methods, + // depending on implementation. + virtual Biome *calcBiomeAtPoint(v3s16 pos) const = 0; + + // Computes any intermediate results needed for biome generation. Must be + // called before using any of: getBiomes, getBiomeAtPoint, or getBiomeAtIndex. + // Calling this invalidates the previous results stored in biomemap. + virtual void calcBiomeNoise(v3s16 pmin) = 0; + + // Gets all biomes in current chunk using each corresponding element of + // heightmap as the y position, then stores the results by biome index in + // biomemap (also returned) + virtual u8 *getBiomes(s16 *heightmap) = 0; + + // Gets a single biome at the specified position, which must be contained + // in the region formed by m_pmin and (m_pmin + m_csize - 1). + virtual Biome *getBiomeAtPoint(v3s16 pos) const = 0; + + // Same as above, but uses a raw numeric index correlating to the (x,z) position. + virtual Biome *getBiomeAtIndex(size_t index, s16 y) const = 0; + + // Result of calcBiomes bulk computation. + u8 *biomemap; + +protected: + BiomeManager *m_bmgr; + v3s16 m_pmin; + v3s16 m_csize; +}; + + +//// +//// BiomeGen implementations +//// + +// +// Original biome algorithm (Whittaker's classification + surface height) +// + +struct BiomeParamsOriginal : public BiomeParams { + BiomeParamsOriginal() : + np_heat(50, 50, v3f(750.0, 750.0, 750.0), 5349, 3, 0.5, 2.0), + np_humidity(50, 50, v3f(750.0, 750.0, 750.0), 842, 3, 0.5, 2.0), + np_heat_blend(0, 1.5, v3f(8.0, 8.0, 8.0), 13, 2, 1.0, 2.0), + np_humidity_blend(0, 1.5, v3f(8.0, 8.0, 8.0), 90003, 2, 1.0, 2.0) + { + } + + virtual void readParams(const Settings *settings); + virtual void writeParams(Settings *settings) const; + + NoiseParams np_heat; + NoiseParams np_humidity; + NoiseParams np_heat_blend; + NoiseParams np_humidity_blend; +}; + +class BiomeGenOriginal : public BiomeGen { public: - static const char *OBJECT_TITLE; + BiomeGenOriginal(BiomeManager *biomemgr, + BiomeParamsOriginal *params, v3s16 chunksize); + virtual ~BiomeGenOriginal(); + BiomeGenType getType() const { return BIOMEGEN_ORIGINAL; } + + Biome *calcBiomeAtPoint(v3s16 pos) const; + void calcBiomeNoise(v3s16 pmin); + + u8 *getBiomes(s16 *heightmap); + Biome *getBiomeAtPoint(v3s16 pos) const; + Biome *getBiomeAtIndex(size_t index, s16 y) const; + + Biome *calcBiomeFromNoise(float heat, float humidity, s16 y) const; + + float *heatmap; + float *humidmap; + +private: + BiomeParamsOriginal *m_params; + + Noise *noise_heat; + Noise *noise_humidity; + Noise *noise_heat_blend; + Noise *noise_humidity_blend; +}; + + +//// +//// BiomeManager +//// + +class BiomeManager : public ObjDefManager { +public: BiomeManager(IGameDef *gamedef); virtual ~BiomeManager(); @@ -73,15 +195,36 @@ public: return new Biome; } + BiomeGen *createBiomeGen(BiomeGenType type, BiomeParams *params, v3s16 chunksize) + { + switch (type) { + case BIOMEGEN_ORIGINAL: + return new BiomeGenOriginal(this, + (BiomeParamsOriginal *)params, chunksize); + default: + return NULL; + } + } + + static BiomeParams *createBiomeParams(BiomeGenType type) + { + switch (type) { + case BIOMEGEN_ORIGINAL: + return new BiomeParamsOriginal; + default: + return NULL; + } + } + virtual void clear(); - void calcBiomes(s16 sx, s16 sy, float *heat_map, float *humidity_map, - s16 *height_map, u8 *biomeid_map); - Biome *getBiome(float heat, float humidity, s16 y); + // Looks for pos in the biome cache, and if non-existent, looks up by noise + u8 getBiomeAtPoint(v3s16 pos); private: IGameDef *m_gamedef; + }; -#endif +#endif diff --git a/src/script/lua_api/l_mapgen.cpp b/src/script/lua_api/l_mapgen.cpp index fb839176b..405b93b86 100644 --- a/src/script/lua_api/l_mapgen.cpp +++ b/src/script/lua_api/l_mapgen.cpp @@ -528,24 +528,26 @@ int ModApiMapgen::l_get_mapgen_object(lua_State *L) return 1; } case MGOBJ_BIOMEMAP: { - if (!mg->biomemap) + if (!mg->biomegen) return 0; lua_newtable(L); for (size_t i = 0; i != maplen; i++) { - lua_pushinteger(L, mg->biomemap[i]); + lua_pushinteger(L, mg->biomegen->biomemap[i]); lua_rawseti(L, -2, i + 1); } return 1; } case MGOBJ_HEATMAP: { - if (!mg->heatmap) + if (!mg->biomegen || mg->biomegen->getType() != BIOMEGEN_ORIGINAL) return 0; + BiomeGenOriginal *bg = (BiomeGenOriginal *)mg->biomegen; + lua_newtable(L); for (size_t i = 0; i != maplen; i++) { - lua_pushnumber(L, mg->heatmap[i]); + lua_pushnumber(L, bg->heatmap[i]); lua_rawseti(L, -2, i + 1); } @@ -553,12 +555,14 @@ int ModApiMapgen::l_get_mapgen_object(lua_State *L) } case MGOBJ_HUMIDMAP: { - if (!mg->humidmap) + if (!mg->biomegen || mg->biomegen->getType() != BIOMEGEN_ORIGINAL) return 0; + BiomeGenOriginal *bg = (BiomeGenOriginal *)mg->biomegen; + lua_newtable(L); for (size_t i = 0; i != maplen; i++) { - lua_pushnumber(L, mg->humidmap[i]); + lua_pushnumber(L, bg->humidmap[i]); lua_rawseti(L, -2, i + 1); } -- cgit v1.2.3 From 87bc39dca733a2cc805bc1071794c4d5d7937115 Mon Sep 17 00:00:00 2001 From: kwolekr Date: Mon, 2 May 2016 02:24:57 -0400 Subject: Mapgen: Combine generateBiomes, dustTopNodes, and generateCaves This commit condenses the above methods into a single implementation used by V7, V5, Flat, Fractal, and Valleys mapgens and introduces MapgenBasic. --- src/mapgen.cpp | 236 +++++++++++++++++++++++++++++++++++++++++++++++++ src/mapgen.h | 56 ++++++++++++ src/mapgen_flat.cpp | 221 +-------------------------------------------- src/mapgen_flat.h | 29 +----- src/mapgen_fractal.cpp | 221 +-------------------------------------------- src/mapgen_fractal.h | 23 +---- src/mapgen_v5.cpp | 221 +-------------------------------------------- src/mapgen_v5.h | 24 +---- src/mapgen_v7.cpp | 230 ++--------------------------------------------- src/mapgen_v7.h | 25 +----- src/mapgen_valleys.cpp | 165 +--------------------------------- src/mapgen_valleys.h | 26 +----- 12 files changed, 316 insertions(+), 1161 deletions(-) (limited to 'src') diff --git a/src/mapgen.cpp b/src/mapgen.cpp index e269bf454..f491ed9eb 100644 --- a/src/mapgen.cpp +++ b/src/mapgen.cpp @@ -39,6 +39,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/numeric.h" #include "filesys.h" #include "log.h" +#include "cavegen.h" FlagDesc flagdesc_mapgen[] = { {"trees", MG_TREES}, @@ -369,6 +370,241 @@ void Mapgen::spreadLight(v3s16 nmin, v3s16 nmax) } +//// +//// MapgenBasic +//// + +MapgenBasic::MapgenBasic(int mapgenid, MapgenParams *params, EmergeManager *emerge) + : Mapgen(mapgenid, params, emerge) +{ + +} + +MgStoneType MapgenBasic::generateBiomes() +{ + v3s16 em = vm->m_area.getExtent(); + u32 index = 0; + MgStoneType stone_type = STONE; + + for (s16 z = node_min.Z; z <= node_max.Z; z++) + for (s16 x = node_min.X; x <= node_max.X; x++, index++) { + Biome *biome = NULL; + u16 depth_top = 0; + u16 base_filler = 0; + u16 depth_water_top = 0; + u32 vi = vm->m_area.index(x, node_max.Y, z); + + // Check node at base of mapchunk above, either a node of a previously + // generated mapchunk or if not, a node of overgenerated base terrain. + content_t c_above = vm->m_data[vi + em.X].getContent(); + bool air_above = c_above == CONTENT_AIR; + bool water_above = (c_above == c_water_source || c_above == c_river_water_source); + + // If there is air or water above enable top/filler placement, otherwise force + // nplaced to stone level by setting a number exceeding any possible filler depth. + u16 nplaced = (air_above || water_above) ? 0 : U16_MAX; + + for (s16 y = node_max.Y; y >= node_min.Y; y--) { + content_t c = vm->m_data[vi].getContent(); + + // Biome is recalculated each time an upper surface is detected while + // working down a column. The selected biome then remains in effect for + // all nodes below until the next surface and biome recalculation. + // Biome is recalculated: + // 1. At the surface of stone below air or water. + // 2. At the surface of water below air. + // 3. When stone or water is detected but biome has not yet been calculated. + if ((c == c_stone && (air_above || water_above || !biome)) + || ((c == c_water_source || c == c_river_water_source) + && (air_above || !biome))) { + biome = biomegen->getBiomeAtIndex(index, y); + + depth_top = biome->depth_top; + base_filler = MYMAX(depth_top + + biome->depth_filler + + noise_filler_depth->result[index], 0.f); + depth_water_top = biome->depth_water_top; + + // Detect stone type for dungeons during every biome calculation. + // This is more efficient than detecting per-node and will not + // miss any desert stone or sandstone biomes. + if (biome->c_stone == c_desert_stone) + stone_type = DESERT_STONE; + else if (biome->c_stone == c_sandstone) + stone_type = SANDSTONE; + } + + if (c == c_stone) { + content_t c_below = vm->m_data[vi - em.X].getContent(); + + // If the node below isn't solid, make this node stone, so that + // any top/filler nodes above are structurally supported. + // This is done by aborting the cycle of top/filler placement + // immediately by forcing nplaced to stone level. + if (c_below == CONTENT_AIR + || c_below == c_water_source + || c_below == c_river_water_source) + nplaced = U16_MAX; + + if (nplaced < depth_top) { + vm->m_data[vi] = MapNode(biome->c_top); + nplaced++; + } else if (nplaced < base_filler) { + vm->m_data[vi] = MapNode(biome->c_filler); + nplaced++; + } else { + vm->m_data[vi] = MapNode(biome->c_stone); + } + + air_above = false; + water_above = false; + } else if (c == c_water_source) { + vm->m_data[vi] = MapNode((y > (s32)(water_level - depth_water_top)) + ? biome->c_water_top : biome->c_water); + nplaced = 0; // Enable top/filler placement for next surface + air_above = false; + water_above = true; + } else if (c == c_river_water_source) { + vm->m_data[vi] = MapNode(biome->c_river_water); + nplaced = depth_top; // Enable filler placement for next surface + air_above = false; + water_above = true; + } else if (c == CONTENT_AIR) { + nplaced = 0; // Enable top/filler placement for next surface + air_above = true; + water_above = false; + } else { // Possible various nodes overgenerated from neighbouring mapchunks + nplaced = U16_MAX; // Disable top/filler placement + air_above = false; + water_above = false; + } + + vm->m_area.add_y(em, vi, -1); + } + } + + return stone_type; +} + + +void MapgenBasic::dustTopNodes() +{ + if (node_max.Y < water_level) + return; + + v3s16 em = vm->m_area.getExtent(); + u32 index = 0; + + for (s16 z = node_min.Z; z <= node_max.Z; z++) + for (s16 x = node_min.X; x <= node_max.X; x++, index++) { + Biome *biome = (Biome *)bmgr->getRaw(biomemap[index]); + + if (biome->c_dust == CONTENT_IGNORE) + continue; + + u32 vi = vm->m_area.index(x, full_node_max.Y, z); + content_t c_full_max = vm->m_data[vi].getContent(); + s16 y_start; + + if (c_full_max == CONTENT_AIR) { + y_start = full_node_max.Y - 1; + } else if (c_full_max == CONTENT_IGNORE) { + vi = vm->m_area.index(x, node_max.Y + 1, z); + content_t c_max = vm->m_data[vi].getContent(); + + if (c_max == CONTENT_AIR) + y_start = node_max.Y; + else + continue; + } else { + continue; + } + + vi = vm->m_area.index(x, y_start, z); + for (s16 y = y_start; y >= node_min.Y - 1; y--) { + if (vm->m_data[vi].getContent() != CONTENT_AIR) + break; + + vm->m_area.add_y(em, vi, -1); + } + + content_t c = vm->m_data[vi].getContent(); + if (!ndef->get(c).buildable_to && c != CONTENT_IGNORE && c != biome->c_dust) { + vm->m_area.add_y(em, vi, 1); + vm->m_data[vi] = MapNode(biome->c_dust); + } + } +} + + +void MapgenBasic::generateCaves(s16 max_stone_y, s16 large_cave_depth) +{ + if (max_stone_y < node_min.Y) + return; + + noise_cave1->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z); + noise_cave2->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z); + + v3s16 em = vm->m_area.getExtent(); + u32 index2d = 0; + + for (s16 z = node_min.Z; z <= node_max.Z; z++) + for (s16 x = node_min.X; x <= node_max.X; x++, index2d++) { + bool column_is_open = false; // Is column open to overground + bool is_tunnel = false; // Is tunnel or tunnel floor + u32 vi = vm->m_area.index(x, node_max.Y, z); + u32 index3d = (z - node_min.Z) * zstride_1d + csize.Y * ystride + + (x - node_min.X); + // Biome of column + Biome *biome = (Biome *)bmgr->getRaw(biomemap[index2d]); + + // Don't excavate the overgenerated stone at node_max.Y + 1, + // this creates a 'roof' over the tunnel, preventing light in + // tunnels at mapchunk borders when generating mapchunks upwards. + // This 'roof' is removed when the mapchunk above is generated. + for (s16 y = node_max.Y; y >= node_min.Y - 1; y--, + index3d -= ystride, + vm->m_area.add_y(em, vi, -1)) { + + content_t c = vm->m_data[vi].getContent(); + if (c == CONTENT_AIR || c == biome->c_water_top || + c == biome->c_water) { + column_is_open = true; + continue; + } + // Ground + float d1 = contour(noise_cave1->result[index3d]); + float d2 = contour(noise_cave2->result[index3d]); + + if (d1 * d2 > cave_width && ndef->get(c).is_ground_content) { + // In tunnel and ground content, excavate + vm->m_data[vi] = MapNode(CONTENT_AIR); + is_tunnel = true; + } else { + // Not in tunnel or not ground content + if (is_tunnel && column_is_open && + (c == biome->c_filler || c == biome->c_stone)) + // Tunnel entrance floor + vm->m_data[vi] = MapNode(biome->c_top); + + column_is_open = false; + is_tunnel = false; + } + } + } + + if (node_max.Y > large_cave_depth) + return; + + PseudoRandom ps(blockseed + 21343); + u32 bruises_count = ps.range(0, 2); + for (u32 i = 0; i < bruises_count; i++) { + CaveV5 cave(this, &ps); ////caves version varies ---- todo- fix this! + cave.makeCave(node_min, node_max, max_stone_y); + } +} + + //// //// GenerateNotifier //// diff --git a/src/mapgen.h b/src/mapgen.h index 554ec6084..bee8e786a 100644 --- a/src/mapgen.h +++ b/src/mapgen.h @@ -46,6 +46,7 @@ extern FlagDesc flagdesc_gennotify[]; class Biome; class BiomeGen; struct BiomeParams; +class BiomeManager; class EmergeManager; class MapBlock; class VoxelManipulator; @@ -137,6 +138,16 @@ struct MapgenParams { void save(Settings &settings) const; }; + +/* + Generic interface for map generators. All mapgens must inherit this class. + If a feature exposed by a public member pointer is not supported by a + certain mapgen, it must be set to NULL. + + Apart from makeChunk, getGroundLevelAtPoint, and getSpawnLevelAtPoint, all + methods can be used by constructing a Mapgen base class and setting the + appropriate public members (e.g. vm, ndef, and so on). +*/ class Mapgen { public: int seed; @@ -189,6 +200,51 @@ private: DISABLE_CLASS_COPY(Mapgen); }; +/* + MapgenBasic is a Mapgen implementation that handles basic functionality + the majority of conventional mapgens will probably want to use, but isn't + generic enough to be included as part of the base Mapgen class (such as + generating biome terrain over terrain node skeletons, generating caves, + dungeons, etc.) + + Inherit MapgenBasic instead of Mapgen to add this basic functionality to + your mapgen without having to reimplement it. Feel free to override any of + these methods if you desire different or more advanced behavior. + + Note that you must still create your own generateTerrain implementation when + inheriting MapgenBasic. +*/ +class MapgenBasic : public Mapgen { +public: + EmergeManager *m_emerge; + BiomeManager *bmgr; + + Noise *noise_filler_depth; + Noise *noise_cave1; + Noise *noise_cave2; + + v3s16 node_min; + v3s16 node_max; + v3s16 full_node_min; + v3s16 full_node_max; + + content_t c_stone; + content_t c_water_source; + content_t c_river_water_source; + content_t c_desert_stone; + content_t c_sandstone; + + int ystride; + int zstride_1d; + float cave_width; + + MapgenBasic(int mapgenid, MapgenParams *params, EmergeManager *emerge); + + virtual MgStoneType generateBiomes(); + virtual void dustTopNodes(); + virtual void generateCaves(s16 max_stone_y, s16 large_cave_depth); +}; + struct MapgenFactory { virtual Mapgen *createMapgen(int mgid, MapgenParams *params, EmergeManager *emerge) = 0; diff --git a/src/mapgen_flat.cpp b/src/mapgen_flat.cpp index cb9aa9a8b..97f1c4b2a 100644 --- a/src/mapgen_flat.cpp +++ b/src/mapgen_flat.cpp @@ -50,7 +50,7 @@ FlagDesc flagdesc_mapgen_flat[] = { MapgenFlat::MapgenFlat(int mapgenid, MapgenParams *params, EmergeManager *emerge) - : Mapgen(mapgenid, params, emerge) + : MapgenBasic(mapgenid, params, emerge) { this->m_emerge = emerge; this->bmgr = emerge->biomemgr; @@ -248,7 +248,7 @@ void MapgenFlat::makeChunk(BlockMakeData *data) MgStoneType stone_type = generateBiomes(); if (flags & MG_CAVES) - generateCaves(stone_surface_max_y); + generateCaves(stone_surface_max_y, large_cave_depth); if ((flags & MG_DUNGEONS) && (stone_surface_max_y >= node_min.Y)) { DungeonParams dp; @@ -381,220 +381,3 @@ s16 MapgenFlat::generateTerrain() return stone_surface_max_y; } - - -MgStoneType MapgenFlat::generateBiomes() -{ - v3s16 em = vm->m_area.getExtent(); - u32 index = 0; - MgStoneType stone_type = STONE; - - for (s16 z = node_min.Z; z <= node_max.Z; z++) - for (s16 x = node_min.X; x <= node_max.X; x++, index++) { - Biome *biome = NULL; - u16 depth_top = 0; - u16 base_filler = 0; - u16 depth_water_top = 0; - u32 vi = vm->m_area.index(x, node_max.Y, z); - - // Check node at base of mapchunk above, either a node of a previously - // generated mapchunk or if not, a node of overgenerated base terrain. - content_t c_above = vm->m_data[vi + em.X].getContent(); - bool air_above = c_above == CONTENT_AIR; - bool water_above = c_above == c_water_source; - - // If there is air or water above enable top/filler placement, otherwise force - // nplaced to stone level by setting a number exceeding any possible filler depth. - u16 nplaced = (air_above || water_above) ? 0 : U16_MAX; - - - for (s16 y = node_max.Y; y >= node_min.Y; y--) { - content_t c = vm->m_data[vi].getContent(); - - // Biome is recalculated each time an upper surface is detected while - // working down a column. The selected biome then remains in effect for - // all nodes below until the next surface and biome recalculation. - // Biome is recalculated: - // 1. At the surface of stone below air or water. - // 2. At the surface of water below air. - // 3. When stone or water is detected but biome has not yet been calculated. - if ((c == c_stone && (air_above || water_above || !biome)) || - (c == c_water_source && (air_above || !biome))) { - biome = biomegen->getBiomeAtIndex(index, y); - - depth_top = biome->depth_top; - base_filler = MYMAX(depth_top + biome->depth_filler - + noise_filler_depth->result[index], 0); - depth_water_top = biome->depth_water_top; - - // Detect stone type for dungeons during every biome calculation. - // This is more efficient than detecting per-node and will not - // miss any desert stone or sandstone biomes. - if (biome->c_stone == c_desert_stone) - stone_type = DESERT_STONE; - else if (biome->c_stone == c_sandstone) - stone_type = SANDSTONE; - } - - if (c == c_stone) { - content_t c_below = vm->m_data[vi - em.X].getContent(); - - // If the node below isn't solid, make this node stone, so that - // any top/filler nodes above are structurally supported. - // This is done by aborting the cycle of top/filler placement - // immediately by forcing nplaced to stone level. - if (c_below == CONTENT_AIR || c_below == c_water_source) - nplaced = U16_MAX; - - if (nplaced < depth_top) { - vm->m_data[vi] = MapNode(biome->c_top); - nplaced++; - } else if (nplaced < base_filler) { - vm->m_data[vi] = MapNode(biome->c_filler); - nplaced++; - } else { - vm->m_data[vi] = MapNode(biome->c_stone); - } - - air_above = false; - water_above = false; - } else if (c == c_water_source) { - vm->m_data[vi] = MapNode((y > (s32)(water_level - depth_water_top)) ? - biome->c_water_top : biome->c_water); - nplaced = 0; // Enable top/filler placement for next surface - air_above = false; - water_above = true; - } else if (c == CONTENT_AIR) { - nplaced = 0; // Enable top/filler placement for next surface - air_above = true; - water_above = false; - } else { // Possible various nodes overgenerated from neighbouring mapchunks - nplaced = U16_MAX; // Disable top/filler placement - air_above = false; - water_above = false; - } - - vm->m_area.add_y(em, vi, -1); - } - } - - return stone_type; -} - - -void MapgenFlat::dustTopNodes() -{ - if (node_max.Y < water_level) - return; - - v3s16 em = vm->m_area.getExtent(); - u32 index = 0; - - for (s16 z = node_min.Z; z <= node_max.Z; z++) - for (s16 x = node_min.X; x <= node_max.X; x++, index++) { - Biome *biome = (Biome *)bmgr->getRaw(biomemap[index]); - - if (biome->c_dust == CONTENT_IGNORE) - continue; - - u32 vi = vm->m_area.index(x, full_node_max.Y, z); - content_t c_full_max = vm->m_data[vi].getContent(); - s16 y_start; - - if (c_full_max == CONTENT_AIR) { - y_start = full_node_max.Y - 1; - } else if (c_full_max == CONTENT_IGNORE) { - vi = vm->m_area.index(x, node_max.Y + 1, z); - content_t c_max = vm->m_data[vi].getContent(); - - if (c_max == CONTENT_AIR) - y_start = node_max.Y; - else - continue; - } else { - continue; - } - - vi = vm->m_area.index(x, y_start, z); - for (s16 y = y_start; y >= node_min.Y - 1; y--) { - if (vm->m_data[vi].getContent() != CONTENT_AIR) - break; - - vm->m_area.add_y(em, vi, -1); - } - - content_t c = vm->m_data[vi].getContent(); - if (!ndef->get(c).buildable_to && c != CONTENT_IGNORE && c != biome->c_dust) { - vm->m_area.add_y(em, vi, 1); - vm->m_data[vi] = MapNode(biome->c_dust); - } - } -} - - -void MapgenFlat::generateCaves(s16 max_stone_y) -{ - if (max_stone_y < node_min.Y) - return; - - noise_cave1->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z); - noise_cave2->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z); - - v3s16 em = vm->m_area.getExtent(); - u32 index2d = 0; - - for (s16 z = node_min.Z; z <= node_max.Z; z++) - for (s16 x = node_min.X; x <= node_max.X; x++, index2d++) { - bool column_is_open = false; // Is column open to overground - bool is_tunnel = false; // Is tunnel or tunnel floor - u32 vi = vm->m_area.index(x, node_max.Y, z); - u32 index3d = (z - node_min.Z) * zstride_1d + csize.Y * ystride + - (x - node_min.X); - // Biome of column - Biome *biome = (Biome *)bmgr->getRaw(biomemap[index2d]); - - // Don't excavate the overgenerated stone at node_max.Y + 1, - // this creates a 'roof' over the tunnel, preventing light in - // tunnels at mapchunk borders when generating mapchunks upwards. - // This 'roof' is removed when the mapchunk above is generated. - for (s16 y = node_max.Y; y >= node_min.Y - 1; y--, - index3d -= ystride, - vm->m_area.add_y(em, vi, -1)) { - - content_t c = vm->m_data[vi].getContent(); - if (c == CONTENT_AIR || c == biome->c_water_top || - c == biome->c_water) { - column_is_open = true; - continue; - } - // Ground - float d1 = contour(noise_cave1->result[index3d]); - float d2 = contour(noise_cave2->result[index3d]); - - if (d1 * d2 > cave_width && ndef->get(c).is_ground_content) { - // In tunnel and ground content, excavate - vm->m_data[vi] = MapNode(CONTENT_AIR); - is_tunnel = true; - } else { - // Not in tunnel or not ground content - if (is_tunnel && column_is_open && - (c == biome->c_filler || c == biome->c_stone)) - // Tunnel entrance floor - vm->m_data[vi] = MapNode(biome->c_top); - - column_is_open = false; - is_tunnel = false; - } - } - } - - if (node_max.Y > large_cave_depth) - return; - - PseudoRandom ps(blockseed + 21343); - u32 bruises_count = ps.range(0, 2); - for (u32 i = 0; i < bruises_count; i++) { - CaveV5 cave(this, &ps); - cave.makeCave(node_min, node_max, max_stone_y); - } -} diff --git a/src/mapgen_flat.h b/src/mapgen_flat.h index 53fd66a67..2b98c1f31 100644 --- a/src/mapgen_flat.h +++ b/src/mapgen_flat.h @@ -53,43 +53,19 @@ struct MapgenFlatParams : public MapgenSpecificParams { void writeParams(Settings *settings) const; }; -class MapgenFlat : public Mapgen { +class MapgenFlat : public MapgenBasic { public: - EmergeManager *m_emerge; - BiomeManager *bmgr; - - int ystride; - int zstride_1d; - - v3s16 node_min; - v3s16 node_max; - v3s16 full_node_min; - v3s16 full_node_max; - u32 spflags; s16 ground_level; s16 large_cave_depth; - float cave_width; float lake_threshold; float lake_steepness; float hill_threshold; float hill_steepness; Noise *noise_terrain; - Noise *noise_filler_depth; - Noise *noise_cave1; - Noise *noise_cave2; - - Noise *noise_heat; - Noise *noise_humidity; - Noise *noise_heat_blend; - Noise *noise_humidity_blend; - content_t c_stone; - content_t c_water_source; content_t c_lava_source; - content_t c_desert_stone; content_t c_ice; - content_t c_sandstone; content_t c_cobble; content_t c_stair_cobble; @@ -104,9 +80,6 @@ public: int getSpawnLevelAtPoint(v2s16 p); void calculateNoise(); s16 generateTerrain(); - MgStoneType generateBiomes(); - void dustTopNodes(); - void generateCaves(s16 max_stone_y); }; struct MapgenFactoryFlat : public MapgenFactory { diff --git a/src/mapgen_fractal.cpp b/src/mapgen_fractal.cpp index 9671a2143..81e162072 100644 --- a/src/mapgen_fractal.cpp +++ b/src/mapgen_fractal.cpp @@ -48,7 +48,7 @@ FlagDesc flagdesc_mapgen_fractal[] = { MapgenFractal::MapgenFractal(int mapgenid, MapgenParams *params, EmergeManager *emerge) - : Mapgen(mapgenid, params, emerge) + : MapgenBasic(mapgenid, params, emerge) { this->m_emerge = emerge; this->bmgr = emerge->biomemgr; @@ -264,7 +264,7 @@ void MapgenFractal::makeChunk(BlockMakeData *data) MgStoneType stone_type = generateBiomes(); if (flags & MG_CAVES) - generateCaves(stone_surface_max_y); + generateCaves(stone_surface_max_y, MGFRACTAL_LARGE_CAVE_DEPTH); if ((flags & MG_DUNGEONS) && (stone_surface_max_y >= node_min.Y)) { DungeonParams dp; @@ -505,220 +505,3 @@ s16 MapgenFractal::generateTerrain() return stone_surface_max_y; } - - -MgStoneType MapgenFractal::generateBiomes() -{ - v3s16 em = vm->m_area.getExtent(); - u32 index = 0; - MgStoneType stone_type = STONE; - - for (s16 z = node_min.Z; z <= node_max.Z; z++) - for (s16 x = node_min.X; x <= node_max.X; x++, index++) { - Biome *biome = NULL; - u16 depth_top = 0; - u16 base_filler = 0; - u16 depth_water_top = 0; - u32 vi = vm->m_area.index(x, node_max.Y, z); - - // Check node at base of mapchunk above, either a node of a previously - // generated mapchunk or if not, a node of overgenerated base terrain. - content_t c_above = vm->m_data[vi + em.X].getContent(); - bool air_above = c_above == CONTENT_AIR; - bool water_above = c_above == c_water_source; - - // If there is air or water above enable top/filler placement, otherwise force - // nplaced to stone level by setting a number exceeding any possible filler depth. - u16 nplaced = (air_above || water_above) ? 0 : U16_MAX; - - - for (s16 y = node_max.Y; y >= node_min.Y; y--) { - content_t c = vm->m_data[vi].getContent(); - - // Biome is recalculated each time an upper surface is detected while - // working down a column. The selected biome then remains in effect for - // all nodes below until the next surface and biome recalculation. - // Biome is recalculated: - // 1. At the surface of stone below air or water. - // 2. At the surface of water below air. - // 3. When stone or water is detected but biome has not yet been calculated. - if ((c == c_stone && (air_above || water_above || !biome)) || - (c == c_water_source && (air_above || !biome))) { - biome = biomegen->getBiomeAtIndex(index, y); - - depth_top = biome->depth_top; - base_filler = MYMAX(depth_top + biome->depth_filler - + noise_filler_depth->result[index], 0); - depth_water_top = biome->depth_water_top; - - // Detect stone type for dungeons during every biome calculation. - // This is more efficient than detecting per-node and will not - // miss any desert stone or sandstone biomes. - if (biome->c_stone == c_desert_stone) - stone_type = DESERT_STONE; - else if (biome->c_stone == c_sandstone) - stone_type = SANDSTONE; - } - - if (c == c_stone) { - content_t c_below = vm->m_data[vi - em.X].getContent(); - - // If the node below isn't solid, make this node stone, so that - // any top/filler nodes above are structurally supported. - // This is done by aborting the cycle of top/filler placement - // immediately by forcing nplaced to stone level. - if (c_below == CONTENT_AIR || c_below == c_water_source) - nplaced = U16_MAX; - - if (nplaced < depth_top) { - vm->m_data[vi] = MapNode(biome->c_top); - nplaced++; - } else if (nplaced < base_filler) { - vm->m_data[vi] = MapNode(biome->c_filler); - nplaced++; - } else { - vm->m_data[vi] = MapNode(biome->c_stone); - } - - air_above = false; - water_above = false; - } else if (c == c_water_source) { - vm->m_data[vi] = MapNode((y > (s32)(water_level - depth_water_top)) ? - biome->c_water_top : biome->c_water); - nplaced = 0; // Enable top/filler placement for next surface - air_above = false; - water_above = true; - } else if (c == CONTENT_AIR) { - nplaced = 0; // Enable top/filler placement for next surface - air_above = true; - water_above = false; - } else { // Possible various nodes overgenerated from neighbouring mapchunks - nplaced = U16_MAX; // Disable top/filler placement - air_above = false; - water_above = false; - } - - vm->m_area.add_y(em, vi, -1); - } - } - - return stone_type; -} - - -void MapgenFractal::dustTopNodes() -{ - if (node_max.Y < water_level) - return; - - v3s16 em = vm->m_area.getExtent(); - u32 index = 0; - - for (s16 z = node_min.Z; z <= node_max.Z; z++) - for (s16 x = node_min.X; x <= node_max.X; x++, index++) { - Biome *biome = (Biome *)bmgr->getRaw(biomemap[index]); - - if (biome->c_dust == CONTENT_IGNORE) - continue; - - u32 vi = vm->m_area.index(x, full_node_max.Y, z); - content_t c_full_max = vm->m_data[vi].getContent(); - s16 y_start; - - if (c_full_max == CONTENT_AIR) { - y_start = full_node_max.Y - 1; - } else if (c_full_max == CONTENT_IGNORE) { - vi = vm->m_area.index(x, node_max.Y + 1, z); - content_t c_max = vm->m_data[vi].getContent(); - - if (c_max == CONTENT_AIR) - y_start = node_max.Y; - else - continue; - } else { - continue; - } - - vi = vm->m_area.index(x, y_start, z); - for (s16 y = y_start; y >= node_min.Y - 1; y--) { - if (vm->m_data[vi].getContent() != CONTENT_AIR) - break; - - vm->m_area.add_y(em, vi, -1); - } - - content_t c = vm->m_data[vi].getContent(); - if (!ndef->get(c).buildable_to && c != CONTENT_IGNORE && c != biome->c_dust) { - vm->m_area.add_y(em, vi, 1); - vm->m_data[vi] = MapNode(biome->c_dust); - } - } -} - - -void MapgenFractal::generateCaves(s16 max_stone_y) -{ - if (max_stone_y < node_min.Y) - return; - - noise_cave1->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z); - noise_cave2->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z); - - v3s16 em = vm->m_area.getExtent(); - u32 index2d = 0; - - for (s16 z = node_min.Z; z <= node_max.Z; z++) - for (s16 x = node_min.X; x <= node_max.X; x++, index2d++) { - bool column_is_open = false; // Is column open to overground - bool is_tunnel = false; // Is tunnel or tunnel floor - u32 vi = vm->m_area.index(x, node_max.Y, z); - u32 index3d = (z - node_min.Z) * zstride_1d + csize.Y * ystride + - (x - node_min.X); - // Biome of column - Biome *biome = (Biome *)bmgr->getRaw(biomemap[index2d]); - - // Don't excavate the overgenerated stone at node_max.Y + 1, - // this creates a 'roof' over the tunnel, preventing light in - // tunnels at mapchunk borders when generating mapchunks upwards. - // This 'roof' is removed when the mapchunk above is generated. - for (s16 y = node_max.Y; y >= node_min.Y - 1; y--, - index3d -= ystride, - vm->m_area.add_y(em, vi, -1)) { - - content_t c = vm->m_data[vi].getContent(); - if (c == CONTENT_AIR || c == biome->c_water_top || - c == biome->c_water) { - column_is_open = true; - continue; - } - // Ground - float d1 = contour(noise_cave1->result[index3d]); - float d2 = contour(noise_cave2->result[index3d]); - - if (d1 * d2 > cave_width && ndef->get(c).is_ground_content) { - // In tunnel and ground content, excavate - vm->m_data[vi] = MapNode(CONTENT_AIR); - is_tunnel = true; - } else { - // Not in tunnel or not ground content - if (is_tunnel && column_is_open && - (c == biome->c_filler || c == biome->c_stone)) - // Tunnel entrance floor - vm->m_data[vi] = MapNode(biome->c_top); - - column_is_open = false; - is_tunnel = false; - } - } - } - - if (node_max.Y > MGFRACTAL_LARGE_CAVE_DEPTH) - return; - - PseudoRandom ps(blockseed + 21343); - u32 bruises_count = ps.range(0, 2); - for (u32 i = 0; i < bruises_count; i++) { - CaveV5 cave(this, &ps); - cave.makeCave(node_min, node_max, max_stone_y); - } -} diff --git a/src/mapgen_fractal.h b/src/mapgen_fractal.h index a0e51b2c6..176885d46 100644 --- a/src/mapgen_fractal.h +++ b/src/mapgen_fractal.h @@ -57,23 +57,12 @@ struct MapgenFractalParams : public MapgenSpecificParams { void writeParams(Settings *settings) const; }; -class MapgenFractal : public Mapgen { +class MapgenFractal : public MapgenBasic { public: - EmergeManager *m_emerge; - BiomeManager *bmgr; - - int ystride; - int zstride_1d; u16 formula; bool julia; - v3s16 node_min; - v3s16 node_max; - v3s16 full_node_min; - v3s16 full_node_max; - u32 spflags; - float cave_width; u16 fractal; u16 iterations; v3f scale; @@ -84,16 +73,9 @@ public: float julia_z; float julia_w; Noise *noise_seabed; - Noise *noise_filler_depth; - Noise *noise_cave1; - Noise *noise_cave2; - content_t c_stone; - content_t c_water_source; content_t c_lava_source; - content_t c_desert_stone; content_t c_ice; - content_t c_sandstone; content_t c_cobble; content_t c_stair_cobble; @@ -109,9 +91,6 @@ public: void calculateNoise(); bool getFractalAtPoint(s16 x, s16 y, s16 z); s16 generateTerrain(); - MgStoneType generateBiomes(); - void dustTopNodes(); - void generateCaves(s16 max_stone_y); }; struct MapgenFactoryFractal : public MapgenFactory { diff --git a/src/mapgen_v5.cpp b/src/mapgen_v5.cpp index 829a1f432..7698af825 100644 --- a/src/mapgen_v5.cpp +++ b/src/mapgen_v5.cpp @@ -46,7 +46,7 @@ FlagDesc flagdesc_mapgen_v5[] = { MapgenV5::MapgenV5(int mapgenid, MapgenParams *params, EmergeManager *emerge) - : Mapgen(mapgenid, params, emerge) + : MapgenBasic(mapgenid, params, emerge) { this->m_emerge = emerge; this->bmgr = emerge->biomemgr; @@ -245,7 +245,7 @@ void MapgenV5::makeChunk(BlockMakeData *data) // Generate caves if ((flags & MG_CAVES) && (stone_surface_max_y >= node_min.Y)) - generateCaves(stone_surface_max_y); + generateCaves(stone_surface_max_y, MGV5_LARGE_CAVE_DEPTH); // Generate dungeons and desert temples if ((flags & MG_DUNGEONS) && (stone_surface_max_y >= node_min.Y)) { @@ -391,220 +391,3 @@ int MapgenV5::generateBaseTerrain() return stone_surface_max_y; } - - -MgStoneType MapgenV5::generateBiomes() -{ - v3s16 em = vm->m_area.getExtent(); - u32 index = 0; - MgStoneType stone_type = STONE; - - for (s16 z = node_min.Z; z <= node_max.Z; z++) - for (s16 x = node_min.X; x <= node_max.X; x++, index++) { - Biome *biome = NULL; - u16 depth_top = 0; - u16 base_filler = 0; - u16 depth_water_top = 0; - u32 vi = vm->m_area.index(x, node_max.Y, z); - - // Check node at base of mapchunk above, either a node of a previously - // generated mapchunk or if not, a node of overgenerated base terrain. - content_t c_above = vm->m_data[vi + em.X].getContent(); - bool air_above = c_above == CONTENT_AIR; - bool water_above = c_above == c_water_source; - - // If there is air or water above enable top/filler placement, otherwise force - // nplaced to stone level by setting a number exceeding any possible filler depth. - u16 nplaced = (air_above || water_above) ? 0 : U16_MAX; - - for (s16 y = node_max.Y; y >= node_min.Y; y--) { - content_t c = vm->m_data[vi].getContent(); - - // Biome is recalculated each time an upper surface is detected while - // working down a column. The selected biome then remains in effect for - // all nodes below until the next surface and biome recalculation. - // Biome is recalculated: - // 1. At the surface of stone below air or water. - // 2. At the surface of water below air. - // 3. When stone or water is detected but biome has not yet been calculated. - if ((c == c_stone && (air_above || water_above || !biome)) || - (c == c_water_source && (air_above || !biome))) { - biome = biomegen->getBiomeAtIndex(index, y); - - depth_top = biome->depth_top; - base_filler = MYMAX(depth_top + biome->depth_filler - + noise_filler_depth->result[index], 0); - depth_water_top = biome->depth_water_top; - - // Detect stone type for dungeons during every biome calculation. - // This is more efficient than detecting per-node and will not - // miss any desert stone or sandstone biomes. - if (biome->c_stone == c_desert_stone) - stone_type = DESERT_STONE; - else if (biome->c_stone == c_sandstone) - stone_type = SANDSTONE; - } - - if (c == c_stone) { - content_t c_below = vm->m_data[vi - em.X].getContent(); - - // If the node below isn't solid, make this node stone, so that - // any top/filler nodes above are structurally supported. - // This is done by aborting the cycle of top/filler placement - // immediately by forcing nplaced to stone level. - if (c_below == CONTENT_AIR || c_below == c_water_source) - nplaced = U16_MAX; - - if (nplaced < depth_top) { - vm->m_data[vi] = MapNode(biome->c_top); - nplaced++; - } else if (nplaced < base_filler) { - vm->m_data[vi] = MapNode(biome->c_filler); - nplaced++; - } else { - vm->m_data[vi] = MapNode(biome->c_stone); - } - - air_above = false; - water_above = false; - } else if (c == c_water_source) { - vm->m_data[vi] = MapNode((y > (s32)(water_level - depth_water_top)) ? - biome->c_water_top : biome->c_water); - nplaced = 0; // Enable top/filler placement for next surface - air_above = false; - water_above = true; - } else if (c == CONTENT_AIR) { - nplaced = 0; // Enable top/filler placement for next surface - air_above = true; - water_above = false; - } else { // Possible various nodes overgenerated from neighbouring mapchunks - nplaced = U16_MAX; // Disable top/filler placement - air_above = false; - water_above = false; - } - - vm->m_area.add_y(em, vi, -1); - } - } - - return stone_type; -} - - -void MapgenV5::dustTopNodes() -{ - if (node_max.Y < water_level) - return; - - v3s16 em = vm->m_area.getExtent(); - u32 index = 0; - - for (s16 z = node_min.Z; z <= node_max.Z; z++) - for (s16 x = node_min.X; x <= node_max.X; x++, index++) { - Biome *biome = (Biome *)bmgr->getRaw(biomemap[index]); - - if (biome->c_dust == CONTENT_IGNORE) - continue; - - u32 vi = vm->m_area.index(x, full_node_max.Y, z); - content_t c_full_max = vm->m_data[vi].getContent(); - s16 y_start; - - if (c_full_max == CONTENT_AIR) { - y_start = full_node_max.Y - 1; - } else if (c_full_max == CONTENT_IGNORE) { - vi = vm->m_area.index(x, node_max.Y + 1, z); - content_t c_max = vm->m_data[vi].getContent(); - - if (c_max == CONTENT_AIR) - y_start = node_max.Y; - else - continue; - } else { - continue; - } - - vi = vm->m_area.index(x, y_start, z); - for (s16 y = y_start; y >= node_min.Y - 1; y--) { - if (vm->m_data[vi].getContent() != CONTENT_AIR) - break; - - vm->m_area.add_y(em, vi, -1); - } - - content_t c = vm->m_data[vi].getContent(); - if (!ndef->get(c).buildable_to && c != CONTENT_IGNORE && c != biome->c_dust) { - vm->m_area.add_y(em, vi, 1); - vm->m_data[vi] = MapNode(biome->c_dust); - } - } -} - - -void MapgenV5::generateCaves(int max_stone_y) -{ - if (max_stone_y < node_min.Y) - return; - - noise_cave1->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z); - noise_cave2->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z); - - v3s16 em = vm->m_area.getExtent(); - u32 index2d = 0; - - for (s16 z = node_min.Z; z <= node_max.Z; z++) - for (s16 x = node_min.X; x <= node_max.X; x++, index2d++) { - bool column_is_open = false; // Is column open to overground - bool is_tunnel = false; // Is tunnel or tunnel floor - // Indexes at column top (node_max.Y) - u32 vi = vm->m_area.index(x, node_max.Y, z); - u32 index3d = (z - node_min.Z) * zstride_1d + csize.Y * ystride + - (x - node_min.X); - // Biome of column - Biome *biome = (Biome *)bmgr->getRaw(biomemap[index2d]); - - // Don't excavate the overgenerated stone at node_max.Y + 1, - // this creates a 'roof' over the tunnel, preventing light in - // tunnels at mapchunk borders when generating mapchunks upwards. - // This 'roof' is removed when the mapchunk above is generated. - for (s16 y = node_max.Y; y >= node_min.Y - 1; y--, - index3d -= ystride, - vm->m_area.add_y(em, vi, -1)) { - - content_t c = vm->m_data[vi].getContent(); - if (c == CONTENT_AIR || c == biome->c_water_top || - c == biome->c_water) { - column_is_open = true; - continue; - } - // Ground - float d1 = contour(noise_cave1->result[index3d]); - float d2 = contour(noise_cave2->result[index3d]); - - if (d1 * d2 > cave_width && ndef->get(c).is_ground_content) { - // In tunnel and ground content, excavate - vm->m_data[vi] = MapNode(CONTENT_AIR); - is_tunnel = true; - } else { - // Not in tunnel or not ground content - if (is_tunnel && column_is_open && - (c == biome->c_filler || c == biome->c_stone)) - // Tunnel entrance floor - vm->m_data[vi] = MapNode(biome->c_top); - - column_is_open = false; - is_tunnel = false; - } - } - } - - if (node_max.Y > MGV5_LARGE_CAVE_DEPTH) - return; - - PseudoRandom ps(blockseed + 21343); - u32 bruises_count = ps.range(0, 2); - for (u32 i = 0; i < bruises_count; i++) { - CaveV5 cave(this, &ps); - cave.makeCave(node_min, node_max, max_stone_y); - } -} diff --git a/src/mapgen_v5.h b/src/mapgen_v5.h index eef3da617..99836b23e 100644 --- a/src/mapgen_v5.h +++ b/src/mapgen_v5.h @@ -48,34 +48,15 @@ struct MapgenV5Params : public MapgenSpecificParams { }; -class MapgenV5 : public Mapgen { +class MapgenV5 : public MapgenBasic { public: - EmergeManager *m_emerge; - BiomeManager *bmgr; - - int ystride; - int zstride_1d; - - v3s16 node_min; - v3s16 node_max; - v3s16 full_node_min; - v3s16 full_node_max; - u32 spflags; - float cave_width; - Noise *noise_filler_depth; Noise *noise_factor; Noise *noise_height; - Noise *noise_cave1; - Noise *noise_cave2; Noise *noise_ground; - content_t c_stone; - content_t c_water_source; content_t c_lava_source; - content_t c_desert_stone; content_t c_ice; - content_t c_sandstone; content_t c_cobble; content_t c_stair_cobble; @@ -90,9 +71,6 @@ public: int getSpawnLevelAtPoint(v2s16 p); void calculateNoise(); int generateBaseTerrain(); - MgStoneType generateBiomes(); - void generateCaves(int max_stone_y); - void dustTopNodes(); }; diff --git a/src/mapgen_v7.cpp b/src/mapgen_v7.cpp index fddb34682..ee609bc95 100644 --- a/src/mapgen_v7.cpp +++ b/src/mapgen_v7.cpp @@ -51,7 +51,7 @@ FlagDesc flagdesc_mapgen_v7[] = { MapgenV7::MapgenV7(int mapgenid, MapgenParams *params, EmergeManager *emerge) - : Mapgen(mapgenid, params, emerge) + : MapgenBasic(mapgenid, params, emerge) { this->m_emerge = emerge; this->bmgr = emerge->biomemgr; @@ -276,7 +276,7 @@ void MapgenV7::makeChunk(BlockMakeData *data) MgStoneType stone_type = generateBiomes(); if (flags & MG_CAVES) - generateCaves(stone_surface_max_y); + generateCaves(stone_surface_max_y, water_level); if ((flags & MG_DUNGEONS) && (stone_surface_max_y >= node_min.Y)) { DungeonParams dp; @@ -517,225 +517,13 @@ void MapgenV7::generateRidgeTerrain() } -MgStoneType MapgenV7::generateBiomes() -{ - v3s16 em = vm->m_area.getExtent(); - u32 index = 0; - MgStoneType stone_type = STONE; - - for (s16 z = node_min.Z; z <= node_max.Z; z++) - for (s16 x = node_min.X; x <= node_max.X; x++, index++) { - Biome *biome = NULL; - u16 depth_top = 0; - u16 base_filler = 0; - u16 depth_water_top = 0; - u32 vi = vm->m_area.index(x, node_max.Y, z); - - // Check node at base of mapchunk above, either a node of a previously - // generated mapchunk or if not, a node of overgenerated base terrain. - content_t c_above = vm->m_data[vi + em.X].getContent(); - bool air_above = c_above == CONTENT_AIR; - bool water_above = c_above == c_water_source; - - // If there is air or water above enable top/filler placement, otherwise force - // nplaced to stone level by setting a number exceeding any possible filler depth. - u16 nplaced = (air_above || water_above) ? 0 : U16_MAX; - - for (s16 y = node_max.Y; y >= node_min.Y; y--) { - content_t c = vm->m_data[vi].getContent(); - - // Biome is recalculated each time an upper surface is detected while - // working down a column. The selected biome then remains in effect for - // all nodes below until the next surface and biome recalculation. - // Biome is recalculated: - // 1. At the surface of stone below air or water. - // 2. At the surface of water below air. - // 3. When stone or water is detected but biome has not yet been calculated. - if ((c == c_stone && (air_above || water_above || !biome)) || - (c == c_water_source && (air_above || !biome))) { - biome = biomegen->getBiomeAtIndex(index, y); - - depth_top = biome->depth_top; - base_filler = MYMAX(depth_top + biome->depth_filler - + noise_filler_depth->result[index], 0); - depth_water_top = biome->depth_water_top; - - // Detect stone type for dungeons during every biome calculation. - // This is more efficient than detecting per-node and will not - // miss any desert stone or sandstone biomes. - if (biome->c_stone == c_desert_stone) - stone_type = DESERT_STONE; - else if (biome->c_stone == c_sandstone) - stone_type = SANDSTONE; - } - - if (c == c_stone) { - content_t c_below = vm->m_data[vi - em.X].getContent(); - - // If the node below isn't solid, make this node stone, so that - // any top/filler nodes above are structurally supported. - // This is done by aborting the cycle of top/filler placement - // immediately by forcing nplaced to stone level. - if (c_below == CONTENT_AIR || c_below == c_water_source) - nplaced = U16_MAX; - - if (nplaced < depth_top) { - vm->m_data[vi] = MapNode(biome->c_top); - nplaced++; - } else if (nplaced < base_filler) { - vm->m_data[vi] = MapNode(biome->c_filler); - nplaced++; - } else { - vm->m_data[vi] = MapNode(biome->c_stone); - } - - air_above = false; - water_above = false; - } else if (c == c_water_source) { - vm->m_data[vi] = MapNode((y > (s32)(water_level - depth_water_top)) ? - biome->c_water_top : biome->c_water); - nplaced = 0; // Enable top/filler placement for next surface - air_above = false; - water_above = true; - } else if (c == CONTENT_AIR) { - nplaced = 0; // Enable top/filler placement for next surface - air_above = true; - water_above = false; - } else { // Possible various nodes overgenerated from neighbouring mapchunks - nplaced = U16_MAX; // Disable top/filler placement - air_above = false; - water_above = false; - } - - vm->m_area.add_y(em, vi, -1); - } - } - - return stone_type; -} - - -void MapgenV7::dustTopNodes() -{ - if (node_max.Y < water_level) - return; - - v3s16 em = vm->m_area.getExtent(); - u32 index = 0; - - for (s16 z = node_min.Z; z <= node_max.Z; z++) - for (s16 x = node_min.X; x <= node_max.X; x++, index++) { - Biome *biome = (Biome *)bmgr->getRaw(biomemap[index]); - - if (biome->c_dust == CONTENT_IGNORE) - continue; - - u32 vi = vm->m_area.index(x, full_node_max.Y, z); - content_t c_full_max = vm->m_data[vi].getContent(); - s16 y_start; - - if (c_full_max == CONTENT_AIR) { - y_start = full_node_max.Y - 1; - } else if (c_full_max == CONTENT_IGNORE) { - vi = vm->m_area.index(x, node_max.Y + 1, z); - content_t c_max = vm->m_data[vi].getContent(); - - if (c_max == CONTENT_AIR) - y_start = node_max.Y; - else - continue; - } else { - continue; - } - - vi = vm->m_area.index(x, y_start, z); - for (s16 y = y_start; y >= node_min.Y - 1; y--) { - if (vm->m_data[vi].getContent() != CONTENT_AIR) - break; - - vm->m_area.add_y(em, vi, -1); - } - - content_t c = vm->m_data[vi].getContent(); - if (!ndef->get(c).buildable_to && c != CONTENT_IGNORE && c != biome->c_dust) { - vm->m_area.add_y(em, vi, 1); - vm->m_data[vi] = MapNode(biome->c_dust); - } - } -} - - -void MapgenV7::generateCaves(s16 max_stone_y) -{ - if (max_stone_y < node_min.Y) - return; - - noise_cave1->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z); - noise_cave2->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z); - - v3s16 em = vm->m_area.getExtent(); - u32 index2d = 0; - - for (s16 z = node_min.Z; z <= node_max.Z; z++) - for (s16 x = node_min.X; x <= node_max.X; x++, index2d++) { - bool column_is_open = false; // Is column open to overground - bool is_tunnel = false; // Is tunnel or tunnel floor - // Indexes at column top (node_max.Y) - u32 vi = vm->m_area.index(x, node_max.Y, z); - u32 index3d = (z - node_min.Z) * zstride_1d + csize.Y * ystride + - (x - node_min.X); - // Biome of column - Biome *biome = (Biome *)bmgr->getRaw(biomemap[index2d]); - - // Don't excavate the overgenerated stone at node_max.Y + 1, - // this creates a 'roof' over the tunnel, preventing light in - // tunnels at mapchunk borders when generating mapchunks upwards. - // This 'roof' is removed when the mapchunk above is generated. - for (s16 y = node_max.Y; y >= node_min.Y - 1; y--, - index3d -= ystride, - vm->m_area.add_y(em, vi, -1)) { - - content_t c = vm->m_data[vi].getContent(); - if (c == CONTENT_AIR || c == biome->c_water_top || - c == biome->c_water) { - column_is_open = true; - continue; - } - // Ground - float d1 = contour(noise_cave1->result[index3d]); - float d2 = contour(noise_cave2->result[index3d]); - - if (d1 * d2 > cave_width && ndef->get(c).is_ground_content) { - // In tunnel and ground content, excavate - vm->m_data[vi] = MapNode(CONTENT_AIR); - is_tunnel = true; - } else { - // Not in tunnel or not ground content - if (is_tunnel && column_is_open && - (c == biome->c_filler || c == biome->c_stone)) - // Tunnel entrance floor - vm->m_data[vi] = MapNode(biome->c_top); - - column_is_open = false; - is_tunnel = false; - } - } - } - - if (node_min.Y >= water_level) - return; - - PseudoRandom ps(blockseed + 21343); - u32 bruises_count = ps.range(0, 2); - for (u32 i = 0; i < bruises_count; i++) { - CaveV7 cave(this, &ps); - cave.makeCave(node_min, node_max, max_stone_y); - } -} - - -/////////////////////////////////////////////////////////////// - +//////////////////////////////////////////////////////////////////////////////// +//// Code Boneyard +//// +//// Much of the stuff here has potential to become useful again at some point +//// in the future, but we don't want it to get lost or forgotten in version +//// control. +//// #if 0 int MapgenV7::generateMountainTerrain(s16 ymax) diff --git a/src/mapgen_v7.h b/src/mapgen_v7.h index a8dd9986d..21beab2b4 100644 --- a/src/mapgen_v7.h +++ b/src/mapgen_v7.h @@ -54,42 +54,24 @@ struct MapgenV7Params : public MapgenSpecificParams { void writeParams(Settings *settings) const; }; -class MapgenV7 : public Mapgen { +class MapgenV7 : public MapgenBasic { public: - EmergeManager *m_emerge; - BiomeManager *bmgr; - - int ystride; int zstride_1u1d; - int zstride_1d; - - v3s16 node_min; - v3s16 node_max; - v3s16 full_node_min; - v3s16 full_node_max; s16 *ridge_heightmap; u32 spflags; - float cave_width; Noise *noise_terrain_base; Noise *noise_terrain_alt; Noise *noise_terrain_persist; Noise *noise_height_select; - Noise *noise_filler_depth; Noise *noise_mount_height; Noise *noise_ridge_uwater; Noise *noise_mountain; Noise *noise_ridge; - Noise *noise_cave1; - Noise *noise_cave2; - content_t c_stone; - content_t c_water_source; content_t c_lava_source; - content_t c_desert_stone; content_t c_ice; - content_t c_sandstone; content_t c_cobble; content_t c_stair_cobble; @@ -112,11 +94,6 @@ public: int generateTerrain(); void generateRidgeTerrain(); - - MgStoneType generateBiomes(); - void dustTopNodes(); - - void generateCaves(s16 max_stone_y); }; struct MapgenFactoryV7 : public MapgenFactory { diff --git a/src/mapgen_valleys.cpp b/src/mapgen_valleys.cpp index 44083d9d3..fa790677a 100644 --- a/src/mapgen_valleys.cpp +++ b/src/mapgen_valleys.cpp @@ -65,7 +65,7 @@ static FlagDesc flagdesc_mapgen_valleys[] = { MapgenValleys::MapgenValleys(int mapgenid, MapgenParams *params, EmergeManager *emerge) - : Mapgen(mapgenid, params, emerge) + : MapgenBasic(mapgenid, params, emerge) { this->m_emerge = emerge; this->bmgr = emerge->biomemgr; @@ -302,7 +302,7 @@ void MapgenValleys::makeChunk(BlockMakeData *data) // Cave creation. if (flags & MG_CAVES) - generateCaves(stone_surface_max_y); + generateCaves(stone_surface_max_y, large_cave_depth); // Dungeon creation if ((flags & MG_DUNGEONS) && node_max.Y < 50 && (stone_surface_max_y >= node_min.Y)) { @@ -692,166 +692,7 @@ int MapgenValleys::generateTerrain() return surface_max_y; } - -MgStoneType MapgenValleys::generateBiomes() -{ - v3s16 em = vm->m_area.getExtent(); - u32 index = 0; - MgStoneType stone_type = STONE; - - for (s16 z = node_min.Z; z <= node_max.Z; z++) - for (s16 x = node_min.X; x <= node_max.X; x++, index++) { - Biome *biome = NULL; - u16 depth_top = 0; - u16 base_filler = 0; - u16 depth_water_top = 0; - u32 vi = vm->m_area.index(x, node_max.Y, z); - - // Check node at base of mapchunk above, either a node of a previously - // generated mapchunk or if not, a node of overgenerated base terrain. - content_t c_above = vm->m_data[vi + em.X].getContent(); - bool air_above = c_above == CONTENT_AIR; - bool water_above = (c_above == c_water_source || c_above == c_river_water_source); - - // If there is air or water above enable top/filler placement, otherwise force - // nplaced to stone level by setting a number exceeding any possible filler depth. - u16 nplaced = (air_above || water_above) ? 0 : U16_MAX; - - for (s16 y = node_max.Y; y >= node_min.Y; y--) { - content_t c = vm->m_data[vi].getContent(); - - // Biome is recalculated each time an upper surface is detected while - // working down a column. The selected biome then remains in effect for - // all nodes below until the next surface and biome recalculation. - // Biome is recalculated: - // 1. At the surface of stone below air or water. - // 2. At the surface of water below air. - // 3. When stone or water is detected but biome has not yet been calculated. - if ((c == c_stone && (air_above || water_above || !biome)) - || ((c == c_water_source || c == c_river_water_source) - && (air_above || !biome))) { - // Both heat and humidity have already been adjusted for altitude. - biome = biomegen->getBiomeAtIndex(index, y); - - depth_top = biome->depth_top; - base_filler = MYMAX(depth_top - + biome->depth_filler - + noise_filler_depth->result[index], 0.f); - depth_water_top = biome->depth_water_top; - - // Detect stone type for dungeons during every biome calculation. - // This is more efficient than detecting per-node and will not - // miss any desert stone or sandstone biomes. - if (biome->c_stone == c_desert_stone) - stone_type = DESERT_STONE; - else if (biome->c_stone == c_sandstone) - stone_type = SANDSTONE; - } - - if (c == c_stone) { - content_t c_below = vm->m_data[vi - em.X].getContent(); - - // If the node below isn't solid, make this node stone, so that - // any top/filler nodes above are structurally supported. - // This is done by aborting the cycle of top/filler placement - // immediately by forcing nplaced to stone level. - if (c_below == CONTENT_AIR - || c_below == c_water_source - || c_below == c_river_water_source) - nplaced = U16_MAX; - - if (nplaced < depth_top) { - vm->m_data[vi] = MapNode(biome->c_top); - nplaced++; - } else if (nplaced < base_filler) { - vm->m_data[vi] = MapNode(biome->c_filler); - nplaced++; - } else { - vm->m_data[vi] = MapNode(biome->c_stone); - } - - air_above = false; - water_above = false; - } else if (c == c_water_source) { - vm->m_data[vi] = MapNode((y > (s32)(water_level - depth_water_top)) - ? biome->c_water_top : biome->c_water); - nplaced = 0; // Enable top/filler placement for next surface - air_above = false; - water_above = true; - } else if (c == c_river_water_source) { - vm->m_data[vi] = MapNode(biome->c_river_water); - nplaced = depth_top; // Enable filler placement for next surface - air_above = false; - water_above = true; - } else if (c == CONTENT_AIR) { - nplaced = 0; // Enable top/filler placement for next surface - air_above = true; - water_above = false; - } else { // Possible various nodes overgenerated from neighbouring mapchunks - nplaced = U16_MAX; // Disable top/filler placement - air_above = false; - water_above = false; - } - - vm->m_area.add_y(em, vi, -1); - } - } - - return stone_type; -} - - -void MapgenValleys::dustTopNodes() -{ - if (node_max.Y < water_level) - return; - - v3s16 em = vm->m_area.getExtent(); - u32 index = 0; - - for (s16 z = node_min.Z; z <= node_max.Z; z++) - for (s16 x = node_min.X; x <= node_max.X; x++, index++) { - Biome *biome = (Biome *)bmgr->getRaw(biomemap[index]); - - if (biome->c_dust == CONTENT_IGNORE) - continue; - - u32 vi = vm->m_area.index(x, full_node_max.Y, z); - content_t c_full_max = vm->m_data[vi].getContent(); - s16 y_start; - - if (c_full_max == CONTENT_AIR) { - y_start = full_node_max.Y - 1; - } else if (c_full_max == CONTENT_IGNORE) { - vi = vm->m_area.index(x, node_max.Y + 1, z); - content_t c_max = vm->m_data[vi].getContent(); - - if (c_max == CONTENT_AIR) - y_start = node_max.Y; - else - continue; - } else { - continue; - } - - vi = vm->m_area.index(x, y_start, z); - for (s16 y = y_start; y >= node_min.Y - 1; y--) { - if (vm->m_data[vi].getContent() != CONTENT_AIR) - break; - - vm->m_area.add_y(em, vi, -1); - } - - content_t c = vm->m_data[vi].getContent(); - if (!ndef->get(c).buildable_to && c != CONTENT_IGNORE && c != biome->c_dust) { - vm->m_area.add_y(em, vi, 1); - vm->m_data[vi] = MapNode(biome->c_dust); - } - } -} - - -void MapgenValleys::generateCaves(s16 max_stone_y) +void MapgenValleys::generateCaves(s16 max_stone_y, s16 large_cave_depth) { if (max_stone_y < node_min.Y) return; diff --git a/src/mapgen_valleys.h b/src/mapgen_valleys.h index 0e268c1cf..2ab253430 100644 --- a/src/mapgen_valleys.h +++ b/src/mapgen_valleys.h @@ -85,7 +85,7 @@ struct TerrainNoise { float inter_valley_fill; }; -class MapgenValleys : public Mapgen { +class MapgenValleys : public MapgenBasic { public: MapgenValleys(int mapgenid, MapgenParams *params, EmergeManager *emerge); @@ -97,13 +97,9 @@ public: s16 large_cave_depth; private: - EmergeManager *m_emerge; - BiomeManager *bmgr; BiomeGenOriginal *m_bgen; - int ystride; int zstride; - int zstride_1d; float map_gen_limit; @@ -113,11 +109,6 @@ private: s16 cave_water_max_height; s16 lava_max_height; - v3s16 node_min; - v3s16 node_max; - v3s16 full_node_min; - v3s16 full_node_max; - u32 spflags; float altitude_chill; s16 lava_features_lim; @@ -126,10 +117,6 @@ private: float river_size_factor; float *tcave_cache; s16 water_features_lim; - float cave_width; - Noise *noise_filler_depth; - Noise *noise_cave1; - Noise *noise_cave2; Noise *noise_inter_valley_fill; Noise *noise_inter_valley_slope; Noise *noise_rivers; @@ -139,19 +126,13 @@ private: Noise *noise_valley_profile; content_t c_cobble; - content_t c_desert_stone; content_t c_dirt; - content_t c_ice; content_t c_lava_source; content_t c_mossycobble; - content_t c_river_water_source; content_t c_sand; - content_t c_sandstone; content_t c_sandstonebrick; content_t c_stair_cobble; content_t c_stair_sandstonebrick; - content_t c_stone; - content_t c_water_source; float terrainLevelAtPoint(s16 x, s16 z); @@ -161,10 +142,7 @@ private: float terrainLevelFromNoise(TerrainNoise *tn); float adjustedTerrainLevelFromNoise(TerrainNoise *tn); - MgStoneType generateBiomes(); - void dustTopNodes(); - - void generateCaves(s16 max_stone_y); + virtual void generateCaves(s16 max_stone_y, s16 large_cave_depth); }; struct MapgenFactoryValleys : public MapgenFactory { -- cgit v1.2.3 From 9b5c492be57945c2df63e84ce8dbf057f45b2754 Mon Sep 17 00:00:00 2001 From: kwolekr Date: Mon, 2 May 2016 02:45:59 -0400 Subject: Fix MgStoneType and BiomeType enum names --- src/mapgen.cpp | 6 +++--- src/mapgen.h | 6 +++--- src/mapgen_flat.cpp | 6 +++--- src/mapgen_fractal.cpp | 6 +++--- src/mapgen_v5.cpp | 6 +++--- src/mapgen_v7.cpp | 6 +++--- src/mapgen_valleys.cpp | 6 +++--- src/mg_biome.h | 14 +++++++------- src/script/lua_api/l_mapgen.cpp | 12 ++++++------ 9 files changed, 34 insertions(+), 34 deletions(-) (limited to 'src') diff --git a/src/mapgen.cpp b/src/mapgen.cpp index f491ed9eb..01593718b 100644 --- a/src/mapgen.cpp +++ b/src/mapgen.cpp @@ -384,7 +384,7 @@ MgStoneType MapgenBasic::generateBiomes() { v3s16 em = vm->m_area.getExtent(); u32 index = 0; - MgStoneType stone_type = STONE; + MgStoneType stone_type = MGSTONE_STONE; for (s16 z = node_min.Z; z <= node_max.Z; z++) for (s16 x = node_min.X; x <= node_max.X; x++, index++) { @@ -429,9 +429,9 @@ MgStoneType MapgenBasic::generateBiomes() // This is more efficient than detecting per-node and will not // miss any desert stone or sandstone biomes. if (biome->c_stone == c_desert_stone) - stone_type = DESERT_STONE; + stone_type = MGSTONE_DESERT_STONE; else if (biome->c_stone == c_sandstone) - stone_type = SANDSTONE; + stone_type = MGSTONE_SANDSTONE; } if (c == c_stone) { diff --git a/src/mapgen.h b/src/mapgen.h index bee8e786a..3ec148296 100644 --- a/src/mapgen.h +++ b/src/mapgen.h @@ -76,9 +76,9 @@ enum GenNotifyType { // TODO(hmmmm/paramat): make stone type selection dynamic enum MgStoneType { - STONE, - DESERT_STONE, - SANDSTONE, + MGSTONE_STONE, + MGSTONE_DESERT_STONE, + MGSTONE_SANDSTONE, }; struct GenNotifyEvent { diff --git a/src/mapgen_flat.cpp b/src/mapgen_flat.cpp index 97f1c4b2a..2cbf9ace0 100644 --- a/src/mapgen_flat.cpp +++ b/src/mapgen_flat.cpp @@ -257,7 +257,7 @@ void MapgenFlat::makeChunk(BlockMakeData *data) dp.np_density = nparams_dungeon_density; dp.np_wetness = nparams_dungeon_wetness; dp.c_water = c_water_source; - if (stone_type == STONE) { + if (stone_type == MGSTONE_STONE) { dp.c_cobble = c_cobble; dp.c_moss = c_mossycobble; dp.c_stair = c_stair_cobble; @@ -267,7 +267,7 @@ void MapgenFlat::makeChunk(BlockMakeData *data) dp.holesize = v3s16(1, 2, 1); dp.roomsize = v3s16(0, 0, 0); dp.notifytype = GENNOTIFY_DUNGEON; - } else if (stone_type == DESERT_STONE) { + } else if (stone_type == MGSTONE_DESERT_STONE) { dp.c_cobble = c_desert_stone; dp.c_moss = c_desert_stone; dp.c_stair = c_desert_stone; @@ -277,7 +277,7 @@ void MapgenFlat::makeChunk(BlockMakeData *data) dp.holesize = v3s16(2, 3, 2); dp.roomsize = v3s16(2, 5, 2); dp.notifytype = GENNOTIFY_TEMPLE; - } else if (stone_type == SANDSTONE) { + } else if (stone_type == MGSTONE_SANDSTONE) { dp.c_cobble = c_sandstonebrick; dp.c_moss = c_sandstonebrick; dp.c_stair = c_sandstonebrick; diff --git a/src/mapgen_fractal.cpp b/src/mapgen_fractal.cpp index 81e162072..3217d52e2 100644 --- a/src/mapgen_fractal.cpp +++ b/src/mapgen_fractal.cpp @@ -273,7 +273,7 @@ void MapgenFractal::makeChunk(BlockMakeData *data) dp.np_density = nparams_dungeon_density; dp.np_wetness = nparams_dungeon_wetness; dp.c_water = c_water_source; - if (stone_type == STONE) { + if (stone_type == MGSTONE_STONE) { dp.c_cobble = c_cobble; dp.c_moss = c_mossycobble; dp.c_stair = c_stair_cobble; @@ -283,7 +283,7 @@ void MapgenFractal::makeChunk(BlockMakeData *data) dp.holesize = v3s16(1, 2, 1); dp.roomsize = v3s16(0, 0, 0); dp.notifytype = GENNOTIFY_DUNGEON; - } else if (stone_type == DESERT_STONE) { + } else if (stone_type == MGSTONE_DESERT_STONE) { dp.c_cobble = c_desert_stone; dp.c_moss = c_desert_stone; dp.c_stair = c_desert_stone; @@ -293,7 +293,7 @@ void MapgenFractal::makeChunk(BlockMakeData *data) dp.holesize = v3s16(2, 3, 2); dp.roomsize = v3s16(2, 5, 2); dp.notifytype = GENNOTIFY_TEMPLE; - } else if (stone_type == SANDSTONE) { + } else if (stone_type == MGSTONE_SANDSTONE) { dp.c_cobble = c_sandstonebrick; dp.c_moss = c_sandstonebrick; dp.c_stair = c_sandstonebrick; diff --git a/src/mapgen_v5.cpp b/src/mapgen_v5.cpp index 7698af825..6c1441bc2 100644 --- a/src/mapgen_v5.cpp +++ b/src/mapgen_v5.cpp @@ -255,7 +255,7 @@ void MapgenV5::makeChunk(BlockMakeData *data) dp.np_density = nparams_dungeon_density; dp.np_wetness = nparams_dungeon_wetness; dp.c_water = c_water_source; - if (stone_type == STONE) { + if (stone_type == MGSTONE_STONE) { dp.c_cobble = c_cobble; dp.c_moss = c_mossycobble; dp.c_stair = c_stair_cobble; @@ -265,7 +265,7 @@ void MapgenV5::makeChunk(BlockMakeData *data) dp.holesize = v3s16(1, 2, 1); dp.roomsize = v3s16(0, 0, 0); dp.notifytype = GENNOTIFY_DUNGEON; - } else if (stone_type == DESERT_STONE) { + } else if (stone_type == MGSTONE_DESERT_STONE) { dp.c_cobble = c_desert_stone; dp.c_moss = c_desert_stone; dp.c_stair = c_desert_stone; @@ -275,7 +275,7 @@ void MapgenV5::makeChunk(BlockMakeData *data) dp.holesize = v3s16(2, 3, 2); dp.roomsize = v3s16(2, 5, 2); dp.notifytype = GENNOTIFY_TEMPLE; - } else if (stone_type == SANDSTONE) { + } else if (stone_type == MGSTONE_SANDSTONE) { dp.c_cobble = c_sandstonebrick; dp.c_moss = c_sandstonebrick; dp.c_stair = c_sandstonebrick; diff --git a/src/mapgen_v7.cpp b/src/mapgen_v7.cpp index ee609bc95..e6801a24b 100644 --- a/src/mapgen_v7.cpp +++ b/src/mapgen_v7.cpp @@ -285,7 +285,7 @@ void MapgenV7::makeChunk(BlockMakeData *data) dp.np_density = nparams_dungeon_density; dp.np_wetness = nparams_dungeon_wetness; dp.c_water = c_water_source; - if (stone_type == STONE) { + if (stone_type == MGSTONE_STONE) { dp.c_cobble = c_cobble; dp.c_moss = c_mossycobble; dp.c_stair = c_stair_cobble; @@ -295,7 +295,7 @@ void MapgenV7::makeChunk(BlockMakeData *data) dp.holesize = v3s16(1, 2, 1); dp.roomsize = v3s16(0, 0, 0); dp.notifytype = GENNOTIFY_DUNGEON; - } else if (stone_type == DESERT_STONE) { + } else if (stone_type == MGSTONE_DESERT_STONE) { dp.c_cobble = c_desert_stone; dp.c_moss = c_desert_stone; dp.c_stair = c_desert_stone; @@ -305,7 +305,7 @@ void MapgenV7::makeChunk(BlockMakeData *data) dp.holesize = v3s16(2, 3, 2); dp.roomsize = v3s16(2, 5, 2); dp.notifytype = GENNOTIFY_TEMPLE; - } else if (stone_type == SANDSTONE) { + } else if (stone_type == MGSTONE_SANDSTONE) { dp.c_cobble = c_sandstonebrick; dp.c_moss = c_sandstonebrick; dp.c_stair = c_sandstonebrick; diff --git a/src/mapgen_valleys.cpp b/src/mapgen_valleys.cpp index fa790677a..d424ca068 100644 --- a/src/mapgen_valleys.cpp +++ b/src/mapgen_valleys.cpp @@ -312,7 +312,7 @@ void MapgenValleys::makeChunk(BlockMakeData *data) dp.np_density = nparams_dungeon_density; dp.np_wetness = nparams_dungeon_wetness; dp.c_water = c_water_source; - if (stone_type == STONE) { + if (stone_type == MGSTONE_STONE) { dp.c_cobble = c_cobble; dp.c_moss = c_mossycobble; dp.c_stair = c_stair_cobble; @@ -322,7 +322,7 @@ void MapgenValleys::makeChunk(BlockMakeData *data) dp.holesize = v3s16(1, 2, 1); dp.roomsize = v3s16(0, 0, 0); dp.notifytype = GENNOTIFY_DUNGEON; - } else if (stone_type == DESERT_STONE) { + } else if (stone_type == MGSTONE_DESERT_STONE) { dp.c_cobble = c_desert_stone; dp.c_moss = c_desert_stone; dp.c_stair = c_desert_stone; @@ -332,7 +332,7 @@ void MapgenValleys::makeChunk(BlockMakeData *data) dp.holesize = v3s16(2, 3, 2); dp.roomsize = v3s16(2, 5, 2); dp.notifytype = GENNOTIFY_TEMPLE; - } else if (stone_type == SANDSTONE) { + } else if (stone_type == MGSTONE_SANDSTONE) { dp.c_cobble = c_sandstonebrick; dp.c_moss = c_sandstonebrick; dp.c_stair = c_sandstonebrick; diff --git a/src/mg_biome.h b/src/mg_biome.h index e9378fd79..e78e90e5f 100644 --- a/src/mg_biome.h +++ b/src/mg_biome.h @@ -33,13 +33,13 @@ class BiomeManager; #define BIOME_NONE ((u8)0) -enum BiomeType -{ - BIOME_NORMAL, - BIOME_LIQUID, - BIOME_NETHER, - BIOME_AETHER, - BIOME_FLAT +// TODO(hmmmm): Decide whether this is obsolete or will be used in the future +enum BiomeType { + BIOMETYPE_NORMAL, + BIOMETYPE_LIQUID, + BIOMETYPE_NETHER, + BIOMETYPE_AETHER, + BIOMETYPE_FLAT, }; class Biome : public ObjDef, public NodeResolver { diff --git a/src/script/lua_api/l_mapgen.cpp b/src/script/lua_api/l_mapgen.cpp index 405b93b86..6baf217af 100644 --- a/src/script/lua_api/l_mapgen.cpp +++ b/src/script/lua_api/l_mapgen.cpp @@ -39,11 +39,11 @@ with this program; if not, write to the Free Software Foundation, Inc., struct EnumString ModApiMapgen::es_BiomeTerrainType[] = { - {BIOME_NORMAL, "normal"}, - {BIOME_LIQUID, "liquid"}, - {BIOME_NETHER, "nether"}, - {BIOME_AETHER, "aether"}, - {BIOME_FLAT, "flat"}, + {BIOMETYPE_NORMAL, "normal"}, + {BIOMETYPE_LIQUID, "liquid"}, + {BIOMETYPE_NETHER, "nether"}, + {BIOMETYPE_AETHER, "aether"}, + {BIOMETYPE_FLAT, "flat"}, {0, NULL}, }; @@ -371,7 +371,7 @@ Biome *read_biome_def(lua_State *L, int index, INodeDefManager *ndef) return NULL; BiomeType biometype = (BiomeType)getenumfield(L, index, "type", - ModApiMapgen::es_BiomeTerrainType, BIOME_NORMAL); + ModApiMapgen::es_BiomeTerrainType, BIOMETYPE_NORMAL); Biome *b = BiomeManager::create(biometype); b->name = getstringfield_default(L, index, "name", ""); -- cgit v1.2.3 From 1bb5eb1da2c2e34c4413c01b45c63f6a67b214a2 Mon Sep 17 00:00:00 2001 From: kwolekr Date: Tue, 10 May 2016 00:01:37 -0400 Subject: Cavegen: Merge CaveV5 and CaveV7 into CavesRandomWalk --- src/cavegen.cpp | 270 +++---------------------------------------------- src/cavegen.h | 57 +---------- src/mapgen.cpp | 2 +- src/mapgen_valleys.cpp | 2 +- 4 files changed, 20 insertions(+), 311 deletions(-) (limited to 'src') diff --git a/src/cavegen.cpp b/src/cavegen.cpp index b8abfbca5..59274101e 100644 --- a/src/cavegen.cpp +++ b/src/cavegen.cpp @@ -31,7 +31,7 @@ NoiseParams nparams_caveliquids(0, 1, v3f(150.0, 150.0, 150.0), 776, 3, 0.6, 2.0 ///////////////////////////////////////// Caves V5 -CaveV5::CaveV5(Mapgen *mg, PseudoRandom *ps) +CavesRandomWalk::CavesRandomWalk(Mapgen *mg, PseudoRandom *ps) { this->mg = mg; this->vm = mg->vm; @@ -43,7 +43,8 @@ CaveV5::CaveV5(Mapgen *mg, PseudoRandom *ps) c_ice = ndef->getId("mapgen_ice"); this->np_caveliquids = &nparams_caveliquids; this->ystride = mg->csize.X; - + this->lava_depth = DEFAULT_LAVA_DEPTH; + if (c_ice == CONTENT_IGNORE) c_ice = CONTENT_AIR; @@ -59,7 +60,7 @@ CaveV5::CaveV5(Mapgen *mg, PseudoRandom *ps) } -void CaveV5::makeCave(v3s16 nmin, v3s16 nmax, int max_stone_height) +void CavesRandomWalk::makeCave(v3s16 nmin, v3s16 nmax, int max_stone_height) { node_min = nmin; node_max = nmax; @@ -84,12 +85,12 @@ void CaveV5::makeCave(v3s16 nmin, v3s16 nmax, int max_stone_height) // Limit maximum to area route_y_max = rangelim(route_y_max, 0, ar.Y - 1); - s16 min = 0; - if (node_min.Y < water_level && node_max.Y > water_level) { - min = water_level - max_tunnel_diameter/3 - of.Y; - route_y_max = water_level + max_tunnel_diameter/3 - of.Y; - } - route_y_min = ps->range(min, min + max_tunnel_diameter); + s16 minpos = 0; + if (node_min.Y < water_level && node_max.Y > water_level) { + minpos = water_level - max_tunnel_diameter / 3 - of.Y; + route_y_max = water_level + max_tunnel_diameter / 3 - of.Y; + } + route_y_min = ps->range(minpos, minpos + max_tunnel_diameter); route_y_min = rangelim(route_y_min, 0, route_y_max); s16 route_start_y_min = route_y_min; @@ -121,7 +122,7 @@ void CaveV5::makeCave(v3s16 nmin, v3s16 nmax, int max_stone_height) } -void CaveV5::makeTunnel(bool dirswitch) +void CavesRandomWalk::makeTunnel(bool dirswitch) { // Randomize size s16 min_d = min_tunnel_diameter; @@ -207,7 +208,7 @@ void CaveV5::makeTunnel(bool dirswitch) } -void CaveV5::carveRoute(v3f vec, float f, bool randomize_xz) +void CavesRandomWalk::carveRoute(v3f vec, float f, bool randomize_xz) { MapNode airnode(CONTENT_AIR); MapNode waternode(c_water_source); @@ -218,7 +219,7 @@ void CaveV5::carveRoute(v3f vec, float f, bool randomize_xz) float nval = NoisePerlin3D(np_caveliquids, startp.X, startp.Y, startp.Z, mg->seed); - MapNode liquidnode = (nval < 0.40 && node_max.Y < MGV5_LAVA_DEPTH) ? + MapNode liquidnode = (nval < 0.40 && node_max.Y < lava_depth) ? lavanode : waternode; v3f fp = orp + vec * f; @@ -567,248 +568,3 @@ void CaveV6::carveRoute(v3f vec, float f, bool randomize_xz, bool tunnel_above_g } } } - - -///////////////////////////////////////// Caves V7 - - -CaveV7::CaveV7(MapgenV7 *mg, PseudoRandom *ps) -{ - this->mg = mg; - this->vm = mg->vm; - this->ndef = mg->ndef; - this->water_level = mg->water_level; - this->ps = ps; - this->c_water_source = mg->c_water_source; - this->c_lava_source = mg->c_lava_source; - this->c_ice = mg->c_ice; - this->np_caveliquids = &nparams_caveliquids; - - dswitchint = ps->range(1, 14); - flooded = ps->range(1, 2) == 2; - - part_max_length_rs = ps->range(2, 4); - tunnel_routepoints = ps->range(5, ps->range(15, 30)); - min_tunnel_diameter = 5; - max_tunnel_diameter = ps->range(7, ps->range(8, 24)); - - large_cave_is_flat = (ps->range(0, 1) == 0); -} - - -void CaveV7::makeCave(v3s16 nmin, v3s16 nmax, int max_stone_height) -{ - node_min = nmin; - node_max = nmax; - max_stone_y = max_stone_height; - main_direction = v3f(0, 0, 0); - - // Allowed route area size in nodes - ar = node_max - node_min + v3s16(1, 1, 1); - // Area starting point in nodes - of = node_min; - - // Allow a bit more - //(this should be more than the maximum radius of the tunnel) - s16 insure = 10; - s16 more = MYMAX(MAP_BLOCKSIZE - max_tunnel_diameter / 2 - insure, 1); - ar += v3s16(1,0,1) * more * 2; - of -= v3s16(1,0,1) * more; - - route_y_min = 0; - // Allow half a diameter + 7 over stone surface - route_y_max = -of.Y + max_stone_y + max_tunnel_diameter / 2 + 7; - - // Limit maximum to area - route_y_max = rangelim(route_y_max, 0, ar.Y - 1); - - s16 min = 0; - if (node_min.Y < water_level && node_max.Y > water_level) { - min = water_level - max_tunnel_diameter/3 - of.Y; - route_y_max = water_level + max_tunnel_diameter/3 - of.Y; - } - route_y_min = ps->range(min, min + max_tunnel_diameter); - route_y_min = rangelim(route_y_min, 0, route_y_max); - - s16 route_start_y_min = route_y_min; - s16 route_start_y_max = route_y_max; - - route_start_y_min = rangelim(route_start_y_min, 0, ar.Y - 1); - route_start_y_max = rangelim(route_start_y_max, route_start_y_min, ar.Y - 1); - - // Randomize starting position - orp = v3f( - (float)(ps->next() % ar.X) + 0.5, - (float)(ps->range(route_start_y_min, route_start_y_max)) + 0.5, - (float)(ps->next() % ar.Z) + 0.5 - ); - - // Add generation notify begin event - v3s16 abs_pos(of.X + orp.X, of.Y + orp.Y, of.Z + orp.Z); - GenNotifyType notifytype = GENNOTIFY_LARGECAVE_BEGIN; - mg->gennotify.addEvent(notifytype, abs_pos); - - // Generate some tunnel starting from orp - for (u16 j = 0; j < tunnel_routepoints; j++) - makeTunnel(j % dswitchint == 0); - - // Add generation notify end event - abs_pos = v3s16(of.X + orp.X, of.Y + orp.Y, of.Z + orp.Z); - notifytype = GENNOTIFY_LARGECAVE_END; - mg->gennotify.addEvent(notifytype, abs_pos); -} - - -void CaveV7::makeTunnel(bool dirswitch) -{ - // Randomize size - s16 min_d = min_tunnel_diameter; - s16 max_d = max_tunnel_diameter; - rs = ps->range(min_d, max_d); - s16 rs_part_max_length_rs = rs * part_max_length_rs; - - v3s16 maxlen; - maxlen = v3s16( - rs_part_max_length_rs, - rs_part_max_length_rs / 2, - rs_part_max_length_rs - ); - - v3f vec; - // Jump downward sometimes - vec = v3f( - (float)(ps->next() % maxlen.X) - (float)maxlen.X / 2, - (float)(ps->next() % maxlen.Y) - (float)maxlen.Y / 2, - (float)(ps->next() % maxlen.Z) - (float)maxlen.Z / 2 - ); - - // Do not make caves that are above ground. - // It is only necessary to check the startpoint and endpoint. - v3s16 orpi(orp.X, orp.Y, orp.Z); - v3s16 veci(vec.X, vec.Y, vec.Z); - v3s16 p; - - p = orpi + veci + of + rs / 2; - if (p.Z >= node_min.Z && p.Z <= node_max.Z && - p.X >= node_min.X && p.X <= node_max.X) { - u32 index = (p.Z - node_min.Z) * mg->ystride + (p.X - node_min.X); - s16 h = mg->ridge_heightmap[index]; - if (h < p.Y) - return; - } else if (p.Y > water_level) { - return; // If it's not in our heightmap, use a simple heuristic - } - - p = orpi + of + rs / 2; - if (p.Z >= node_min.Z && p.Z <= node_max.Z && - p.X >= node_min.X && p.X <= node_max.X) { - u32 index = (p.Z - node_min.Z) * mg->ystride + (p.X - node_min.X); - s16 h = mg->ridge_heightmap[index]; - if (h < p.Y) - return; - } else if (p.Y > water_level) { - return; - } - - vec += main_direction; - - v3f rp = orp + vec; - if (rp.X < 0) - rp.X = 0; - else if (rp.X >= ar.X) - rp.X = ar.X - 1; - - if (rp.Y < route_y_min) - rp.Y = route_y_min; - else if (rp.Y >= route_y_max) - rp.Y = route_y_max - 1; - - if (rp.Z < 0) - rp.Z = 0; - else if (rp.Z >= ar.Z) - rp.Z = ar.Z - 1; - - vec = rp - orp; - - float veclen = vec.getLength(); - if (veclen < 0.05) - veclen = 1.0; - - // Every second section is rough - bool randomize_xz = (ps->range(1, 2) == 1); - - // Carve routes - for (float f = 0; f < 1.0; f += 1.0 / veclen) - carveRoute(vec, f, randomize_xz); - - orp = rp; -} - - -void CaveV7::carveRoute(v3f vec, float f, bool randomize_xz) -{ - MapNode airnode(CONTENT_AIR); - MapNode waternode(c_water_source); - MapNode lavanode(c_lava_source); - - v3s16 startp(orp.X, orp.Y, orp.Z); - startp += of; - - float nval = NoisePerlin3D(np_caveliquids, startp.X, - startp.Y, startp.Z, mg->seed); - MapNode liquidnode = (nval < 0.40 && node_max.Y < MGV7_LAVA_DEPTH) ? - lavanode : waternode; - - v3f fp = orp + vec * f; - fp.X += 0.1 * ps->range(-10, 10); - fp.Z += 0.1 * ps->range(-10, 10); - v3s16 cp(fp.X, fp.Y, fp.Z); - - s16 d0 = -rs/2; - s16 d1 = d0 + rs; - if (randomize_xz) { - d0 += ps->range(-1, 1); - d1 += ps->range(-1, 1); - } - - for (s16 z0 = d0; z0 <= d1; z0++) { - s16 si = rs / 2 - MYMAX(0, abs(z0) - rs / 7 - 1); - for (s16 x0 = -si - ps->range(0,1); x0 <= si - 1 + ps->range(0,1); x0++) { - s16 maxabsxz = MYMAX(abs(x0), abs(z0)); - - s16 si2 = rs / 2 - MYMAX(0, maxabsxz - rs / 7 - 1); - - for (s16 y0 = -si2; y0 <= si2; y0++) { - if (large_cave_is_flat) { - // Make large caves not so tall - if (rs > 7 && abs(y0) >= rs / 3) - continue; - } - - v3s16 p(cp.X + x0, cp.Y + y0, cp.Z + z0); - p += of; - - if (vm->m_area.contains(p) == false) - continue; - - u32 i = vm->m_area.index(p); - content_t c = vm->m_data[i].getContent(); - if (!ndef->get(c).is_ground_content) - continue; - - int full_ymin = node_min.Y - MAP_BLOCKSIZE; - int full_ymax = node_max.Y + MAP_BLOCKSIZE; - - if (flooded && full_ymin < water_level && - full_ymax > water_level) - vm->m_data[i] = (p.Y <= water_level) ? - waternode : airnode; - else if (flooded && full_ymax < water_level) - vm->m_data[i] = (p.Y < startp.Y - 4) ? - liquidnode : airnode; - else - vm->m_data[i] = airnode; - } - } - } -} diff --git a/src/cavegen.h b/src/cavegen.h index a1124711b..c907fe294 100644 --- a/src/cavegen.h +++ b/src/cavegen.h @@ -21,19 +21,18 @@ with this program; if not, write to the Free Software Foundation, Inc., #define CAVEGEN_HEADER #define VMANIP_FLAG_CAVE VOXELFLAG_CHECKED1 -#define MGV5_LAVA_DEPTH -256 -#define MGV7_LAVA_DEPTH -256 +#define DEFAULT_LAVA_DEPTH (-256) -class MapgenV5; class MapgenV6; -class MapgenV7; -class CaveV5 { +class CavesRandomWalk { public: Mapgen *mg; MMVManip *vm; INodeDefManager *ndef; + // variables + int lava_depth; NoiseParams *np_caveliquids; s16 min_tunnel_diameter; @@ -67,8 +66,7 @@ public: int water_level; int ystride; - CaveV5() {} - CaveV5(Mapgen *mg, PseudoRandom *ps); + CavesRandomWalk(Mapgen *mg, PseudoRandom *ps); void makeCave(v3s16 nmin, v3s16 nmax, int max_stone_height); void makeTunnel(bool dirswitch); void carveRoute(v3f vec, float f, bool randomize_xz); @@ -118,49 +116,4 @@ public: void carveRoute(v3f vec, float f, bool randomize_xz, bool tunnel_above_ground); }; -class CaveV7 { -public: - MapgenV7 *mg; - MMVManip *vm; - INodeDefManager *ndef; - - NoiseParams *np_caveliquids; - - s16 min_tunnel_diameter; - s16 max_tunnel_diameter; - u16 tunnel_routepoints; - int dswitchint; - int part_max_length_rs; - - bool large_cave_is_flat; - bool flooded; - - s16 max_stone_y; - v3s16 node_min; - v3s16 node_max; - - v3f orp; // starting point, relative to caved space - v3s16 of; // absolute coordinates of caved space - v3s16 ar; // allowed route area - s16 rs; // tunnel radius size - v3f main_direction; - - s16 route_y_min; - s16 route_y_max; - - PseudoRandom *ps; - - content_t c_water_source; - content_t c_lava_source; - content_t c_ice; - - int water_level; - - CaveV7() {} - CaveV7(MapgenV7 *mg, PseudoRandom *ps); - void makeCave(v3s16 nmin, v3s16 nmax, int max_stone_height); - void makeTunnel(bool dirswitch); - void carveRoute(v3f vec, float f, bool randomize_xz); -}; - #endif diff --git a/src/mapgen.cpp b/src/mapgen.cpp index 01593718b..43ac6e51a 100644 --- a/src/mapgen.cpp +++ b/src/mapgen.cpp @@ -599,7 +599,7 @@ void MapgenBasic::generateCaves(s16 max_stone_y, s16 large_cave_depth) PseudoRandom ps(blockseed + 21343); u32 bruises_count = ps.range(0, 2); for (u32 i = 0; i < bruises_count; i++) { - CaveV5 cave(this, &ps); ////caves version varies ---- todo- fix this! + CavesRandomWalk cave(this, &ps); cave.makeCave(node_min, node_max, max_stone_y); } } diff --git a/src/mapgen_valleys.cpp b/src/mapgen_valleys.cpp index d424ca068..77fa1ed06 100644 --- a/src/mapgen_valleys.cpp +++ b/src/mapgen_valleys.cpp @@ -841,7 +841,7 @@ void MapgenValleys::generateCaves(s16 max_stone_y, s16 large_cave_depth) if (node_max.Y <= large_cave_depth && !made_a_big_one) { u32 bruises_count = ps.range(0, 2); for (u32 i = 0; i < bruises_count; i++) { - CaveV5 cave(this, &ps); + CavesRandomWalk cave(this, &ps); cave.makeCave(node_min, node_max, max_stone_y); } } -- cgit v1.2.3 From bf25837617541eee0317e1231abca65233edeb8e Mon Sep 17 00:00:00 2001 From: kwolekr Date: Tue, 10 May 2016 00:10:59 -0400 Subject: Cavegen: Rename CaveV6 to CavesV6 - Add comment explaining why it exists - Remove unused 'flooded' variable - Rename shadowed variable - Fix some code style --- src/cavegen.cpp | 49 +++++++++++++++++++++++++------------------------ src/cavegen.h | 19 +++++++++++++++---- src/mapgen_v6.cpp | 2 +- 3 files changed, 41 insertions(+), 29 deletions(-) (limited to 'src') diff --git a/src/cavegen.cpp b/src/cavegen.cpp index 59274101e..e6e21be40 100644 --- a/src/cavegen.cpp +++ b/src/cavegen.cpp @@ -277,10 +277,12 @@ void CavesRandomWalk::carveRoute(v3f vec, float f, bool randomize_xz) } -///////////////////////////////////////// Caves V6 +//// +//// CavesV6 +//// - -CaveV6::CaveV6(MapgenV6 *mg, PseudoRandom *ps, PseudoRandom *ps2, bool is_large_cave) +CavesV6::CavesV6(MapgenV6 *mg, PseudoRandom *ps, PseudoRandom *ps2, + bool is_large_cave) { this->mg = mg; this->vm = mg->vm; @@ -295,23 +297,22 @@ CaveV6::CaveV6(MapgenV6 *mg, PseudoRandom *ps, PseudoRandom *ps2, bool is_large_ min_tunnel_diameter = 2; max_tunnel_diameter = ps->range(2, 6); dswitchint = ps->range(1, 14); - flooded = true; if (large_cave) { - part_max_length_rs = ps->range(2,4); - tunnel_routepoints = ps->range(5, ps->range(15,30)); + part_max_length_rs = ps->range(2, 4); + tunnel_routepoints = ps->range(5, ps->range(15, 30)); min_tunnel_diameter = 5; - max_tunnel_diameter = ps->range(7, ps->range(8,24)); + max_tunnel_diameter = ps->range(7, ps->range(8, 24)); } else { - part_max_length_rs = ps->range(2,9); - tunnel_routepoints = ps->range(10, ps->range(15,30)); + part_max_length_rs = ps->range(2, 9); + tunnel_routepoints = ps->range(10, ps->range(15, 30)); } - large_cave_is_flat = (ps->range(0,1) == 0); + large_cave_is_flat = (ps->range(0, 1) == 0); } -void CaveV6::makeCave(v3s16 nmin, v3s16 nmax, int max_stone_height) +void CavesV6::makeCave(v3s16 nmin, v3s16 nmax, int max_stone_height) { node_min = nmin; node_max = nmax; @@ -328,8 +329,8 @@ void CaveV6::makeCave(v3s16 nmin, v3s16 nmax, int max_stone_height) const s16 max_spread_amount = MAP_BLOCKSIZE; s16 insure = 10; s16 more = MYMAX(max_spread_amount - max_tunnel_diameter / 2 - insure, 1); - ar += v3s16(1,0,1) * more * 2; - of -= v3s16(1,0,1) * more; + ar += v3s16(1, 0, 1) * more * 2; + of -= v3s16(1, 0, 1) * more; route_y_min = 0; // Allow half a diameter + 7 over stone surface @@ -339,20 +340,20 @@ void CaveV6::makeCave(v3s16 nmin, v3s16 nmax, int max_stone_height) route_y_max = rangelim(route_y_max, 0, ar.Y - 1); if (large_cave) { - s16 min = 0; + s16 minpos = 0; if (node_min.Y < water_level && node_max.Y > water_level) { - min = water_level - max_tunnel_diameter/3 - of.Y; - route_y_max = water_level + max_tunnel_diameter/3 - of.Y; + minpos = water_level - max_tunnel_diameter / 3 - of.Y; + route_y_max = water_level + max_tunnel_diameter / 3 - of.Y; } - route_y_min = ps->range(min, min + max_tunnel_diameter); + route_y_min = ps->range(minpos, minpos + max_tunnel_diameter); route_y_min = rangelim(route_y_min, 0, route_y_max); } s16 route_start_y_min = route_y_min; s16 route_start_y_max = route_y_max; - route_start_y_min = rangelim(route_start_y_min, 0, ar.Y-1); - route_start_y_max = rangelim(route_start_y_max, route_start_y_min, ar.Y-1); + route_start_y_min = rangelim(route_start_y_min, 0, ar.Y - 1); + route_start_y_max = rangelim(route_start_y_max, route_start_y_min, ar.Y - 1); // Randomize starting position orp = v3f( @@ -379,7 +380,7 @@ void CaveV6::makeCave(v3s16 nmin, v3s16 nmax, int max_stone_height) } -void CaveV6::makeTunnel(bool dirswitch) +void CavesV6::makeTunnel(bool dirswitch) { if (dirswitch && !large_cave) { main_direction = v3f( @@ -496,7 +497,8 @@ void CaveV6::makeTunnel(bool dirswitch) } -void CaveV6::carveRoute(v3f vec, float f, bool randomize_xz, bool tunnel_above_ground) +void CavesV6::carveRoute(v3f vec, float f, bool randomize_xz, + bool tunnel_above_ground) { MapNode airnode(CONTENT_AIR); MapNode waternode(c_water_source); @@ -547,11 +549,10 @@ void CaveV6::carveRoute(v3f vec, float f, bool randomize_xz, bool tunnel_above_g int full_ymin = node_min.Y - MAP_BLOCKSIZE; int full_ymax = node_max.Y + MAP_BLOCKSIZE; - if (flooded && full_ymin < water_level && - full_ymax > water_level) { + if (full_ymin < water_level && full_ymax > water_level) { vm->m_data[i] = (p.Y <= water_level) ? waternode : airnode; - } else if (flooded && full_ymax < water_level) { + } else if (full_ymax < water_level) { vm->m_data[i] = (p.Y < startp.Y - 2) ? lavanode : airnode; } else { diff --git a/src/cavegen.h b/src/cavegen.h index c907fe294..b50455c1c 100644 --- a/src/cavegen.h +++ b/src/cavegen.h @@ -72,7 +72,20 @@ public: void carveRoute(v3f vec, float f, bool randomize_xz); }; -class CaveV6 { +/* + CavesV6 is the original version of caves used with Mapgen V6. + + Though it uses the same fundamental algorithm as CavesRandomWalk, it is made + separate to preserve the exact sequence of PseudoRandom calls - any change + to this ordering results in the output being radically different. + Because caves in Mapgen V6 are responsible for a large portion of the basic + terrain shape, modifying this will break our contract of reverse + compatibility for a 'stable' mapgen such as V6. + + tl;dr, + *** DO NOT TOUCH THIS CLASS UNLESS YOU KNOW WHAT YOU ARE DOING *** +*/ +class CavesV6 { public: MapgenV6 *mg; MMVManip *vm; @@ -86,7 +99,6 @@ public: bool large_cave; bool large_cave_is_flat; - bool flooded; s16 max_stone_y; v3s16 node_min; @@ -109,8 +121,7 @@ public: int water_level; - CaveV6() {} - CaveV6(MapgenV6 *mg, PseudoRandom *ps, PseudoRandom *ps2, bool large_cave); + CavesV6(MapgenV6 *mg, PseudoRandom *ps, PseudoRandom *ps2, bool large_cave); void makeCave(v3s16 nmin, v3s16 nmax, int max_stone_height); void makeTunnel(bool dirswitch); void carveRoute(v3f vec, float f, bool randomize_xz, bool tunnel_above_ground); diff --git a/src/mapgen_v6.cpp b/src/mapgen_v6.cpp index c389b2ed4..d143f12e9 100644 --- a/src/mapgen_v6.cpp +++ b/src/mapgen_v6.cpp @@ -1067,7 +1067,7 @@ void MapgenV6::generateCaves(int max_stone_y) for (u32 i = 0; i < caves_count + bruises_count; i++) { bool large_cave = (i >= caves_count); - CaveV6 cave(this, &ps, &ps2, large_cave); + CavesV6 cave(this, &ps, &ps2, large_cave); cave.makeCave(node_min, node_max, max_stone_y); } -- cgit v1.2.3 From a605d690921670008d605d067f1cf8cd1619def7 Mon Sep 17 00:00:00 2001 From: kwolekr Date: Tue, 10 May 2016 13:21:13 -0400 Subject: Cavegen: Remove CavesV6 dependency on Mapgen --- src/cavegen.cpp | 113 +++++++++++++++++++++++++++++++----------------------- src/cavegen.h | 43 ++++++++++++++------- src/mapgen_v6.cpp | 7 ++-- 3 files changed, 99 insertions(+), 64 deletions(-) (limited to 'src') diff --git a/src/cavegen.cpp b/src/cavegen.cpp index e6e21be40..bc73b7206 100644 --- a/src/cavegen.cpp +++ b/src/cavegen.cpp @@ -27,9 +27,9 @@ with this program; if not, write to the Free Software Foundation, Inc., NoiseParams nparams_caveliquids(0, 1, v3f(150.0, 150.0, 150.0), 776, 3, 0.6, 2.0); - -///////////////////////////////////////// Caves V5 - +//// +//// CavesRandomWalk +//// CavesRandomWalk::CavesRandomWalk(Mapgen *mg, PseudoRandom *ps) { @@ -262,13 +262,10 @@ void CavesRandomWalk::carveRoute(v3f vec, float f, bool randomize_xz) int full_ymin = node_min.Y - MAP_BLOCKSIZE; int full_ymax = node_max.Y + MAP_BLOCKSIZE; - if (flooded && full_ymin < water_level && - full_ymax > water_level) - vm->m_data[i] = (p.Y <= water_level) ? - waternode : airnode; + if (flooded && full_ymin < water_level && full_ymax > water_level) + vm->m_data[i] = (p.Y <= water_level) ? waternode : airnode; else if (flooded && full_ymax < water_level) - vm->m_data[i] = (p.Y < startp.Y - 4) ? - liquidnode : airnode; + vm->m_data[i] = (p.Y < startp.Y - 4) ? liquidnode : airnode; else vm->m_data[i] = airnode; } @@ -281,23 +278,51 @@ void CavesRandomWalk::carveRoute(v3f vec, float f, bool randomize_xz) //// CavesV6 //// -CavesV6::CavesV6(MapgenV6 *mg, PseudoRandom *ps, PseudoRandom *ps2, - bool is_large_cave) +CavesV6::CavesV6(INodeDefManager *ndef, GenerateNotifier *gennotify, + int water_level, content_t water_source, content_t lava_source) { - this->mg = mg; - this->vm = mg->vm; - this->ndef = mg->ndef; - this->water_level = mg->water_level; - this->large_cave = is_large_cave; - this->ps = ps; - this->ps2 = ps2; - this->c_water_source = mg->c_water_source; - this->c_lava_source = mg->c_lava_source; + assert(ndef); + + this->ndef = ndef; + this->gennotify = gennotify; + this->water_level = water_level; + + c_water_source = water_source; + if (c_water_source == CONTENT_IGNORE) + c_water_source = ndef->getId("mapgen_water_source"); + if (c_water_source == CONTENT_IGNORE) + c_water_source = CONTENT_AIR; + + c_lava_source = lava_source; + if (c_lava_source == CONTENT_IGNORE) + c_lava_source = ndef->getId("mapgen_lava_source"); + if (c_lava_source == CONTENT_IGNORE) + c_lava_source = CONTENT_AIR; +} + + +void CavesV6::makeCave(MMVManip *vm, v3s16 nmin, v3s16 nmax, + PseudoRandom *ps, PseudoRandom *ps2, + bool is_large_cave, int max_stone_height, s16 *heightmap) +{ + assert(vm); + assert(ps); + assert(ps2); + + this->vm = vm; + this->ps = ps; + this->ps2 = ps2; + this->node_min = nmin; + this->node_max = nmax; + this->heightmap = heightmap; + this->large_cave = is_large_cave; + + this->ystride = nmax.X - nmin.X + 1; + // Set initial parameters from randomness min_tunnel_diameter = 2; max_tunnel_diameter = ps->range(2, 6); dswitchint = ps->range(1, 14); - if (large_cave) { part_max_length_rs = ps->range(2, 4); tunnel_routepoints = ps->range(5, ps->range(15, 30)); @@ -307,16 +332,8 @@ CavesV6::CavesV6(MapgenV6 *mg, PseudoRandom *ps, PseudoRandom *ps2, part_max_length_rs = ps->range(2, 9); tunnel_routepoints = ps->range(10, ps->range(15, 30)); } - large_cave_is_flat = (ps->range(0, 1) == 0); -} - -void CavesV6::makeCave(v3s16 nmin, v3s16 nmax, int max_stone_height) -{ - node_min = nmin; - node_max = nmax; - max_stone_y = max_stone_height; main_direction = v3f(0, 0, 0); // Allowed route area size in nodes @@ -334,7 +351,7 @@ void CavesV6::makeCave(v3s16 nmin, v3s16 nmax, int max_stone_height) route_y_min = 0; // Allow half a diameter + 7 over stone surface - route_y_max = -of.Y + max_stone_y + max_tunnel_diameter / 2 + 7; + route_y_max = -of.Y + max_stone_height + max_tunnel_diameter / 2 + 7; // Limit maximum to area route_y_max = rangelim(route_y_max, 0, ar.Y - 1); @@ -363,20 +380,24 @@ void CavesV6::makeCave(v3s16 nmin, v3s16 nmax, int max_stone_height) ); // Add generation notify begin event - v3s16 abs_pos(of.X + orp.X, of.Y + orp.Y, of.Z + orp.Z); - GenNotifyType notifytype = large_cave ? - GENNOTIFY_LARGECAVE_BEGIN : GENNOTIFY_CAVE_BEGIN; - mg->gennotify.addEvent(notifytype, abs_pos); + if (gennotify != NULL) { + v3s16 abs_pos(of.X + orp.X, of.Y + orp.Y, of.Z + orp.Z); + GenNotifyType notifytype = large_cave ? + GENNOTIFY_LARGECAVE_BEGIN : GENNOTIFY_CAVE_BEGIN; + gennotify->addEvent(notifytype, abs_pos); + } // Generate some tunnel starting from orp for (u16 j = 0; j < tunnel_routepoints; j++) makeTunnel(j % dswitchint == 0); // Add generation notify end event - abs_pos = v3s16(of.X + orp.X, of.Y + orp.Y, of.Z + orp.Z); - notifytype = large_cave ? - GENNOTIFY_LARGECAVE_END : GENNOTIFY_CAVE_END; - mg->gennotify.addEvent(notifytype, abs_pos); + if (gennotify != NULL) { + v3s16 abs_pos = v3s16(of.X + orp.X, of.Y + orp.Y, of.Z + orp.Z); + GenNotifyType notifytype = large_cave ? + GENNOTIFY_LARGECAVE_END : GENNOTIFY_CAVE_END; + gennotify->addEvent(notifytype, abs_pos); + } } @@ -438,9 +459,9 @@ void CavesV6::makeTunnel(bool dirswitch) v3s16 p1 = orpi + veci + of + rs / 2; if (p1.Z >= node_min.Z && p1.Z <= node_max.Z && p1.X >= node_min.X && p1.X <= node_max.X) { - u32 index1 = (p1.Z - node_min.Z) * mg->ystride + + u32 index1 = (p1.Z - node_min.Z) * ystride + (p1.X - node_min.X); - h1 = mg->heightmap[index1]; + h1 = heightmap[index1]; } else { h1 = water_level; // If not in heightmap } @@ -448,9 +469,9 @@ void CavesV6::makeTunnel(bool dirswitch) v3s16 p2 = orpi + of + rs / 2; if (p2.Z >= node_min.Z && p2.Z <= node_max.Z && p2.X >= node_min.X && p2.X <= node_max.X) { - u32 index2 = (p2.Z - node_min.Z) * mg->ystride + + u32 index2 = (p2.Z - node_min.Z) * ystride + (p2.X - node_min.X); - h2 = mg->heightmap[index2]; + h2 = heightmap[index2]; } else { h2 = water_level; } @@ -512,7 +533,7 @@ void CavesV6::carveRoute(v3f vec, float f, bool randomize_xz, fp.Z += 0.1 * ps->range(-10, 10); v3s16 cp(fp.X, fp.Y, fp.Z); - s16 d0 = -rs/2; + s16 d0 = -rs / 2; s16 d1 = d0 + rs; if (randomize_xz) { d0 += ps->range(-1, 1); @@ -550,11 +571,9 @@ void CavesV6::carveRoute(v3f vec, float f, bool randomize_xz, int full_ymax = node_max.Y + MAP_BLOCKSIZE; if (full_ymin < water_level && full_ymax > water_level) { - vm->m_data[i] = (p.Y <= water_level) ? - waternode : airnode; + vm->m_data[i] = (p.Y <= water_level) ? waternode : airnode; } else if (full_ymax < water_level) { - vm->m_data[i] = (p.Y < startp.Y - 2) ? - lavanode : airnode; + vm->m_data[i] = (p.Y < startp.Y - 2) ? lavanode : airnode; } else { vm->m_data[i] = airnode; } diff --git a/src/cavegen.h b/src/cavegen.h index b50455c1c..74cd0b416 100644 --- a/src/cavegen.h +++ b/src/cavegen.h @@ -23,13 +23,14 @@ with this program; if not, write to the Free Software Foundation, Inc., #define VMANIP_FLAG_CAVE VOXELFLAG_CHECKED1 #define DEFAULT_LAVA_DEPTH (-256) -class MapgenV6; +class GenerateNotifier; class CavesRandomWalk { public: Mapgen *mg; MMVManip *vm; INodeDefManager *ndef; + s16 *heightmap; // variables int lava_depth; @@ -64,7 +65,7 @@ public: content_t c_ice; int water_level; - int ystride; + u16 ystride; CavesRandomWalk(Mapgen *mg, PseudoRandom *ps); void makeCave(v3s16 nmin, v3s16 nmax, int max_stone_height); @@ -87,9 +88,18 @@ public: */ class CavesV6 { public: - MapgenV6 *mg; MMVManip *vm; INodeDefManager *ndef; + GenerateNotifier *gennotify; + PseudoRandom *ps; + PseudoRandom *ps2; + + s16 *heightmap; + content_t c_water_source; + content_t c_lava_source; + int water_level; + + u16 ystride; s16 min_tunnel_diameter; s16 max_tunnel_diameter; @@ -100,7 +110,6 @@ public: bool large_cave; bool large_cave_is_flat; - s16 max_stone_y; v3s16 node_min; v3s16 node_max; @@ -113,16 +122,22 @@ public: s16 route_y_min; s16 route_y_max; - PseudoRandom *ps; - PseudoRandom *ps2; - - content_t c_water_source; - content_t c_lava_source; - - int water_level; - - CavesV6(MapgenV6 *mg, PseudoRandom *ps, PseudoRandom *ps2, bool large_cave); - void makeCave(v3s16 nmin, v3s16 nmax, int max_stone_height); + // ndef is a mandatory parameter. + // If gennotify is NULL, generation events are not logged. + CavesV6(INodeDefManager *ndef, + GenerateNotifier *gennotify = NULL, + int water_level = 1, + content_t water_source = CONTENT_IGNORE, + content_t lava_source = CONTENT_IGNORE); + + // vm, ps, and ps2 are mandatory parameters. + // If heightmap is NULL, the surface level at all points is assumed to + // be water_level. + void makeCave(MMVManip *vm, v3s16 nmin, v3s16 nmax, + PseudoRandom *ps, PseudoRandom *ps2, + bool is_large_cave, int max_stone_height, s16 *heightmap = NULL); + +private: void makeTunnel(bool dirswitch); void carveRoute(v3f vec, float f, bool randomize_xz, bool tunnel_above_ground); }; diff --git a/src/mapgen_v6.cpp b/src/mapgen_v6.cpp index d143f12e9..65acfaf93 100644 --- a/src/mapgen_v6.cpp +++ b/src/mapgen_v6.cpp @@ -1066,9 +1066,10 @@ void MapgenV6::generateCaves(int max_stone_y) } for (u32 i = 0; i < caves_count + bruises_count; i++) { - bool large_cave = (i >= caves_count); - CavesV6 cave(this, &ps, &ps2, large_cave); + CavesV6 cave(ndef, &gennotify, water_level, c_water_source, c_lava_source); - cave.makeCave(node_min, node_max, max_stone_y); + bool large_cave = (i >= caves_count); + cave.makeCave(vm, node_min, node_max, &ps, &ps2, + large_cave, max_stone_y, heightmap); } } -- cgit v1.2.3 From 68b1cd8d1bda704dc174c2e39a03276f68984164 Mon Sep 17 00:00:00 2001 From: kwolekr Date: Tue, 10 May 2016 13:35:39 -0400 Subject: Cavegen: Merge instances of repetitive surface level-finding code --- src/cavegen.cpp | 97 +++++++++++++++++++++++++-------------------------------- src/cavegen.h | 5 +++ 2 files changed, 47 insertions(+), 55 deletions(-) (limited to 'src') diff --git a/src/cavegen.cpp b/src/cavegen.cpp index bc73b7206..59878c890 100644 --- a/src/cavegen.cpp +++ b/src/cavegen.cpp @@ -147,31 +147,10 @@ void CavesRandomWalk::makeTunnel(bool dirswitch) // Do not make caves that are above ground. // It is only necessary to check the startpoint and endpoint. - v3s16 orpi(orp.X, orp.Y, orp.Z); - v3s16 veci(vec.X, vec.Y, vec.Z); - v3s16 p; - - p = orpi + veci + of + rs / 2; - if (p.Z >= node_min.Z && p.Z <= node_max.Z && - p.X >= node_min.X && p.X <= node_max.X) { - u32 index = (p.Z - node_min.Z) * ystride + (p.X - node_min.X); - s16 h = mg->heightmap[index]; - if (h < p.Y) - return; - } else if (p.Y > water_level) { - return; // If it's not in our heightmap, use a simple heuristic - } - - p = orpi + of + rs / 2; - if (p.Z >= node_min.Z && p.Z <= node_max.Z && - p.X >= node_min.X && p.X <= node_max.X) { - u32 index = (p.Z - node_min.Z) * ystride + (p.X - node_min.X); - s16 h = mg->heightmap[index]; - if (h < p.Y) - return; - } else if (p.Y > water_level) { + v3s16 p1 = v3s16(orp.X, orp.Y, orp.Z) + of + rs / 2; + v3s16 p2 = v3s16(vec.X, vec.Y, vec.Z) + p1; + if (isPosAboveSurface(p1) || isPosAboveSurface(p2)) return; - } vec += main_direction; @@ -274,6 +253,22 @@ void CavesRandomWalk::carveRoute(v3f vec, float f, bool randomize_xz) } +inline bool CavesRandomWalk::isPosAboveSurface(v3s16 p) +{ + if (heightmap != NULL && + p.Z >= node_min.Z && p.Z <= node_max.Z && + p.X >= node_min.X && p.X <= node_max.X) { + u32 index = (p.Z - node_min.Z) * ystride + (p.X - node_min.X); + if (heightmap[index] < p.Y) + return true; + } else if (p.Y > water_level) { + return true; + } + + return false; +} + + //// //// CavesV6 //// @@ -448,39 +443,18 @@ void CavesV6::makeTunnel(bool dirswitch) ); } - // Do not make caves that are entirely above ground, to fix - // shadow bugs caused by overgenerated large caves. + // Do not make caves that are entirely above ground, to fix shadow bugs + // caused by overgenerated large caves. // It is only necessary to check the startpoint and endpoint. - v3s16 orpi(orp.X, orp.Y, orp.Z); - v3s16 veci(vec.X, vec.Y, vec.Z); - s16 h1; - s16 h2; - - v3s16 p1 = orpi + veci + of + rs / 2; - if (p1.Z >= node_min.Z && p1.Z <= node_max.Z && - p1.X >= node_min.X && p1.X <= node_max.X) { - u32 index1 = (p1.Z - node_min.Z) * ystride + - (p1.X - node_min.X); - h1 = heightmap[index1]; - } else { - h1 = water_level; // If not in heightmap - } - - v3s16 p2 = orpi + of + rs / 2; - if (p2.Z >= node_min.Z && p2.Z <= node_max.Z && - p2.X >= node_min.X && p2.X <= node_max.X) { - u32 index2 = (p2.Z - node_min.Z) * ystride + - (p2.X - node_min.X); - h2 = heightmap[index2]; - } else { - h2 = water_level; - } + v3s16 p1 = v3s16(orp.X, orp.Y, orp.Z) + of + rs / 2; + v3s16 p2 = v3s16(vec.X, vec.Y, vec.Z) + p1; - // If startpoint and endpoint are above ground, - // disable placing of nodes in carveRoute while - // still running all pseudorandom calls to ensure - // caves consistent with existing worlds. - bool tunnel_above_ground = p1.Y > h1 && p2.Y > h2; + // If startpoint and endpoint are above ground, disable placement of nodes + // in carveRoute while still running all PseudoRandom calls to ensure caves + // are consistent with existing worlds. + bool tunnel_above_ground = + p1.Y > getSurfaceFromHeightmap(p1) && + p2.Y > getSurfaceFromHeightmap(p2); vec += main_direction; @@ -588,3 +562,16 @@ void CavesV6::carveRoute(v3f vec, float f, bool randomize_xz, } } } + + +inline s16 CavesV6::getSurfaceFromHeightmap(v3s16 p) +{ + if (heightmap != NULL && + p.Z >= node_min.Z && p.Z <= node_max.Z && + p.X >= node_min.X && p.X <= node_max.X) { + u32 index = (p.Z - node_min.Z) * ystride + (p.X - node_min.X); + return heightmap[index]; + } else { + return water_level; + } +} diff --git a/src/cavegen.h b/src/cavegen.h index 74cd0b416..5a41966fe 100644 --- a/src/cavegen.h +++ b/src/cavegen.h @@ -71,6 +71,9 @@ public: void makeCave(v3s16 nmin, v3s16 nmax, int max_stone_height); void makeTunnel(bool dirswitch); void carveRoute(v3f vec, float f, bool randomize_xz); + +private: + inline bool isPosAboveSurface(v3s16 p); }; /* @@ -140,6 +143,8 @@ public: private: void makeTunnel(bool dirswitch); void carveRoute(v3f vec, float f, bool randomize_xz, bool tunnel_above_ground); + + inline s16 getSurfaceFromHeightmap(v3s16 p); }; #endif -- cgit v1.2.3 From db1b4dc8908c83aef5560718af7466d95d86e44f Mon Sep 17 00:00:00 2001 From: kwolekr Date: Tue, 10 May 2016 22:56:03 -0400 Subject: Cavegen: Remove CavesRandomWalk dependency on Mapgen --- src/cavegen.cpp | 79 +++++++++++++++++++++++++++++++++----------------- src/cavegen.h | 45 +++++++++++++++++++++++----- src/mapgen.cpp | 6 ++-- src/mapgen_valleys.cpp | 6 ++-- 4 files changed, 98 insertions(+), 38 deletions(-) (limited to 'src') diff --git a/src/cavegen.cpp b/src/cavegen.cpp index 59878c890..25e8473bc 100644 --- a/src/cavegen.cpp +++ b/src/cavegen.cpp @@ -31,23 +31,52 @@ NoiseParams nparams_caveliquids(0, 1, v3f(150.0, 150.0, 150.0), 776, 3, 0.6, 2.0 //// CavesRandomWalk //// -CavesRandomWalk::CavesRandomWalk(Mapgen *mg, PseudoRandom *ps) +CavesRandomWalk::CavesRandomWalk( + INodeDefManager *ndef, + GenerateNotifier *gennotify, + int seed, + int water_level, + content_t water_source, + content_t lava_source) { - this->mg = mg; - this->vm = mg->vm; - this->ndef = mg->ndef; - this->water_level = mg->water_level; - this->ps = ps; - c_water_source = ndef->getId("mapgen_water_source"); - c_lava_source = ndef->getId("mapgen_lava_source"); - c_ice = ndef->getId("mapgen_ice"); + assert(ndef); + + this->ndef = ndef; + this->gennotify = gennotify; + this->seed = seed; + this->water_level = water_level; this->np_caveliquids = &nparams_caveliquids; - this->ystride = mg->csize.X; this->lava_depth = DEFAULT_LAVA_DEPTH; - if (c_ice == CONTENT_IGNORE) - c_ice = CONTENT_AIR; + c_water_source = water_source; + if (c_water_source == CONTENT_IGNORE) + c_water_source = ndef->getId("mapgen_water_source"); + if (c_water_source == CONTENT_IGNORE) + c_water_source = CONTENT_AIR; + c_lava_source = lava_source; + if (c_lava_source == CONTENT_IGNORE) + c_lava_source = ndef->getId("mapgen_lava_source"); + if (c_lava_source == CONTENT_IGNORE) + c_lava_source = CONTENT_AIR; +} + + +void CavesRandomWalk::makeCave(MMVManip *vm, v3s16 nmin, v3s16 nmax, + PseudoRandom *ps, int max_stone_height, s16 *heightmap) +{ + assert(vm); + assert(ps); + + this->vm = vm; + this->ps = ps; + this->node_min = nmin; + this->node_max = nmax; + this->heightmap = heightmap; + + this->ystride = nmax.X - nmin.X + 1; + + // Set initial parameters from randomness dswitchint = ps->range(1, 14); flooded = ps->range(1, 2) == 2; @@ -57,13 +86,7 @@ CavesRandomWalk::CavesRandomWalk(Mapgen *mg, PseudoRandom *ps) max_tunnel_diameter = ps->range(7, ps->range(8, 24)); large_cave_is_flat = (ps->range(0, 1) == 0); -} - -void CavesRandomWalk::makeCave(v3s16 nmin, v3s16 nmax, int max_stone_height) -{ - node_min = nmin; - node_max = nmax; main_direction = v3f(0, 0, 0); // Allowed route area size in nodes @@ -107,18 +130,22 @@ void CavesRandomWalk::makeCave(v3s16 nmin, v3s16 nmax, int max_stone_height) ); // Add generation notify begin event - v3s16 abs_pos(of.X + orp.X, of.Y + orp.Y, of.Z + orp.Z); - GenNotifyType notifytype = GENNOTIFY_LARGECAVE_BEGIN; - mg->gennotify.addEvent(notifytype, abs_pos); + if (gennotify) { + v3s16 abs_pos(of.X + orp.X, of.Y + orp.Y, of.Z + orp.Z); + GenNotifyType notifytype = GENNOTIFY_LARGECAVE_BEGIN; + gennotify->addEvent(notifytype, abs_pos); + } // Generate some tunnel starting from orp for (u16 j = 0; j < tunnel_routepoints; j++) makeTunnel(j % dswitchint == 0); // Add generation notify end event - abs_pos = v3s16(of.X + orp.X, of.Y + orp.Y, of.Z + orp.Z); - notifytype = GENNOTIFY_LARGECAVE_END; - mg->gennotify.addEvent(notifytype, abs_pos); + if (gennotify) { + v3s16 abs_pos(of.X + orp.X, of.Y + orp.Y, of.Z + orp.Z); + GenNotifyType notifytype = GENNOTIFY_LARGECAVE_END; + gennotify->addEvent(notifytype, abs_pos); + } } @@ -197,7 +224,7 @@ void CavesRandomWalk::carveRoute(v3f vec, float f, bool randomize_xz) startp += of; float nval = NoisePerlin3D(np_caveliquids, startp.X, - startp.Y, startp.Z, mg->seed); + startp.Y, startp.Z, seed); MapNode liquidnode = (nval < 0.40 && node_max.Y < lava_depth) ? lavanode : waternode; @@ -388,7 +415,7 @@ void CavesV6::makeCave(MMVManip *vm, v3s16 nmin, v3s16 nmax, // Add generation notify end event if (gennotify != NULL) { - v3s16 abs_pos = v3s16(of.X + orp.X, of.Y + orp.Y, of.Z + orp.Z); + v3s16 abs_pos(of.X + orp.X, of.Y + orp.Y, of.Z + orp.Z); GenNotifyType notifytype = large_cave ? GENNOTIFY_LARGECAVE_END : GENNOTIFY_CAVE_END; gennotify->addEvent(notifytype, abs_pos); diff --git a/src/cavegen.h b/src/cavegen.h index 5a41966fe..2db5e9d4f 100644 --- a/src/cavegen.h +++ b/src/cavegen.h @@ -25,17 +25,35 @@ with this program; if not, write to the Free Software Foundation, Inc., class GenerateNotifier; +/* + CavesRandomWalk is an implementation of a cave-digging algorithm that + operates on the principle of a "random walk" to approximate the stochiastic + activity of cavern development. + + In summary, this algorithm works by carving a randomly sized tunnel in a + random direction a random amount of times, randomly varying in width. + All randomness here is uniformly distributed; alternative distributions have + not yet been implemented. + + This algorithm is very fast, executing in less than 1ms on average for an + 80x80x80 chunk of map on a modern processor. +*/ class CavesRandomWalk { public: - Mapgen *mg; MMVManip *vm; INodeDefManager *ndef; + GenerateNotifier *gennotify; s16 *heightmap; - // variables + // configurable parameters + int seed; + int water_level; int lava_depth; NoiseParams *np_caveliquids; + // intermediate state variables + u16 ystride; + s16 min_tunnel_diameter; s16 max_tunnel_diameter; u16 tunnel_routepoints; @@ -62,17 +80,26 @@ public: content_t c_water_source; content_t c_lava_source; - content_t c_ice; - int water_level; - u16 ystride; + // ndef is a mandatory parameter. + // If gennotify is NULL, generation events are not logged. + CavesRandomWalk(INodeDefManager *ndef, + GenerateNotifier *gennotify = NULL, + int seed = 0, + int water_level = 1, + content_t water_source = CONTENT_IGNORE, + content_t lava_source = CONTENT_IGNORE); - CavesRandomWalk(Mapgen *mg, PseudoRandom *ps); - void makeCave(v3s16 nmin, v3s16 nmax, int max_stone_height); + // vm and ps are mandatory parameters. + // If heightmap is NULL, the surface level at all points is assumed to + // be water_level. + void makeCave(MMVManip *vm, v3s16 nmin, v3s16 nmax, + PseudoRandom *ps, int max_stone_height, s16 *heightmap); + +private: void makeTunnel(bool dirswitch); void carveRoute(v3f vec, float f, bool randomize_xz); -private: inline bool isPosAboveSurface(v3s16 p); }; @@ -97,11 +124,13 @@ public: PseudoRandom *ps; PseudoRandom *ps2; + // configurable parameters s16 *heightmap; content_t c_water_source; content_t c_lava_source; int water_level; + // intermediate state variables u16 ystride; s16 min_tunnel_diameter; diff --git a/src/mapgen.cpp b/src/mapgen.cpp index 43ac6e51a..7e74ad642 100644 --- a/src/mapgen.cpp +++ b/src/mapgen.cpp @@ -599,8 +599,10 @@ void MapgenBasic::generateCaves(s16 max_stone_y, s16 large_cave_depth) PseudoRandom ps(blockseed + 21343); u32 bruises_count = ps.range(0, 2); for (u32 i = 0; i < bruises_count; i++) { - CavesRandomWalk cave(this, &ps); - cave.makeCave(node_min, node_max, max_stone_y); + CavesRandomWalk cave(ndef, &gennotify, seed, water_level, + c_water_source, CONTENT_IGNORE); + + cave.makeCave(vm, node_min, node_max, &ps, max_stone_y, heightmap); } } diff --git a/src/mapgen_valleys.cpp b/src/mapgen_valleys.cpp index 77fa1ed06..af7a8b153 100644 --- a/src/mapgen_valleys.cpp +++ b/src/mapgen_valleys.cpp @@ -841,8 +841,10 @@ void MapgenValleys::generateCaves(s16 max_stone_y, s16 large_cave_depth) if (node_max.Y <= large_cave_depth && !made_a_big_one) { u32 bruises_count = ps.range(0, 2); for (u32 i = 0; i < bruises_count; i++) { - CavesRandomWalk cave(this, &ps); - cave.makeCave(node_min, node_max, max_stone_y); + CavesRandomWalk cave(ndef, &gennotify, seed, water_level, + c_water_source, c_lava_source); + + cave.makeCave(vm, node_min, node_max, &ps, max_stone_y, heightmap); } } } -- cgit v1.2.3 From 5b05f75a272ede582d7f86d8dbbbf7bc81e81970 Mon Sep 17 00:00:00 2001 From: kwolekr Date: Wed, 11 May 2016 00:00:05 -0400 Subject: Cavegen: Re-add small caves to CavesRandomWalk --- src/cavegen.cpp | 121 +++++++++++++++++++++++++++++++++++-------------- src/cavegen.h | 5 +- src/mapgen.cpp | 2 +- src/mapgen_valleys.cpp | 2 +- 4 files changed, 91 insertions(+), 39 deletions(-) (limited to 'src') diff --git a/src/cavegen.cpp b/src/cavegen.cpp index 25e8473bc..1beac6f27 100644 --- a/src/cavegen.cpp +++ b/src/cavegen.cpp @@ -63,16 +63,17 @@ CavesRandomWalk::CavesRandomWalk( void CavesRandomWalk::makeCave(MMVManip *vm, v3s16 nmin, v3s16 nmax, - PseudoRandom *ps, int max_stone_height, s16 *heightmap) + PseudoRandom *ps, bool is_large_cave, int max_stone_height, s16 *heightmap) { assert(vm); assert(ps); - this->vm = vm; - this->ps = ps; - this->node_min = nmin; - this->node_max = nmax; - this->heightmap = heightmap; + this->vm = vm; + this->ps = ps; + this->node_min = nmin; + this->node_max = nmax; + this->heightmap = heightmap; + this->large_cave = is_large_cave; this->ystride = nmax.X - nmin.X + 1; @@ -80,10 +81,17 @@ void CavesRandomWalk::makeCave(MMVManip *vm, v3s16 nmin, v3s16 nmax, dswitchint = ps->range(1, 14); flooded = ps->range(1, 2) == 2; - part_max_length_rs = ps->range(2, 4); - tunnel_routepoints = ps->range(5, ps->range(15, 30)); - min_tunnel_diameter = 5; - max_tunnel_diameter = ps->range(7, ps->range(8, 24)); + if (large_cave) { + part_max_length_rs = ps->range(2, 4); + tunnel_routepoints = ps->range(5, ps->range(15, 30)); + min_tunnel_diameter = 5; + max_tunnel_diameter = ps->range(7, ps->range(8, 24)); + } else { + part_max_length_rs = ps->range(2, 9); + tunnel_routepoints = ps->range(10, ps->range(15, 30)); + min_tunnel_diameter = 2; + max_tunnel_diameter = ps->range(2, 6); + } large_cave_is_flat = (ps->range(0, 1) == 0); @@ -108,13 +116,15 @@ void CavesRandomWalk::makeCave(MMVManip *vm, v3s16 nmin, v3s16 nmax, // Limit maximum to area route_y_max = rangelim(route_y_max, 0, ar.Y - 1); - s16 minpos = 0; - if (node_min.Y < water_level && node_max.Y > water_level) { - minpos = water_level - max_tunnel_diameter / 3 - of.Y; - route_y_max = water_level + max_tunnel_diameter / 3 - of.Y; + if (large_cave) { + s16 minpos = 0; + if (node_min.Y < water_level && node_max.Y > water_level) { + minpos = water_level - max_tunnel_diameter / 3 - of.Y; + route_y_max = water_level + max_tunnel_diameter / 3 - of.Y; + } + route_y_min = ps->range(minpos, minpos + max_tunnel_diameter); + route_y_min = rangelim(route_y_min, 0, route_y_max); } - route_y_min = ps->range(minpos, minpos + max_tunnel_diameter); - route_y_min = rangelim(route_y_min, 0, route_y_max); s16 route_start_y_min = route_y_min; s16 route_start_y_max = route_y_max; @@ -132,7 +142,8 @@ void CavesRandomWalk::makeCave(MMVManip *vm, v3s16 nmin, v3s16 nmax, // Add generation notify begin event if (gennotify) { v3s16 abs_pos(of.X + orp.X, of.Y + orp.Y, of.Z + orp.Z); - GenNotifyType notifytype = GENNOTIFY_LARGECAVE_BEGIN; + GenNotifyType notifytype = large_cave ? + GENNOTIFY_LARGECAVE_BEGIN : GENNOTIFY_CAVE_BEGIN; gennotify->addEvent(notifytype, abs_pos); } @@ -143,7 +154,8 @@ void CavesRandomWalk::makeCave(MMVManip *vm, v3s16 nmin, v3s16 nmax, // Add generation notify end event if (gennotify) { v3s16 abs_pos(of.X + orp.X, of.Y + orp.Y, of.Z + orp.Z); - GenNotifyType notifytype = GENNOTIFY_LARGECAVE_END; + GenNotifyType notifytype = large_cave ? + GENNOTIFY_LARGECAVE_END : GENNOTIFY_CAVE_END; gennotify->addEvent(notifytype, abs_pos); } } @@ -151,6 +163,15 @@ void CavesRandomWalk::makeCave(MMVManip *vm, v3s16 nmin, v3s16 nmax, void CavesRandomWalk::makeTunnel(bool dirswitch) { + if (dirswitch && !large_cave) { + main_direction = v3f( + ((float)(ps->next() % 20) - (float)10) / 10, + ((float)(ps->next() % 20) - (float)10) / 30, + ((float)(ps->next() % 20) - (float)10) / 10 + ); + main_direction *= (float)ps->range(0, 10) / 10; + } + // Randomize size s16 min_d = min_tunnel_diameter; s16 max_d = max_tunnel_diameter; @@ -158,19 +179,35 @@ void CavesRandomWalk::makeTunnel(bool dirswitch) s16 rs_part_max_length_rs = rs * part_max_length_rs; v3s16 maxlen; - maxlen = v3s16( - rs_part_max_length_rs, - rs_part_max_length_rs / 2, - rs_part_max_length_rs - ); + if (large_cave) { + maxlen = v3s16( + rs_part_max_length_rs, + rs_part_max_length_rs / 2, + rs_part_max_length_rs + ); + } else { + maxlen = v3s16( + rs_part_max_length_rs, + ps->range(1, rs_part_max_length_rs), + rs_part_max_length_rs + ); + } v3f vec; // Jump downward sometimes - vec = v3f( - (float)(ps->next() % maxlen.X) - (float)maxlen.X / 2, - (float)(ps->next() % maxlen.Y) - (float)maxlen.Y / 2, - (float)(ps->next() % maxlen.Z) - (float)maxlen.Z / 2 - ); + if (!large_cave && ps->range(0, 12) == 0) { + vec = v3f( + (float)(ps->next() % (maxlen.X * 1)) - (float)maxlen.X / 2, + (float)(ps->next() % (maxlen.Y * 2)) - (float)maxlen.Y, + (float)(ps->next() % (maxlen.Z * 1)) - (float)maxlen.Z / 2 + ); + } else { + vec = v3f( + (float)(ps->next() % (maxlen.X * 1)) - (float)maxlen.X / 2, + (float)(ps->next() % (maxlen.Y * 1)) - (float)maxlen.Y / 2, + (float)(ps->next() % (maxlen.Z * 1)) - (float)maxlen.Z / 2 + ); + } // Do not make caves that are above ground. // It is only necessary to check the startpoint and endpoint. @@ -240,6 +277,8 @@ void CavesRandomWalk::carveRoute(v3f vec, float f, bool randomize_xz) d1 += ps->range(-1, 1); } + bool flat_cave_floor = !large_cave && ps->range(0, 2) == 2; + for (s16 z0 = d0; z0 <= d1; z0++) { s16 si = rs / 2 - MYMAX(0, abs(z0) - rs / 7 - 1); for (s16 x0 = -si - ps->range(0,1); x0 <= si - 1 + ps->range(0,1); x0++) { @@ -248,6 +287,10 @@ void CavesRandomWalk::carveRoute(v3f vec, float f, bool randomize_xz) s16 si2 = rs / 2 - MYMAX(0, maxabsxz - rs / 7 - 1); for (s16 y0 = -si2; y0 <= si2; y0++) { + // Make better floors in small caves + if (flat_cave_floor && y0 <= -rs / 2 && rs <= 7) + continue; + if (large_cave_is_flat) { // Make large caves not so tall if (rs > 7 && abs(y0) >= rs / 3) @@ -265,15 +308,23 @@ void CavesRandomWalk::carveRoute(v3f vec, float f, bool randomize_xz) if (!ndef->get(c).is_ground_content) continue; - int full_ymin = node_min.Y - MAP_BLOCKSIZE; - int full_ymax = node_max.Y + MAP_BLOCKSIZE; + if (large_cave) { + int full_ymin = node_min.Y - MAP_BLOCKSIZE; + int full_ymax = node_max.Y + MAP_BLOCKSIZE; + + if (flooded && full_ymin < water_level && full_ymax > water_level) + vm->m_data[i] = (p.Y <= water_level) ? waternode : airnode; + else if (flooded && full_ymax < water_level) + vm->m_data[i] = (p.Y < startp.Y - 4) ? liquidnode : airnode; + else + vm->m_data[i] = airnode; + } else { + if (c == CONTENT_IGNORE) + continue; - if (flooded && full_ymin < water_level && full_ymax > water_level) - vm->m_data[i] = (p.Y <= water_level) ? waternode : airnode; - else if (flooded && full_ymax < water_level) - vm->m_data[i] = (p.Y < startp.Y - 4) ? liquidnode : airnode; - else vm->m_data[i] = airnode; + vm->m_flags[i] |= VMANIP_FLAG_CAVE; + } } } } diff --git a/src/cavegen.h b/src/cavegen.h index 2db5e9d4f..e250ad452 100644 --- a/src/cavegen.h +++ b/src/cavegen.h @@ -60,6 +60,7 @@ public: int dswitchint; int part_max_length_rs; + bool large_cave; bool large_cave_is_flat; bool flooded; @@ -93,8 +94,8 @@ public: // vm and ps are mandatory parameters. // If heightmap is NULL, the surface level at all points is assumed to // be water_level. - void makeCave(MMVManip *vm, v3s16 nmin, v3s16 nmax, - PseudoRandom *ps, int max_stone_height, s16 *heightmap); + void makeCave(MMVManip *vm, v3s16 nmin, v3s16 nmax, PseudoRandom *ps, + bool is_large_cave, int max_stone_height, s16 *heightmap); private: void makeTunnel(bool dirswitch); diff --git a/src/mapgen.cpp b/src/mapgen.cpp index 7e74ad642..09671d845 100644 --- a/src/mapgen.cpp +++ b/src/mapgen.cpp @@ -602,7 +602,7 @@ void MapgenBasic::generateCaves(s16 max_stone_y, s16 large_cave_depth) CavesRandomWalk cave(ndef, &gennotify, seed, water_level, c_water_source, CONTENT_IGNORE); - cave.makeCave(vm, node_min, node_max, &ps, max_stone_y, heightmap); + cave.makeCave(vm, node_min, node_max, &ps, true, max_stone_y, heightmap); } } diff --git a/src/mapgen_valleys.cpp b/src/mapgen_valleys.cpp index af7a8b153..699554ecd 100644 --- a/src/mapgen_valleys.cpp +++ b/src/mapgen_valleys.cpp @@ -844,7 +844,7 @@ void MapgenValleys::generateCaves(s16 max_stone_y, s16 large_cave_depth) CavesRandomWalk cave(ndef, &gennotify, seed, water_level, c_water_source, c_lava_source); - cave.makeCave(vm, node_min, node_max, &ps, max_stone_y, heightmap); + cave.makeCave(vm, node_min, node_max, &ps, true, max_stone_y, heightmap); } } } -- cgit v1.2.3 From b1eb757e60c155707b8fc1b2497b632246819c7b Mon Sep 17 00:00:00 2001 From: kwolekr Date: Wed, 11 May 2016 03:47:45 -0400 Subject: Fix undefined evaluation order when constructing random vectors MSVC and GCC evaluate parameters in right-to-left order, whereas Clang evaluates in left-to-right order, and of course, an optimization could leave the order of evaluation completely indeterminate. This commit fixes all instances of the error by explicitly assigning the results of expressions that use PseudoRandom::next() or range() to their respective vector components. The right-to-left evaluation behavior is preserved since Clang is much less commonly used to compile Minetest than GCC and MSVC combined, and would therefore cause the least harm. --- src/cavegen.cpp | 67 ++++++++++++++++++++++-------------------------------- src/dungeongen.cpp | 29 +++++++++++++++-------- 2 files changed, 47 insertions(+), 49 deletions(-) (limited to 'src') diff --git a/src/cavegen.cpp b/src/cavegen.cpp index 1beac6f27..5728c1337 100644 --- a/src/cavegen.cpp +++ b/src/cavegen.cpp @@ -133,11 +133,9 @@ void CavesRandomWalk::makeCave(MMVManip *vm, v3s16 nmin, v3s16 nmax, route_start_y_max = rangelim(route_start_y_max, route_start_y_min, ar.Y - 1); // Randomize starting position - orp = v3f( - (float)(ps->next() % ar.X) + 0.5, - (float)(ps->range(route_start_y_min, route_start_y_max)) + 0.5, - (float)(ps->next() % ar.Z) + 0.5 - ); + orp.Z = (float)(ps->next() % ar.Z) + 0.5; + orp.Y = (float)(ps->range(route_start_y_min, route_start_y_max)) + 0.5; + orp.X = (float)(ps->next() % ar.X) + 0.5; // Add generation notify begin event if (gennotify) { @@ -164,11 +162,10 @@ void CavesRandomWalk::makeCave(MMVManip *vm, v3s16 nmin, v3s16 nmax, void CavesRandomWalk::makeTunnel(bool dirswitch) { if (dirswitch && !large_cave) { - main_direction = v3f( - ((float)(ps->next() % 20) - (float)10) / 10, - ((float)(ps->next() % 20) - (float)10) / 30, - ((float)(ps->next() % 20) - (float)10) / 10 - ); + main_direction.Z = ((float)(ps->next() % 20) - (float)10) / 10; + main_direction.Y = ((float)(ps->next() % 20) - (float)10) / 30; + main_direction.X = ((float)(ps->next() % 20) - (float)10) / 10; + main_direction *= (float)ps->range(0, 10) / 10; } @@ -196,17 +193,13 @@ void CavesRandomWalk::makeTunnel(bool dirswitch) v3f vec; // Jump downward sometimes if (!large_cave && ps->range(0, 12) == 0) { - vec = v3f( - (float)(ps->next() % (maxlen.X * 1)) - (float)maxlen.X / 2, - (float)(ps->next() % (maxlen.Y * 2)) - (float)maxlen.Y, - (float)(ps->next() % (maxlen.Z * 1)) - (float)maxlen.Z / 2 - ); + vec.Z = (float)(ps->next() % (maxlen.Z * 1)) - (float)maxlen.Z / 2; + vec.Y = (float)(ps->next() % (maxlen.Y * 2)) - (float)maxlen.Y; + vec.X = (float)(ps->next() % (maxlen.X * 1)) - (float)maxlen.X / 2; } else { - vec = v3f( - (float)(ps->next() % (maxlen.X * 1)) - (float)maxlen.X / 2, - (float)(ps->next() % (maxlen.Y * 1)) - (float)maxlen.Y / 2, - (float)(ps->next() % (maxlen.Z * 1)) - (float)maxlen.Z / 2 - ); + vec.Z = (float)(ps->next() % (maxlen.Z * 1)) - (float)maxlen.Z / 2; + vec.Y = (float)(ps->next() % (maxlen.Y * 1)) - (float)maxlen.Y / 2; + vec.X = (float)(ps->next() % (maxlen.X * 1)) - (float)maxlen.X / 2; } // Do not make caves that are above ground. @@ -446,11 +439,9 @@ void CavesV6::makeCave(MMVManip *vm, v3s16 nmin, v3s16 nmax, route_start_y_max = rangelim(route_start_y_max, route_start_y_min, ar.Y - 1); // Randomize starting position - orp = v3f( - (float)(ps->next() % ar.X) + 0.5, - (float)(ps->range(route_start_y_min, route_start_y_max)) + 0.5, - (float)(ps->next() % ar.Z) + 0.5 - ); + orp.Z = (float)(ps->next() % ar.Z) + 0.5; + orp.Y = (float)(ps->range(route_start_y_min, route_start_y_max)) + 0.5; + orp.X = (float)(ps->next() % ar.X) + 0.5; // Add generation notify begin event if (gennotify != NULL) { @@ -477,11 +468,10 @@ void CavesV6::makeCave(MMVManip *vm, v3s16 nmin, v3s16 nmax, void CavesV6::makeTunnel(bool dirswitch) { if (dirswitch && !large_cave) { - main_direction = v3f( - ((float)(ps->next() % 20) - (float)10) / 10, - ((float)(ps->next() % 20) - (float)10) / 30, - ((float)(ps->next() % 20) - (float)10) / 10 - ); + main_direction.Z = ((float)(ps->next() % 20) - (float)10) / 10; + main_direction.Y = ((float)(ps->next() % 20) - (float)10) / 30; + main_direction.X = ((float)(ps->next() % 20) - (float)10) / 10; + main_direction *= (float)ps->range(0, 10) / 10; } @@ -506,19 +496,16 @@ void CavesV6::makeTunnel(bool dirswitch) ); } - v3f vec( - (float)(ps->next() % maxlen.X) - (float)maxlen.X / 2, - (float)(ps->next() % maxlen.Y) - (float)maxlen.Y / 2, - (float)(ps->next() % maxlen.Z) - (float)maxlen.Z / 2 - ); + v3f vec; + vec.Z = (float)(ps->next() % maxlen.Z) - (float)maxlen.Z / 2; + vec.Y = (float)(ps->next() % maxlen.Y) - (float)maxlen.Y / 2; + vec.X = (float)(ps->next() % maxlen.X) - (float)maxlen.X / 2; // Jump downward sometimes if (!large_cave && ps->range(0, 12) == 0) { - vec = v3f( - (float)(ps->next() % maxlen.X) - (float)maxlen.X / 2, - (float)(ps->next() % (maxlen.Y * 2)) - (float)maxlen.Y, - (float)(ps->next() % maxlen.Z) - (float)maxlen.Z / 2 - ); + vec.Z = (float)(ps->next() % maxlen.Z) - (float)maxlen.Z / 2; + vec.Y = (float)(ps->next() % (maxlen.Y * 2)) - (float)maxlen.Y; + vec.X = (float)(ps->next() % maxlen.X) - (float)maxlen.X / 2; } // Do not make caves that are entirely above ground, to fix shadow bugs diff --git a/src/dungeongen.cpp b/src/dungeongen.cpp index 5bfc320c9..f7f9b8b50 100644 --- a/src/dungeongen.cpp +++ b/src/dungeongen.cpp @@ -135,17 +135,23 @@ void DungeonGen::makeDungeon(v3s16 start_padding) bool fits = false; for (u32 i = 0; i < 100 && !fits; i++) { bool is_large_room = ((random.next() & 3) == 1); - roomsize = is_large_room ? - v3s16(random.range(8, 16), random.range(8, 16), random.range(8, 16)) : - v3s16(random.range(4, 8), random.range(4, 6), random.range(4, 8)); + if (is_large_room) { + roomsize.Z = random.range(8, 16); + roomsize.Y = random.range(8, 16); + roomsize.X = random.range(8, 16); + } else { + roomsize.Z = random.range(4, 8); + roomsize.Y = random.range(4, 6); + roomsize.X = random.range(4, 8); + } roomsize += dp.roomsize; // start_padding is used to disallow starting the generation of // a dungeon in a neighboring generation chunk - roomplace = vm->m_area.MinEdge + start_padding + v3s16( - random.range(0, areasize.X - roomsize.X - start_padding.X), - random.range(0, areasize.Y - roomsize.Y - start_padding.Y), - random.range(0, areasize.Z - roomsize.Z - start_padding.Z)); + roomplace = vm->m_area.MinEdge + start_padding; + roomplace.Z += random.range(0, areasize.Z - roomsize.Z - start_padding.Z); + roomplace.Y += random.range(0, areasize.Y - roomsize.Y - start_padding.Y); + roomplace.X += random.range(0, areasize.X - roomsize.X - start_padding.X); /* Check that we're not putting the room to an unknown place, @@ -227,7 +233,9 @@ void DungeonGen::makeDungeon(v3s16 start_padding) makeCorridor(doorplace, doordir, corridor_end, corridor_end_dir); // Find a place for a random sized room - roomsize = v3s16(random.range(4, 8), random.range(4, 6), random.range(4, 8)); + roomsize.Z = random.range(4, 8); + roomsize.Y = random.range(4, 6); + roomsize.X = random.range(4, 8); roomsize += dp.roomsize; m_pos = corridor_end; @@ -587,7 +595,10 @@ v3s16 rand_ortho_dir(PseudoRandom &random, bool diagonal_dirs) do { trycount++; - dir = v3s16(random.next() % 3 - 1, 0, random.next() % 3 - 1); + + dir.Z = random.next() % 3 - 1; + dir.Y = 0; + dir.X = random.next() % 3 - 1; } while ((dir.X == 0 && dir.Z == 0) && trycount < 10); return dir; -- cgit v1.2.3 From 6151f7bc4b32c2576035bd3381bd81ae287c57eb Mon Sep 17 00:00:00 2001 From: kwolekr Date: Sat, 14 May 2016 22:46:50 -0400 Subject: Cavegen: Minor misc. fixes - Convert instances of numeric literal doubles to floats - Move dswitchint to a local variable now that being a member is unnecessary - Improve const correctness - Indentation fixes --- src/cavegen.cpp | 50 +++++++++++++++++++++++++------------------------- src/cavegen.h | 4 +--- 2 files changed, 26 insertions(+), 28 deletions(-) (limited to 'src') diff --git a/src/cavegen.cpp b/src/cavegen.cpp index 5728c1337..5f0af9699 100644 --- a/src/cavegen.cpp +++ b/src/cavegen.cpp @@ -78,8 +78,8 @@ void CavesRandomWalk::makeCave(MMVManip *vm, v3s16 nmin, v3s16 nmax, this->ystride = nmax.X - nmin.X + 1; // Set initial parameters from randomness - dswitchint = ps->range(1, 14); - flooded = ps->range(1, 2) == 2; + int dswitchint = ps->range(1, 14); + flooded = ps->range(1, 2) == 2; if (large_cave) { part_max_length_rs = ps->range(2, 4); @@ -104,10 +104,10 @@ void CavesRandomWalk::makeCave(MMVManip *vm, v3s16 nmin, v3s16 nmax, // Allow a bit more //(this should be more than the maximum radius of the tunnel) - s16 insure = 10; + const s16 insure = 10; s16 more = MYMAX(MAP_BLOCKSIZE - max_tunnel_diameter / 2 - insure, 1); - ar += v3s16(1,0,1) * more * 2; - of -= v3s16(1,0,1) * more; + ar += v3s16(1, 0, 1) * more * 2; + of -= v3s16(1, 0, 1) * more; route_y_min = 0; // Allow half a diameter + 7 over stone surface @@ -133,9 +133,9 @@ void CavesRandomWalk::makeCave(MMVManip *vm, v3s16 nmin, v3s16 nmax, route_start_y_max = rangelim(route_start_y_max, route_start_y_min, ar.Y - 1); // Randomize starting position - orp.Z = (float)(ps->next() % ar.Z) + 0.5; - orp.Y = (float)(ps->range(route_start_y_min, route_start_y_max)) + 0.5; - orp.X = (float)(ps->next() % ar.X) + 0.5; + orp.Z = (float)(ps->next() % ar.Z) + 0.5f; + orp.Y = (float)(ps->range(route_start_y_min, route_start_y_max)) + 0.5f; + orp.X = (float)(ps->next() % ar.X) + 0.5f; // Add generation notify begin event if (gennotify) { @@ -230,14 +230,14 @@ void CavesRandomWalk::makeTunnel(bool dirswitch) vec = rp - orp; float veclen = vec.getLength(); - if (veclen < 0.05) - veclen = 1.0; + if (veclen < 0.05f) + veclen = 1.0f; // Every second section is rough bool randomize_xz = (ps->range(1, 2) == 1); // Carve routes - for (float f = 0; f < 1.0; f += 1.0 / veclen) + for (float f = 0.f; f < 1.0f; f += 1.0f / veclen) carveRoute(vec, f, randomize_xz); orp = rp; @@ -255,15 +255,15 @@ void CavesRandomWalk::carveRoute(v3f vec, float f, bool randomize_xz) float nval = NoisePerlin3D(np_caveliquids, startp.X, startp.Y, startp.Z, seed); - MapNode liquidnode = (nval < 0.40 && node_max.Y < lava_depth) ? + MapNode liquidnode = (nval < 0.40f && node_max.Y < lava_depth) ? lavanode : waternode; v3f fp = orp + vec * f; - fp.X += 0.1 * ps->range(-10, 10); - fp.Z += 0.1 * ps->range(-10, 10); + fp.X += 0.1f * ps->range(-10, 10); + fp.Z += 0.1f * ps->range(-10, 10); v3s16 cp(fp.X, fp.Y, fp.Z); - s16 d0 = -rs/2; + s16 d0 = -rs / 2; s16 d1 = d0 + rs; if (randomize_xz) { d0 += ps->range(-1, 1); @@ -388,7 +388,7 @@ void CavesV6::makeCave(MMVManip *vm, v3s16 nmin, v3s16 nmax, // Set initial parameters from randomness min_tunnel_diameter = 2; max_tunnel_diameter = ps->range(2, 6); - dswitchint = ps->range(1, 14); + int dswitchint = ps->range(1, 14); if (large_cave) { part_max_length_rs = ps->range(2, 4); tunnel_routepoints = ps->range(5, ps->range(15, 30)); @@ -410,7 +410,7 @@ void CavesV6::makeCave(MMVManip *vm, v3s16 nmin, v3s16 nmax, // Allow a bit more //(this should be more than the maximum radius of the tunnel) const s16 max_spread_amount = MAP_BLOCKSIZE; - s16 insure = 10; + const s16 insure = 10; s16 more = MYMAX(max_spread_amount - max_tunnel_diameter / 2 - insure, 1); ar += v3s16(1, 0, 1) * more * 2; of -= v3s16(1, 0, 1) * more; @@ -439,9 +439,9 @@ void CavesV6::makeCave(MMVManip *vm, v3s16 nmin, v3s16 nmax, route_start_y_max = rangelim(route_start_y_max, route_start_y_min, ar.Y - 1); // Randomize starting position - orp.Z = (float)(ps->next() % ar.Z) + 0.5; - orp.Y = (float)(ps->range(route_start_y_min, route_start_y_max)) + 0.5; - orp.X = (float)(ps->next() % ar.X) + 0.5; + orp.Z = (float)(ps->next() % ar.Z) + 0.5f; + orp.Y = (float)(ps->range(route_start_y_min, route_start_y_max)) + 0.5f; + orp.X = (float)(ps->next() % ar.X) + 0.5f; // Add generation notify begin event if (gennotify != NULL) { @@ -543,14 +543,14 @@ void CavesV6::makeTunnel(bool dirswitch) float veclen = vec.getLength(); // As odd as it sounds, veclen is *exactly* 0.0 sometimes, causing a FPE - if (veclen < 0.05) - veclen = 1.0; + if (veclen < 0.05f) + veclen = 1.0f; // Every second section is rough bool randomize_xz = (ps2->range(1, 2) == 1); // Carve routes - for (float f = 0; f < 1.0; f += 1.0 / veclen) + for (float f = 0.f; f < 1.0f; f += 1.0f / veclen) carveRoute(vec, f, randomize_xz, tunnel_above_ground); orp = rp; @@ -568,8 +568,8 @@ void CavesV6::carveRoute(v3f vec, float f, bool randomize_xz, startp += of; v3f fp = orp + vec * f; - fp.X += 0.1 * ps->range(-10, 10); - fp.Z += 0.1 * ps->range(-10, 10); + fp.X += 0.1f * ps->range(-10, 10); + fp.Z += 0.1f * ps->range(-10, 10); v3s16 cp(fp.X, fp.Y, fp.Z); s16 d0 = -rs / 2; diff --git a/src/cavegen.h b/src/cavegen.h index e250ad452..68e0c8bc5 100644 --- a/src/cavegen.h +++ b/src/cavegen.h @@ -57,7 +57,6 @@ public: s16 min_tunnel_diameter; s16 max_tunnel_diameter; u16 tunnel_routepoints; - int dswitchint; int part_max_length_rs; bool large_cave; @@ -95,7 +94,7 @@ public: // If heightmap is NULL, the surface level at all points is assumed to // be water_level. void makeCave(MMVManip *vm, v3s16 nmin, v3s16 nmax, PseudoRandom *ps, - bool is_large_cave, int max_stone_height, s16 *heightmap); + bool is_large_cave, int max_stone_height, s16 *heightmap); private: void makeTunnel(bool dirswitch); @@ -137,7 +136,6 @@ public: s16 min_tunnel_diameter; s16 max_tunnel_diameter; u16 tunnel_routepoints; - int dswitchint; int part_max_length_rs; bool large_cave; -- cgit v1.2.3 From 548d99bb456931192609e8c6fa1eb4c80679af42 Mon Sep 17 00:00:00 2001 From: kwolekr Date: Thu, 19 May 2016 03:40:22 -0400 Subject: Cavegen: Move V5-style caves to CavesNoiseIntersection --- src/cavegen.cpp | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/cavegen.h | 37 +++++++++++++++++++ src/mapgen.cpp | 52 ++------------------------- src/mapgen.h | 5 ++- src/mapgen_flat.cpp | 10 ++---- src/mapgen_fractal.cpp | 10 ++---- src/mapgen_v5.cpp | 10 ++---- src/mapgen_v7.cpp | 10 ++---- src/mapgen_valleys.h | 3 ++ 9 files changed, 151 insertions(+), 82 deletions(-) (limited to 'src') diff --git a/src/cavegen.cpp b/src/cavegen.cpp index 5f0af9699..51309ad60 100644 --- a/src/cavegen.cpp +++ b/src/cavegen.cpp @@ -23,10 +23,106 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "mapgen_v5.h" #include "mapgen_v6.h" #include "mapgen_v7.h" +#include "mg_biome.h" #include "cavegen.h" NoiseParams nparams_caveliquids(0, 1, v3f(150.0, 150.0, 150.0), 776, 3, 0.6, 2.0); + +//// +//// CavesNoiseIntersection +//// + +CavesNoiseIntersection::CavesNoiseIntersection( + INodeDefManager *nodedef, BiomeManager *biomemgr, v3s16 chunksize, + NoiseParams *np_cave1, NoiseParams *np_cave2, int seed, float cave_width) +{ + assert(nodedef); + assert(biomemgr); + + m_ndef = nodedef; + m_bmgr = biomemgr; + + m_csize = chunksize; + m_cave_width = cave_width; + + m_ystride = m_csize.X; + m_zstride_1d = m_csize.X * (m_csize.Y + 1); + + // Noises are created using 1-down overgeneration + // A Nx-by-1-by-Nz-sized plane is at the bottom of the desired for + // re-carving the solid overtop placed for blocking sunlight + noise_cave1 = new Noise(np_cave1, seed, m_csize.X, m_csize.Y + 1, m_csize.Z); + noise_cave2 = new Noise(np_cave2, seed, m_csize.X, m_csize.Y + 1, m_csize.Z); +} + + +CavesNoiseIntersection::~CavesNoiseIntersection() +{ + delete noise_cave1; + delete noise_cave2; +} + + +void CavesNoiseIntersection::generateCaves(MMVManip *vm, + v3s16 nmin, v3s16 nmax, u8 *biomemap) +{ + assert(vm); + assert(biomemap); + + noise_cave1->perlinMap3D(nmin.X, nmin.Y - 1, nmin.Z); + noise_cave2->perlinMap3D(nmin.X, nmin.Y - 1, nmin.Z); + + v3s16 em = vm->m_area.getExtent(); + u32 index2d = 0; + + for (s16 z = nmin.Z; z <= nmax.Z; z++) + for (s16 x = nmin.X; x <= nmax.X; x++, index2d++) { + bool column_is_open = false; // Is column open to overground + bool is_tunnel = false; // Is tunnel or tunnel floor + u32 vi = vm->m_area.index(x, nmax.Y, z); + u32 index3d = (z - nmin.Z) * m_zstride_1d + m_csize.Y * m_ystride + + (x - nmin.X); + // Biome of column + Biome *biome = (Biome *)m_bmgr->getRaw(biomemap[index2d]); + + // Don't excavate the overgenerated stone at nmax.Y + 1, + // this creates a 'roof' over the tunnel, preventing light in + // tunnels at mapchunk borders when generating mapchunks upwards. + // This 'roof' is removed when the mapchunk above is generated. + for (s16 y = nmax.Y; y >= nmin.Y - 1; y--, + index3d -= m_ystride, + vm->m_area.add_y(em, vi, -1)) { + + content_t c = vm->m_data[vi].getContent(); + if (c == CONTENT_AIR || c == biome->c_water_top || + c == biome->c_water) { + column_is_open = true; + continue; + } + // Ground + float d1 = contour(noise_cave1->result[index3d]); + float d2 = contour(noise_cave2->result[index3d]); + + if (d1 * d2 > m_cave_width && m_ndef->get(c).is_ground_content) { + // In tunnel and ground content, excavate + vm->m_data[vi] = MapNode(CONTENT_AIR); + is_tunnel = true; + } else { + // Not in tunnel or not ground content + if (is_tunnel && column_is_open && + (c == biome->c_filler || c == biome->c_stone)) + // Tunnel entrance floor + vm->m_data[vi] = MapNode(biome->c_top); + + column_is_open = false; + is_tunnel = false; + } + } + } +} + + //// //// CavesRandomWalk //// diff --git a/src/cavegen.h b/src/cavegen.h index 68e0c8bc5..da0894635 100644 --- a/src/cavegen.h +++ b/src/cavegen.h @@ -25,6 +25,43 @@ with this program; if not, write to the Free Software Foundation, Inc., class GenerateNotifier; +/* + CavesNoiseIntersection is a cave digging algorithm that carves smooth, + web-like, continuous tunnels at points where the density of the intersection + between two separate 3d noises is above a certain value. This value, + cave_width, can be modified to set the effective width of these tunnels. + + This algorithm is relatively heavyweight, taking ~80ms to generate an + 80x80x80 chunk of map on a modern processor. Use sparingly! + + TODO(hmmmm): Remove dependency on biomes + TODO(hmmmm): Find alternative to overgeneration as solution for sunlight issue +*/ +class CavesNoiseIntersection { +public: + CavesNoiseIntersection(INodeDefManager *nodedef, BiomeManager *biomemgr, + v3s16 chunksize, NoiseParams *np_cave1, NoiseParams *np_cave2, + int seed, float cave_width); + ~CavesNoiseIntersection(); + + void generateCaves(MMVManip *vm, v3s16 nmin, v3s16 nmax, u8 *biomemap); + +private: + INodeDefManager *m_ndef; + BiomeManager *m_bmgr; + + // configurable parameters + v3s16 m_csize; + float m_cave_width; + + // intermediate state variables + u16 m_ystride; + u16 m_zstride_1d; + + Noise *noise_cave1; + Noise *noise_cave2; +}; + /* CavesRandomWalk is an implementation of a cave-digging algorithm that operates on the principle of a "random walk" to approximate the stochiastic diff --git a/src/mapgen.cpp b/src/mapgen.cpp index 09671d845..7e959533d 100644 --- a/src/mapgen.cpp +++ b/src/mapgen.cpp @@ -542,56 +542,10 @@ void MapgenBasic::generateCaves(s16 max_stone_y, s16 large_cave_depth) if (max_stone_y < node_min.Y) return; - noise_cave1->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z); - noise_cave2->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z); + CavesNoiseIntersection caves_noise(ndef, bmgr, csize, + &np_cave1, &np_cave2, seed, cave_width); - v3s16 em = vm->m_area.getExtent(); - u32 index2d = 0; - - for (s16 z = node_min.Z; z <= node_max.Z; z++) - for (s16 x = node_min.X; x <= node_max.X; x++, index2d++) { - bool column_is_open = false; // Is column open to overground - bool is_tunnel = false; // Is tunnel or tunnel floor - u32 vi = vm->m_area.index(x, node_max.Y, z); - u32 index3d = (z - node_min.Z) * zstride_1d + csize.Y * ystride + - (x - node_min.X); - // Biome of column - Biome *biome = (Biome *)bmgr->getRaw(biomemap[index2d]); - - // Don't excavate the overgenerated stone at node_max.Y + 1, - // this creates a 'roof' over the tunnel, preventing light in - // tunnels at mapchunk borders when generating mapchunks upwards. - // This 'roof' is removed when the mapchunk above is generated. - for (s16 y = node_max.Y; y >= node_min.Y - 1; y--, - index3d -= ystride, - vm->m_area.add_y(em, vi, -1)) { - - content_t c = vm->m_data[vi].getContent(); - if (c == CONTENT_AIR || c == biome->c_water_top || - c == biome->c_water) { - column_is_open = true; - continue; - } - // Ground - float d1 = contour(noise_cave1->result[index3d]); - float d2 = contour(noise_cave2->result[index3d]); - - if (d1 * d2 > cave_width && ndef->get(c).is_ground_content) { - // In tunnel and ground content, excavate - vm->m_data[vi] = MapNode(CONTENT_AIR); - is_tunnel = true; - } else { - // Not in tunnel or not ground content - if (is_tunnel && column_is_open && - (c == biome->c_filler || c == biome->c_stone)) - // Tunnel entrance floor - vm->m_data[vi] = MapNode(biome->c_top); - - column_is_open = false; - is_tunnel = false; - } - } - } + caves_noise.generateCaves(vm, node_min, node_max, biomemap); if (node_max.Y > large_cave_depth) return; diff --git a/src/mapgen.h b/src/mapgen.h index 3ec148296..8db01f5c1 100644 --- a/src/mapgen.h +++ b/src/mapgen.h @@ -220,8 +220,6 @@ public: BiomeManager *bmgr; Noise *noise_filler_depth; - Noise *noise_cave1; - Noise *noise_cave2; v3s16 node_min; v3s16 node_max; @@ -235,7 +233,8 @@ public: content_t c_sandstone; int ystride; - int zstride_1d; + NoiseParams np_cave1; + NoiseParams np_cave2; float cave_width; MapgenBasic(int mapgenid, MapgenParams *params, EmergeManager *emerge); diff --git a/src/mapgen_flat.cpp b/src/mapgen_flat.cpp index 2cbf9ace0..a541a54f3 100644 --- a/src/mapgen_flat.cpp +++ b/src/mapgen_flat.cpp @@ -58,8 +58,6 @@ MapgenFlat::MapgenFlat(int mapgenid, MapgenParams *params, EmergeManager *emerge //// amount of elements to skip for the next index //// for noise/height/biome maps (not vmanip) this->ystride = csize.X; - // 1-down overgeneration - this->zstride_1d = csize.X * (csize.Y + 1); this->heightmap = new s16[csize.X * csize.Z]; @@ -78,10 +76,8 @@ MapgenFlat::MapgenFlat(int mapgenid, MapgenParams *params, EmergeManager *emerge noise_terrain = new Noise(&sp->np_terrain, seed, csize.X, csize.Z); noise_filler_depth = new Noise(&sp->np_filler_depth, seed, csize.X, csize.Z); - //// 3D noise - // 1-down overgeneraion - noise_cave1 = new Noise(&sp->np_cave1, seed, csize.X, csize.Y + 1, csize.Z); - noise_cave2 = new Noise(&sp->np_cave2, seed, csize.X, csize.Y + 1, csize.Z); + MapgenBasic::np_cave1 = sp->np_cave1; + MapgenBasic::np_cave2 = sp->np_cave2; //// Initialize biome generator biomegen = emerge->biomemgr->createBiomeGen( @@ -119,8 +115,6 @@ MapgenFlat::~MapgenFlat() { delete noise_terrain; delete noise_filler_depth; - delete noise_cave1; - delete noise_cave2; delete biomegen; diff --git a/src/mapgen_fractal.cpp b/src/mapgen_fractal.cpp index 3217d52e2..dca10d253 100644 --- a/src/mapgen_fractal.cpp +++ b/src/mapgen_fractal.cpp @@ -56,8 +56,6 @@ MapgenFractal::MapgenFractal(int mapgenid, MapgenParams *params, EmergeManager * //// amount of elements to skip for the next index //// for noise/height/biome maps (not vmanip) this->ystride = csize.X; - // 1-down overgeneration - this->zstride_1d = csize.X * (csize.Y + 1); this->heightmap = new s16[csize.X * csize.Z]; @@ -79,10 +77,8 @@ MapgenFractal::MapgenFractal(int mapgenid, MapgenParams *params, EmergeManager * noise_seabed = new Noise(&sp->np_seabed, seed, csize.X, csize.Z); noise_filler_depth = new Noise(&sp->np_filler_depth, seed, csize.X, csize.Z); - //// 3D terrain noise - // 1-down overgeneraion - noise_cave1 = new Noise(&sp->np_cave1, seed, csize.X, csize.Y + 1, csize.Z); - noise_cave2 = new Noise(&sp->np_cave2, seed, csize.X, csize.Y + 1, csize.Z); + MapgenBasic::np_cave1 = sp->np_cave1; + MapgenBasic::np_cave2 = sp->np_cave2; //// Initialize biome generator biomegen = emerge->biomemgr->createBiomeGen( @@ -123,8 +119,6 @@ MapgenFractal::~MapgenFractal() { delete noise_seabed; delete noise_filler_depth; - delete noise_cave1; - delete noise_cave2; delete biomegen; diff --git a/src/mapgen_v5.cpp b/src/mapgen_v5.cpp index 6c1441bc2..0c063dc6f 100644 --- a/src/mapgen_v5.cpp +++ b/src/mapgen_v5.cpp @@ -54,8 +54,6 @@ MapgenV5::MapgenV5(int mapgenid, MapgenParams *params, EmergeManager *emerge) // amount of elements to skip for the next index // for noise/height/biome maps (not vmanip) this->ystride = csize.X; - // 1-down overgeneration - this->zstride_1d = csize.X * (csize.Y + 1); this->heightmap = new s16[csize.X * csize.Z]; @@ -72,9 +70,9 @@ MapgenV5::MapgenV5(int mapgenid, MapgenParams *params, EmergeManager *emerge) // 3D terrain noise // 1-up 1-down overgeneration noise_ground = new Noise(&sp->np_ground, seed, csize.X, csize.Y + 2, csize.Z); - // 1-down overgeneraion - noise_cave1 = new Noise(&sp->np_cave1, seed, csize.X, csize.Y + 1, csize.Z); - noise_cave2 = new Noise(&sp->np_cave2, seed, csize.X, csize.Y + 1, csize.Z); + + MapgenBasic::np_cave1 = sp->np_cave1; + MapgenBasic::np_cave2 = sp->np_cave2; //// Initialize biome generator biomegen = emerge->biomemgr->createBiomeGen( @@ -113,8 +111,6 @@ MapgenV5::~MapgenV5() delete noise_filler_depth; delete noise_factor; delete noise_height; - delete noise_cave1; - delete noise_cave2; delete noise_ground; delete biomegen; diff --git a/src/mapgen_v7.cpp b/src/mapgen_v7.cpp index e6801a24b..e297a1d3e 100644 --- a/src/mapgen_v7.cpp +++ b/src/mapgen_v7.cpp @@ -61,8 +61,6 @@ MapgenV7::MapgenV7(int mapgenid, MapgenParams *params, EmergeManager *emerge) this->ystride = csize.X; // 1-up 1-down overgeneration this->zstride_1u1d = csize.X * (csize.Y + 2); - // 1-down overgeneration - this->zstride_1d = csize.X * (csize.Y + 1); this->heightmap = new s16[csize.X * csize.Z]; this->ridge_heightmap = new s16[csize.X * csize.Z]; @@ -85,9 +83,9 @@ MapgenV7::MapgenV7(int mapgenid, MapgenParams *params, EmergeManager *emerge) // 1-up 1-down overgeneration noise_mountain = new Noise(&sp->np_mountain, seed, csize.X, csize.Y + 2, csize.Z); noise_ridge = new Noise(&sp->np_ridge, seed, csize.X, csize.Y + 2, csize.Z); - // 1-down overgeneraion - noise_cave1 = new Noise(&sp->np_cave1, seed, csize.X, csize.Y + 1, csize.Z); - noise_cave2 = new Noise(&sp->np_cave2, seed, csize.X, csize.Y + 1, csize.Z); + + MapgenBasic::np_cave1 = sp->np_cave1; + MapgenBasic::np_cave2 = sp->np_cave2; // TODO(hmmmm): should we have a way to disable biomemanager biomes? //// Initialize biome generator @@ -133,8 +131,6 @@ MapgenV7::~MapgenV7() delete noise_ridge_uwater; delete noise_mountain; delete noise_ridge; - delete noise_cave1; - delete noise_cave2; delete biomegen; diff --git a/src/mapgen_valleys.h b/src/mapgen_valleys.h index 2ab253430..4f87be6b6 100644 --- a/src/mapgen_valleys.h +++ b/src/mapgen_valleys.h @@ -100,6 +100,7 @@ private: BiomeGenOriginal *m_bgen; int zstride; + int zstride_1d; float map_gen_limit; @@ -120,6 +121,8 @@ private: Noise *noise_inter_valley_fill; Noise *noise_inter_valley_slope; Noise *noise_rivers; + Noise *noise_cave1; + Noise *noise_cave2; Noise *noise_massive_caves; Noise *noise_terrain_height; Noise *noise_valley_depth; -- cgit v1.2.3 From c5968049bbf73ceff08a2b1d35bb34192fa3f315 Mon Sep 17 00:00:00 2001 From: kwolekr Date: Thu, 19 May 2016 14:05:51 -0400 Subject: Mapgen V7: Remove now-unused ridge heightmap --- src/mapgen_v7.cpp | 10 ++-------- src/mapgen_v7.h | 2 -- 2 files changed, 2 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/mapgen_v7.cpp b/src/mapgen_v7.cpp index e297a1d3e..ee2c5f45f 100644 --- a/src/mapgen_v7.cpp +++ b/src/mapgen_v7.cpp @@ -62,8 +62,7 @@ MapgenV7::MapgenV7(int mapgenid, MapgenParams *params, EmergeManager *emerge) // 1-up 1-down overgeneration this->zstride_1u1d = csize.X * (csize.Y + 2); - this->heightmap = new s16[csize.X * csize.Z]; - this->ridge_heightmap = new s16[csize.X * csize.Z]; + this->heightmap = new s16[csize.X * csize.Z]; MapgenV7Params *sp = (MapgenV7Params *)params->sparams; @@ -134,7 +133,6 @@ MapgenV7::~MapgenV7() delete biomegen; - delete[] ridge_heightmap; delete[] heightmap; } @@ -440,8 +438,7 @@ int MapgenV7::generateTerrain() for (s16 z = node_min.Z; z <= node_max.Z; z++) for (s16 x = node_min.X; x <= node_max.X; x++, index2d++) { s16 surface_y = baseTerrainLevelFromMap(index2d); - heightmap[index2d] = surface_y; // Create base terrain heightmap - ridge_heightmap[index2d] = surface_y; + heightmap[index2d] = surface_y; // Create base terrain heightmap if (surface_y > stone_surface_max_y) stone_surface_max_y = surface_y; @@ -504,9 +501,6 @@ void MapgenV7::generateRidgeTerrain() if (nridge + width_mod * height_mod < 0.6) continue; - if (y < ridge_heightmap[j]) - ridge_heightmap[j] = y - 1; - vm->m_data[vi] = (y > water_level) ? n_air : n_water; } } diff --git a/src/mapgen_v7.h b/src/mapgen_v7.h index 21beab2b4..eb89d5978 100644 --- a/src/mapgen_v7.h +++ b/src/mapgen_v7.h @@ -58,8 +58,6 @@ class MapgenV7 : public MapgenBasic { public: int zstride_1u1d; - s16 *ridge_heightmap; - u32 spflags; Noise *noise_terrain_base; Noise *noise_terrain_alt; -- cgit v1.2.3 From 0df5c01a8ce927c33ae9b67f459365505b980c33 Mon Sep 17 00:00:00 2001 From: kwolekr Date: Fri, 20 May 2016 03:37:31 -0400 Subject: Mapgen: Remove calculateNoise from most mapgens This commit moves noise calculation to the functions where the noise is actually required, increasing the separation of concerns and level of interdependency for each mapgen method. Valleys Mapgen is left unmodified. --- src/mapgen.cpp | 2 ++ src/mapgen_flat.cpp | 30 +++++----------------------- src/mapgen_flat.h | 1 - src/mapgen_fractal.cpp | 22 ++------------------ src/mapgen_fractal.h | 1 - src/mapgen_v5.cpp | 27 ++++--------------------- src/mapgen_v5.h | 1 - src/mapgen_v7.cpp | 54 +++++++++++++++++--------------------------------- src/mapgen_v7.h | 3 --- src/mapgen_valleys.cpp | 1 - 10 files changed, 31 insertions(+), 111 deletions(-) (limited to 'src') diff --git a/src/mapgen.cpp b/src/mapgen.cpp index 7e959533d..009a81f0c 100644 --- a/src/mapgen.cpp +++ b/src/mapgen.cpp @@ -386,6 +386,8 @@ MgStoneType MapgenBasic::generateBiomes() u32 index = 0; MgStoneType stone_type = MGSTONE_STONE; + noise_filler_depth->perlinMap2D(node_min.X, node_min.Z); + for (s16 z = node_min.Z; z <= node_max.Z; z++) for (s16 x = node_min.X; x <= node_max.X; x++, index++) { Biome *biome = NULL; diff --git a/src/mapgen_flat.cpp b/src/mapgen_flat.cpp index a541a54f3..ded859f65 100644 --- a/src/mapgen_flat.cpp +++ b/src/mapgen_flat.cpp @@ -227,9 +227,6 @@ void MapgenFlat::makeChunk(BlockMakeData *data) blockseed = getBlockSeed2(full_node_min, seed); - // Make some noise - calculateNoise(); - // Generate base terrain, mountains, and ridges with initial heightmaps s16 stone_surface_max_y = generateTerrain(); @@ -312,24 +309,6 @@ void MapgenFlat::makeChunk(BlockMakeData *data) } -void MapgenFlat::calculateNoise() -{ - //TimeTaker t("calculateNoise", NULL, PRECISION_MICRO); - s16 x = node_min.X; - s16 z = node_min.Z; - - if ((spflags & MGFLAT_LAKES) || (spflags & MGFLAT_HILLS)) - noise_terrain->perlinMap2D(x, z); - - // Cave noises are calculated in generateCaves() - // only if solid terrain is present in mapchunk - - noise_filler_depth->perlinMap2D(x, z); - - //printf("calculateNoise: %dus\n", t.stop()); -} - - s16 MapgenFlat::generateTerrain() { MapNode n_air(CONTENT_AIR); @@ -340,13 +319,14 @@ s16 MapgenFlat::generateTerrain() s16 stone_surface_max_y = -MAX_MAP_GENERATION_LIMIT; u32 ni2d = 0; + bool use_noise = (spflags & MGFLAT_LAKES) || (spflags & MGFLAT_HILLS); + if (use_noise) + noise_terrain->perlinMap2D(node_min.X, node_min.Z); + for (s16 z = node_min.Z; z <= node_max.Z; z++) for (s16 x = node_min.X; x <= node_max.X; x++, ni2d++) { s16 stone_level = ground_level; - float n_terrain = 0.0f; - - if ((spflags & MGFLAT_LAKES) || (spflags & MGFLAT_HILLS)) - n_terrain = noise_terrain->result[ni2d]; + float n_terrain = use_noise ? noise_terrain->result[ni2d] : 0.0f; if ((spflags & MGFLAT_LAKES) && n_terrain < lake_threshold) { s16 depress = (lake_threshold - n_terrain) * lake_steepness; diff --git a/src/mapgen_flat.h b/src/mapgen_flat.h index 2b98c1f31..39da6e025 100644 --- a/src/mapgen_flat.h +++ b/src/mapgen_flat.h @@ -78,7 +78,6 @@ public: virtual void makeChunk(BlockMakeData *data); int getSpawnLevelAtPoint(v2s16 p); - void calculateNoise(); s16 generateTerrain(); }; diff --git a/src/mapgen_fractal.cpp b/src/mapgen_fractal.cpp index dca10d253..f54de6275 100644 --- a/src/mapgen_fractal.cpp +++ b/src/mapgen_fractal.cpp @@ -243,9 +243,6 @@ void MapgenFractal::makeChunk(BlockMakeData *data) blockseed = getBlockSeed2(full_node_min, seed); - // Make some noise - calculateNoise(); - // Generate base terrain, mountains, and ridges with initial heightmaps s16 stone_surface_max_y = generateTerrain(); @@ -328,23 +325,6 @@ void MapgenFractal::makeChunk(BlockMakeData *data) } -void MapgenFractal::calculateNoise() -{ - //TimeTaker t("calculateNoise", NULL, PRECISION_MICRO); - s16 x = node_min.X; - s16 z = node_min.Z; - - noise_seabed->perlinMap2D(x, z); - - // Cave noises are calculated in generateCaves() - // only if solid terrain is present in mapchunk - - noise_filler_depth->perlinMap2D(x, z); - - //printf("calculateNoise: %dus\n", t.stop()); -} - - bool MapgenFractal::getFractalAtPoint(s16 x, s16 y, s16 z) { float cx, cy, cz, cw, ox, oy, oz, ow; @@ -474,6 +454,8 @@ s16 MapgenFractal::generateTerrain() s16 stone_surface_max_y = -MAX_MAP_GENERATION_LIMIT; u32 index2d = 0; + noise_seabed->perlinMap2D(node_min.X, node_min.Z); + for (s16 z = node_min.Z; z <= node_max.Z; z++) { for (s16 y = node_min.Y - 1; y <= node_max.Y + 1; y++) { u32 vi = vm->m_area.index(node_min.X, y, z); diff --git a/src/mapgen_fractal.h b/src/mapgen_fractal.h index 176885d46..e30550405 100644 --- a/src/mapgen_fractal.h +++ b/src/mapgen_fractal.h @@ -88,7 +88,6 @@ public: virtual void makeChunk(BlockMakeData *data); int getSpawnLevelAtPoint(v2s16 p); - void calculateNoise(); bool getFractalAtPoint(s16 x, s16 y, s16 z); s16 generateTerrain(); }; diff --git a/src/mapgen_v5.cpp b/src/mapgen_v5.cpp index 0c063dc6f..85df34353 100644 --- a/src/mapgen_v5.cpp +++ b/src/mapgen_v5.cpp @@ -225,9 +225,6 @@ void MapgenV5::makeChunk(BlockMakeData *data) // Create a block-specific seed blockseed = getBlockSeed2(full_node_min, seed); - // Make some noise - calculateNoise(); - // Generate base terrain s16 stone_surface_max_y = generateBaseTerrain(); @@ -312,26 +309,6 @@ void MapgenV5::makeChunk(BlockMakeData *data) } -void MapgenV5::calculateNoise() -{ - //TimeTaker t("calculateNoise", NULL, PRECISION_MICRO); - s16 x = node_min.X; - s16 y = node_min.Y - 1; - s16 z = node_min.Z; - - noise_factor->perlinMap2D(x, z); - noise_height->perlinMap2D(x, z); - noise_ground->perlinMap3D(x, y, z); - - // Cave noises are calculated in generateCaves() - // only if solid terrain is present in mapchunk - - noise_filler_depth->perlinMap2D(x, z); - - //printf("calculateNoise: %dus\n", t.stop()); -} - - //bool is_cave(u32 index) { // double d1 = contour(noise_cave1->result[index]); // double d2 = contour(noise_cave2->result[index]); @@ -355,6 +332,10 @@ int MapgenV5::generateBaseTerrain() u32 index2d = 0; int stone_surface_max_y = -MAX_MAP_GENERATION_LIMIT; + noise_factor->perlinMap2D(node_min.X, node_min.Z); + noise_height->perlinMap2D(node_min.X, node_min.Z); + noise_ground->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z); + for (s16 z=node_min.Z; z<=node_max.Z; z++) { for (s16 y=node_min.Y - 1; y<=node_max.Y + 1; y++) { u32 vi = vm->m_area.index(node_min.X, y, z); diff --git a/src/mapgen_v5.h b/src/mapgen_v5.h index 99836b23e..dd5ca0a31 100644 --- a/src/mapgen_v5.h +++ b/src/mapgen_v5.h @@ -69,7 +69,6 @@ public: virtual void makeChunk(BlockMakeData *data); int getSpawnLevelAtPoint(v2s16 p); - void calculateNoise(); int generateBaseTerrain(); }; diff --git a/src/mapgen_v7.cpp b/src/mapgen_v7.cpp index ee2c5f45f..5c5f13b88 100644 --- a/src/mapgen_v7.cpp +++ b/src/mapgen_v7.cpp @@ -252,9 +252,6 @@ void MapgenV7::makeChunk(BlockMakeData *data) blockseed = getBlockSeed2(full_node_min, seed); - // Make some noise - calculateNoise(); - // Generate terrain and ridges with initial heightmaps s16 stone_surface_max_y = generateTerrain(); @@ -340,37 +337,6 @@ void MapgenV7::makeChunk(BlockMakeData *data) } -void MapgenV7::calculateNoise() -{ - //TimeTaker t("calculateNoise", NULL, PRECISION_MICRO); - s16 x = node_min.X; - s16 y = node_min.Y - 1; - s16 z = node_min.Z; - - noise_terrain_persist->perlinMap2D(x, z); - float *persistmap = noise_terrain_persist->result; - - noise_terrain_base->perlinMap2D(x, z, persistmap); - noise_terrain_alt->perlinMap2D(x, z, persistmap); - noise_height_select->perlinMap2D(x, z); - - if (spflags & MGV7_MOUNTAINS) { - noise_mountain->perlinMap3D(x, y, z); - noise_mount_height->perlinMap2D(x, z); - } - - if ((spflags & MGV7_RIDGES) && node_max.Y >= water_level) { - noise_ridge->perlinMap3D(x, y, z); - noise_ridge_uwater->perlinMap2D(x, z); - } - - // Cave noises are calculated in generateCaves() - // only if solid terrain is present in mapchunk - - //printf("calculateNoise: %dus\n", t.stop()); -} - - float MapgenV7::baseTerrainLevelAtPoint(s16 x, s16 z) { float hselect = NoisePerlin2D(&noise_height_select->np, x, z, seed); @@ -430,10 +396,23 @@ int MapgenV7::generateTerrain() MapNode n_stone(c_stone); MapNode n_water(c_water_source); + //// Calculate noise for terrain generation + noise_terrain_persist->perlinMap2D(node_min.X, node_min.Z); + float *persistmap = noise_terrain_persist->result; + + noise_terrain_base->perlinMap2D(node_min.X, node_min.Z, persistmap); + noise_terrain_alt->perlinMap2D(node_min.X, node_min.Z, persistmap); + noise_height_select->perlinMap2D(node_min.X, node_min.Z); + + if (spflags & MGV7_MOUNTAINS) { + noise_mountain->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z); + noise_mount_height->perlinMap2D(node_min.X, node_min.Z); + } + + //// Place nodes v3s16 em = vm->m_area.getExtent(); s16 stone_surface_max_y = -MAX_MAP_GENERATION_LIMIT; u32 index2d = 0; - bool mountain_flag = spflags & MGV7_MOUNTAINS; for (s16 z = node_min.Z; z <= node_max.Z; z++) for (s16 x = node_min.X; x <= node_max.X; x++, index2d++) { @@ -450,7 +429,7 @@ int MapgenV7::generateTerrain() if (vm->m_data[vi].getContent() == CONTENT_IGNORE) { if (y <= surface_y) { vm->m_data[vi] = n_stone; // Base terrain - } else if (mountain_flag && + } else if ((spflags & MGV7_MOUNTAINS) && getMountainTerrainFromMap(index3d, index2d, y)) { vm->m_data[vi] = n_stone; // Mountain terrain if (y > stone_surface_max_y) @@ -475,6 +454,9 @@ void MapgenV7::generateRidgeTerrain() if (node_max.Y < water_level - 16) return; + noise_ridge->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z); + noise_ridge_uwater->perlinMap2D(node_min.X, node_min.Z); + MapNode n_water(c_water_source); MapNode n_air(CONTENT_AIR); u32 index = 0; diff --git a/src/mapgen_v7.h b/src/mapgen_v7.h index eb89d5978..723f1217f 100644 --- a/src/mapgen_v7.h +++ b/src/mapgen_v7.h @@ -87,9 +87,6 @@ public: float baseTerrainLevelFromMap(int index); bool getMountainTerrainAtPoint(s16 x, s16 y, s16 z); bool getMountainTerrainFromMap(int idx_xyz, int idx_xz, s16 y); - - void calculateNoise(); - int generateTerrain(); void generateRidgeTerrain(); }; diff --git a/src/mapgen_valleys.cpp b/src/mapgen_valleys.cpp index 699554ecd..3a4f984f9 100644 --- a/src/mapgen_valleys.cpp +++ b/src/mapgen_valleys.cpp @@ -388,7 +388,6 @@ void MapgenValleys::calculateNoise() //TimeTaker tcn("actualNoise"); - noise_filler_depth->perlinMap2D(x, z); noise_inter_valley_slope->perlinMap2D(x, z); noise_rivers->perlinMap2D(x, z); noise_terrain_height->perlinMap2D(x, z); -- cgit v1.2.3 From 081090176666e849d01f9a02b15279bdf72570bc Mon Sep 17 00:00:00 2001 From: kwolekr Date: Sun, 22 May 2016 02:17:19 -0400 Subject: Mapgen: Deduplicate common constructor code --- src/mapgen.cpp | 55 ++++++++++++++++++++++++++++++++++++++++++++++++-- src/mapgen.h | 22 +++++++++++++------- src/mapgen_flat.cpp | 30 ++------------------------- src/mapgen_flat.h | 19 ++++++++--------- src/mapgen_fractal.cpp | 30 ++------------------------- src/mapgen_fractal.h | 21 +++++++++---------- src/mapgen_v5.cpp | 30 ++------------------------- src/mapgen_v5.h | 19 ++++++++--------- src/mapgen_v7.cpp | 33 ++---------------------------- src/mapgen_v7.h | 31 ++++++++++++---------------- src/mapgen_valleys.cpp | 42 ++++++++------------------------------ src/mapgen_valleys.h | 5 ----- 12 files changed, 122 insertions(+), 215 deletions(-) (limited to 'src') diff --git a/src/mapgen.cpp b/src/mapgen.cpp index 009a81f0c..a2d207bff 100644 --- a/src/mapgen.cpp +++ b/src/mapgen.cpp @@ -377,9 +377,60 @@ void Mapgen::spreadLight(v3s16 nmin, v3s16 nmax) MapgenBasic::MapgenBasic(int mapgenid, MapgenParams *params, EmergeManager *emerge) : Mapgen(mapgenid, params, emerge) { + this->m_emerge = emerge; + this->m_bmgr = emerge->biomemgr; + + //// Here, 'stride' refers to the number of elements needed to skip to index + //// an adjacent element for that coordinate in noise/height/biome maps + //// (*not* vmanip content map!) + + // Note there is no X stride explicitly defined. Items adjacent in the X + // coordinate are assumed to be adjacent in memory as well (i.e. stride of 1). + + // Number of elements to skip to get to the next Y coordinate + this->ystride = csize.X; + + // Number of elements to skip to get to the next Z coordinate + this->zstride = csize.X * csize.Y; + + // Z-stride value for maps oversized for 1-down overgeneration + this->zstride_1d = csize.X * (csize.Y + 1); + + // Z-stride value for maps oversized for 1-up 1-down overgeneration + this->zstride_1u1d = csize.X * (csize.Y + 2); + + //// Allocate heightmap + this->heightmap = new s16[csize.X * csize.Z]; + + //// Initialize biome generator + // TODO(hmmmm): should we have a way to disable biomemanager biomes? + biomegen = m_bmgr->createBiomeGen(BIOMEGEN_ORIGINAL, params->bparams, csize); + biomemap = biomegen->biomemap; + + //// Look up some commonly used content + c_stone = ndef->getId("mapgen_stone"); + c_water_source = ndef->getId("mapgen_water_source"); + c_desert_stone = ndef->getId("mapgen_desert_stone"); + c_sandstone = ndef->getId("mapgen_sandstone"); + c_river_water_source = ndef->getId("mapgen_river_water_source"); + + //// Fall back to more basic content if not defined + if (c_desert_stone == CONTENT_IGNORE) + c_desert_stone = c_stone; + if (c_sandstone == CONTENT_IGNORE) + c_sandstone = c_stone; + if (c_river_water_source == CONTENT_IGNORE) + c_river_water_source = c_water_source; +} + +MapgenBasic::~MapgenBasic() +{ + delete biomegen; + delete []heightmap; } + MgStoneType MapgenBasic::generateBiomes() { v3s16 em = vm->m_area.getExtent(); @@ -499,7 +550,7 @@ void MapgenBasic::dustTopNodes() for (s16 z = node_min.Z; z <= node_max.Z; z++) for (s16 x = node_min.X; x <= node_max.X; x++, index++) { - Biome *biome = (Biome *)bmgr->getRaw(biomemap[index]); + Biome *biome = (Biome *)m_bmgr->getRaw(biomemap[index]); if (biome->c_dust == CONTENT_IGNORE) continue; @@ -544,7 +595,7 @@ void MapgenBasic::generateCaves(s16 max_stone_y, s16 large_cave_depth) if (max_stone_y < node_min.Y) return; - CavesNoiseIntersection caves_noise(ndef, bmgr, csize, + CavesNoiseIntersection caves_noise(ndef, m_bmgr, csize, &np_cave1, &np_cave2, seed, cave_width); caves_noise.generateCaves(vm, node_min, node_max, biomemap); diff --git a/src/mapgen.h b/src/mapgen.h index 8db01f5c1..0b552e508 100644 --- a/src/mapgen.h +++ b/src/mapgen.h @@ -216,8 +216,16 @@ private: */ class MapgenBasic : public Mapgen { public: + MapgenBasic(int mapgenid, MapgenParams *params, EmergeManager *emerge); + virtual ~MapgenBasic(); + + virtual MgStoneType generateBiomes(); + virtual void dustTopNodes(); + virtual void generateCaves(s16 max_stone_y, s16 large_cave_depth); + +protected: EmergeManager *m_emerge; - BiomeManager *bmgr; + BiomeManager *m_bmgr; Noise *noise_filler_depth; @@ -233,15 +241,15 @@ public: content_t c_sandstone; int ystride; + int zstride; + int zstride_1d; + int zstride_1u1d; + + u32 spflags; + NoiseParams np_cave1; NoiseParams np_cave2; float cave_width; - - MapgenBasic(int mapgenid, MapgenParams *params, EmergeManager *emerge); - - virtual MgStoneType generateBiomes(); - virtual void dustTopNodes(); - virtual void generateCaves(s16 max_stone_y, s16 large_cave_depth); }; struct MapgenFactory { diff --git a/src/mapgen_flat.cpp b/src/mapgen_flat.cpp index ded859f65..cc43684c4 100644 --- a/src/mapgen_flat.cpp +++ b/src/mapgen_flat.cpp @@ -52,15 +52,6 @@ FlagDesc flagdesc_mapgen_flat[] = { MapgenFlat::MapgenFlat(int mapgenid, MapgenParams *params, EmergeManager *emerge) : MapgenBasic(mapgenid, params, emerge) { - this->m_emerge = emerge; - this->bmgr = emerge->biomemgr; - - //// amount of elements to skip for the next index - //// for noise/height/biome maps (not vmanip) - this->ystride = csize.X; - - this->heightmap = new s16[csize.X * csize.Z]; - MapgenFlatParams *sp = (MapgenFlatParams *)params->sparams; this->spflags = sp->spflags; @@ -79,27 +70,14 @@ MapgenFlat::MapgenFlat(int mapgenid, MapgenParams *params, EmergeManager *emerge MapgenBasic::np_cave1 = sp->np_cave1; MapgenBasic::np_cave2 = sp->np_cave2; - //// Initialize biome generator - biomegen = emerge->biomemgr->createBiomeGen( - BIOMEGEN_ORIGINAL, params->bparams, csize); - biomemap = biomegen->biomemap; - - //// Resolve nodes to be used - c_stone = ndef->getId("mapgen_stone"); - c_water_source = ndef->getId("mapgen_water_source"); - c_lava_source = ndef->getId("mapgen_lava_source"); - c_desert_stone = ndef->getId("mapgen_desert_stone"); - c_ice = ndef->getId("mapgen_ice"); - c_sandstone = ndef->getId("mapgen_sandstone"); - + // Content used for dungeon generation c_cobble = ndef->getId("mapgen_cobble"); c_stair_cobble = ndef->getId("mapgen_stair_cobble"); c_mossycobble = ndef->getId("mapgen_mossycobble"); c_sandstonebrick = ndef->getId("mapgen_sandstonebrick"); c_stair_sandstonebrick = ndef->getId("mapgen_stair_sandstonebrick"); - if (c_ice == CONTENT_IGNORE) - c_ice = CONTENT_AIR; + // Fall back to more basic content if not defined if (c_mossycobble == CONTENT_IGNORE) c_mossycobble = c_cobble; if (c_stair_cobble == CONTENT_IGNORE) @@ -115,10 +93,6 @@ MapgenFlat::~MapgenFlat() { delete noise_terrain; delete noise_filler_depth; - - delete biomegen; - - delete[] heightmap; } diff --git a/src/mapgen_flat.h b/src/mapgen_flat.h index 39da6e025..ffec4b924 100644 --- a/src/mapgen_flat.h +++ b/src/mapgen_flat.h @@ -55,7 +55,14 @@ struct MapgenFlatParams : public MapgenSpecificParams { class MapgenFlat : public MapgenBasic { public: - u32 spflags; + MapgenFlat(int mapgenid, MapgenParams *params, EmergeManager *emerge); + ~MapgenFlat(); + + virtual void makeChunk(BlockMakeData *data); + int getSpawnLevelAtPoint(v2s16 p); + s16 generateTerrain(); + +private: s16 ground_level; s16 large_cave_depth; float lake_threshold; @@ -64,21 +71,11 @@ public: float hill_steepness; Noise *noise_terrain; - content_t c_lava_source; - content_t c_ice; - content_t c_cobble; content_t c_stair_cobble; content_t c_mossycobble; content_t c_sandstonebrick; content_t c_stair_sandstonebrick; - - MapgenFlat(int mapgenid, MapgenParams *params, EmergeManager *emerge); - ~MapgenFlat(); - - virtual void makeChunk(BlockMakeData *data); - int getSpawnLevelAtPoint(v2s16 p); - s16 generateTerrain(); }; struct MapgenFactoryFlat : public MapgenFactory { diff --git a/src/mapgen_fractal.cpp b/src/mapgen_fractal.cpp index f54de6275..50e10d128 100644 --- a/src/mapgen_fractal.cpp +++ b/src/mapgen_fractal.cpp @@ -50,15 +50,6 @@ FlagDesc flagdesc_mapgen_fractal[] = { MapgenFractal::MapgenFractal(int mapgenid, MapgenParams *params, EmergeManager *emerge) : MapgenBasic(mapgenid, params, emerge) { - this->m_emerge = emerge; - this->bmgr = emerge->biomemgr; - - //// amount of elements to skip for the next index - //// for noise/height/biome maps (not vmanip) - this->ystride = csize.X; - - this->heightmap = new s16[csize.X * csize.Z]; - MapgenFractalParams *sp = (MapgenFractalParams *)params->sparams; this->spflags = sp->spflags; @@ -80,30 +71,17 @@ MapgenFractal::MapgenFractal(int mapgenid, MapgenParams *params, EmergeManager * MapgenBasic::np_cave1 = sp->np_cave1; MapgenBasic::np_cave2 = sp->np_cave2; - //// Initialize biome generator - biomegen = emerge->biomemgr->createBiomeGen( - BIOMEGEN_ORIGINAL, params->bparams, csize); - biomemap = biomegen->biomemap; - this->formula = fractal / 2 + fractal % 2; this->julia = fractal % 2 == 0; - //// Resolve nodes to be used - c_stone = ndef->getId("mapgen_stone"); - c_water_source = ndef->getId("mapgen_water_source"); - c_lava_source = ndef->getId("mapgen_lava_source"); - c_desert_stone = ndef->getId("mapgen_desert_stone"); - c_ice = ndef->getId("mapgen_ice"); - c_sandstone = ndef->getId("mapgen_sandstone"); - + // Content used for dungeon generation c_cobble = ndef->getId("mapgen_cobble"); c_stair_cobble = ndef->getId("mapgen_stair_cobble"); c_mossycobble = ndef->getId("mapgen_mossycobble"); c_sandstonebrick = ndef->getId("mapgen_sandstonebrick"); c_stair_sandstonebrick = ndef->getId("mapgen_stair_sandstonebrick"); - if (c_ice == CONTENT_IGNORE) - c_ice = CONTENT_AIR; + // Fall back to more basic content if not defined if (c_mossycobble == CONTENT_IGNORE) c_mossycobble = c_cobble; if (c_stair_cobble == CONTENT_IGNORE) @@ -119,10 +97,6 @@ MapgenFractal::~MapgenFractal() { delete noise_seabed; delete noise_filler_depth; - - delete biomegen; - - delete[] heightmap; } diff --git a/src/mapgen_fractal.h b/src/mapgen_fractal.h index e30550405..de0ccaa2f 100644 --- a/src/mapgen_fractal.h +++ b/src/mapgen_fractal.h @@ -59,10 +59,18 @@ struct MapgenFractalParams : public MapgenSpecificParams { class MapgenFractal : public MapgenBasic { public: + MapgenFractal(int mapgenid, MapgenParams *params, EmergeManager *emerge); + ~MapgenFractal(); + + virtual void makeChunk(BlockMakeData *data); + int getSpawnLevelAtPoint(v2s16 p); + bool getFractalAtPoint(s16 x, s16 y, s16 z); + s16 generateTerrain(); + +private: u16 formula; bool julia; - u32 spflags; u16 fractal; u16 iterations; v3f scale; @@ -74,22 +82,11 @@ public: float julia_w; Noise *noise_seabed; - content_t c_lava_source; - content_t c_ice; - content_t c_cobble; content_t c_stair_cobble; content_t c_mossycobble; content_t c_sandstonebrick; content_t c_stair_sandstonebrick; - - MapgenFractal(int mapgenid, MapgenParams *params, EmergeManager *emerge); - ~MapgenFractal(); - - virtual void makeChunk(BlockMakeData *data); - int getSpawnLevelAtPoint(v2s16 p); - bool getFractalAtPoint(s16 x, s16 y, s16 z); - s16 generateTerrain(); }; struct MapgenFactoryFractal : public MapgenFactory { diff --git a/src/mapgen_v5.cpp b/src/mapgen_v5.cpp index 85df34353..167a38282 100644 --- a/src/mapgen_v5.cpp +++ b/src/mapgen_v5.cpp @@ -48,15 +48,6 @@ FlagDesc flagdesc_mapgen_v5[] = { MapgenV5::MapgenV5(int mapgenid, MapgenParams *params, EmergeManager *emerge) : MapgenBasic(mapgenid, params, emerge) { - this->m_emerge = emerge; - this->bmgr = emerge->biomemgr; - - // amount of elements to skip for the next index - // for noise/height/biome maps (not vmanip) - this->ystride = csize.X; - - this->heightmap = new s16[csize.X * csize.Z]; - MapgenV5Params *sp = (MapgenV5Params *)params->sparams; this->spflags = sp->spflags; @@ -74,27 +65,14 @@ MapgenV5::MapgenV5(int mapgenid, MapgenParams *params, EmergeManager *emerge) MapgenBasic::np_cave1 = sp->np_cave1; MapgenBasic::np_cave2 = sp->np_cave2; - //// Initialize biome generator - biomegen = emerge->biomemgr->createBiomeGen( - BIOMEGEN_ORIGINAL, params->bparams, csize); - biomemap = biomegen->biomemap; - - //// Resolve nodes to be used - c_stone = ndef->getId("mapgen_stone"); - c_water_source = ndef->getId("mapgen_water_source"); - c_lava_source = ndef->getId("mapgen_lava_source"); - c_desert_stone = ndef->getId("mapgen_desert_stone"); - c_ice = ndef->getId("mapgen_ice"); - c_sandstone = ndef->getId("mapgen_sandstone"); - + // Content used for dungeon generation c_cobble = ndef->getId("mapgen_cobble"); c_stair_cobble = ndef->getId("mapgen_stair_cobble"); c_mossycobble = ndef->getId("mapgen_mossycobble"); c_sandstonebrick = ndef->getId("mapgen_sandstonebrick"); c_stair_sandstonebrick = ndef->getId("mapgen_stair_sandstonebrick"); - if (c_ice == CONTENT_IGNORE) - c_ice = CONTENT_AIR; + // Fall back to more basic content if not defined if (c_mossycobble == CONTENT_IGNORE) c_mossycobble = c_cobble; if (c_stair_cobble == CONTENT_IGNORE) @@ -112,10 +90,6 @@ MapgenV5::~MapgenV5() delete noise_factor; delete noise_height; delete noise_ground; - - delete biomegen; - - delete[] heightmap; } diff --git a/src/mapgen_v5.h b/src/mapgen_v5.h index dd5ca0a31..e7e5b2327 100644 --- a/src/mapgen_v5.h +++ b/src/mapgen_v5.h @@ -50,26 +50,23 @@ struct MapgenV5Params : public MapgenSpecificParams { class MapgenV5 : public MapgenBasic { public: - u32 spflags; + MapgenV5(int mapgenid, MapgenParams *params, EmergeManager *emerge); + ~MapgenV5(); + + virtual void makeChunk(BlockMakeData *data); + int getSpawnLevelAtPoint(v2s16 p); + int generateBaseTerrain(); + +private: Noise *noise_factor; Noise *noise_height; Noise *noise_ground; - content_t c_lava_source; - content_t c_ice; - content_t c_cobble; content_t c_stair_cobble; content_t c_mossycobble; content_t c_sandstonebrick; content_t c_stair_sandstonebrick; - - MapgenV5(int mapgenid, MapgenParams *params, EmergeManager *emerge); - ~MapgenV5(); - - virtual void makeChunk(BlockMakeData *data); - int getSpawnLevelAtPoint(v2s16 p); - int generateBaseTerrain(); }; diff --git a/src/mapgen_v7.cpp b/src/mapgen_v7.cpp index 5c5f13b88..29c81e8a7 100644 --- a/src/mapgen_v7.cpp +++ b/src/mapgen_v7.cpp @@ -53,17 +53,6 @@ FlagDesc flagdesc_mapgen_v7[] = { MapgenV7::MapgenV7(int mapgenid, MapgenParams *params, EmergeManager *emerge) : MapgenBasic(mapgenid, params, emerge) { - this->m_emerge = emerge; - this->bmgr = emerge->biomemgr; - - //// amount of elements to skip for the next index - //// for noise/height/biome maps (not vmanip) - this->ystride = csize.X; - // 1-up 1-down overgeneration - this->zstride_1u1d = csize.X * (csize.Y + 2); - - this->heightmap = new s16[csize.X * csize.Z]; - MapgenV7Params *sp = (MapgenV7Params *)params->sparams; this->spflags = sp->spflags; @@ -86,28 +75,14 @@ MapgenV7::MapgenV7(int mapgenid, MapgenParams *params, EmergeManager *emerge) MapgenBasic::np_cave1 = sp->np_cave1; MapgenBasic::np_cave2 = sp->np_cave2; - // TODO(hmmmm): should we have a way to disable biomemanager biomes? - //// Initialize biome generator - biomegen = emerge->biomemgr->createBiomeGen( - BIOMEGEN_ORIGINAL, params->bparams, csize); - biomemap = biomegen->biomemap; - - //// Resolve nodes to be used - c_stone = ndef->getId("mapgen_stone"); - c_water_source = ndef->getId("mapgen_water_source"); - c_lava_source = ndef->getId("mapgen_lava_source"); - c_desert_stone = ndef->getId("mapgen_desert_stone"); - c_ice = ndef->getId("mapgen_ice"); - c_sandstone = ndef->getId("mapgen_sandstone"); - + // Content used for dungeon generation c_cobble = ndef->getId("mapgen_cobble"); c_stair_cobble = ndef->getId("mapgen_stair_cobble"); c_mossycobble = ndef->getId("mapgen_mossycobble"); c_sandstonebrick = ndef->getId("mapgen_sandstonebrick"); c_stair_sandstonebrick = ndef->getId("mapgen_stair_sandstonebrick"); - if (c_ice == CONTENT_IGNORE) - c_ice = CONTENT_AIR; + // Fall back to more basic content if not defined if (c_mossycobble == CONTENT_IGNORE) c_mossycobble = c_cobble; if (c_stair_cobble == CONTENT_IGNORE) @@ -130,10 +105,6 @@ MapgenV7::~MapgenV7() delete noise_ridge_uwater; delete noise_mountain; delete noise_ridge; - - delete biomegen; - - delete[] heightmap; } diff --git a/src/mapgen_v7.h b/src/mapgen_v7.h index 723f1217f..aee171b48 100644 --- a/src/mapgen_v7.h +++ b/src/mapgen_v7.h @@ -56,9 +56,20 @@ struct MapgenV7Params : public MapgenSpecificParams { class MapgenV7 : public MapgenBasic { public: - int zstride_1u1d; + MapgenV7(int mapgenid, MapgenParams *params, EmergeManager *emerge); + ~MapgenV7(); - u32 spflags; + virtual void makeChunk(BlockMakeData *data); + int getSpawnLevelAtPoint(v2s16 p); + + float baseTerrainLevelAtPoint(s16 x, s16 z); + float baseTerrainLevelFromMap(int index); + bool getMountainTerrainAtPoint(s16 x, s16 y, s16 z); + bool getMountainTerrainFromMap(int idx_xyz, int idx_xz, s16 y); + int generateTerrain(); + void generateRidgeTerrain(); + +private: Noise *noise_terrain_base; Noise *noise_terrain_alt; Noise *noise_terrain_persist; @@ -68,27 +79,11 @@ public: Noise *noise_mountain; Noise *noise_ridge; - content_t c_lava_source; - content_t c_ice; - content_t c_cobble; content_t c_stair_cobble; content_t c_mossycobble; content_t c_sandstonebrick; content_t c_stair_sandstonebrick; - - MapgenV7(int mapgenid, MapgenParams *params, EmergeManager *emerge); - ~MapgenV7(); - - virtual void makeChunk(BlockMakeData *data); - int getSpawnLevelAtPoint(v2s16 p); - - float baseTerrainLevelAtPoint(s16 x, s16 z); - float baseTerrainLevelFromMap(int index); - bool getMountainTerrainAtPoint(s16 x, s16 y, s16 z); - bool getMountainTerrainFromMap(int idx_xyz, int idx_xz, s16 y); - int generateTerrain(); - void generateRidgeTerrain(); }; struct MapgenFactoryV7 : public MapgenFactory { diff --git a/src/mapgen_valleys.cpp b/src/mapgen_valleys.cpp index 3a4f984f9..c0094eb3b 100644 --- a/src/mapgen_valleys.cpp +++ b/src/mapgen_valleys.cpp @@ -67,17 +67,8 @@ static FlagDesc flagdesc_mapgen_valleys[] = { MapgenValleys::MapgenValleys(int mapgenid, MapgenParams *params, EmergeManager *emerge) : MapgenBasic(mapgenid, params, emerge) { - this->m_emerge = emerge; - this->bmgr = emerge->biomemgr; - - //// amount of elements to skip for the next index - //// for noise/height/biome maps (not vmanip) - this->ystride = csize.X; - this->zstride = csize.X * (csize.Y + 2); - // 1-down overgeneration - this->zstride_1d = csize.X * (csize.Y + 1); - - this->heightmap = new s16[csize.X * csize.Z]; + // NOTE: MapgenValleys has a hard dependency on BiomeGenOriginal + this->m_bgen = (BiomeGenOriginal *)biomegen; this->map_gen_limit = MYMIN(MAX_MAP_GENERATION_LIMIT, g_settings->getU16("map_generation_limit")); @@ -111,13 +102,6 @@ MapgenValleys::MapgenValleys(int mapgenid, MapgenParams *params, EmergeManager * noise_cave2 = new Noise(&sp->np_cave2, seed, csize.X, csize.Y + 1, csize.Z); noise_massive_caves = new Noise(&sp->np_massive_caves, seed, csize.X, csize.Y + 1, csize.Z); - //// Initialize biome generator - // NOTE: valleys mapgen can only use BiomeGenOriginal - biomegen = emerge->biomemgr->createBiomeGen( - BIOMEGEN_ORIGINAL, params->bparams, csize); - biomemap = biomegen->biomemap; - m_bgen = (BiomeGenOriginal *)biomegen; - this->humid_rivers = (spflags & MGVALLEYS_HUMID_RIVERS); this->use_altitude_chill = (spflags & MGVALLEYS_ALT_CHILL); this->humidity_adjust = bp->np_humidity.offset - 50.f; @@ -128,25 +112,18 @@ MapgenValleys::MapgenValleys(int mapgenid, MapgenParams *params, EmergeManager * tcave_cache = new float[csize.Y + 2]; - //// Resolve nodes to be used + // Resolve content to be used c_cobble = ndef->getId("mapgen_cobble"); - c_desert_stone = ndef->getId("mapgen_desert_stone"); - c_dirt = ndef->getId("mapgen_dirt"); c_lava_source = ndef->getId("mapgen_lava_source"); c_mossycobble = ndef->getId("mapgen_mossycobble"); - c_river_water_source = ndef->getId("mapgen_river_water_source"); c_sand = ndef->getId("mapgen_sand"); c_sandstonebrick = ndef->getId("mapgen_sandstonebrick"); - c_sandstone = ndef->getId("mapgen_sandstone"); c_stair_cobble = ndef->getId("mapgen_stair_cobble"); c_stair_sandstonebrick = ndef->getId("mapgen_stair_sandstonebrick"); - c_stone = ndef->getId("mapgen_stone"); - c_water_source = ndef->getId("mapgen_water_source"); + // Fall back to more basic content if not defined if (c_mossycobble == CONTENT_IGNORE) c_mossycobble = c_cobble; - if (c_river_water_source == CONTENT_IGNORE) - c_river_water_source = c_water_source; if (c_sand == CONTENT_IGNORE) c_sand = c_stone; if (c_sandstonebrick == CONTENT_IGNORE) @@ -171,9 +148,6 @@ MapgenValleys::~MapgenValleys() delete noise_valley_depth; delete noise_valley_profile; - delete biomegen; - - delete[] heightmap; delete[] tcave_cache; } @@ -289,13 +263,13 @@ void MapgenValleys::makeChunk(BlockMakeData *data) // Generate biome noises. Note this must be executed strictly before // generateTerrain, because generateTerrain depends on intermediate // biome-related noises. - biomegen->calcBiomeNoise(node_min); + m_bgen->calcBiomeNoise(node_min); // Generate base terrain with initial heightmaps s16 stone_surface_max_y = generateTerrain(); // Build biomemap - biomegen->getBiomes(heightmap); + m_bgen->getBiomes(heightmap); // Place biome-specific nodes MgStoneType stone_type = generateBiomes(); @@ -606,7 +580,7 @@ int MapgenValleys::generateTerrain() } } - u32 index_3d = (z - node_min.Z) * zstride + (x - node_min.X); + u32 index_3d = (z - node_min.Z) * zstride_1u1d + (x - node_min.X); u32 index_data = vm->m_area.index(x, node_min.Y - 1, z); // Mapgens concern themselves with stone and water. @@ -755,7 +729,7 @@ void MapgenValleys::generateCaves(s16 max_stone_y, s16 large_cave_depth) u32 index_2d = 0; for (s16 z = node_min.Z; z <= node_max.Z; z++) for (s16 x = node_min.X; x <= node_max.X; x++, index_2d++) { - Biome *biome = (Biome *)bmgr->getRaw(biomemap[index_2d]); + Biome *biome = (Biome *)m_bmgr->getRaw(biomemap[index_2d]); bool tunnel_air_above = false; bool underground = false; u32 index_data = vm->m_area.index(x, node_max.Y, z); diff --git a/src/mapgen_valleys.h b/src/mapgen_valleys.h index 4f87be6b6..bab3a756e 100644 --- a/src/mapgen_valleys.h +++ b/src/mapgen_valleys.h @@ -99,9 +99,6 @@ public: private: BiomeGenOriginal *m_bgen; - int zstride; - int zstride_1d; - float map_gen_limit; bool humid_rivers; @@ -110,7 +107,6 @@ private: s16 cave_water_max_height; s16 lava_max_height; - u32 spflags; float altitude_chill; s16 lava_features_lim; s16 massive_cave_depth; @@ -129,7 +125,6 @@ private: Noise *noise_valley_profile; content_t c_cobble; - content_t c_dirt; content_t c_lava_source; content_t c_mossycobble; content_t c_sand; -- cgit v1.2.3 From fd0efb21c3d40a963a813d73a09bb80df625b247 Mon Sep 17 00:00:00 2001 From: kwolekr Date: Sun, 22 May 2016 16:27:31 -0400 Subject: Mapgen: Combine dungeon generation code --- src/mapgen.cpp | 73 +++++++++++++++++++++++++++++++++++++++++++++++++- src/mapgen.h | 11 +++++++- src/mapgen_flat.cpp | 61 ++--------------------------------------- src/mapgen_flat.h | 6 ----- src/mapgen_fractal.cpp | 61 ++--------------------------------------- src/mapgen_fractal.h | 6 ----- src/mapgen_v5.cpp | 61 ++--------------------------------------- src/mapgen_v5.h | 6 ----- src/mapgen_v7.cpp | 61 ++--------------------------------------- src/mapgen_v7.h | 6 ----- src/mapgen_valleys.cpp | 61 +++-------------------------------------- src/mapgen_valleys.h | 5 ---- 12 files changed, 94 insertions(+), 324 deletions(-) (limited to 'src') diff --git a/src/mapgen.cpp b/src/mapgen.cpp index a2d207bff..56fe34e16 100644 --- a/src/mapgen.cpp +++ b/src/mapgen.cpp @@ -40,6 +40,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "filesys.h" #include "log.h" #include "cavegen.h" +#include "dungeongen.h" FlagDesc flagdesc_mapgen[] = { {"trees", MG_TREES}, @@ -414,13 +415,30 @@ MapgenBasic::MapgenBasic(int mapgenid, MapgenParams *params, EmergeManager *emer c_sandstone = ndef->getId("mapgen_sandstone"); c_river_water_source = ndef->getId("mapgen_river_water_source"); - //// Fall back to more basic content if not defined + // Fall back to more basic content if not defined if (c_desert_stone == CONTENT_IGNORE) c_desert_stone = c_stone; if (c_sandstone == CONTENT_IGNORE) c_sandstone = c_stone; if (c_river_water_source == CONTENT_IGNORE) c_river_water_source = c_water_source; + + //// Content used for dungeon generation + c_cobble = ndef->getId("mapgen_cobble"); + c_stair_cobble = ndef->getId("mapgen_stair_cobble"); + c_mossycobble = ndef->getId("mapgen_mossycobble"); + c_sandstonebrick = ndef->getId("mapgen_sandstonebrick"); + c_stair_sandstonebrick = ndef->getId("mapgen_stair_sandstonebrick"); + + // Fall back to more basic content if not defined + if (c_mossycobble == CONTENT_IGNORE) + c_mossycobble = c_cobble; + if (c_stair_cobble == CONTENT_IGNORE) + c_stair_cobble = c_cobble; + if (c_sandstonebrick == CONTENT_IGNORE) + c_sandstonebrick = c_sandstone; + if (c_stair_sandstonebrick == CONTENT_IGNORE) + c_stair_sandstonebrick = c_sandstone; } @@ -614,6 +632,59 @@ void MapgenBasic::generateCaves(s16 max_stone_y, s16 large_cave_depth) } +void MapgenBasic::generateDungeons(s16 max_stone_y, MgStoneType stone_type) +{ + if (max_stone_y < node_min.Y) + return; + + DungeonParams dp; + + dp.np_rarity = nparams_dungeon_rarity; + dp.np_density = nparams_dungeon_density; + dp.np_wetness = nparams_dungeon_wetness; + dp.c_water = c_water_source; + switch (stone_type) { + default: + case MGSTONE_STONE: + dp.c_cobble = c_cobble; + dp.c_moss = c_mossycobble; + dp.c_stair = c_stair_cobble; + + dp.diagonal_dirs = false; + dp.mossratio = 3.0; + dp.holesize = v3s16(1, 2, 1); + dp.roomsize = v3s16(0, 0, 0); + dp.notifytype = GENNOTIFY_DUNGEON; + break; + case MGSTONE_DESERT_STONE: + dp.c_cobble = c_desert_stone; + dp.c_moss = c_desert_stone; + dp.c_stair = c_desert_stone; + + dp.diagonal_dirs = true; + dp.mossratio = 0.0; + dp.holesize = v3s16(2, 3, 2); + dp.roomsize = v3s16(2, 5, 2); + dp.notifytype = GENNOTIFY_TEMPLE; + break; + case MGSTONE_SANDSTONE: + dp.c_cobble = c_sandstonebrick; + dp.c_moss = c_sandstonebrick; + dp.c_stair = c_sandstonebrick; + + dp.diagonal_dirs = false; + dp.mossratio = 0.0; + dp.holesize = v3s16(2, 2, 2); + dp.roomsize = v3s16(2, 0, 2); + dp.notifytype = GENNOTIFY_DUNGEON; + break; + } + + DungeonGen dgen(this, &dp); + dgen.generate(blockseed, full_node_min, full_node_max); +} + + //// //// GenerateNotifier //// diff --git a/src/mapgen.h b/src/mapgen.h index 0b552e508..10595fafc 100644 --- a/src/mapgen.h +++ b/src/mapgen.h @@ -219,9 +219,10 @@ public: MapgenBasic(int mapgenid, MapgenParams *params, EmergeManager *emerge); virtual ~MapgenBasic(); + virtual void generateCaves(s16 max_stone_y, s16 large_cave_depth); + virtual void generateDungeons(s16 max_stone_y, MgStoneType stone_type); virtual MgStoneType generateBiomes(); virtual void dustTopNodes(); - virtual void generateCaves(s16 max_stone_y, s16 large_cave_depth); protected: EmergeManager *m_emerge; @@ -234,12 +235,20 @@ protected: v3s16 full_node_min; v3s16 full_node_max; + // Content required for generateBiomes content_t c_stone; content_t c_water_source; content_t c_river_water_source; content_t c_desert_stone; content_t c_sandstone; + // Content required for generateDungeons + content_t c_cobble; + content_t c_stair_cobble; + content_t c_mossycobble; + content_t c_sandstonebrick; + content_t c_stair_sandstonebrick; + int ystride; int zstride; int zstride_1d; diff --git a/src/mapgen_flat.cpp b/src/mapgen_flat.cpp index cc43684c4..724f06825 100644 --- a/src/mapgen_flat.cpp +++ b/src/mapgen_flat.cpp @@ -69,23 +69,6 @@ MapgenFlat::MapgenFlat(int mapgenid, MapgenParams *params, EmergeManager *emerge MapgenBasic::np_cave1 = sp->np_cave1; MapgenBasic::np_cave2 = sp->np_cave2; - - // Content used for dungeon generation - c_cobble = ndef->getId("mapgen_cobble"); - c_stair_cobble = ndef->getId("mapgen_stair_cobble"); - c_mossycobble = ndef->getId("mapgen_mossycobble"); - c_sandstonebrick = ndef->getId("mapgen_sandstonebrick"); - c_stair_sandstonebrick = ndef->getId("mapgen_stair_sandstonebrick"); - - // Fall back to more basic content if not defined - if (c_mossycobble == CONTENT_IGNORE) - c_mossycobble = c_cobble; - if (c_stair_cobble == CONTENT_IGNORE) - c_stair_cobble = c_cobble; - if (c_sandstonebrick == CONTENT_IGNORE) - c_sandstonebrick = c_sandstone; - if (c_stair_sandstonebrick == CONTENT_IGNORE) - c_stair_sandstonebrick = c_sandstone; } @@ -215,48 +198,8 @@ void MapgenFlat::makeChunk(BlockMakeData *data) if (flags & MG_CAVES) generateCaves(stone_surface_max_y, large_cave_depth); - if ((flags & MG_DUNGEONS) && (stone_surface_max_y >= node_min.Y)) { - DungeonParams dp; - - dp.np_rarity = nparams_dungeon_rarity; - dp.np_density = nparams_dungeon_density; - dp.np_wetness = nparams_dungeon_wetness; - dp.c_water = c_water_source; - if (stone_type == MGSTONE_STONE) { - dp.c_cobble = c_cobble; - dp.c_moss = c_mossycobble; - dp.c_stair = c_stair_cobble; - - dp.diagonal_dirs = false; - dp.mossratio = 3.0; - dp.holesize = v3s16(1, 2, 1); - dp.roomsize = v3s16(0, 0, 0); - dp.notifytype = GENNOTIFY_DUNGEON; - } else if (stone_type == MGSTONE_DESERT_STONE) { - dp.c_cobble = c_desert_stone; - dp.c_moss = c_desert_stone; - dp.c_stair = c_desert_stone; - - dp.diagonal_dirs = true; - dp.mossratio = 0.0; - dp.holesize = v3s16(2, 3, 2); - dp.roomsize = v3s16(2, 5, 2); - dp.notifytype = GENNOTIFY_TEMPLE; - } else if (stone_type == MGSTONE_SANDSTONE) { - dp.c_cobble = c_sandstonebrick; - dp.c_moss = c_sandstonebrick; - dp.c_stair = c_sandstonebrick; - - dp.diagonal_dirs = false; - dp.mossratio = 0.0; - dp.holesize = v3s16(2, 2, 2); - dp.roomsize = v3s16(2, 0, 2); - dp.notifytype = GENNOTIFY_DUNGEON; - } - - DungeonGen dgen(this, &dp); - dgen.generate(blockseed, full_node_min, full_node_max); - } + if (flags & MG_DUNGEONS) + generateDungeons(stone_surface_max_y, stone_type); // Generate the registered decorations if (flags & MG_DECORATIONS) diff --git a/src/mapgen_flat.h b/src/mapgen_flat.h index ffec4b924..afe67b04e 100644 --- a/src/mapgen_flat.h +++ b/src/mapgen_flat.h @@ -70,12 +70,6 @@ private: float hill_threshold; float hill_steepness; Noise *noise_terrain; - - content_t c_cobble; - content_t c_stair_cobble; - content_t c_mossycobble; - content_t c_sandstonebrick; - content_t c_stair_sandstonebrick; }; struct MapgenFactoryFlat : public MapgenFactory { diff --git a/src/mapgen_fractal.cpp b/src/mapgen_fractal.cpp index 50e10d128..b613bf358 100644 --- a/src/mapgen_fractal.cpp +++ b/src/mapgen_fractal.cpp @@ -73,23 +73,6 @@ MapgenFractal::MapgenFractal(int mapgenid, MapgenParams *params, EmergeManager * this->formula = fractal / 2 + fractal % 2; this->julia = fractal % 2 == 0; - - // Content used for dungeon generation - c_cobble = ndef->getId("mapgen_cobble"); - c_stair_cobble = ndef->getId("mapgen_stair_cobble"); - c_mossycobble = ndef->getId("mapgen_mossycobble"); - c_sandstonebrick = ndef->getId("mapgen_sandstonebrick"); - c_stair_sandstonebrick = ndef->getId("mapgen_stair_sandstonebrick"); - - // Fall back to more basic content if not defined - if (c_mossycobble == CONTENT_IGNORE) - c_mossycobble = c_cobble; - if (c_stair_cobble == CONTENT_IGNORE) - c_stair_cobble = c_cobble; - if (c_sandstonebrick == CONTENT_IGNORE) - c_sandstonebrick = c_sandstone; - if (c_stair_sandstonebrick == CONTENT_IGNORE) - c_stair_sandstonebrick = c_sandstone; } @@ -231,48 +214,8 @@ void MapgenFractal::makeChunk(BlockMakeData *data) if (flags & MG_CAVES) generateCaves(stone_surface_max_y, MGFRACTAL_LARGE_CAVE_DEPTH); - if ((flags & MG_DUNGEONS) && (stone_surface_max_y >= node_min.Y)) { - DungeonParams dp; - - dp.np_rarity = nparams_dungeon_rarity; - dp.np_density = nparams_dungeon_density; - dp.np_wetness = nparams_dungeon_wetness; - dp.c_water = c_water_source; - if (stone_type == MGSTONE_STONE) { - dp.c_cobble = c_cobble; - dp.c_moss = c_mossycobble; - dp.c_stair = c_stair_cobble; - - dp.diagonal_dirs = false; - dp.mossratio = 3.0; - dp.holesize = v3s16(1, 2, 1); - dp.roomsize = v3s16(0, 0, 0); - dp.notifytype = GENNOTIFY_DUNGEON; - } else if (stone_type == MGSTONE_DESERT_STONE) { - dp.c_cobble = c_desert_stone; - dp.c_moss = c_desert_stone; - dp.c_stair = c_desert_stone; - - dp.diagonal_dirs = true; - dp.mossratio = 0.0; - dp.holesize = v3s16(2, 3, 2); - dp.roomsize = v3s16(2, 5, 2); - dp.notifytype = GENNOTIFY_TEMPLE; - } else if (stone_type == MGSTONE_SANDSTONE) { - dp.c_cobble = c_sandstonebrick; - dp.c_moss = c_sandstonebrick; - dp.c_stair = c_sandstonebrick; - - dp.diagonal_dirs = false; - dp.mossratio = 0.0; - dp.holesize = v3s16(2, 2, 2); - dp.roomsize = v3s16(2, 0, 2); - dp.notifytype = GENNOTIFY_DUNGEON; - } - - DungeonGen dgen(this, &dp); - dgen.generate(blockseed, full_node_min, full_node_max); - } + if (flags & MG_DUNGEONS) + generateDungeons(stone_surface_max_y, stone_type); // Generate the registered decorations if (flags & MG_DECORATIONS) diff --git a/src/mapgen_fractal.h b/src/mapgen_fractal.h index de0ccaa2f..065142b84 100644 --- a/src/mapgen_fractal.h +++ b/src/mapgen_fractal.h @@ -81,12 +81,6 @@ private: float julia_z; float julia_w; Noise *noise_seabed; - - content_t c_cobble; - content_t c_stair_cobble; - content_t c_mossycobble; - content_t c_sandstonebrick; - content_t c_stair_sandstonebrick; }; struct MapgenFactoryFractal : public MapgenFactory { diff --git a/src/mapgen_v5.cpp b/src/mapgen_v5.cpp index 167a38282..3e10ec2a5 100644 --- a/src/mapgen_v5.cpp +++ b/src/mapgen_v5.cpp @@ -64,23 +64,6 @@ MapgenV5::MapgenV5(int mapgenid, MapgenParams *params, EmergeManager *emerge) MapgenBasic::np_cave1 = sp->np_cave1; MapgenBasic::np_cave2 = sp->np_cave2; - - // Content used for dungeon generation - c_cobble = ndef->getId("mapgen_cobble"); - c_stair_cobble = ndef->getId("mapgen_stair_cobble"); - c_mossycobble = ndef->getId("mapgen_mossycobble"); - c_sandstonebrick = ndef->getId("mapgen_sandstonebrick"); - c_stair_sandstonebrick = ndef->getId("mapgen_stair_sandstonebrick"); - - // Fall back to more basic content if not defined - if (c_mossycobble == CONTENT_IGNORE) - c_mossycobble = c_cobble; - if (c_stair_cobble == CONTENT_IGNORE) - c_stair_cobble = c_cobble; - if (c_sandstonebrick == CONTENT_IGNORE) - c_sandstonebrick = c_sandstone; - if (c_stair_sandstonebrick == CONTENT_IGNORE) - c_stair_sandstonebrick = c_sandstone; } @@ -215,48 +198,8 @@ void MapgenV5::makeChunk(BlockMakeData *data) generateCaves(stone_surface_max_y, MGV5_LARGE_CAVE_DEPTH); // Generate dungeons and desert temples - if ((flags & MG_DUNGEONS) && (stone_surface_max_y >= node_min.Y)) { - DungeonParams dp; - - dp.np_rarity = nparams_dungeon_rarity; - dp.np_density = nparams_dungeon_density; - dp.np_wetness = nparams_dungeon_wetness; - dp.c_water = c_water_source; - if (stone_type == MGSTONE_STONE) { - dp.c_cobble = c_cobble; - dp.c_moss = c_mossycobble; - dp.c_stair = c_stair_cobble; - - dp.diagonal_dirs = false; - dp.mossratio = 3.0; - dp.holesize = v3s16(1, 2, 1); - dp.roomsize = v3s16(0, 0, 0); - dp.notifytype = GENNOTIFY_DUNGEON; - } else if (stone_type == MGSTONE_DESERT_STONE) { - dp.c_cobble = c_desert_stone; - dp.c_moss = c_desert_stone; - dp.c_stair = c_desert_stone; - - dp.diagonal_dirs = true; - dp.mossratio = 0.0; - dp.holesize = v3s16(2, 3, 2); - dp.roomsize = v3s16(2, 5, 2); - dp.notifytype = GENNOTIFY_TEMPLE; - } else if (stone_type == MGSTONE_SANDSTONE) { - dp.c_cobble = c_sandstonebrick; - dp.c_moss = c_sandstonebrick; - dp.c_stair = c_sandstonebrick; - - dp.diagonal_dirs = false; - dp.mossratio = 0.0; - dp.holesize = v3s16(2, 2, 2); - dp.roomsize = v3s16(2, 0, 2); - dp.notifytype = GENNOTIFY_DUNGEON; - } - - DungeonGen dgen(this, &dp); - dgen.generate(blockseed, full_node_min, full_node_max); - } + if (flags & MG_DUNGEONS) + generateDungeons(stone_surface_max_y, stone_type); // Generate the registered decorations if (flags & MG_DECORATIONS) diff --git a/src/mapgen_v5.h b/src/mapgen_v5.h index e7e5b2327..4e1772a64 100644 --- a/src/mapgen_v5.h +++ b/src/mapgen_v5.h @@ -61,12 +61,6 @@ private: Noise *noise_factor; Noise *noise_height; Noise *noise_ground; - - content_t c_cobble; - content_t c_stair_cobble; - content_t c_mossycobble; - content_t c_sandstonebrick; - content_t c_stair_sandstonebrick; }; diff --git a/src/mapgen_v7.cpp b/src/mapgen_v7.cpp index 29c81e8a7..9ff2fbe66 100644 --- a/src/mapgen_v7.cpp +++ b/src/mapgen_v7.cpp @@ -74,23 +74,6 @@ MapgenV7::MapgenV7(int mapgenid, MapgenParams *params, EmergeManager *emerge) MapgenBasic::np_cave1 = sp->np_cave1; MapgenBasic::np_cave2 = sp->np_cave2; - - // Content used for dungeon generation - c_cobble = ndef->getId("mapgen_cobble"); - c_stair_cobble = ndef->getId("mapgen_stair_cobble"); - c_mossycobble = ndef->getId("mapgen_mossycobble"); - c_sandstonebrick = ndef->getId("mapgen_sandstonebrick"); - c_stair_sandstonebrick = ndef->getId("mapgen_stair_sandstonebrick"); - - // Fall back to more basic content if not defined - if (c_mossycobble == CONTENT_IGNORE) - c_mossycobble = c_cobble; - if (c_stair_cobble == CONTENT_IGNORE) - c_stair_cobble = c_cobble; - if (c_sandstonebrick == CONTENT_IGNORE) - c_sandstonebrick = c_sandstone; - if (c_stair_sandstonebrick == CONTENT_IGNORE) - c_stair_sandstonebrick = c_sandstone; } @@ -240,48 +223,8 @@ void MapgenV7::makeChunk(BlockMakeData *data) if (flags & MG_CAVES) generateCaves(stone_surface_max_y, water_level); - if ((flags & MG_DUNGEONS) && (stone_surface_max_y >= node_min.Y)) { - DungeonParams dp; - - dp.np_rarity = nparams_dungeon_rarity; - dp.np_density = nparams_dungeon_density; - dp.np_wetness = nparams_dungeon_wetness; - dp.c_water = c_water_source; - if (stone_type == MGSTONE_STONE) { - dp.c_cobble = c_cobble; - dp.c_moss = c_mossycobble; - dp.c_stair = c_stair_cobble; - - dp.diagonal_dirs = false; - dp.mossratio = 3.0; - dp.holesize = v3s16(1, 2, 1); - dp.roomsize = v3s16(0, 0, 0); - dp.notifytype = GENNOTIFY_DUNGEON; - } else if (stone_type == MGSTONE_DESERT_STONE) { - dp.c_cobble = c_desert_stone; - dp.c_moss = c_desert_stone; - dp.c_stair = c_desert_stone; - - dp.diagonal_dirs = true; - dp.mossratio = 0.0; - dp.holesize = v3s16(2, 3, 2); - dp.roomsize = v3s16(2, 5, 2); - dp.notifytype = GENNOTIFY_TEMPLE; - } else if (stone_type == MGSTONE_SANDSTONE) { - dp.c_cobble = c_sandstonebrick; - dp.c_moss = c_sandstonebrick; - dp.c_stair = c_sandstonebrick; - - dp.diagonal_dirs = false; - dp.mossratio = 0.0; - dp.holesize = v3s16(2, 2, 2); - dp.roomsize = v3s16(2, 0, 2); - dp.notifytype = GENNOTIFY_DUNGEON; - } - - DungeonGen dgen(this, &dp); - dgen.generate(blockseed, full_node_min, full_node_max); - } + if (flags & MG_DUNGEONS) + generateDungeons(stone_surface_max_y, stone_type); // Generate the registered decorations if (flags & MG_DECORATIONS) diff --git a/src/mapgen_v7.h b/src/mapgen_v7.h index aee171b48..9ed89a511 100644 --- a/src/mapgen_v7.h +++ b/src/mapgen_v7.h @@ -78,12 +78,6 @@ private: Noise *noise_ridge_uwater; Noise *noise_mountain; Noise *noise_ridge; - - content_t c_cobble; - content_t c_stair_cobble; - content_t c_mossycobble; - content_t c_sandstonebrick; - content_t c_stair_sandstonebrick; }; struct MapgenFactoryV7 : public MapgenFactory { diff --git a/src/mapgen_valleys.cpp b/src/mapgen_valleys.cpp index c0094eb3b..2cd733d36 100644 --- a/src/mapgen_valleys.cpp +++ b/src/mapgen_valleys.cpp @@ -113,25 +113,12 @@ MapgenValleys::MapgenValleys(int mapgenid, MapgenParams *params, EmergeManager * tcave_cache = new float[csize.Y + 2]; // Resolve content to be used - c_cobble = ndef->getId("mapgen_cobble"); - c_lava_source = ndef->getId("mapgen_lava_source"); - c_mossycobble = ndef->getId("mapgen_mossycobble"); - c_sand = ndef->getId("mapgen_sand"); - c_sandstonebrick = ndef->getId("mapgen_sandstonebrick"); - c_stair_cobble = ndef->getId("mapgen_stair_cobble"); - c_stair_sandstonebrick = ndef->getId("mapgen_stair_sandstonebrick"); + c_lava_source = ndef->getId("mapgen_lava_source"); + c_sand = ndef->getId("mapgen_sand"); // Fall back to more basic content if not defined - if (c_mossycobble == CONTENT_IGNORE) - c_mossycobble = c_cobble; if (c_sand == CONTENT_IGNORE) c_sand = c_stone; - if (c_sandstonebrick == CONTENT_IGNORE) - c_sandstonebrick = c_sandstone; - if (c_stair_cobble == CONTENT_IGNORE) - c_stair_cobble = c_cobble; - if (c_stair_sandstonebrick == CONTENT_IGNORE) - c_stair_sandstonebrick = c_sandstone; } @@ -279,48 +266,8 @@ void MapgenValleys::makeChunk(BlockMakeData *data) generateCaves(stone_surface_max_y, large_cave_depth); // Dungeon creation - if ((flags & MG_DUNGEONS) && node_max.Y < 50 && (stone_surface_max_y >= node_min.Y)) { - DungeonParams dp; - - dp.np_rarity = nparams_dungeon_rarity; - dp.np_density = nparams_dungeon_density; - dp.np_wetness = nparams_dungeon_wetness; - dp.c_water = c_water_source; - if (stone_type == MGSTONE_STONE) { - dp.c_cobble = c_cobble; - dp.c_moss = c_mossycobble; - dp.c_stair = c_stair_cobble; - - dp.diagonal_dirs = false; - dp.mossratio = 3.f; - dp.holesize = v3s16(1, 2, 1); - dp.roomsize = v3s16(0, 0, 0); - dp.notifytype = GENNOTIFY_DUNGEON; - } else if (stone_type == MGSTONE_DESERT_STONE) { - dp.c_cobble = c_desert_stone; - dp.c_moss = c_desert_stone; - dp.c_stair = c_desert_stone; - - dp.diagonal_dirs = true; - dp.mossratio = 0.f; - dp.holesize = v3s16(2, 3, 2); - dp.roomsize = v3s16(2, 5, 2); - dp.notifytype = GENNOTIFY_TEMPLE; - } else if (stone_type == MGSTONE_SANDSTONE) { - dp.c_cobble = c_sandstonebrick; - dp.c_moss = c_sandstonebrick; - dp.c_stair = c_sandstonebrick; - - dp.diagonal_dirs = false; - dp.mossratio = 0.f; - dp.holesize = v3s16(2, 2, 2); - dp.roomsize = v3s16(2, 0, 2); - dp.notifytype = GENNOTIFY_DUNGEON; - } - - DungeonGen dgen(this, &dp); - dgen.generate(blockseed, full_node_min, full_node_max); - } + if ((flags & MG_DUNGEONS) && node_max.Y < 50) + generateDungeons(stone_surface_max_y, stone_type); // Generate the registered decorations if (flags & MG_DECORATIONS) diff --git a/src/mapgen_valleys.h b/src/mapgen_valleys.h index bab3a756e..a7369ea92 100644 --- a/src/mapgen_valleys.h +++ b/src/mapgen_valleys.h @@ -124,13 +124,8 @@ private: Noise *noise_valley_depth; Noise *noise_valley_profile; - content_t c_cobble; content_t c_lava_source; - content_t c_mossycobble; content_t c_sand; - content_t c_sandstonebrick; - content_t c_stair_cobble; - content_t c_stair_sandstonebrick; float terrainLevelAtPoint(s16 x, s16 z); -- cgit v1.2.3 From c8fd232678698b8be469b3792e7acc9418231a38 Mon Sep 17 00:00:00 2001 From: kwolekr Date: Sun, 22 May 2016 16:50:43 -0400 Subject: Dungeongen: Remove dependency on Mapgen --- src/dungeongen.cpp | 37 +++++++++++++++++++++++-------------- src/dungeongen.h | 19 +++++++++++++------ src/mapgen.cpp | 7 +++++-- src/mapgen_v6.cpp | 6 ++++-- 4 files changed, 45 insertions(+), 24 deletions(-) (limited to 'src') diff --git a/src/dungeongen.cpp b/src/dungeongen.cpp index f7f9b8b50..6be7e9086 100644 --- a/src/dungeongen.cpp +++ b/src/dungeongen.cpp @@ -38,22 +38,27 @@ NoiseParams nparams_dungeon_density(0.0, 1.0, v3f(2.5, 2.5, 2.5), 0, 2, 1.4, 2.0 /////////////////////////////////////////////////////////////////////////////// -DungeonGen::DungeonGen(Mapgen *mapgen, DungeonParams *dparams) +DungeonGen::DungeonGen(INodeDefManager *ndef, + GenerateNotifier *gennotify, DungeonParams *dparams) { - this->mg = mapgen; - this->vm = mapgen->vm; + assert(ndef); + + this->ndef = ndef; + this->gennotify = gennotify; #ifdef DGEN_USE_TORCHES - c_torch = mg->ndef->getId("default:torch"); + c_torch = ndef->getId("default:torch"); #endif if (dparams) { memcpy(&dp, dparams, sizeof(dp)); } else { - dp.c_water = mg->ndef->getId("mapgen_water_source"); - dp.c_cobble = mg->ndef->getId("mapgen_cobble"); - dp.c_moss = mg->ndef->getId("mapgen_mossycobble"); - dp.c_stair = mg->ndef->getId("mapgen_stair_cobble"); + dp.seed = 0; + + dp.c_water = ndef->getId("mapgen_water_source"); + dp.c_cobble = ndef->getId("mapgen_cobble"); + dp.c_moss = ndef->getId("mapgen_mossycobble"); + dp.c_stair = ndef->getId("mapgen_stair_cobble"); dp.diagonal_dirs = false; dp.mossratio = 3.0; @@ -67,18 +72,21 @@ DungeonGen::DungeonGen(Mapgen *mapgen, DungeonParams *dparams) } // For mapgens using river water - dp.c_river_water = mg->ndef->getId("mapgen_river_water_source"); + dp.c_river_water = ndef->getId("mapgen_river_water_source"); if (dp.c_river_water == CONTENT_IGNORE) - dp.c_river_water = mg->ndef->getId("mapgen_water_source"); + dp.c_river_water = ndef->getId("mapgen_water_source"); } -void DungeonGen::generate(u32 bseed, v3s16 nmin, v3s16 nmax) +void DungeonGen::generate(MMVManip *vm, u32 bseed, v3s16 nmin, v3s16 nmax) { + assert(vm); + //TimeTaker t("gen dungeons"); - if (NoisePerlin3D(&dp.np_rarity, nmin.X, nmin.Y, nmin.Z, mg->seed) < 0.2) + if (NoisePerlin3D(&dp.np_rarity, nmin.X, nmin.Y, nmin.Z, dp.seed) < 0.2) return; + this->vm = vm; this->blockseed = bseed; random.seed(bseed + 2); @@ -109,7 +117,7 @@ void DungeonGen::generate(u32 bseed, v3s16 nmin, v3s16 nmax) u32 i = vm->m_area.index(nmin.X, y, z); for (s16 x = nmin.X; x <= nmax.X; x++) { if (vm->m_data[i].getContent() == dp.c_cobble) { - float wetness = NoisePerlin3D(&dp.np_wetness, x, y, z, mg->seed); + float wetness = NoisePerlin3D(&dp.np_wetness, x, y, z, dp.seed); float density = NoisePerlin3D(&dp.np_density, x, y, z, blockseed); if (density < wetness / dp.mossratio) vm->m_data[i].setContent(dp.c_moss); @@ -187,7 +195,8 @@ void DungeonGen::makeDungeon(v3s16 start_padding) makeRoom(roomsize, roomplace); v3s16 room_center = roomplace + v3s16(roomsize.X / 2, 1, roomsize.Z / 2); - mg->gennotify.addEvent(dp.notifytype, room_center); + if (gennotify) + gennotify->addEvent(dp.notifytype, room_center); #ifdef DGEN_USE_TORCHES // Place torch at room center (for testing) diff --git a/src/dungeongen.h b/src/dungeongen.h index d209dd4bf..f0a5e5ba8 100644 --- a/src/dungeongen.h +++ b/src/dungeongen.h @@ -39,6 +39,8 @@ int dir_to_facedir(v3s16 d); struct DungeonParams { + int seed; + content_t c_water; content_t c_river_water; content_t c_cobble; @@ -59,7 +61,9 @@ struct DungeonParams { class DungeonGen { public: MMVManip *vm; - Mapgen *mg; + INodeDefManager *ndef; + GenerateNotifier *gennotify; + u32 blockseed; PseudoRandom random; v3s16 csize; @@ -67,17 +71,20 @@ public: content_t c_torch; DungeonParams dp; - //RoomWalker + // RoomWalker v3s16 m_pos; v3s16 m_dir; - DungeonGen(Mapgen *mg, DungeonParams *dparams); - void generate(u32 bseed, v3s16 full_node_min, v3s16 full_node_max); + DungeonGen(INodeDefManager *ndef, + GenerateNotifier *gennotify, DungeonParams *dparams); + + void generate(MMVManip *vm, u32 bseed, + v3s16 full_node_min, v3s16 full_node_max); void makeDungeon(v3s16 start_padding); void makeRoom(v3s16 roomsize, v3s16 roomplace); void makeCorridor(v3s16 doorplace, v3s16 doordir, - v3s16 &result_place, v3s16 &result_dir); + v3s16 &result_place, v3s16 &result_dir); void makeDoor(v3s16 doorplace, v3s16 doordir); void makeFill(v3s16 place, v3s16 size, u8 avoid_flags, MapNode n, u8 or_flags); void makeHole(v3s16 place); @@ -86,7 +93,7 @@ public: bool findPlaceForRoomDoor(v3s16 roomsize, v3s16 &result_doorplace, v3s16 &result_doordir, v3s16 &result_roomplace); - void randomizeDir() + inline void randomizeDir() { m_dir = rand_ortho_dir(random, dp.diagonal_dirs); } diff --git a/src/mapgen.cpp b/src/mapgen.cpp index 56fe34e16..041356e3d 100644 --- a/src/mapgen.cpp +++ b/src/mapgen.cpp @@ -639,10 +639,13 @@ void MapgenBasic::generateDungeons(s16 max_stone_y, MgStoneType stone_type) DungeonParams dp; + dp.seed = seed; + dp.np_rarity = nparams_dungeon_rarity; dp.np_density = nparams_dungeon_density; dp.np_wetness = nparams_dungeon_wetness; dp.c_water = c_water_source; + switch (stone_type) { default: case MGSTONE_STONE: @@ -680,8 +683,8 @@ void MapgenBasic::generateDungeons(s16 max_stone_y, MgStoneType stone_type) break; } - DungeonGen dgen(this, &dp); - dgen.generate(blockseed, full_node_min, full_node_max); + DungeonGen dgen(ndef, &gennotify, &dp); + dgen.generate(vm, blockseed, full_node_min, full_node_max); } diff --git a/src/mapgen_v6.cpp b/src/mapgen_v6.cpp index 65acfaf93..103120b6a 100644 --- a/src/mapgen_v6.cpp +++ b/src/mapgen_v6.cpp @@ -559,6 +559,8 @@ void MapgenV6::makeChunk(BlockMakeData *data) if ((flags & MG_DUNGEONS) && (stone_surface_max_y >= node_min.Y)) { DungeonParams dp; + dp.seed = seed; + dp.np_rarity = nparams_dungeon_rarity; dp.np_density = nparams_dungeon_density; dp.np_wetness = nparams_dungeon_wetness; @@ -585,8 +587,8 @@ void MapgenV6::makeChunk(BlockMakeData *data) dp.notifytype = GENNOTIFY_DUNGEON; } - DungeonGen dgen(this, &dp); - dgen.generate(blockseed, full_node_min, full_node_max); + DungeonGen dgen(ndef, &gennotify, &dp); + dgen.generate(vm, blockseed, full_node_min, full_node_max); } // Add top and bottom side of water to transforming_liquid queue -- cgit v1.2.3 From c4e083f7e142d8e25e906bb19f9b1f03bb7b430d Mon Sep 17 00:00:00 2001 From: kwolekr Date: Thu, 26 May 2016 23:23:05 -0400 Subject: Disallow external linkage for default Cavegen NoiseParams --- src/cavegen.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/cavegen.cpp b/src/cavegen.cpp index 51309ad60..1c25ce711 100644 --- a/src/cavegen.cpp +++ b/src/cavegen.cpp @@ -26,7 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "mg_biome.h" #include "cavegen.h" -NoiseParams nparams_caveliquids(0, 1, v3f(150.0, 150.0, 150.0), 776, 3, 0.6, 2.0); +static NoiseParams nparams_caveliquids(0, 1, v3f(150.0, 150.0, 150.0), 776, 3, 0.6, 2.0); //// -- cgit v1.2.3 From 62d15ac7c1fcd7214a9e45d46bbc560f998edb95 Mon Sep 17 00:00:00 2001 From: red-001 Date: Sat, 28 May 2016 04:37:28 +0100 Subject: Add base64 encoding and decoding to the lua api. (#3919) --- doc/lua_api.txt | 4 ++++ doc/menu_lua_api.txt | 4 ++++ src/script/lua_api/l_util.cpp | 35 +++++++++++++++++++++++++++++++++++ src/script/lua_api/l_util.h | 6 ++++++ 4 files changed, 49 insertions(+) (limited to 'src') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 593e0c438..01763fd73 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -2438,6 +2438,10 @@ These functions return the leftover itemstack. * See documentation on `minetest.compress()` for supported compression methods. * currently supported. * `...` indicates method-specific arguments. Currently, no methods use this. +* `minetest.encode_base64(string)`: returns string encoded in base64 + * Encodes a string in base64. +* `minetest.decode_base64(string)`: returns string + * Decodes a string encoded in base64. * `minetest.is_protected(pos, name)`: returns boolean * Returns true, if player `name` shouldn't be abled to dig at `pos` or do other actions, defineable by mods, due to some mod-defined ownership-like concept. diff --git a/doc/menu_lua_api.txt b/doc/menu_lua_api.txt index ac8713a32..479880266 100644 --- a/doc/menu_lua_api.txt +++ b/doc/menu_lua_api.txt @@ -210,6 +210,10 @@ string:trim() ^ eg. string.trim("\n \t\tfoo bar\t ") == "foo bar" core.is_yes(arg) (possible in async calls) ^ returns whether arg can be interpreted as yes +minetest.encode_base64(string) (possible in async calls) +^ Encodes a string in base64. +minetest.decode_base64(string) (possible in async calls) +^ Decodes a string encoded in base64. Version compat: core.get_min_supp_proto() diff --git a/src/script/lua_api/l_util.cpp b/src/script/lua_api/l_util.cpp index c3e6c8964..e90b7fbcf 100644 --- a/src/script/lua_api/l_util.cpp +++ b/src/script/lua_api/l_util.cpp @@ -32,6 +32,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "filesys.h" #include "settings.h" #include "util/auth.h" +#include "util/base64.h" #include // log([level,] text) @@ -320,6 +321,34 @@ int ModApiUtil::l_decompress(lua_State *L) return 1; } +// encode_base64(string) +int ModApiUtil::l_encode_base64(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + size_t size; + const char *data = luaL_checklstring(L, 1, &size); + + std::string out = base64_encode((const unsigned char *)(data), size); + + lua_pushlstring(L, out.data(), out.size()); + return 1; +} + +// decode_base64(string) +int ModApiUtil::l_decode_base64(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + size_t size; + const char *data = luaL_checklstring(L, 1, &size); + + std::string out = base64_decode(std::string(data, size)); + + lua_pushlstring(L, out.data(), out.size()); + return 1; +} + // mkdir(path) int ModApiUtil::l_mkdir(lua_State *L) { @@ -433,6 +462,9 @@ void ModApiUtil::Initialize(lua_State *L, int top) API_FCT(get_dir_list); API_FCT(request_insecure_environment); + + API_FCT(encode_base64); + API_FCT(decode_base64); } void ModApiUtil::InitializeAsync(AsyncEngine& engine) @@ -459,5 +491,8 @@ void ModApiUtil::InitializeAsync(AsyncEngine& engine) ASYNC_API_FCT(mkdir); ASYNC_API_FCT(get_dir_list); + + ASYNC_API_FCT(encode_base64); + ASYNC_API_FCT(decode_base64); } diff --git a/src/script/lua_api/l_util.h b/src/script/lua_api/l_util.h index 6fac7e7eb..779dbe281 100644 --- a/src/script/lua_api/l_util.h +++ b/src/script/lua_api/l_util.h @@ -95,6 +95,12 @@ private: // request_insecure_environment() static int l_request_insecure_environment(lua_State *L); + // encode_base64(string) + static int l_encode_base64(lua_State *L); + + // decode_base64(string) + static int l_decode_base64(lua_State *L); + public: static void Initialize(lua_State *L, int top); -- cgit v1.2.3 From d499ec483837fa7210176ef39beba2d5a3a5a61d Mon Sep 17 00:00:00 2001 From: Auke Kok Date: Fri, 27 May 2016 21:08:23 -0700 Subject: Particles: Add option to remove particles on collision Adds the particle option `collision_removal = bool` Some particles are hard to use right now since they either go through solid blocks (without collision detection), and with collision detection enabled they (e.g. raindrops) would just stop dead on the floor and sit there until they expire, or worse, scrape along a wall or ceiling. We can solve the problem by adding a boolean flag that tells the particle to be removed if it ever collides with something. This will make it easier to add rain that doesn't fall through your roof or stick on the top of it. Or clouds and smoke that don't go through trees. Particles that collide with this flag are marked expired unconditionally, causing them to be treated like normal expired particles and cleaned up normally. Documentation is adjusted accordingly. An added bonus of this patch is that particles can potentially collide many times with nodes, and this reduces the amount of collisions to 1 (max), which may end up reducing particle load on the client. --- doc/lua_api.txt | 6 ++++++ src/client.h | 2 ++ src/network/clientpackethandler.cpp | 7 +++++++ src/network/networkprotocol.h | 2 ++ src/particles.cpp | 39 +++++++++++++++++++++++-------------- src/particles.h | 5 +++++ src/script/lua_api/l_particles.cpp | 24 ++++++++++++++++------- src/server.cpp | 19 ++++++++++++------ src/server.h | 12 ++++++++---- 9 files changed, 84 insertions(+), 32 deletions(-) (limited to 'src') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 01763fd73..03f2dad32 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -3885,6 +3885,9 @@ Definition tables size = 1, collisiondetection = false, -- ^ collisiondetection: if true collides with physical objects + collision_removal = false, + -- ^ collision_removal: if true then particle is removed when it collides, + -- ^ requires collisiondetection = true to have any effect vertical = false, -- ^ vertical: if true faces player using y axis only texture = "image.png", @@ -3914,6 +3917,9 @@ Definition tables -- ^ minsize/maxsize, minexptime/maxexptime (expirationtime) collisiondetection = false, -- ^ collisiondetection: if true uses collision detection + collision_removal = false, + -- ^ collision_removal: if true then particle is removed when it collides, + -- ^ requires collisiondetection = true to have any effect vertical = false, -- ^ vertical: if true faces player using y axis only texture = "image.png", diff --git a/src/client.h b/src/client.h index cdadb9d3e..a7eb22ad9 100644 --- a/src/client.h +++ b/src/client.h @@ -182,6 +182,7 @@ struct ClientEvent f32 expirationtime; f32 size; bool collisiondetection; + bool collision_removal; bool vertical; std::string *texture; } spawn_particle; @@ -199,6 +200,7 @@ struct ClientEvent f32 minsize; f32 maxsize; bool collisiondetection; + bool collision_removal; bool vertical; std::string *texture; u32 id; diff --git a/src/network/clientpackethandler.cpp b/src/network/clientpackethandler.cpp index 0498f4048..48c573da5 100644 --- a/src/network/clientpackethandler.cpp +++ b/src/network/clientpackethandler.cpp @@ -898,8 +898,10 @@ void Client::handleCommand_SpawnParticle(NetworkPacket* pkt) bool collisiondetection = readU8(is); std::string texture = deSerializeLongString(is); bool vertical = false; + bool collision_removal = false; try { vertical = readU8(is); + collision_removal = readU8(is); } catch (...) {} ClientEvent event; @@ -910,6 +912,7 @@ void Client::handleCommand_SpawnParticle(NetworkPacket* pkt) event.spawn_particle.expirationtime = expirationtime; event.spawn_particle.size = size; event.spawn_particle.collisiondetection = collisiondetection; + event.spawn_particle.collision_removal = collision_removal; event.spawn_particle.vertical = vertical; event.spawn_particle.texture = new std::string(texture); @@ -942,8 +945,11 @@ void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt) *pkt >> id; bool vertical = false; + bool collision_removal = false; try { *pkt >> vertical; + *pkt >> collision_removal; + } catch (...) {} ClientEvent event; @@ -961,6 +967,7 @@ void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt) event.add_particlespawner.minsize = minsize; event.add_particlespawner.maxsize = maxsize; event.add_particlespawner.collisiondetection = collisiondetection; + event.add_particlespawner.collision_removal = collision_removal; event.add_particlespawner.vertical = vertical; event.add_particlespawner.texture = new std::string(texture); event.add_particlespawner.id = id; diff --git a/src/network/networkprotocol.h b/src/network/networkprotocol.h index 177b97680..3a9483efb 100644 --- a/src/network/networkprotocol.h +++ b/src/network/networkprotocol.h @@ -474,6 +474,7 @@ enum ToClientCommand u8 bool vertical u32 len u8[len] texture + u8 collision_removal */ TOCLIENT_ADD_PARTICLESPAWNER = 0x47, @@ -495,6 +496,7 @@ enum ToClientCommand u32 len u8[len] texture u32 id + u8 collision_removal */ TOCLIENT_DELETE_PARTICLESPAWNER_LEGACY = 0x48, diff --git a/src/particles.cpp b/src/particles.cpp index 525258a25..ccca691d1 100644 --- a/src/particles.cpp +++ b/src/particles.cpp @@ -54,6 +54,7 @@ Particle::Particle( float expirationtime, float size, bool collisiondetection, + bool collision_removal, bool vertical, video::ITexture *texture, v2f texpos, @@ -85,6 +86,7 @@ Particle::Particle( m_player = player; m_size = size; m_collisiondetection = collisiondetection; + m_collision_removal = collision_removal; m_vertical = vertical; // Irrlicht stuff @@ -126,20 +128,21 @@ void Particle::render() void Particle::step(float dtime) { m_time += dtime; - if (m_collisiondetection) - { + if (m_collisiondetection) { aabb3f box = m_collisionbox; - v3f p_pos = m_pos*BS; - v3f p_velocity = m_velocity*BS; - collisionMoveSimple(m_env, m_gamedef, - BS*0.5, box, - 0, dtime, - &p_pos, &p_velocity, m_acceleration * BS); - m_pos = p_pos/BS; - m_velocity = p_velocity/BS; - } - else - { + v3f p_pos = m_pos * BS; + v3f p_velocity = m_velocity * BS; + collisionMoveResult r = collisionMoveSimple(m_env, + m_gamedef, BS * 0.5, box, 0, dtime, &p_pos, + &p_velocity, m_acceleration * BS); + if (m_collision_removal && r.collides) { + // force expiration of the particle + m_expiration = -1.0; + } else { + m_pos = p_pos / BS; + m_velocity = p_velocity / BS; + } + } else { m_velocity += m_acceleration * dtime; m_pos += m_velocity * dtime; } @@ -210,8 +213,8 @@ ParticleSpawner::ParticleSpawner(IGameDef* gamedef, scene::ISceneManager *smgr, u16 amount, float time, v3f minpos, v3f maxpos, v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime, float minsize, float maxsize, - bool collisiondetection, bool vertical, video::ITexture *texture, u32 id, - ParticleManager *p_manager) : + bool collisiondetection, bool collision_removal, bool vertical, + video::ITexture *texture, u32 id, ParticleManager *p_manager) : m_particlemanager(p_manager) { m_gamedef = gamedef; @@ -230,6 +233,7 @@ ParticleSpawner::ParticleSpawner(IGameDef* gamedef, scene::ISceneManager *smgr, m_minsize = minsize; m_maxsize = maxsize; m_collisiondetection = collisiondetection; + m_collision_removal = collision_removal; m_vertical = vertical; m_texture = texture; m_time = 0; @@ -277,6 +281,7 @@ void ParticleSpawner::step(float dtime, ClientEnvironment* env) exptime, size, m_collisiondetection, + m_collision_removal, m_vertical, m_texture, v2f(0.0, 0.0), @@ -317,6 +322,7 @@ void ParticleSpawner::step(float dtime, ClientEnvironment* env) exptime, size, m_collisiondetection, + m_collision_removal, m_vertical, m_texture, v2f(0.0, 0.0), @@ -446,6 +452,7 @@ void ParticleManager::handleParticleEvent(ClientEvent *event, IGameDef *gamedef, event->add_particlespawner.minsize, event->add_particlespawner.maxsize, event->add_particlespawner.collisiondetection, + event->add_particlespawner.collision_removal, event->add_particlespawner.vertical, texture, event->add_particlespawner.id, @@ -480,6 +487,7 @@ void ParticleManager::handleParticleEvent(ClientEvent *event, IGameDef *gamedef, event->spawn_particle.expirationtime, event->spawn_particle.size, event->spawn_particle.collisiondetection, + event->spawn_particle.collision_removal, event->spawn_particle.vertical, texture, v2f(0.0, 0.0), @@ -555,6 +563,7 @@ void ParticleManager::addNodeParticle(IGameDef* gamedef, scene::ISceneManager* s visual_size, true, false, + false, texture, texpos, texsize); diff --git a/src/particles.h b/src/particles.h index dda84385c..bc3ca53b7 100644 --- a/src/particles.h +++ b/src/particles.h @@ -30,6 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc., struct ClientEvent; class ParticleManager; +class ClientEnvironment; class Particle : public scene::ISceneNode { @@ -45,6 +46,7 @@ class Particle : public scene::ISceneNode float expirationtime, float size, bool collisiondetection, + bool collision_removal, bool vertical, video::ITexture *texture, v2f texpos, @@ -97,6 +99,7 @@ private: float m_size; u8 m_light; bool m_collisiondetection; + bool m_collision_removal; bool m_vertical; v3s16 m_camera_offset; }; @@ -115,6 +118,7 @@ class ParticleSpawner float minexptime, float maxexptime, float minsize, float maxsize, bool collisiondetection, + bool collision_removal, bool vertical, video::ITexture *texture, u32 id, @@ -148,6 +152,7 @@ class ParticleSpawner video::ITexture *m_texture; std::vector m_spawntimes; bool m_collisiondetection; + bool m_collision_removal; bool m_vertical; }; diff --git a/src/script/lua_api/l_particles.cpp b/src/script/lua_api/l_particles.cpp index f6c1725de..263e35407 100644 --- a/src/script/lua_api/l_particles.cpp +++ b/src/script/lua_api/l_particles.cpp @@ -21,13 +21,16 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "lua_api/l_internal.h" #include "common/c_converter.h" #include "server.h" +#include "particles.h" // add_particle({pos=, velocity=, acceleration=, expirationtime=, -// size=, collisiondetection=, vertical=, texture=, player=}) +// size=, collisiondetection=, collision_removal=, vertical=, +// texture=, player=}) // pos/velocity/acceleration = {x=num, y=num, z=num} // expirationtime = num (seconds) // size = num // collisiondetection = bool +// collision_removal = bool // vertical = bool // texture = e.g."default_wood.png" int ModApiParticles::l_add_particle(lua_State *L) @@ -41,8 +44,8 @@ int ModApiParticles::l_add_particle(lua_State *L) float expirationtime, size; expirationtime = size = 1; - bool collisiondetection, vertical; - collisiondetection = vertical = false; + bool collisiondetection, vertical, collision_removal; + collisiondetection = vertical = collision_removal = false; std::string texture = ""; std::string playername = ""; @@ -94,12 +97,14 @@ int ModApiParticles::l_add_particle(lua_State *L) size = getfloatfield_default(L, 1, "size", 1); collisiondetection = getboolfield_default(L, 1, "collisiondetection", collisiondetection); + collision_removal = getboolfield_default(L, 1, + "collision_removal", collision_removal); vertical = getboolfield_default(L, 1, "vertical", vertical); texture = getstringfield_default(L, 1, "texture", ""); playername = getstringfield_default(L, 1, "playername", ""); } - getServer(L)->spawnParticle(playername, pos, vel, acc, - expirationtime, size, collisiondetection, vertical, texture); + getServer(L)->spawnParticle(playername, pos, vel, acc, expirationtime, size, + collisiondetection, collision_removal, vertical, texture); return 1; } @@ -110,6 +115,7 @@ int ModApiParticles::l_add_particle(lua_State *L) // minexptime=, maxexptime=, // minsize=, maxsize=, // collisiondetection=, +// collision_removal=, // vertical=, // texture=, // player=}) @@ -117,6 +123,7 @@ int ModApiParticles::l_add_particle(lua_State *L) // minexptime/maxexptime = num (seconds) // minsize/maxsize = num // collisiondetection = bool +// collision_removal = bool // vertical = bool // texture = e.g."default_wood.png" int ModApiParticles::l_add_particlespawner(lua_State *L) @@ -129,8 +136,8 @@ int ModApiParticles::l_add_particlespawner(lua_State *L) minpos= maxpos= minvel= maxvel= minacc= maxacc= v3f(0, 0, 0); float time, minexptime, maxexptime, minsize, maxsize; time= minexptime= maxexptime= minsize= maxsize= 1; - bool collisiondetection, vertical; - collisiondetection= vertical= false; + bool collisiondetection, vertical, collision_removal; + collisiondetection = vertical = collision_removal = false; std::string texture = ""; std::string playername = ""; @@ -189,6 +196,8 @@ int ModApiParticles::l_add_particlespawner(lua_State *L) maxsize = getfloatfield_default(L, 1, "maxsize", maxsize); collisiondetection = getboolfield_default(L, 1, "collisiondetection", collisiondetection); + collision_removal = getboolfield_default(L, 1, + "collision_removal", collision_removal); vertical = getboolfield_default(L, 1, "vertical", vertical); texture = getstringfield_default(L, 1, "texture", ""); playername = getstringfield_default(L, 1, "playername", ""); @@ -201,6 +210,7 @@ int ModApiParticles::l_add_particlespawner(lua_State *L) minexptime, maxexptime, minsize, maxsize, collisiondetection, + collision_removal, vertical, texture, playername); lua_pushnumber(L, id); diff --git a/src/server.cpp b/src/server.cpp index a3b686c25..ada45dc68 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -1673,7 +1673,8 @@ void Server::SendShowFormspecMessage(u16 peer_id, const std::string &formspec, // Spawns a particle on peer with peer_id void Server::SendSpawnParticle(u16 peer_id, v3f pos, v3f velocity, v3f acceleration, float expirationtime, float size, bool collisiondetection, - bool vertical, std::string texture) + bool collision_removal, + bool vertical, const std::string &texture) { DSTACK(FUNCTION_NAME); @@ -1683,6 +1684,7 @@ void Server::SendSpawnParticle(u16 peer_id, v3f pos, v3f velocity, v3f accelerat << size << collisiondetection; pkt.putLongString(texture); pkt << vertical; + pkt << collision_removal; if (peer_id != PEER_ID_INEXISTENT) { Send(&pkt); @@ -1695,7 +1697,8 @@ void Server::SendSpawnParticle(u16 peer_id, v3f pos, v3f velocity, v3f accelerat // Adds a ParticleSpawner on peer with peer_id void Server::SendAddParticleSpawner(u16 peer_id, u16 amount, float spawntime, v3f minpos, v3f maxpos, v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime, - float minsize, float maxsize, bool collisiondetection, bool vertical, std::string texture, u32 id) + float minsize, float maxsize, bool collisiondetection, bool collision_removal, + bool vertical, const std::string &texture, u32 id) { DSTACK(FUNCTION_NAME); @@ -1708,6 +1711,7 @@ void Server::SendAddParticleSpawner(u16 peer_id, u16 amount, float spawntime, v3 pkt.putLongString(texture); pkt << id << vertical; + pkt << collision_removal; if (peer_id != PEER_ID_INEXISTENT) { Send(&pkt); @@ -3160,7 +3164,8 @@ void Server::notifyPlayers(const std::wstring &msg) void Server::spawnParticle(const std::string &playername, v3f pos, v3f velocity, v3f acceleration, float expirationtime, float size, bool - collisiondetection, bool vertical, const std::string &texture) + collisiondetection, bool collision_removal, + bool vertical, const std::string &texture) { // m_env will be NULL if the server is initializing if (!m_env) @@ -3175,13 +3180,15 @@ void Server::spawnParticle(const std::string &playername, v3f pos, } SendSpawnParticle(peer_id, pos, velocity, acceleration, - expirationtime, size, collisiondetection, vertical, texture); + expirationtime, size, collisiondetection, + collision_removal, vertical, texture); } u32 Server::addParticleSpawner(u16 amount, float spawntime, v3f minpos, v3f maxpos, v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime, float minsize, float maxsize, - bool collisiondetection, bool vertical, const std::string &texture, + bool collisiondetection, bool collision_removal, + bool vertical, const std::string &texture, const std::string &playername) { // m_env will be NULL if the server is initializing @@ -3200,7 +3207,7 @@ u32 Server::addParticleSpawner(u16 amount, float spawntime, SendAddParticleSpawner(peer_id, amount, spawntime, minpos, maxpos, minvel, maxvel, minacc, maxacc, minexptime, maxexptime, minsize, maxsize, - collisiondetection, vertical, texture, id); + collisiondetection, collision_removal, vertical, texture, id); return id; } diff --git a/src/server.h b/src/server.h index daf51dee1..7ee15a463 100644 --- a/src/server.h +++ b/src/server.h @@ -275,7 +275,8 @@ public: void spawnParticle(const std::string &playername, v3f pos, v3f velocity, v3f acceleration, float expirationtime, float size, - bool collisiondetection, bool vertical, const std::string &texture); + bool collisiondetection, bool collision_removal, + bool vertical, const std::string &texture); u32 addParticleSpawner(u16 amount, float spawntime, v3f minpos, v3f maxpos, @@ -283,7 +284,8 @@ public: v3f minacc, v3f maxacc, float minexptime, float maxexptime, float minsize, float maxsize, - bool collisiondetection, bool vertical, const std::string &texture, + bool collisiondetection, bool collision_removal, + bool vertical, const std::string &texture, const std::string &playername); void deleteParticleSpawner(const std::string &playername, u32 id); @@ -456,7 +458,8 @@ private: v3f minacc, v3f maxacc, float minexptime, float maxexptime, float minsize, float maxsize, - bool collisiondetection, bool vertical, std::string texture, u32 id); + bool collisiondetection, bool collision_removal, + bool vertical, const std::string &texture, u32 id); void SendDeleteParticleSpawner(u16 peer_id, u32 id); @@ -464,7 +467,8 @@ private: void SendSpawnParticle(u16 peer_id, v3f pos, v3f velocity, v3f acceleration, float expirationtime, float size, - bool collisiondetection, bool vertical, std::string texture); + bool collisiondetection, bool collision_removal, + bool vertical, const std::string &texture); u32 SendActiveObjectRemoveAdd(u16 peer_id, const std::string &datas); void SendActiveObjectMessages(u16 peer_id, const std::string &datas, bool reliable = true); -- cgit v1.2.3 From a9bc7dc405df04d5a3cb5fe5de43190200dc692d Mon Sep 17 00:00:00 2001 From: Zeno- Date: Mon, 30 May 2016 22:37:40 +1000 Subject: Remove unused code in s_security.cpp (#4172) Note that the macro CHECK_FILE_ERR implements the code removed --- src/script/cpp_api/s_security.cpp | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/script/cpp_api/s_security.cpp b/src/script/cpp_api/s_security.cpp index 730235c7b..e117a4811 100644 --- a/src/script/cpp_api/s_security.cpp +++ b/src/script/cpp_api/s_security.cpp @@ -249,8 +249,8 @@ bool ScriptApiSecurity::isSecure(lua_State *L) #define CHECK_FILE_ERR(ret, fp) \ if (ret) { \ - if (fp) std::fclose(fp); \ lua_pushfstring(L, "%s: %s", path, strerror(errno)); \ + if (fp) std::fclose(fp); \ return false; \ } @@ -291,20 +291,12 @@ bool ScriptApiSecurity::safeLoadFile(lua_State *L, const char *path) // Read the file int ret = std::fseek(fp, 0, SEEK_END); CHECK_FILE_ERR(ret, fp); - if (ret) { - std::fclose(fp); - lua_pushfstring(L, "%s: %s", path, strerror(errno)); - return false; - } + size_t size = std::ftell(fp) - start; char *code = new char[size]; ret = std::fseek(fp, start, SEEK_SET); CHECK_FILE_ERR(ret, fp); - if (ret) { - std::fclose(fp); - lua_pushfstring(L, "%s: %s", path, strerror(errno)); - return false; - } + size_t num_read = std::fread(code, 1, size, fp); if (path) { std::fclose(fp); -- cgit v1.2.3 From 27db92925261ed6646d0a1c0512711ab3aeb5fb8 Mon Sep 17 00:00:00 2001 From: est31 Date: Mon, 30 May 2016 23:27:48 +1000 Subject: Add minetest.check_password_entry callback Gives a convenient way to check a player's password. This entirely bypasses the SRP protocol, so should be used with great care. This function is not intended to be used in-game, but solely by external protocols, where no authentication of the minetest engine is provided, and also only for protocols, in which the user already gives the server the plaintext password. Examples for good use are the classical http form, or irc, an example for a bad use is a password change dialog inside formspec. Users should be aware that they lose the advantages of the SRP protocol if they enter their passwords for servers outside the normal entry box, like in in-game formspec menus, or through irc /msg s, This patch also fixes an auth.h mistake which has mixed up the order of params inside the decode_srp_verifier_and_salt function. Zeno-: Added errorstream message for invalid format when I committed --- doc/lua_api.txt | 9 +++++++++ src/script/lua_api/l_util.cpp | 30 ++++++++++++++++++++++++++++++ src/script/lua_api/l_util.h | 3 +++ src/util/auth.h | 2 +- 4 files changed, 43 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 03f2dad32..82a0acbee 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -1951,12 +1951,21 @@ Call these functions only at load time! * `minetest.notify_authentication_modified(name)` * Should be called by the authentication handler if privileges changes. * To report everybody, set `name=nil`. +* `minetest.check_password_entry(name, entry, password)` + * Returns true if the "db entry" for a player with name matches given + * password, false otherwise. + * The "db entry" is the usually player-individual value that is derived + * from the player's chosen password and stored on the server in order to allow + * authentication whenever the player desires to log in. + * Only use this function for making it possible to log in via the password from + * via protocols like IRC, other uses for inside the game are frowned upon. * `minetest.get_password_hash(name, raw_password)` * Convert a name-password pair to a password hash that Minetest can use. * The returned value alone is not a good basis for password checks based * on comparing the password hash in the database with the password hash * from the function, with an externally provided password, as the hash * in the db might use the new SRP verifier format. + * For this purpose, use minetest.check_password_entry instead. * `minetest.string_to_privs(str)`: returns `{priv1=true,...}` * `minetest.privs_to_string(privs)`: returns `"priv1,priv2,..."` * Convert between two privilege representations diff --git a/src/script/lua_api/l_util.cpp b/src/script/lua_api/l_util.cpp index e90b7fbcf..d090fc91c 100644 --- a/src/script/lua_api/l_util.cpp +++ b/src/script/lua_api/l_util.cpp @@ -246,6 +246,35 @@ int ModApiUtil::l_get_hit_params(lua_State *L) return 1; } +// check_password_entry(name, entry, password) +int ModApiUtil::l_check_password_entry(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + std::string name = luaL_checkstring(L, 1); + std::string entry = luaL_checkstring(L, 2); + std::string password = luaL_checkstring(L, 3); + + if (base64_is_valid(entry)) { + std::string hash = translate_password(name, password); + lua_pushboolean(L, hash == entry); + return 1; + } + + std::string salt; + std::string verifier; + + if (!decode_srp_verifier_and_salt(entry, &verifier, &salt)) { + // invalid format + warningstream << "Invalid password format for " << name << std::endl; + lua_pushboolean(L, false); + return 1; + } + std::string gen_verifier = generate_srp_verifier(name, password, salt); + + lua_pushboolean(L, gen_verifier == verifier); + return 1; +} + // get_password_hash(name, raw_password) int ModApiUtil::l_get_password_hash(lua_State *L) { @@ -449,6 +478,7 @@ void ModApiUtil::Initialize(lua_State *L, int top) API_FCT(get_dig_params); API_FCT(get_hit_params); + API_FCT(check_password_entry); API_FCT(get_password_hash); API_FCT(is_yes); diff --git a/src/script/lua_api/l_util.h b/src/script/lua_api/l_util.h index 779dbe281..3012d55aa 100644 --- a/src/script/lua_api/l_util.h +++ b/src/script/lua_api/l_util.h @@ -71,6 +71,9 @@ private: // get_hit_params(groups, tool_capabilities[, time_from_last_punch]) static int l_get_hit_params(lua_State *L); + // check_password_entry(name, entry, password) + static int l_check_password_entry(lua_State *L); + // get_password_hash(name, raw_password) static int l_get_password_hash(lua_State *L); diff --git a/src/util/auth.h b/src/util/auth.h index 1fd6ab453..7cdc7d74d 100644 --- a/src/util/auth.h +++ b/src/util/auth.h @@ -45,6 +45,6 @@ std::string encode_srp_verifier(const std::string &verifier, /// Reads the DB-formatted SRP verifier and gets the verifier /// and salt components. bool decode_srp_verifier_and_salt(const std::string &encoded, - std::string *salt, std::string *bytes_v); + std::string *verifier, std::string *salt); #endif -- cgit v1.2.3 From 1d40385d4aacf0cbea4b19ff06940e8c9bebaf47 Mon Sep 17 00:00:00 2001 From: TriBlade9 Date: Fri, 16 Jan 2015 14:54:26 +0800 Subject: Colored chat working as expected for both freetype and non-freetype builds. @nerzhul improvements * Add unit tests * Fix coding style * move guiChatConsole.hpp to client/ --- builtin/game/chatcommands.lua | 2 +- builtin/game/misc.lua | 17 ++ src/chat.cpp | 29 +- src/chat.h | 6 +- src/client/CMakeLists.txt | 1 + src/client/guiChatConsole.cpp | 664 ++++++++++++++++++++++++++++++++++++++++++ src/client/guiChatConsole.h | 138 +++++++++ src/game.cpp | 14 +- src/guiChatConsole.cpp | 649 ----------------------------------------- src/guiChatConsole.h | 138 --------- src/util/CMakeLists.txt | 10 + src/util/coloredstring.cpp | 68 +++++ src/util/coloredstring.h | 44 +++ src/util/statictext.cpp | 654 +++++++++++++++++++++++++++++++++++++++++ src/util/statictext.h | 150 ++++++++++ 15 files changed, 1777 insertions(+), 807 deletions(-) create mode 100644 src/client/guiChatConsole.cpp create mode 100644 src/client/guiChatConsole.h delete mode 100644 src/guiChatConsole.cpp delete mode 100644 src/guiChatConsole.h create mode 100644 src/util/coloredstring.cpp create mode 100644 src/util/coloredstring.h create mode 100644 src/util/statictext.cpp create mode 100644 src/util/statictext.h (limited to 'src') diff --git a/builtin/game/chatcommands.lua b/builtin/game/chatcommands.lua index 3350140ee..2627559a5 100644 --- a/builtin/game/chatcommands.lua +++ b/builtin/game/chatcommands.lua @@ -102,7 +102,7 @@ core.register_chatcommand("help", { description = "Get help for commands or list privileges", func = function(name, param) local function format_help_line(cmd, def) - local msg = "/"..cmd + local msg = core.colorize("00ffff", "/"..cmd) if def.params and def.params ~= "" then msg = msg .. " " .. def.params end diff --git a/builtin/game/misc.lua b/builtin/game/misc.lua index de41cfc91..8d5c80216 100644 --- a/builtin/game/misc.lua +++ b/builtin/game/misc.lua @@ -197,3 +197,20 @@ function core.http_add_fetch(httpenv) return httpenv end + +function core.get_color_escape_sequence(color) + --if string.len(color) == 3 then + -- local r = string.sub(color, 1, 1) + -- local g = string.sub(color, 2, 2) + -- local b = string.sub(color, 3, 3) + -- color = r .. r .. g .. g .. b .. b + --end + + --assert(#color == 6, "Color must be six characters in length.") + --return "\v" .. color + return "\v(color;" .. color .. ")" +end + +function core.colorize(color, message) + return core.get_color_escape_sequence(color) .. message .. core.get_color_escape_sequence("ffffff") +end diff --git a/src/chat.cpp b/src/chat.cpp index cebe31225..958389df5 100644 --- a/src/chat.cpp +++ b/src/chat.cpp @@ -19,6 +19,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "chat.h" #include "debug.h" +#include "config.h" #include "util/strfnd.h" #include #include @@ -251,8 +252,7 @@ u32 ChatBuffer::formatChatLine(const ChatLine& line, u32 cols, u32 hanging_indentation = 0; // Format the sender name and produce fragments - if (!line.name.empty()) - { + if (!line.name.empty()) { temp_frag.text = L"<"; temp_frag.column = 0; //temp_frag.bold = 0; @@ -267,28 +267,28 @@ u32 ChatBuffer::formatChatLine(const ChatLine& line, u32 cols, next_frags.push_back(temp_frag); } + std::wstring name_sanitized = removeEscapes(line.name); + // Choose an indentation level - if (line.name.empty()) - { + if (line.name.empty()) { // Server messages hanging_indentation = 0; } - else if (line.name.size() + 3 <= cols/2) - { + else if (name_sanitized.size() + 3 <= cols/2) { // Names shorter than about half the console width hanging_indentation = line.name.size() + 3; } - else - { + else { // Very long names hanging_indentation = 2; } + ColoredString line_text(line.text); next_line.first = true; bool text_processing = false; // Produce fragments and layout them into lines - while (!next_frags.empty() || in_pos < line.text.size()) + while (!next_frags.empty() || in_pos < line_text.size()) { // Layout fragments into lines while (!next_frags.empty()) @@ -326,9 +326,9 @@ u32 ChatBuffer::formatChatLine(const ChatLine& line, u32 cols, } // Produce fragment - if (in_pos < line.text.size()) + if (in_pos < line_text.size()) { - u32 remaining_in_input = line.text.size() - in_pos; + u32 remaining_in_input = line_text.size() - in_pos; u32 remaining_in_output = cols - out_column; // Determine a fragment length <= the minimum of @@ -338,14 +338,14 @@ u32 ChatBuffer::formatChatLine(const ChatLine& line, u32 cols, while (frag_length < remaining_in_input && frag_length < remaining_in_output) { - if (isspace(line.text[in_pos + frag_length])) + if (isspace(line_text[in_pos + frag_length])) space_pos = frag_length; ++frag_length; } if (space_pos != 0 && frag_length < remaining_in_input) frag_length = space_pos + 1; - temp_frag.text = line.text.substr(in_pos, frag_length); + temp_frag.text = line_text.substr(in_pos, frag_length); temp_frag.column = 0; //temp_frag.bold = 0; next_frags.push_back(temp_frag); @@ -686,9 +686,6 @@ ChatBackend::~ChatBackend() void ChatBackend::addMessage(std::wstring name, std::wstring text) { - name = unescape_enriched(name); - text = unescape_enriched(text); - // Note: A message may consist of multiple lines, for example the MOTD. WStrfnd fnd(text); while (!fnd.at_end()) diff --git a/src/chat.h b/src/chat.h index db4146d35..661cafc82 100644 --- a/src/chat.h +++ b/src/chat.h @@ -20,11 +20,13 @@ with this program; if not, write to the Free Software Foundation, Inc., #ifndef CHAT_HEADER #define CHAT_HEADER -#include "irrlichttypes.h" #include #include #include +#include "irrlichttypes.h" +#include "util/coloredstring.h" + // Chat console related classes struct ChatLine @@ -34,7 +36,7 @@ struct ChatLine // name of sending player, or empty if sent by server std::wstring name; // message text - std::wstring text; + ColoredString text; ChatLine(std::wstring a_name, std::wstring a_text): age(0.0), diff --git a/src/client/CMakeLists.txt b/src/client/CMakeLists.txt index a1ec37fe3..bcf114760 100644 --- a/src/client/CMakeLists.txt +++ b/src/client/CMakeLists.txt @@ -1,5 +1,6 @@ set(client_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/clientlauncher.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/guiChatConsole.cpp ${CMAKE_CURRENT_SOURCE_DIR}/tile.cpp PARENT_SCOPE ) diff --git a/src/client/guiChatConsole.cpp b/src/client/guiChatConsole.cpp new file mode 100644 index 000000000..d8837556a --- /dev/null +++ b/src/client/guiChatConsole.cpp @@ -0,0 +1,664 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "guiChatConsole.h" +#include "chat.h" +#include "client.h" +#include "debug.h" +#include "gettime.h" +#include "keycode.h" +#include "settings.h" +#include "porting.h" +#include "client/tile.h" +#include "fontengine.h" +#include "log.h" +#include "gettext.h" +#include + +#if USE_FREETYPE + #include "xCGUITTFont.h" +#endif + +inline u32 clamp_u8(s32 value) +{ + return (u32) MYMIN(MYMAX(value, 0), 255); +} + + +GUIChatConsole::GUIChatConsole( + gui::IGUIEnvironment* env, + gui::IGUIElement* parent, + s32 id, + ChatBackend* backend, + Client* client, + IMenuManager* menumgr +): + IGUIElement(gui::EGUIET_ELEMENT, env, parent, id, + core::rect(0,0,100,100)), + m_chat_backend(backend), + m_client(client), + m_menumgr(menumgr), + m_screensize(v2u32(0,0)), + m_animate_time_old(0), + m_open(false), + m_close_on_enter(false), + m_height(0), + m_desired_height(0), + m_desired_height_fraction(0.0), + m_height_speed(5.0), + m_open_inhibited(0), + m_cursor_blink(0.0), + m_cursor_blink_speed(0.0), + m_cursor_height(0.0), + m_background(NULL), + m_background_color(255, 0, 0, 0), + m_font(NULL), + m_fontsize(0, 0) +{ + m_animate_time_old = getTimeMs(); + + // load background settings + s32 console_alpha = g_settings->getS32("console_alpha"); + m_background_color.setAlpha(clamp_u8(console_alpha)); + + // load the background texture depending on settings + ITextureSource *tsrc = client->getTextureSource(); + if (tsrc->isKnownSourceImage("background_chat.jpg")) { + m_background = tsrc->getTexture("background_chat.jpg"); + m_background_color.setRed(255); + m_background_color.setGreen(255); + m_background_color.setBlue(255); + } else { + v3f console_color = g_settings->getV3F("console_color"); + m_background_color.setRed(clamp_u8(myround(console_color.X))); + m_background_color.setGreen(clamp_u8(myround(console_color.Y))); + m_background_color.setBlue(clamp_u8(myround(console_color.Z))); + } + + m_font = g_fontengine->getFont(FONT_SIZE_UNSPECIFIED, FM_Mono); + + if (m_font == NULL) + { + errorstream << "GUIChatConsole: Unable to load mono font "; + } + else + { + core::dimension2d dim = m_font->getDimension(L"M"); + m_fontsize = v2u32(dim.Width, dim.Height); + m_font->grab(); + } + m_fontsize.X = MYMAX(m_fontsize.X, 1); + m_fontsize.Y = MYMAX(m_fontsize.Y, 1); + + // set default cursor options + setCursor(true, true, 2.0, 0.1); +} + +GUIChatConsole::~GUIChatConsole() +{ + if (m_font) + m_font->drop(); +} + +void GUIChatConsole::openConsole(f32 height) +{ + m_open = true; + m_desired_height_fraction = height; + m_desired_height = height * m_screensize.Y; + reformatConsole(); + m_animate_time_old = getTimeMs(); + IGUIElement::setVisible(true); + Environment->setFocus(this); + m_menumgr->createdMenu(this); +} + +bool GUIChatConsole::isOpen() const +{ + return m_open; +} + +bool GUIChatConsole::isOpenInhibited() const +{ + return m_open_inhibited > 0; +} + +void GUIChatConsole::closeConsole() +{ + m_open = false; + Environment->removeFocus(this); + m_menumgr->deletingMenu(this); +} + +void GUIChatConsole::closeConsoleAtOnce() +{ + closeConsole(); + m_height = 0; + recalculateConsolePosition(); +} + +f32 GUIChatConsole::getDesiredHeight() const +{ + return m_desired_height_fraction; +} + +void GUIChatConsole::replaceAndAddToHistory(std::wstring line) +{ + ChatPrompt& prompt = m_chat_backend->getPrompt(); + prompt.addToHistory(prompt.getLine()); + prompt.replace(line); +} + + +void GUIChatConsole::setCursor( + bool visible, bool blinking, f32 blink_speed, f32 relative_height) +{ + if (visible) + { + if (blinking) + { + // leave m_cursor_blink unchanged + m_cursor_blink_speed = blink_speed; + } + else + { + m_cursor_blink = 0x8000; // on + m_cursor_blink_speed = 0.0; + } + } + else + { + m_cursor_blink = 0; // off + m_cursor_blink_speed = 0.0; + } + m_cursor_height = relative_height; +} + +void GUIChatConsole::draw() +{ + if(!IsVisible) + return; + + video::IVideoDriver* driver = Environment->getVideoDriver(); + + // Check screen size + v2u32 screensize = driver->getScreenSize(); + if (screensize != m_screensize) + { + // screen size has changed + // scale current console height to new window size + if (m_screensize.Y != 0) + m_height = m_height * screensize.Y / m_screensize.Y; + m_desired_height = m_desired_height_fraction * m_screensize.Y; + m_screensize = screensize; + reformatConsole(); + } + + // Animation + u32 now = getTimeMs(); + animate(now - m_animate_time_old); + m_animate_time_old = now; + + // Draw console elements if visible + if (m_height > 0) + { + drawBackground(); + drawText(); + drawPrompt(); + } + + gui::IGUIElement::draw(); +} + +void GUIChatConsole::reformatConsole() +{ + s32 cols = m_screensize.X / m_fontsize.X - 2; // make room for a margin (looks better) + s32 rows = m_desired_height / m_fontsize.Y - 1; // make room for the input prompt + if (cols <= 0 || rows <= 0) + cols = rows = 0; + m_chat_backend->reformat(cols, rows); +} + +void GUIChatConsole::recalculateConsolePosition() +{ + core::rect rect(0, 0, m_screensize.X, m_height); + DesiredRect = rect; + recalculateAbsolutePosition(false); +} + +void GUIChatConsole::animate(u32 msec) +{ + // animate the console height + s32 goal = m_open ? m_desired_height : 0; + + // Set invisible if close animation finished (reset by openConsole) + // This function (animate()) is never called once its visibility becomes false so do not + // actually set visible to false before the inhibited period is over + if (!m_open && m_height == 0 && m_open_inhibited == 0) + IGUIElement::setVisible(false); + + if (m_height != goal) + { + s32 max_change = msec * m_screensize.Y * (m_height_speed / 1000.0); + if (max_change == 0) + max_change = 1; + + if (m_height < goal) + { + // increase height + if (m_height + max_change < goal) + m_height += max_change; + else + m_height = goal; + } + else + { + // decrease height + if (m_height > goal + max_change) + m_height -= max_change; + else + m_height = goal; + } + + recalculateConsolePosition(); + } + + // blink the cursor + if (m_cursor_blink_speed != 0.0) + { + u32 blink_increase = 0x10000 * msec * (m_cursor_blink_speed / 1000.0); + if (blink_increase == 0) + blink_increase = 1; + m_cursor_blink = ((m_cursor_blink + blink_increase) & 0xffff); + } + + // decrease open inhibit counter + if (m_open_inhibited > msec) + m_open_inhibited -= msec; + else + m_open_inhibited = 0; +} + +void GUIChatConsole::drawBackground() +{ + video::IVideoDriver* driver = Environment->getVideoDriver(); + if (m_background != NULL) + { + core::rect sourcerect(0, -m_height, m_screensize.X, 0); + driver->draw2DImage( + m_background, + v2s32(0, 0), + sourcerect, + &AbsoluteClippingRect, + m_background_color, + false); + } + else + { + driver->draw2DRectangle( + m_background_color, + core::rect(0, 0, m_screensize.X, m_height), + &AbsoluteClippingRect); + } +} + +void GUIChatConsole::drawText() +{ + if (m_font == NULL) + return; + + ChatBuffer& buf = m_chat_backend->getConsoleBuffer(); + for (u32 row = 0; row < buf.getRows(); ++row) + { + const ChatFormattedLine& line = buf.getFormattedLine(row); + if (line.fragments.empty()) + continue; + + s32 line_height = m_fontsize.Y; + s32 y = row * line_height + m_height - m_desired_height; + if (y + line_height < 0) + continue; + + for (u32 i = 0; i < line.fragments.size(); ++i) + { + const ChatFormattedFragment& fragment = line.fragments[i]; + s32 x = (fragment.column + 1) * m_fontsize.X; + core::rect destrect( + x, y, x + m_fontsize.X * fragment.text.size(), y + m_fontsize.Y); + + + #if USE_FREETYPE + // Draw colored text if FreeType is enabled + irr::gui::CGUITTFont *tmp = static_cast(m_font); + tmp->draw( + fragment.text.c_str(), + destrect, + fragment.text.getColors(), + false, + false, + &AbsoluteClippingRect); + #else + // Otherwise use standard text + m_font->draw( + fragment.text.c_str(), + destrect, + video::SColor(255, 255, 255, 255), + false, + false, + &AbsoluteClippingRect); + #endif + } + } +} + +void GUIChatConsole::drawPrompt() +{ + if (m_font == NULL) + return; + + u32 row = m_chat_backend->getConsoleBuffer().getRows(); + s32 line_height = m_fontsize.Y; + s32 y = row * line_height + m_height - m_desired_height; + + ChatPrompt& prompt = m_chat_backend->getPrompt(); + std::wstring prompt_text = prompt.getVisiblePortion(); + + // FIXME Draw string at once, not character by character + // That will only work with the cursor once we have a monospace font + for (u32 i = 0; i < prompt_text.size(); ++i) + { + wchar_t ws[2] = {prompt_text[i], 0}; + s32 x = (1 + i) * m_fontsize.X; + core::rect destrect( + x, y, x + m_fontsize.X, y + m_fontsize.Y); + m_font->draw( + ws, + destrect, + video::SColor(255, 255, 255, 255), + false, + false, + &AbsoluteClippingRect); + } + + // Draw the cursor during on periods + if ((m_cursor_blink & 0x8000) != 0) + { + s32 cursor_pos = prompt.getVisibleCursorPosition(); + if (cursor_pos >= 0) + { + s32 cursor_len = prompt.getCursorLength(); + video::IVideoDriver* driver = Environment->getVideoDriver(); + s32 x = (1 + cursor_pos) * m_fontsize.X; + core::rect destrect( + x, + y + m_fontsize.Y * (1.0 - m_cursor_height), + x + m_fontsize.X * MYMAX(cursor_len, 1), + y + m_fontsize.Y * (cursor_len ? m_cursor_height+1 : 1) + ); + video::SColor cursor_color(255,255,255,255); + driver->draw2DRectangle( + cursor_color, + destrect, + &AbsoluteClippingRect); + } + } + +} + +bool GUIChatConsole::OnEvent(const SEvent& event) +{ + + ChatPrompt &prompt = m_chat_backend->getPrompt(); + + if(event.EventType == EET_KEY_INPUT_EVENT && event.KeyInput.PressedDown) + { + // Key input + if(KeyPress(event.KeyInput) == getKeySetting("keymap_console")) + { + closeConsole(); + + // inhibit open so the_game doesn't reopen immediately + m_open_inhibited = 50; + m_close_on_enter = false; + return true; + } + else if(event.KeyInput.Key == KEY_ESCAPE) + { + closeConsoleAtOnce(); + m_close_on_enter = false; + // inhibit open so the_game doesn't reopen immediately + m_open_inhibited = 1; // so the ESCAPE button doesn't open the "pause menu" + return true; + } + else if(event.KeyInput.Key == KEY_PRIOR) + { + m_chat_backend->scrollPageUp(); + return true; + } + else if(event.KeyInput.Key == KEY_NEXT) + { + m_chat_backend->scrollPageDown(); + return true; + } + else if(event.KeyInput.Key == KEY_RETURN) + { + prompt.addToHistory(prompt.getLine()); + std::wstring text = prompt.replace(L""); + m_client->typeChatMessage(text); + if (m_close_on_enter) { + closeConsoleAtOnce(); + m_close_on_enter = false; + } + return true; + } + else if(event.KeyInput.Key == KEY_UP) + { + // Up pressed + // Move back in history + prompt.historyPrev(); + return true; + } + else if(event.KeyInput.Key == KEY_DOWN) + { + // Down pressed + // Move forward in history + prompt.historyNext(); + return true; + } + else if(event.KeyInput.Key == KEY_LEFT || event.KeyInput.Key == KEY_RIGHT) + { + // Left/right pressed + // Move/select character/word to the left depending on control and shift keys + ChatPrompt::CursorOp op = event.KeyInput.Shift ? + ChatPrompt::CURSOROP_SELECT : + ChatPrompt::CURSOROP_MOVE; + ChatPrompt::CursorOpDir dir = event.KeyInput.Key == KEY_LEFT ? + ChatPrompt::CURSOROP_DIR_LEFT : + ChatPrompt::CURSOROP_DIR_RIGHT; + ChatPrompt::CursorOpScope scope = event.KeyInput.Control ? + ChatPrompt::CURSOROP_SCOPE_WORD : + ChatPrompt::CURSOROP_SCOPE_CHARACTER; + prompt.cursorOperation(op, dir, scope); + return true; + } + else if(event.KeyInput.Key == KEY_HOME) + { + // Home pressed + // move to beginning of line + prompt.cursorOperation( + ChatPrompt::CURSOROP_MOVE, + ChatPrompt::CURSOROP_DIR_LEFT, + ChatPrompt::CURSOROP_SCOPE_LINE); + return true; + } + else if(event.KeyInput.Key == KEY_END) + { + // End pressed + // move to end of line + prompt.cursorOperation( + ChatPrompt::CURSOROP_MOVE, + ChatPrompt::CURSOROP_DIR_RIGHT, + ChatPrompt::CURSOROP_SCOPE_LINE); + return true; + } + else if(event.KeyInput.Key == KEY_BACK) + { + // Backspace or Ctrl-Backspace pressed + // delete character / word to the left + ChatPrompt::CursorOpScope scope = + event.KeyInput.Control ? + ChatPrompt::CURSOROP_SCOPE_WORD : + ChatPrompt::CURSOROP_SCOPE_CHARACTER; + prompt.cursorOperation( + ChatPrompt::CURSOROP_DELETE, + ChatPrompt::CURSOROP_DIR_LEFT, + scope); + return true; + } + else if(event.KeyInput.Key == KEY_DELETE) + { + // Delete or Ctrl-Delete pressed + // delete character / word to the right + ChatPrompt::CursorOpScope scope = + event.KeyInput.Control ? + ChatPrompt::CURSOROP_SCOPE_WORD : + ChatPrompt::CURSOROP_SCOPE_CHARACTER; + prompt.cursorOperation( + ChatPrompt::CURSOROP_DELETE, + ChatPrompt::CURSOROP_DIR_RIGHT, + scope); + return true; + } + else if(event.KeyInput.Key == KEY_KEY_A && event.KeyInput.Control) + { + // Ctrl-A pressed + // Select all text + prompt.cursorOperation( + ChatPrompt::CURSOROP_SELECT, + ChatPrompt::CURSOROP_DIR_LEFT, // Ignored + ChatPrompt::CURSOROP_SCOPE_LINE); + return true; + } + else if(event.KeyInput.Key == KEY_KEY_C && event.KeyInput.Control) + { + // Ctrl-C pressed + // Copy text to clipboard + if (prompt.getCursorLength() <= 0) + return true; + std::wstring wselected = prompt.getSelection(); + std::string selected(wselected.begin(), wselected.end()); + Environment->getOSOperator()->copyToClipboard(selected.c_str()); + return true; + } + else if(event.KeyInput.Key == KEY_KEY_V && event.KeyInput.Control) + { + // Ctrl-V pressed + // paste text from clipboard + if (prompt.getCursorLength() > 0) { + // Delete selected section of text + prompt.cursorOperation( + ChatPrompt::CURSOROP_DELETE, + ChatPrompt::CURSOROP_DIR_LEFT, // Ignored + ChatPrompt::CURSOROP_SCOPE_SELECTION); + } + IOSOperator *os_operator = Environment->getOSOperator(); + const c8 *text = os_operator->getTextFromClipboard(); + if (!text) + return true; + std::basic_string str((const unsigned char*)text); + prompt.input(std::wstring(str.begin(), str.end())); + return true; + } + else if(event.KeyInput.Key == KEY_KEY_X && event.KeyInput.Control) + { + // Ctrl-X pressed + // Cut text to clipboard + if (prompt.getCursorLength() <= 0) + return true; + std::wstring wselected = prompt.getSelection(); + std::string selected(wselected.begin(), wselected.end()); + Environment->getOSOperator()->copyToClipboard(selected.c_str()); + prompt.cursorOperation( + ChatPrompt::CURSOROP_DELETE, + ChatPrompt::CURSOROP_DIR_LEFT, // Ignored + ChatPrompt::CURSOROP_SCOPE_SELECTION); + return true; + } + else if(event.KeyInput.Key == KEY_KEY_U && event.KeyInput.Control) + { + // Ctrl-U pressed + // kill line to left end + prompt.cursorOperation( + ChatPrompt::CURSOROP_DELETE, + ChatPrompt::CURSOROP_DIR_LEFT, + ChatPrompt::CURSOROP_SCOPE_LINE); + return true; + } + else if(event.KeyInput.Key == KEY_KEY_K && event.KeyInput.Control) + { + // Ctrl-K pressed + // kill line to right end + prompt.cursorOperation( + ChatPrompt::CURSOROP_DELETE, + ChatPrompt::CURSOROP_DIR_RIGHT, + ChatPrompt::CURSOROP_SCOPE_LINE); + return true; + } + else if(event.KeyInput.Key == KEY_TAB) + { + // Tab or Shift-Tab pressed + // Nick completion + std::list names = m_client->getConnectedPlayerNames(); + bool backwards = event.KeyInput.Shift; + prompt.nickCompletion(names, backwards); + return true; + } + else if(event.KeyInput.Char != 0 && !event.KeyInput.Control) + { + #if (defined(linux) || defined(__linux)) + wchar_t wc = L'_'; + mbtowc( &wc, (char *) &event.KeyInput.Char, sizeof(event.KeyInput.Char) ); + prompt.input(wc); + #else + prompt.input(event.KeyInput.Char); + #endif + return true; + } + } + else if(event.EventType == EET_MOUSE_INPUT_EVENT) + { + if(event.MouseInput.Event == EMIE_MOUSE_WHEEL) + { + s32 rows = myround(-3.0 * event.MouseInput.Wheel); + m_chat_backend->scroll(rows); + } + } + + return Parent ? Parent->OnEvent(event) : false; +} + +void GUIChatConsole::setVisible(bool visible) +{ + m_open = visible; + IGUIElement::setVisible(visible); + if (!visible) { + m_height = 0; + recalculateConsolePosition(); + } +} + diff --git a/src/client/guiChatConsole.h b/src/client/guiChatConsole.h new file mode 100644 index 000000000..3013a1d31 --- /dev/null +++ b/src/client/guiChatConsole.h @@ -0,0 +1,138 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef GUICHATCONSOLE_HEADER +#define GUICHATCONSOLE_HEADER + +#include "irrlichttypes_extrabloated.h" +#include "modalMenu.h" +#include "chat.h" +#include "config.h" + +class Client; + +class GUIChatConsole : public gui::IGUIElement +{ +public: + GUIChatConsole(gui::IGUIEnvironment* env, + gui::IGUIElement* parent, + s32 id, + ChatBackend* backend, + Client* client, + IMenuManager* menumgr); + virtual ~GUIChatConsole(); + + // Open the console (height = desired fraction of screen size) + // This doesn't open immediately but initiates an animation. + // You should call isOpenInhibited() before this. + void openConsole(f32 height); + + bool isOpen() const; + + // Check if the console should not be opened at the moment + // This is to avoid reopening the console immediately after closing + bool isOpenInhibited() const; + // Close the console, equivalent to openConsole(0). + // This doesn't close immediately but initiates an animation. + void closeConsole(); + // Close the console immediately, without animation. + void closeConsoleAtOnce(); + // Set whether to close the console after the user presses enter. + void setCloseOnEnter(bool close) { m_close_on_enter = close; } + + // Return the desired height (fraction of screen size) + // Zero if the console is closed or getting closed + f32 getDesiredHeight() const; + + // Replace actual line when adding the actual to the history (if there is any) + void replaceAndAddToHistory(std::wstring line); + + // Change how the cursor looks + void setCursor( + bool visible, + bool blinking = false, + f32 blink_speed = 1.0, + f32 relative_height = 1.0); + + // Irrlicht draw method + virtual void draw(); + + bool canTakeFocus(gui::IGUIElement* element) { return false; } + + virtual bool OnEvent(const SEvent& event); + + virtual void setVisible(bool visible); + +private: + void reformatConsole(); + void recalculateConsolePosition(); + + // These methods are called by draw + void animate(u32 msec); + void drawBackground(); + void drawText(); + void drawPrompt(); + +private: + ChatBackend* m_chat_backend; + Client* m_client; + IMenuManager* m_menumgr; + + // current screen size + v2u32 m_screensize; + + // used to compute how much time passed since last animate() + u32 m_animate_time_old; + + // should the console be opened or closed? + bool m_open; + // should it close after you press enter? + bool m_close_on_enter; + // current console height [pixels] + s32 m_height; + // desired height [pixels] + f32 m_desired_height; + // desired height [screen height fraction] + f32 m_desired_height_fraction; + // console open/close animation speed [screen height fraction / second] + f32 m_height_speed; + // if nonzero, opening the console is inhibited [milliseconds] + u32 m_open_inhibited; + + // cursor blink frame (16-bit value) + // cursor is off during [0,32767] and on during [32768,65535] + u32 m_cursor_blink; + // cursor blink speed [on/off toggles / second] + f32 m_cursor_blink_speed; + // cursor height [line height] + f32 m_cursor_height; + + // background texture + video::ITexture* m_background; + // background color (including alpha) + video::SColor m_background_color; + + // font + gui::IGUIFont* m_font; + v2u32 m_fontsize; +}; + + +#endif + diff --git a/src/game.cpp b/src/game.cpp index c5211a042..71a04aef5 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -34,7 +34,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "log.h" #include "filesys.h" #include "gettext.h" -#include "guiChatConsole.h" +#include "client/guiChatConsole.h" #include "guiFormSpecMenu.h" #include "guiKeyChangeMenu.h" #include "guiPasswordChange.h" @@ -59,6 +59,10 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "minimap.h" #include "mapblock_mesh.h" +#if USE_FREETYPE + #include "util/statictext.h" +#endif + #include "sound.h" #if USE_SOUND @@ -2239,12 +2243,20 @@ bool Game::initGui() false, false, guiroot); guitext_status->setVisible(false); +#if USE_FREETYPE + // Colored chat support when using FreeType + guitext_chat = new gui::StaticText(L"", false, guienv, guiroot, -1, core::rect(0, 0, 0, 0), false); + guitext_chat->setWordWrap(true); + guitext_chat->drop(); +#else + // Standard chat when FreeType is disabled // Chat text guitext_chat = guienv->addStaticText( L"", core::rect(0, 0, 0, 0), //false, false); // Disable word wrap as of now false, true, guiroot); +#endif // Remove stale "recent" chat messages from previous connections chat_backend->clearRecentChat(); diff --git a/src/guiChatConsole.cpp b/src/guiChatConsole.cpp deleted file mode 100644 index 17a1689c7..000000000 --- a/src/guiChatConsole.cpp +++ /dev/null @@ -1,649 +0,0 @@ -/* -Minetest -Copyright (C) 2013 celeron55, Perttu Ahola - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation; either version 2.1 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License along -with this program; if not, write to the Free Software Foundation, Inc., -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -*/ - -#include "guiChatConsole.h" -#include "chat.h" -#include "client.h" -#include "debug.h" -#include "gettime.h" -#include "keycode.h" -#include "settings.h" -#include "porting.h" -#include "client/tile.h" -#include "fontengine.h" -#include "log.h" -#include "gettext.h" -#include - -#if USE_FREETYPE -#include "xCGUITTFont.h" -#endif - -inline u32 clamp_u8(s32 value) -{ - return (u32) MYMIN(MYMAX(value, 0), 255); -} - - -GUIChatConsole::GUIChatConsole( - gui::IGUIEnvironment* env, - gui::IGUIElement* parent, - s32 id, - ChatBackend* backend, - Client* client, - IMenuManager* menumgr -): - IGUIElement(gui::EGUIET_ELEMENT, env, parent, id, - core::rect(0,0,100,100)), - m_chat_backend(backend), - m_client(client), - m_menumgr(menumgr), - m_screensize(v2u32(0,0)), - m_animate_time_old(0), - m_open(false), - m_close_on_enter(false), - m_height(0), - m_desired_height(0), - m_desired_height_fraction(0.0), - m_height_speed(5.0), - m_open_inhibited(0), - m_cursor_blink(0.0), - m_cursor_blink_speed(0.0), - m_cursor_height(0.0), - m_background(NULL), - m_background_color(255, 0, 0, 0), - m_font(NULL), - m_fontsize(0, 0) -{ - m_animate_time_old = getTimeMs(); - - // load background settings - s32 console_alpha = g_settings->getS32("console_alpha"); - m_background_color.setAlpha(clamp_u8(console_alpha)); - - // load the background texture depending on settings - ITextureSource *tsrc = client->getTextureSource(); - if (tsrc->isKnownSourceImage("background_chat.jpg")) { - m_background = tsrc->getTexture("background_chat.jpg"); - m_background_color.setRed(255); - m_background_color.setGreen(255); - m_background_color.setBlue(255); - } else { - v3f console_color = g_settings->getV3F("console_color"); - m_background_color.setRed(clamp_u8(myround(console_color.X))); - m_background_color.setGreen(clamp_u8(myround(console_color.Y))); - m_background_color.setBlue(clamp_u8(myround(console_color.Z))); - } - - m_font = g_fontengine->getFont(FONT_SIZE_UNSPECIFIED, FM_Mono); - - if (m_font == NULL) - { - errorstream << "GUIChatConsole: Unable to load mono font "; - } - else - { - core::dimension2d dim = m_font->getDimension(L"M"); - m_fontsize = v2u32(dim.Width, dim.Height); - m_font->grab(); - } - m_fontsize.X = MYMAX(m_fontsize.X, 1); - m_fontsize.Y = MYMAX(m_fontsize.Y, 1); - - // set default cursor options - setCursor(true, true, 2.0, 0.1); -} - -GUIChatConsole::~GUIChatConsole() -{ - if (m_font) - m_font->drop(); -} - -void GUIChatConsole::openConsole(f32 height) -{ - m_open = true; - m_desired_height_fraction = height; - m_desired_height = height * m_screensize.Y; - reformatConsole(); - m_animate_time_old = getTimeMs(); - IGUIElement::setVisible(true); - Environment->setFocus(this); - m_menumgr->createdMenu(this); -} - -bool GUIChatConsole::isOpen() const -{ - return m_open; -} - -bool GUIChatConsole::isOpenInhibited() const -{ - return m_open_inhibited > 0; -} - -void GUIChatConsole::closeConsole() -{ - m_open = false; - Environment->removeFocus(this); - m_menumgr->deletingMenu(this); -} - -void GUIChatConsole::closeConsoleAtOnce() -{ - closeConsole(); - m_height = 0; - recalculateConsolePosition(); -} - -f32 GUIChatConsole::getDesiredHeight() const -{ - return m_desired_height_fraction; -} - -void GUIChatConsole::replaceAndAddToHistory(std::wstring line) -{ - ChatPrompt& prompt = m_chat_backend->getPrompt(); - prompt.addToHistory(prompt.getLine()); - prompt.replace(line); -} - - -void GUIChatConsole::setCursor( - bool visible, bool blinking, f32 blink_speed, f32 relative_height) -{ - if (visible) - { - if (blinking) - { - // leave m_cursor_blink unchanged - m_cursor_blink_speed = blink_speed; - } - else - { - m_cursor_blink = 0x8000; // on - m_cursor_blink_speed = 0.0; - } - } - else - { - m_cursor_blink = 0; // off - m_cursor_blink_speed = 0.0; - } - m_cursor_height = relative_height; -} - -void GUIChatConsole::draw() -{ - if(!IsVisible) - return; - - video::IVideoDriver* driver = Environment->getVideoDriver(); - - // Check screen size - v2u32 screensize = driver->getScreenSize(); - if (screensize != m_screensize) - { - // screen size has changed - // scale current console height to new window size - if (m_screensize.Y != 0) - m_height = m_height * screensize.Y / m_screensize.Y; - m_desired_height = m_desired_height_fraction * m_screensize.Y; - m_screensize = screensize; - reformatConsole(); - } - - // Animation - u32 now = getTimeMs(); - animate(now - m_animate_time_old); - m_animate_time_old = now; - - // Draw console elements if visible - if (m_height > 0) - { - drawBackground(); - drawText(); - drawPrompt(); - } - - gui::IGUIElement::draw(); -} - -void GUIChatConsole::reformatConsole() -{ - s32 cols = m_screensize.X / m_fontsize.X - 2; // make room for a margin (looks better) - s32 rows = m_desired_height / m_fontsize.Y - 1; // make room for the input prompt - if (cols <= 0 || rows <= 0) - cols = rows = 0; - m_chat_backend->reformat(cols, rows); -} - -void GUIChatConsole::recalculateConsolePosition() -{ - core::rect rect(0, 0, m_screensize.X, m_height); - DesiredRect = rect; - recalculateAbsolutePosition(false); -} - -void GUIChatConsole::animate(u32 msec) -{ - // animate the console height - s32 goal = m_open ? m_desired_height : 0; - - // Set invisible if close animation finished (reset by openConsole) - // This function (animate()) is never called once its visibility becomes false so do not - // actually set visible to false before the inhibited period is over - if (!m_open && m_height == 0 && m_open_inhibited == 0) - IGUIElement::setVisible(false); - - if (m_height != goal) - { - s32 max_change = msec * m_screensize.Y * (m_height_speed / 1000.0); - if (max_change == 0) - max_change = 1; - - if (m_height < goal) - { - // increase height - if (m_height + max_change < goal) - m_height += max_change; - else - m_height = goal; - } - else - { - // decrease height - if (m_height > goal + max_change) - m_height -= max_change; - else - m_height = goal; - } - - recalculateConsolePosition(); - } - - // blink the cursor - if (m_cursor_blink_speed != 0.0) - { - u32 blink_increase = 0x10000 * msec * (m_cursor_blink_speed / 1000.0); - if (blink_increase == 0) - blink_increase = 1; - m_cursor_blink = ((m_cursor_blink + blink_increase) & 0xffff); - } - - // decrease open inhibit counter - if (m_open_inhibited > msec) - m_open_inhibited -= msec; - else - m_open_inhibited = 0; -} - -void GUIChatConsole::drawBackground() -{ - video::IVideoDriver* driver = Environment->getVideoDriver(); - if (m_background != NULL) - { - core::rect sourcerect(0, -m_height, m_screensize.X, 0); - driver->draw2DImage( - m_background, - v2s32(0, 0), - sourcerect, - &AbsoluteClippingRect, - m_background_color, - false); - } - else - { - driver->draw2DRectangle( - m_background_color, - core::rect(0, 0, m_screensize.X, m_height), - &AbsoluteClippingRect); - } -} - -void GUIChatConsole::drawText() -{ - if (m_font == NULL) - return; - - ChatBuffer& buf = m_chat_backend->getConsoleBuffer(); - for (u32 row = 0; row < buf.getRows(); ++row) - { - const ChatFormattedLine& line = buf.getFormattedLine(row); - if (line.fragments.empty()) - continue; - - s32 line_height = m_fontsize.Y; - s32 y = row * line_height + m_height - m_desired_height; - if (y + line_height < 0) - continue; - - for (u32 i = 0; i < line.fragments.size(); ++i) - { - const ChatFormattedFragment& fragment = line.fragments[i]; - s32 x = (fragment.column + 1) * m_fontsize.X; - core::rect destrect( - x, y, x + m_fontsize.X * fragment.text.size(), y + m_fontsize.Y); - m_font->draw( - fragment.text.c_str(), - destrect, - video::SColor(255, 255, 255, 255), - false, - false, - &AbsoluteClippingRect); - } - } -} - -void GUIChatConsole::drawPrompt() -{ - if (m_font == NULL) - return; - - u32 row = m_chat_backend->getConsoleBuffer().getRows(); - s32 line_height = m_fontsize.Y; - s32 y = row * line_height + m_height - m_desired_height; - - ChatPrompt& prompt = m_chat_backend->getPrompt(); - std::wstring prompt_text = prompt.getVisiblePortion(); - - // FIXME Draw string at once, not character by character - // That will only work with the cursor once we have a monospace font - for (u32 i = 0; i < prompt_text.size(); ++i) - { - wchar_t ws[2] = {prompt_text[i], 0}; - s32 x = (1 + i) * m_fontsize.X; - core::rect destrect( - x, y, x + m_fontsize.X, y + m_fontsize.Y); - m_font->draw( - ws, - destrect, - video::SColor(255, 255, 255, 255), - false, - false, - &AbsoluteClippingRect); - } - - // Draw the cursor during on periods - if ((m_cursor_blink & 0x8000) != 0) - { - s32 cursor_pos = prompt.getVisibleCursorPosition(); - if (cursor_pos >= 0) - { - s32 cursor_len = prompt.getCursorLength(); - video::IVideoDriver* driver = Environment->getVideoDriver(); - s32 x = (1 + cursor_pos) * m_fontsize.X; - core::rect destrect( - x, - y + m_fontsize.Y * (1.0 - m_cursor_height), - x + m_fontsize.X * MYMAX(cursor_len, 1), - y + m_fontsize.Y * (cursor_len ? m_cursor_height+1 : 1) - ); - video::SColor cursor_color(255,255,255,255); - driver->draw2DRectangle( - cursor_color, - destrect, - &AbsoluteClippingRect); - } - } - -} - -bool GUIChatConsole::OnEvent(const SEvent& event) -{ - - ChatPrompt &prompt = m_chat_backend->getPrompt(); - - if(event.EventType == EET_KEY_INPUT_EVENT && event.KeyInput.PressedDown) - { - // Key input - if(KeyPress(event.KeyInput) == getKeySetting("keymap_console")) - { - closeConsole(); - - // inhibit open so the_game doesn't reopen immediately - m_open_inhibited = 50; - m_close_on_enter = false; - return true; - } - else if(event.KeyInput.Key == KEY_ESCAPE) - { - closeConsoleAtOnce(); - m_close_on_enter = false; - // inhibit open so the_game doesn't reopen immediately - m_open_inhibited = 1; // so the ESCAPE button doesn't open the "pause menu" - return true; - } - else if(event.KeyInput.Key == KEY_PRIOR) - { - m_chat_backend->scrollPageUp(); - return true; - } - else if(event.KeyInput.Key == KEY_NEXT) - { - m_chat_backend->scrollPageDown(); - return true; - } - else if(event.KeyInput.Key == KEY_RETURN) - { - prompt.addToHistory(prompt.getLine()); - std::wstring text = prompt.replace(L""); - m_client->typeChatMessage(text); - if (m_close_on_enter) { - closeConsoleAtOnce(); - m_close_on_enter = false; - } - return true; - } - else if(event.KeyInput.Key == KEY_UP) - { - // Up pressed - // Move back in history - prompt.historyPrev(); - return true; - } - else if(event.KeyInput.Key == KEY_DOWN) - { - // Down pressed - // Move forward in history - prompt.historyNext(); - return true; - } - else if(event.KeyInput.Key == KEY_LEFT || event.KeyInput.Key == KEY_RIGHT) - { - // Left/right pressed - // Move/select character/word to the left depending on control and shift keys - ChatPrompt::CursorOp op = event.KeyInput.Shift ? - ChatPrompt::CURSOROP_SELECT : - ChatPrompt::CURSOROP_MOVE; - ChatPrompt::CursorOpDir dir = event.KeyInput.Key == KEY_LEFT ? - ChatPrompt::CURSOROP_DIR_LEFT : - ChatPrompt::CURSOROP_DIR_RIGHT; - ChatPrompt::CursorOpScope scope = event.KeyInput.Control ? - ChatPrompt::CURSOROP_SCOPE_WORD : - ChatPrompt::CURSOROP_SCOPE_CHARACTER; - prompt.cursorOperation(op, dir, scope); - return true; - } - else if(event.KeyInput.Key == KEY_HOME) - { - // Home pressed - // move to beginning of line - prompt.cursorOperation( - ChatPrompt::CURSOROP_MOVE, - ChatPrompt::CURSOROP_DIR_LEFT, - ChatPrompt::CURSOROP_SCOPE_LINE); - return true; - } - else if(event.KeyInput.Key == KEY_END) - { - // End pressed - // move to end of line - prompt.cursorOperation( - ChatPrompt::CURSOROP_MOVE, - ChatPrompt::CURSOROP_DIR_RIGHT, - ChatPrompt::CURSOROP_SCOPE_LINE); - return true; - } - else if(event.KeyInput.Key == KEY_BACK) - { - // Backspace or Ctrl-Backspace pressed - // delete character / word to the left - ChatPrompt::CursorOpScope scope = - event.KeyInput.Control ? - ChatPrompt::CURSOROP_SCOPE_WORD : - ChatPrompt::CURSOROP_SCOPE_CHARACTER; - prompt.cursorOperation( - ChatPrompt::CURSOROP_DELETE, - ChatPrompt::CURSOROP_DIR_LEFT, - scope); - return true; - } - else if(event.KeyInput.Key == KEY_DELETE) - { - // Delete or Ctrl-Delete pressed - // delete character / word to the right - ChatPrompt::CursorOpScope scope = - event.KeyInput.Control ? - ChatPrompt::CURSOROP_SCOPE_WORD : - ChatPrompt::CURSOROP_SCOPE_CHARACTER; - prompt.cursorOperation( - ChatPrompt::CURSOROP_DELETE, - ChatPrompt::CURSOROP_DIR_RIGHT, - scope); - return true; - } - else if(event.KeyInput.Key == KEY_KEY_A && event.KeyInput.Control) - { - // Ctrl-A pressed - // Select all text - prompt.cursorOperation( - ChatPrompt::CURSOROP_SELECT, - ChatPrompt::CURSOROP_DIR_LEFT, // Ignored - ChatPrompt::CURSOROP_SCOPE_LINE); - return true; - } - else if(event.KeyInput.Key == KEY_KEY_C && event.KeyInput.Control) - { - // Ctrl-C pressed - // Copy text to clipboard - if (prompt.getCursorLength() <= 0) - return true; - std::wstring wselected = prompt.getSelection(); - std::string selected(wselected.begin(), wselected.end()); - Environment->getOSOperator()->copyToClipboard(selected.c_str()); - return true; - } - else if(event.KeyInput.Key == KEY_KEY_V && event.KeyInput.Control) - { - // Ctrl-V pressed - // paste text from clipboard - if (prompt.getCursorLength() > 0) { - // Delete selected section of text - prompt.cursorOperation( - ChatPrompt::CURSOROP_DELETE, - ChatPrompt::CURSOROP_DIR_LEFT, // Ignored - ChatPrompt::CURSOROP_SCOPE_SELECTION); - } - IOSOperator *os_operator = Environment->getOSOperator(); - const c8 *text = os_operator->getTextFromClipboard(); - if (!text) - return true; - std::basic_string str((const unsigned char*)text); - prompt.input(std::wstring(str.begin(), str.end())); - return true; - } - else if(event.KeyInput.Key == KEY_KEY_X && event.KeyInput.Control) - { - // Ctrl-X pressed - // Cut text to clipboard - if (prompt.getCursorLength() <= 0) - return true; - std::wstring wselected = prompt.getSelection(); - std::string selected(wselected.begin(), wselected.end()); - Environment->getOSOperator()->copyToClipboard(selected.c_str()); - prompt.cursorOperation( - ChatPrompt::CURSOROP_DELETE, - ChatPrompt::CURSOROP_DIR_LEFT, // Ignored - ChatPrompt::CURSOROP_SCOPE_SELECTION); - return true; - } - else if(event.KeyInput.Key == KEY_KEY_U && event.KeyInput.Control) - { - // Ctrl-U pressed - // kill line to left end - prompt.cursorOperation( - ChatPrompt::CURSOROP_DELETE, - ChatPrompt::CURSOROP_DIR_LEFT, - ChatPrompt::CURSOROP_SCOPE_LINE); - return true; - } - else if(event.KeyInput.Key == KEY_KEY_K && event.KeyInput.Control) - { - // Ctrl-K pressed - // kill line to right end - prompt.cursorOperation( - ChatPrompt::CURSOROP_DELETE, - ChatPrompt::CURSOROP_DIR_RIGHT, - ChatPrompt::CURSOROP_SCOPE_LINE); - return true; - } - else if(event.KeyInput.Key == KEY_TAB) - { - // Tab or Shift-Tab pressed - // Nick completion - std::list names = m_client->getConnectedPlayerNames(); - bool backwards = event.KeyInput.Shift; - prompt.nickCompletion(names, backwards); - return true; - } - else if(event.KeyInput.Char != 0 && !event.KeyInput.Control) - { - #if (defined(linux) || defined(__linux)) - wchar_t wc = L'_'; - mbtowc( &wc, (char *) &event.KeyInput.Char, sizeof(event.KeyInput.Char) ); - prompt.input(wc); - #else - prompt.input(event.KeyInput.Char); - #endif - return true; - } - } - else if(event.EventType == EET_MOUSE_INPUT_EVENT) - { - if(event.MouseInput.Event == EMIE_MOUSE_WHEEL) - { - s32 rows = myround(-3.0 * event.MouseInput.Wheel); - m_chat_backend->scroll(rows); - } - } - - return Parent ? Parent->OnEvent(event) : false; -} - -void GUIChatConsole::setVisible(bool visible) -{ - m_open = visible; - IGUIElement::setVisible(visible); - if (!visible) { - m_height = 0; - recalculateConsolePosition(); - } -} - diff --git a/src/guiChatConsole.h b/src/guiChatConsole.h deleted file mode 100644 index 3013a1d31..000000000 --- a/src/guiChatConsole.h +++ /dev/null @@ -1,138 +0,0 @@ -/* -Minetest -Copyright (C) 2013 celeron55, Perttu Ahola - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation; either version 2.1 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License along -with this program; if not, write to the Free Software Foundation, Inc., -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -*/ - -#ifndef GUICHATCONSOLE_HEADER -#define GUICHATCONSOLE_HEADER - -#include "irrlichttypes_extrabloated.h" -#include "modalMenu.h" -#include "chat.h" -#include "config.h" - -class Client; - -class GUIChatConsole : public gui::IGUIElement -{ -public: - GUIChatConsole(gui::IGUIEnvironment* env, - gui::IGUIElement* parent, - s32 id, - ChatBackend* backend, - Client* client, - IMenuManager* menumgr); - virtual ~GUIChatConsole(); - - // Open the console (height = desired fraction of screen size) - // This doesn't open immediately but initiates an animation. - // You should call isOpenInhibited() before this. - void openConsole(f32 height); - - bool isOpen() const; - - // Check if the console should not be opened at the moment - // This is to avoid reopening the console immediately after closing - bool isOpenInhibited() const; - // Close the console, equivalent to openConsole(0). - // This doesn't close immediately but initiates an animation. - void closeConsole(); - // Close the console immediately, without animation. - void closeConsoleAtOnce(); - // Set whether to close the console after the user presses enter. - void setCloseOnEnter(bool close) { m_close_on_enter = close; } - - // Return the desired height (fraction of screen size) - // Zero if the console is closed or getting closed - f32 getDesiredHeight() const; - - // Replace actual line when adding the actual to the history (if there is any) - void replaceAndAddToHistory(std::wstring line); - - // Change how the cursor looks - void setCursor( - bool visible, - bool blinking = false, - f32 blink_speed = 1.0, - f32 relative_height = 1.0); - - // Irrlicht draw method - virtual void draw(); - - bool canTakeFocus(gui::IGUIElement* element) { return false; } - - virtual bool OnEvent(const SEvent& event); - - virtual void setVisible(bool visible); - -private: - void reformatConsole(); - void recalculateConsolePosition(); - - // These methods are called by draw - void animate(u32 msec); - void drawBackground(); - void drawText(); - void drawPrompt(); - -private: - ChatBackend* m_chat_backend; - Client* m_client; - IMenuManager* m_menumgr; - - // current screen size - v2u32 m_screensize; - - // used to compute how much time passed since last animate() - u32 m_animate_time_old; - - // should the console be opened or closed? - bool m_open; - // should it close after you press enter? - bool m_close_on_enter; - // current console height [pixels] - s32 m_height; - // desired height [pixels] - f32 m_desired_height; - // desired height [screen height fraction] - f32 m_desired_height_fraction; - // console open/close animation speed [screen height fraction / second] - f32 m_height_speed; - // if nonzero, opening the console is inhibited [milliseconds] - u32 m_open_inhibited; - - // cursor blink frame (16-bit value) - // cursor is off during [0,32767] and on during [32768,65535] - u32 m_cursor_blink; - // cursor blink speed [on/off toggles / second] - f32 m_cursor_blink_speed; - // cursor height [line height] - f32 m_cursor_height; - - // background texture - video::ITexture* m_background; - // background color (including alpha) - video::SColor m_background_color; - - // font - gui::IGUIFont* m_font; - v2u32 m_fontsize; -}; - - -#endif - diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt index 0e7cbad07..e028a0435 100644 --- a/src/util/CMakeLists.txt +++ b/src/util/CMakeLists.txt @@ -1,7 +1,16 @@ +if(USE_FREETYPE) + set(UTIL_FREETYPEDEP_SRCS + ${CMAKE_CURRENT_SOURCE_DIR}/statictext.cpp + ) +else() + set(UTIL_FREETYPEDEP_SRCS ) +endif(USE_FREETYPE) + set(UTIL_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/areastore.cpp ${CMAKE_CURRENT_SOURCE_DIR}/auth.cpp ${CMAKE_CURRENT_SOURCE_DIR}/base64.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/coloredstring.cpp ${CMAKE_CURRENT_SOURCE_DIR}/directiontables.cpp ${CMAKE_CURRENT_SOURCE_DIR}/numeric.cpp ${CMAKE_CURRENT_SOURCE_DIR}/pointedthing.cpp @@ -11,5 +20,6 @@ set(UTIL_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/string.cpp ${CMAKE_CURRENT_SOURCE_DIR}/srp.cpp ${CMAKE_CURRENT_SOURCE_DIR}/timetaker.cpp + ${UTIL_FREETYPEDEP_SRCS} PARENT_SCOPE) diff --git a/src/util/coloredstring.cpp b/src/util/coloredstring.cpp new file mode 100644 index 000000000..7db586550 --- /dev/null +++ b/src/util/coloredstring.cpp @@ -0,0 +1,68 @@ +/* +Copyright (C) 2013 xyz, Ilya Zhuravlev + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "coloredstring.h" +#include "util/string.h" + +ColoredString::ColoredString() +{} + +ColoredString::ColoredString(const std::wstring &string, const std::vector &colors): + m_string(string), + m_colors(colors) +{} + +ColoredString::ColoredString(const std::wstring &s) { + m_string = colorizeText(s, m_colors, SColor(255, 255, 255, 255)); +} + +void ColoredString::operator=(const wchar_t *str) { + m_string = colorizeText(str, m_colors, SColor(255, 255, 255, 255)); +} + +size_t ColoredString::size() const { + return m_string.size(); +} + +ColoredString ColoredString::substr(size_t pos, size_t len) const { + if (pos == m_string.length()) + return ColoredString(); + if (len == std::string::npos || pos + len > m_string.length()) { + return ColoredString( + m_string.substr(pos, std::string::npos), + std::vector(m_colors.begin() + pos, m_colors.end()) + ); + } else { + return ColoredString( + m_string.substr(pos, len), + std::vector(m_colors.begin() + pos, m_colors.begin() + pos + len) + ); + } +} + +const wchar_t *ColoredString::c_str() const { + return m_string.c_str(); +} + +const std::vector &ColoredString::getColors() const { + return m_colors; +} + +const std::wstring &ColoredString::getString() const { + return m_string; +} diff --git a/src/util/coloredstring.h b/src/util/coloredstring.h new file mode 100644 index 000000000..a6d98db30 --- /dev/null +++ b/src/util/coloredstring.h @@ -0,0 +1,44 @@ +/* +Copyright (C) 2013 xyz, Ilya Zhuravlev + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef COLOREDSTRING_HEADER +#define COLOREDSTRING_HEADER + +#include +#include +#include + +using namespace irr::video; + +class ColoredString { +public: + ColoredString(); + ColoredString(const std::wstring &s); + ColoredString(const std::wstring &string, const std::vector &colors); + void operator=(const wchar_t *str); + size_t size() const; + ColoredString substr(size_t pos = 0, size_t len = std::string::npos) const; + const wchar_t *c_str() const; + const std::vector &getColors() const; + const std::wstring &getString() const; +private: + std::wstring m_string; + std::vector m_colors; +}; + +#endif diff --git a/src/util/statictext.cpp b/src/util/statictext.cpp new file mode 100644 index 000000000..b534b560e --- /dev/null +++ b/src/util/statictext.cpp @@ -0,0 +1,654 @@ +// Copyright (C) 2002-2012 Nikolaus Gebhardt +// This file is part of the "Irrlicht Engine". +// For conditions of distribution and use, see copyright notice in irrlicht.h + +#include "statictext.h" +#ifdef _IRR_COMPILE_WITH_GUI_ + +//Only compile this if freetype is enabled. + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cguittfont/xCGUITTFont.h" +#include "util/string.h" + +namespace irr +{ +namespace gui +{ +//! constructor +StaticText::StaticText(const wchar_t* text, bool border, + IGUIEnvironment* environment, IGUIElement* parent, + s32 id, const core::rect& rectangle, + bool background) +: IGUIStaticText(environment, parent, id, rectangle), + HAlign(EGUIA_UPPERLEFT), VAlign(EGUIA_UPPERLEFT), + Border(border), OverrideColorEnabled(false), OverrideBGColorEnabled(false), WordWrap(false), Background(background), + RestrainTextInside(true), RightToLeft(false), + OverrideColor(video::SColor(101,255,255,255)), BGColor(video::SColor(101,210,210,210)), + OverrideFont(0), LastBreakFont(0) +{ + #ifdef _DEBUG + setDebugName("StaticText"); + #endif + + Text = text; + if (environment && environment->getSkin()) + { + BGColor = environment->getSkin()->getColor(gui::EGDC_3D_FACE); + } +} + + +//! destructor +StaticText::~StaticText() +{ + if (OverrideFont) + OverrideFont->drop(); +} + + +//! draws the element and its children +void StaticText::draw() +{ + if (!IsVisible) + return; + + IGUISkin* skin = Environment->getSkin(); + if (!skin) + return; + video::IVideoDriver* driver = Environment->getVideoDriver(); + + core::rect frameRect(AbsoluteRect); + + // draw background + + if (Background) + { + if ( !OverrideBGColorEnabled ) // skin-colors can change + BGColor = skin->getColor(gui::EGDC_3D_FACE); + + driver->draw2DRectangle(BGColor, frameRect, &AbsoluteClippingRect); + } + + // draw the border + + if (Border) + { + skin->draw3DSunkenPane(this, 0, true, false, frameRect, &AbsoluteClippingRect); + frameRect.UpperLeftCorner.X += skin->getSize(EGDS_TEXT_DISTANCE_X); + } + + // draw the text + if (Text.size()) + { + IGUIFont* font = getActiveFont(); + + if (font) + { + if (!WordWrap) + { + // TODO: add colors here + if (VAlign == EGUIA_LOWERRIGHT) + { + frameRect.UpperLeftCorner.Y = frameRect.LowerRightCorner.Y - + font->getDimension(L"A").Height - font->getKerningHeight(); + } + if (HAlign == EGUIA_LOWERRIGHT) + { + frameRect.UpperLeftCorner.X = frameRect.LowerRightCorner.X - + font->getDimension(Text.c_str()).Width; + } + + font->draw(Text.c_str(), frameRect, + OverrideColorEnabled ? OverrideColor : skin->getColor(isEnabled() ? EGDC_BUTTON_TEXT : EGDC_GRAY_TEXT), + HAlign == EGUIA_CENTER, VAlign == EGUIA_CENTER, (RestrainTextInside ? &AbsoluteClippingRect : NULL)); + } + else + { + if (font != LastBreakFont) + breakText(); + + core::rect r = frameRect; + s32 height = font->getDimension(L"A").Height + font->getKerningHeight(); + s32 totalHeight = height * BrokenText.size(); + if (VAlign == EGUIA_CENTER) + { + r.UpperLeftCorner.Y = r.getCenter().Y - (totalHeight / 2); + } + else if (VAlign == EGUIA_LOWERRIGHT) + { + r.UpperLeftCorner.Y = r.LowerRightCorner.Y - totalHeight; + } + + irr::video::SColor previous_color(255, 255, 255, 255); + for (u32 i=0; igetDimension(BrokenText[i].c_str()).Width; + } + + std::vector colors; + std::wstring str; + + str = colorizeText(BrokenText[i].c_str(), colors, previous_color); + if (!colors.empty()) + previous_color = colors[colors.size() - 1]; + + irr::gui::CGUITTFont *tmp = static_cast(font); + tmp->draw(str.c_str(), r, + colors, + HAlign == EGUIA_CENTER, false, (RestrainTextInside ? &AbsoluteClippingRect : NULL)); + + r.LowerRightCorner.Y += height; + r.UpperLeftCorner.Y += height; + } + } + } + } + + IGUIElement::draw(); +} + + +//! Sets another skin independent font. +void StaticText::setOverrideFont(IGUIFont* font) +{ + if (OverrideFont == font) + return; + + if (OverrideFont) + OverrideFont->drop(); + + OverrideFont = font; + + if (OverrideFont) + OverrideFont->grab(); + + breakText(); +} + +//! Gets the override font (if any) +IGUIFont * StaticText::getOverrideFont() const +{ + return OverrideFont; +} + +//! Get the font which is used right now for drawing +IGUIFont* StaticText::getActiveFont() const +{ + if ( OverrideFont ) + return OverrideFont; + IGUISkin* skin = Environment->getSkin(); + if (skin) + return skin->getFont(); + return 0; +} + +//! Sets another color for the text. +void StaticText::setOverrideColor(video::SColor color) +{ + OverrideColor = color; + OverrideColorEnabled = true; +} + + +//! Sets another color for the text. +void StaticText::setBackgroundColor(video::SColor color) +{ + BGColor = color; + OverrideBGColorEnabled = true; + Background = true; +} + + +//! Sets whether to draw the background +void StaticText::setDrawBackground(bool draw) +{ + Background = draw; +} + + +//! Gets the background color +video::SColor StaticText::getBackgroundColor() const +{ + return BGColor; +} + + +//! Checks if background drawing is enabled +bool StaticText::isDrawBackgroundEnabled() const +{ + _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX; + return Background; +} + + +//! Sets whether to draw the border +void StaticText::setDrawBorder(bool draw) +{ + Border = draw; +} + + +//! Checks if border drawing is enabled +bool StaticText::isDrawBorderEnabled() const +{ + _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX; + return Border; +} + + +void StaticText::setTextRestrainedInside(bool restrainTextInside) +{ + RestrainTextInside = restrainTextInside; +} + + +bool StaticText::isTextRestrainedInside() const +{ + return RestrainTextInside; +} + + +void StaticText::setTextAlignment(EGUI_ALIGNMENT horizontal, EGUI_ALIGNMENT vertical) +{ + HAlign = horizontal; + VAlign = vertical; +} + + +#if IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR <= 7 +const video::SColor& StaticText::getOverrideColor() const +#else +video::SColor StaticText::getOverrideColor() const +#endif +{ + return OverrideColor; +} + + +//! Sets if the static text should use the overide color or the +//! color in the gui skin. +void StaticText::enableOverrideColor(bool enable) +{ + OverrideColorEnabled = enable; +} + + +bool StaticText::isOverrideColorEnabled() const +{ + _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX; + return OverrideColorEnabled; +} + + +//! Enables or disables word wrap for using the static text as +//! multiline text control. +void StaticText::setWordWrap(bool enable) +{ + WordWrap = enable; + breakText(); +} + + +bool StaticText::isWordWrapEnabled() const +{ + _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX; + return WordWrap; +} + + +void StaticText::setRightToLeft(bool rtl) +{ + if (RightToLeft != rtl) + { + RightToLeft = rtl; + breakText(); + } +} + + +bool StaticText::isRightToLeft() const +{ + return RightToLeft; +} + + +//! Breaks the single text line. +void StaticText::breakText() +{ + if (!WordWrap) + return; + + BrokenText.clear(); + + IGUISkin* skin = Environment->getSkin(); + IGUIFont* font = getActiveFont(); + if (!font) + return; + + LastBreakFont = font; + + core::stringw line; + core::stringw word; + core::stringw whitespace; + s32 size = Text.size(); + s32 length = 0; + s32 elWidth = RelativeRect.getWidth(); + if (Border) + elWidth -= 2*skin->getSize(EGDS_TEXT_DISTANCE_X); + wchar_t c; + + std::vector colors; + + // We have to deal with right-to-left and left-to-right differently + // However, most parts of the following code is the same, it's just + // some order and boundaries which change. + if (!RightToLeft) + { + // regular (left-to-right) + for (s32 i=0; igetDimension(whitespace.c_str()).Width; + const std::wstring sanitized = removeEscapes(word.c_str()); + const s32 wordlgth = font->getDimension(sanitized.c_str()).Width; + + if (wordlgth > elWidth) + { + // This word is too long to fit in the available space, look for + // the Unicode Soft HYphen (SHY / 00AD) character for a place to + // break the word at + int where = word.findFirst( wchar_t(0x00AD) ); + if (where != -1) + { + core::stringw first = word.subString(0, where); + core::stringw second = word.subString(where, word.size() - where); + BrokenText.push_back(line + first + L"-"); + const s32 secondLength = font->getDimension(second.c_str()).Width; + + length = secondLength; + line = second; + } + else + { + // No soft hyphen found, so there's nothing more we can do + // break to next line + if (length) + BrokenText.push_back(line); + length = wordlgth; + line = word; + } + } + else if (length && (length + wordlgth + whitelgth > elWidth)) + { + // break to next line + BrokenText.push_back(line); + length = wordlgth; + line = word; + } + else + { + // add word to line + line += whitespace; + line += word; + length += whitelgth + wordlgth; + } + + word = L""; + whitespace = L""; + } + + if ( isWhitespace ) + { + whitespace += c; + } + + // compute line break + if (lineBreak) + { + line += whitespace; + line += word; + BrokenText.push_back(line); + line = L""; + word = L""; + whitespace = L""; + length = 0; + } + } + } + + line += whitespace; + line += word; + BrokenText.push_back(line); + } + else + { + // right-to-left + for (s32 i=size; i>=0; --i) + { + c = Text[i]; + bool lineBreak = false; + + if (c == L'\r') // Mac or Windows breaks + { + lineBreak = true; + if ((i>0) && Text[i-1] == L'\n') // Windows breaks + { + Text.erase(i-1); + --size; + } + c = '\0'; + } + else if (c == L'\n') // Unix breaks + { + lineBreak = true; + c = '\0'; + } + + if (c==L' ' || c==0 || i==0) + { + if (word.size()) + { + // here comes the next whitespace, look if + // we must break the last word to the next line. + const s32 whitelgth = font->getDimension(whitespace.c_str()).Width; + const s32 wordlgth = font->getDimension(word.c_str()).Width; + + if (length && (length + wordlgth + whitelgth > elWidth)) + { + // break to next line + BrokenText.push_back(line); + length = wordlgth; + line = word; + } + else + { + // add word to line + line = whitespace + line; + line = word + line; + length += whitelgth + wordlgth; + } + + word = L""; + whitespace = L""; + } + + if (c != 0) + whitespace = core::stringw(&c, 1) + whitespace; + + // compute line break + if (lineBreak) + { + line = whitespace + line; + line = word + line; + BrokenText.push_back(line); + line = L""; + word = L""; + whitespace = L""; + length = 0; + } + } + else + { + // yippee this is a word.. + word = core::stringw(&c, 1) + word; + } + } + + line = whitespace + line; + line = word + line; + BrokenText.push_back(line); + } +} + + +//! Sets the new caption of this element. +void StaticText::setText(const wchar_t* text) +{ + IGUIElement::setText(text); + breakText(); +} + + +void StaticText::updateAbsolutePosition() +{ + IGUIElement::updateAbsolutePosition(); + breakText(); +} + + +//! Returns the height of the text in pixels when it is drawn. +s32 StaticText::getTextHeight() const +{ + IGUIFont* font = getActiveFont(); + if (!font) + return 0; + + s32 height = font->getDimension(L"A").Height + font->getKerningHeight(); + + if (WordWrap) + height *= BrokenText.size(); + + return height; +} + + +s32 StaticText::getTextWidth() const +{ + IGUIFont * font = getActiveFont(); + if(!font) + return 0; + + if(WordWrap) + { + s32 widest = 0; + + for(u32 line = 0; line < BrokenText.size(); ++line) + { + s32 width = font->getDimension(BrokenText[line].c_str()).Width; + + if(width > widest) + widest = width; + } + + return widest; + } + else + { + return font->getDimension(Text.c_str()).Width; + } +} + + +//! Writes attributes of the element. +//! Implement this to expose the attributes of your element for +//! scripting languages, editors, debuggers or xml serialization purposes. +void StaticText::serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options=0) const +{ + IGUIStaticText::serializeAttributes(out,options); + + out->addBool ("Border", Border); + out->addBool ("OverrideColorEnabled",OverrideColorEnabled); + out->addBool ("OverrideBGColorEnabled",OverrideBGColorEnabled); + out->addBool ("WordWrap", WordWrap); + out->addBool ("Background", Background); + out->addBool ("RightToLeft", RightToLeft); + out->addBool ("RestrainTextInside", RestrainTextInside); + out->addColor ("OverrideColor", OverrideColor); + out->addColor ("BGColor", BGColor); + out->addEnum ("HTextAlign", HAlign, GUIAlignmentNames); + out->addEnum ("VTextAlign", VAlign, GUIAlignmentNames); + + // out->addFont ("OverrideFont", OverrideFont); +} + + +//! Reads attributes of the element +void StaticText::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options=0) +{ + IGUIStaticText::deserializeAttributes(in,options); + + Border = in->getAttributeAsBool("Border"); + enableOverrideColor(in->getAttributeAsBool("OverrideColorEnabled")); + OverrideBGColorEnabled = in->getAttributeAsBool("OverrideBGColorEnabled"); + setWordWrap(in->getAttributeAsBool("WordWrap")); + Background = in->getAttributeAsBool("Background"); + RightToLeft = in->getAttributeAsBool("RightToLeft"); + RestrainTextInside = in->getAttributeAsBool("RestrainTextInside"); + OverrideColor = in->getAttributeAsColor("OverrideColor"); + BGColor = in->getAttributeAsColor("BGColor"); + + setTextAlignment( (EGUI_ALIGNMENT) in->getAttributeAsEnumeration("HTextAlign", GUIAlignmentNames), + (EGUI_ALIGNMENT) in->getAttributeAsEnumeration("VTextAlign", GUIAlignmentNames)); + + // OverrideFont = in->getAttributeAsFont("OverrideFont"); +} + +} // end namespace gui +} // end namespace irr + + +#endif // _IRR_COMPILE_WITH_GUI_ diff --git a/src/util/statictext.h b/src/util/statictext.h new file mode 100644 index 000000000..8d2f879e7 --- /dev/null +++ b/src/util/statictext.h @@ -0,0 +1,150 @@ +// Copyright (C) 2002-2012 Nikolaus Gebhardt +// This file is part of the "Irrlicht Engine". +// For conditions of distribution and use, see copyright notice in irrlicht.h + +#ifndef __C_GUI_STATIC_TEXT_H_INCLUDED__ +#define __C_GUI_STATIC_TEXT_H_INCLUDED__ + +#include "IrrCompileConfig.h" +#ifdef _IRR_COMPILE_WITH_GUI_ + +#include "IGUIStaticText.h" +#include "irrArray.h" + +#include + +namespace irr +{ +namespace gui +{ + class StaticText : public IGUIStaticText + { + public: + + //! constructor + StaticText(const wchar_t* text, bool border, IGUIEnvironment* environment, + IGUIElement* parent, s32 id, const core::rect& rectangle, + bool background = false); + + //! destructor + virtual ~StaticText(); + + //! draws the element and its children + virtual void draw(); + + //! Sets another skin independent font. + virtual void setOverrideFont(IGUIFont* font=0); + + //! Gets the override font (if any) + virtual IGUIFont* getOverrideFont() const; + + //! Get the font which is used right now for drawing + virtual IGUIFont* getActiveFont() const; + + //! Sets another color for the text. + virtual void setOverrideColor(video::SColor color); + + //! Sets another color for the background. + virtual void setBackgroundColor(video::SColor color); + + //! Sets whether to draw the background + virtual void setDrawBackground(bool draw); + + //! Gets the background color + virtual video::SColor getBackgroundColor() const; + + //! Checks if background drawing is enabled + virtual bool isDrawBackgroundEnabled() const; + + //! Sets whether to draw the border + virtual void setDrawBorder(bool draw); + + //! Checks if border drawing is enabled + virtual bool isDrawBorderEnabled() const; + + //! Sets alignment mode for text + virtual void setTextAlignment(EGUI_ALIGNMENT horizontal, EGUI_ALIGNMENT vertical); + + //! Gets the override color + #if IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR <= 7 + virtual const video::SColor& getOverrideColor() const; + #else + virtual video::SColor getOverrideColor() const; + #endif + + //! Sets if the static text should use the overide color or the + //! color in the gui skin. + virtual void enableOverrideColor(bool enable); + + //! Checks if an override color is enabled + virtual bool isOverrideColorEnabled() const; + + //! Set whether the text in this label should be clipped if it goes outside bounds + virtual void setTextRestrainedInside(bool restrainedInside); + + //! Checks if the text in this label should be clipped if it goes outside bounds + virtual bool isTextRestrainedInside() const; + + //! Enables or disables word wrap for using the static text as + //! multiline text control. + virtual void setWordWrap(bool enable); + + //! Checks if word wrap is enabled + virtual bool isWordWrapEnabled() const; + + //! Sets the new caption of this element. + virtual void setText(const wchar_t* text); + + //! Returns the height of the text in pixels when it is drawn. + virtual s32 getTextHeight() const; + + //! Returns the width of the current text, in the current font + virtual s32 getTextWidth() const; + + //! Updates the absolute position, splits text if word wrap is enabled + virtual void updateAbsolutePosition(); + + //! Set whether the string should be interpreted as right-to-left (RTL) text + /** \note This component does not implement the Unicode bidi standard, the + text of the component should be already RTL if you call this. The + main difference when RTL is enabled is that the linebreaks for multiline + elements are performed starting from the end. + */ + virtual void setRightToLeft(bool rtl); + + //! Checks if the text should be interpreted as right-to-left text + virtual bool isRightToLeft() const; + + //! Writes attributes of the element. + virtual void serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options) const; + + //! Reads attributes of the element + virtual void deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options); + + private: + + //! Breaks the single text line. + void breakText(); + + EGUI_ALIGNMENT HAlign, VAlign; + bool Border; + bool OverrideColorEnabled; + bool OverrideBGColorEnabled; + bool WordWrap; + bool Background; + bool RestrainTextInside; + bool RightToLeft; + + video::SColor OverrideColor, BGColor; + gui::IGUIFont* OverrideFont; + gui::IGUIFont* LastBreakFont; // stored because: if skin changes, line break must be recalculated. + + core::array< core::stringw > BrokenText; + }; + +} // end namespace gui +} // end namespace irr + +#endif // _IRR_COMPILE_WITH_GUI_ + +#endif // C_GUI_STATIC_TEXT_H_INCLUDED -- cgit v1.2.3 From 14ef2b445adcec770defe1abf83af9d22ccf39d8 Mon Sep 17 00:00:00 2001 From: Ekdohibs Date: Tue, 31 May 2016 17:30:11 +0200 Subject: Add colored text (not only colored chat). Add documentation, move files to a proper place and avoid memory leaks. Make it work with most kind of texts, and allow backgrounds too. --- builtin/game/chatcommands.lua | 2 +- builtin/game/misc.lua | 43 ++- builtin/settingtypes.txt | 5 + doc/lua_api.txt | 18 + src/CMakeLists.txt | 2 + src/cguittfont/CGUITTFont.cpp | 30 +- src/cguittfont/CGUITTFont.h | 7 + src/chat.cpp | 37 +- src/chat.h | 17 +- src/client/CMakeLists.txt | 1 - src/client/guiChatConsole.cpp | 664 ---------------------------------- src/client/guiChatConsole.h | 138 ------- src/defaultsettings.cpp | 2 + src/game.cpp | 53 ++- src/guiChatConsole.cpp | 664 ++++++++++++++++++++++++++++++++++ src/guiChatConsole.h | 138 +++++++ src/guiEngine.cpp | 23 +- src/guiEngine.h | 3 + src/guiFormSpecMenu.cpp | 59 +-- src/guiFormSpecMenu.h | 13 +- src/irrlicht_changes/CMakeLists.txt | 7 + src/irrlicht_changes/static_text.cpp | 679 +++++++++++++++++++++++++++++++++++ src/irrlicht_changes/static_text.h | 268 ++++++++++++++ src/terminal_chat_console.cpp | 10 +- src/util/CMakeLists.txt | 11 +- src/util/coloredstring.cpp | 68 ---- src/util/coloredstring.h | 44 --- src/util/enriched_string.cpp | 166 +++++++++ src/util/enriched_string.h | 91 +++++ src/util/statictext.cpp | 654 --------------------------------- src/util/statictext.h | 150 -------- src/util/string.h | 32 ++ 32 files changed, 2235 insertions(+), 1864 deletions(-) delete mode 100644 src/client/guiChatConsole.cpp delete mode 100644 src/client/guiChatConsole.h create mode 100644 src/guiChatConsole.cpp create mode 100644 src/guiChatConsole.h create mode 100644 src/irrlicht_changes/CMakeLists.txt create mode 100644 src/irrlicht_changes/static_text.cpp create mode 100644 src/irrlicht_changes/static_text.h delete mode 100644 src/util/coloredstring.cpp delete mode 100644 src/util/coloredstring.h create mode 100644 src/util/enriched_string.cpp create mode 100644 src/util/enriched_string.h delete mode 100644 src/util/statictext.cpp delete mode 100644 src/util/statictext.h (limited to 'src') diff --git a/builtin/game/chatcommands.lua b/builtin/game/chatcommands.lua index 2627559a5..22755386b 100644 --- a/builtin/game/chatcommands.lua +++ b/builtin/game/chatcommands.lua @@ -102,7 +102,7 @@ core.register_chatcommand("help", { description = "Get help for commands or list privileges", func = function(name, param) local function format_help_line(cmd, def) - local msg = core.colorize("00ffff", "/"..cmd) + local msg = core.colorize("#00ffff", "/"..cmd) if def.params and def.params ~= "" then msg = msg .. " " .. def.params end diff --git a/builtin/game/misc.lua b/builtin/game/misc.lua index 8d5c80216..918315656 100644 --- a/builtin/game/misc.lua +++ b/builtin/game/misc.lua @@ -198,19 +198,34 @@ function core.http_add_fetch(httpenv) return httpenv end -function core.get_color_escape_sequence(color) - --if string.len(color) == 3 then - -- local r = string.sub(color, 1, 1) - -- local g = string.sub(color, 2, 2) - -- local b = string.sub(color, 3, 3) - -- color = r .. r .. g .. g .. b .. b - --end - - --assert(#color == 6, "Color must be six characters in length.") - --return "\v" .. color - return "\v(color;" .. color .. ")" -end +if minetest.setting_getbool("disable_escape_sequences") then + + function core.get_color_escape_sequence(color) + return "" + end + + function core.get_background_escape_sequence(color) + return "" + end + + function core.colorize(color, message) + return message + end + +else + + local ESCAPE_CHAR = string.char(0x1b) + function core.get_color_escape_sequence(color) + return ESCAPE_CHAR .. "(c@" .. color .. ")" + end + + function core.get_background_escape_sequence(color) + return ESCAPE_CHAR .. "(b@" .. color .. ")" + end + + function core.colorize(color, message) + return core.get_color_escape_sequence(color) .. message .. core.get_color_escape_sequence("#ffffff") + end -function core.colorize(color, message) - return core.get_color_escape_sequence(color) .. message .. core.get_color_escape_sequence("ffffff") end + diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index 93fb8e952..538a04f33 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -615,6 +615,11 @@ server_announce (Announce server) bool false # If you want to announce your ipv6 address, use serverlist_url = v6.servers.minetest.net. serverlist_url (Serverlist URL) string servers.minetest.net +# Disable escape sequences, e.g. chat coloring. +# Use this if you want to run a server with pre-0.4.14 clients and you want to disable +# the escape sequences generated by mods. +disable_escape_sequences (Disable escape sequences) bool false + [*Network] # Network port to listen (UDP). diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 82a0acbee..aa0d7e45d 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -1701,6 +1701,24 @@ numerical form, the raw integer value of an ARGB8 quad: or string form, a ColorString (defined above): `colorspec = "green"` +Escape sequences +---------------- +Most text can contain escape sequences, that can for example color the text. +There are a few exceptions: tab headers, dropdowns and vertical labels can't. +The following functions provide escape sequences: +* `core.get_color_escape_sequence(color)`: + * `color` is a ColorString + * The escape sequence sets the text color to `color` +* `core.colorize(color, message)`: + * Equivalent to: + `core.get_color_escape_sequence(color) .. + message .. + core.get_color_escape_sequence("#ffffff")` +* `color.get_background_escape_sequence(color)` + * `color` is a ColorString + * The escape sequence sets the background of the whole text element to + `color`. Only defined for item descriptions and tooltips. + Spatial Vectors --------------- * `vector.new(a[, b, c])`: returns a vector: diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ea1564eeb..f02812415 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -376,6 +376,7 @@ add_subdirectory(network) add_subdirectory(script) add_subdirectory(unittest) add_subdirectory(util) +add_subdirectory(irrlicht_changes) set(common_SRCS ban.cpp @@ -493,6 +494,7 @@ set(client_SRCS ${common_SRCS} ${sound_SRCS} ${client_network_SRCS} + ${client_irrlicht_changes_SRCS} camera.cpp client.cpp clientmap.cpp diff --git a/src/cguittfont/CGUITTFont.cpp b/src/cguittfont/CGUITTFont.cpp index 2342eb748..c2d37c6c0 100644 --- a/src/cguittfont/CGUITTFont.cpp +++ b/src/cguittfont/CGUITTFont.cpp @@ -1,6 +1,7 @@ /* CGUITTFont FreeType class for Irrlicht Copyright (c) 2009-2010 John Norman + Copyright (c) 2016 Nathanaël Courant This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -545,6 +546,13 @@ void CGUITTFont::setFontHinting(const bool enable, const bool enable_auto_hintin void CGUITTFont::draw(const core::stringw& text, const core::rect& position, video::SColor color, bool hcenter, bool vcenter, const core::rect* clip) { + draw(EnrichedString(std::wstring(text.c_str()), color), position, color, hcenter, vcenter, clip); +} + +void CGUITTFont::draw(const EnrichedString &text, const core::rect& position, video::SColor color, bool hcenter, bool vcenter, const core::rect* clip) +{ + std::vector colors = text.getColors(); + if (!Driver) return; @@ -572,7 +580,7 @@ void CGUITTFont::draw(const core::stringw& text, const core::rect& position } // Convert to a unicode string. - core::ustring utext(text); + core::ustring utext = text.getString(); // Set up our render map. core::map Render_Map; @@ -581,6 +589,7 @@ void CGUITTFont::draw(const core::stringw& text, const core::rect& position u32 n; uchar32_t previousChar = 0; core::ustring::const_iterator iter(utext); + std::vector applied_colors; while (!iter.atEnd()) { uchar32_t currentChar = *iter; @@ -590,7 +599,7 @@ void CGUITTFont::draw(const core::stringw& text, const core::rect& position if (currentChar == L'\r') // Mac or Windows breaks { lineBreak = true; - if (*(iter + 1) == (uchar32_t)'\n') // Windows line breaks. + if (*(iter + 1) == (uchar32_t)'\n') // Windows line breaks. currentChar = *(++iter); } else if (currentChar == (uchar32_t)'\n') // Unix breaks @@ -627,6 +636,9 @@ void CGUITTFont::draw(const core::stringw& text, const core::rect& position page->render_positions.push_back(core::position2di(offset.X + offx, offset.Y + offy)); page->render_source_rects.push_back(glyph.source_rect); Render_Map.set(glyph.glyph_page, page); + u32 current_color = iter.getPos(); + if (current_color < colors.size()) + applied_colors.push_back(colors[current_color]); } offset.X += getWidthFromCharacter(currentChar); @@ -645,8 +657,6 @@ void CGUITTFont::draw(const core::stringw& text, const core::rect& position CGUITTGlyphPage* page = n->getValue(); - if (!use_transparency) color.color |= 0xff000000; - if (shadow_offset) { for (size_t i = 0; i < page->render_positions.size(); ++i) page->render_positions[i] += core::vector2di(shadow_offset, shadow_offset); @@ -654,7 +664,17 @@ void CGUITTFont::draw(const core::stringw& text, const core::rect& position for (size_t i = 0; i < page->render_positions.size(); ++i) page->render_positions[i] -= core::vector2di(shadow_offset, shadow_offset); } - Driver->draw2DImageBatch(page->texture, page->render_positions, page->render_source_rects, clip, color, true); + for (size_t i = 0; i < page->render_positions.size(); ++i) { + irr::video::SColor col; + if (!applied_colors.empty()) { + col = applied_colors[i < applied_colors.size() ? i : 0]; + } else { + col = irr::video::SColor(255, 255, 255, 255); + } + if (!use_transparency) + col.color |= 0xff000000; + Driver->draw2DImage(page->texture, page->render_positions[i], page->render_source_rects[i], clip, col, true); + } } } diff --git a/src/cguittfont/CGUITTFont.h b/src/cguittfont/CGUITTFont.h index e24d8f18b..0aa540c5c 100644 --- a/src/cguittfont/CGUITTFont.h +++ b/src/cguittfont/CGUITTFont.h @@ -1,6 +1,7 @@ /* CGUITTFont FreeType class for Irrlicht Copyright (c) 2009-2010 John Norman + Copyright (c) 2016 Nathanaël Courant This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any @@ -33,6 +34,8 @@ #include #include +#include +#include "util/enriched_string.h" #include FT_FREETYPE_H namespace irr @@ -258,6 +261,10 @@ namespace gui virtual void draw(const core::stringw& text, const core::rect& position, video::SColor color, bool hcenter=false, bool vcenter=false, const core::rect* clip=0); + + virtual void draw(const EnrichedString& text, const core::rect& position, + video::SColor color, bool hcenter=false, bool vcenter=false, + const core::rect* clip=0); //! Returns the dimension of a character produced by this font. virtual core::dimension2d getCharDimension(const wchar_t ch) const; diff --git a/src/chat.cpp b/src/chat.cpp index 958389df5..46555b3dc 100644 --- a/src/chat.cpp +++ b/src/chat.cpp @@ -267,28 +267,26 @@ u32 ChatBuffer::formatChatLine(const ChatLine& line, u32 cols, next_frags.push_back(temp_frag); } - std::wstring name_sanitized = removeEscapes(line.name); + std::wstring name_sanitized = line.name.c_str(); // Choose an indentation level if (line.name.empty()) { // Server messages hanging_indentation = 0; - } - else if (name_sanitized.size() + 3 <= cols/2) { + } else if (name_sanitized.size() + 3 <= cols/2) { // Names shorter than about half the console width hanging_indentation = line.name.size() + 3; - } - else { + } else { // Very long names hanging_indentation = 2; } - ColoredString line_text(line.text); + //EnrichedString line_text(line.text); next_line.first = true; bool text_processing = false; // Produce fragments and layout them into lines - while (!next_frags.empty() || in_pos < line_text.size()) + while (!next_frags.empty() || in_pos < line.text.size()) { // Layout fragments into lines while (!next_frags.empty()) @@ -326,9 +324,9 @@ u32 ChatBuffer::formatChatLine(const ChatLine& line, u32 cols, } // Produce fragment - if (in_pos < line_text.size()) + if (in_pos < line.text.size()) { - u32 remaining_in_input = line_text.size() - in_pos; + u32 remaining_in_input = line.text.size() - in_pos; u32 remaining_in_output = cols - out_column; // Determine a fragment length <= the minimum of @@ -338,14 +336,14 @@ u32 ChatBuffer::formatChatLine(const ChatLine& line, u32 cols, while (frag_length < remaining_in_input && frag_length < remaining_in_output) { - if (isspace(line_text[in_pos + frag_length])) + if (isspace(line.text.getString()[in_pos + frag_length])) space_pos = frag_length; ++frag_length; } if (space_pos != 0 && frag_length < remaining_in_input) frag_length = space_pos + 1; - temp_frag.text = line_text.substr(in_pos, frag_length); + temp_frag.text = line.text.substr(in_pos, frag_length); temp_frag.column = 0; //temp_frag.bold = 0; next_frags.push_back(temp_frag); @@ -729,19 +727,22 @@ ChatBuffer& ChatBackend::getRecentBuffer() return m_recent_buffer; } -std::wstring ChatBackend::getRecentChat() +EnrichedString ChatBackend::getRecentChat() { - std::wostringstream stream; + EnrichedString result; for (u32 i = 0; i < m_recent_buffer.getLineCount(); ++i) { const ChatLine& line = m_recent_buffer.getLine(i); if (i != 0) - stream << L"\n"; - if (!line.name.empty()) - stream << L"<" << line.name << L"> "; - stream << line.text; + result += L"\n"; + if (!line.name.empty()) { + result += L"<"; + result += line.name; + result += L"> "; + } + result += line.text; } - return stream.str(); + return result; } ChatPrompt& ChatBackend::getPrompt() diff --git a/src/chat.h b/src/chat.h index 661cafc82..11061fd39 100644 --- a/src/chat.h +++ b/src/chat.h @@ -25,7 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include "irrlichttypes.h" -#include "util/coloredstring.h" +#include "util/enriched_string.h" // Chat console related classes @@ -34,9 +34,9 @@ struct ChatLine // age in seconds f32 age; // name of sending player, or empty if sent by server - std::wstring name; + EnrichedString name; // message text - ColoredString text; + EnrichedString text; ChatLine(std::wstring a_name, std::wstring a_text): age(0.0), @@ -44,12 +44,19 @@ struct ChatLine text(a_text) { } + + ChatLine(EnrichedString a_name, EnrichedString a_text): + age(0.0), + name(a_name), + text(a_text) + { + } }; struct ChatFormattedFragment { // text string - std::wstring text; + EnrichedString text; // starting column u32 column; // formatting @@ -262,7 +269,7 @@ public: // Get the recent messages buffer ChatBuffer& getRecentBuffer(); // Concatenate all recent messages - std::wstring getRecentChat(); + EnrichedString getRecentChat(); // Get the console prompt ChatPrompt& getPrompt(); diff --git a/src/client/CMakeLists.txt b/src/client/CMakeLists.txt index bcf114760..a1ec37fe3 100644 --- a/src/client/CMakeLists.txt +++ b/src/client/CMakeLists.txt @@ -1,6 +1,5 @@ set(client_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/clientlauncher.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/guiChatConsole.cpp ${CMAKE_CURRENT_SOURCE_DIR}/tile.cpp PARENT_SCOPE ) diff --git a/src/client/guiChatConsole.cpp b/src/client/guiChatConsole.cpp deleted file mode 100644 index d8837556a..000000000 --- a/src/client/guiChatConsole.cpp +++ /dev/null @@ -1,664 +0,0 @@ -/* -Minetest -Copyright (C) 2013 celeron55, Perttu Ahola - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation; either version 2.1 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License along -with this program; if not, write to the Free Software Foundation, Inc., -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -*/ - -#include "guiChatConsole.h" -#include "chat.h" -#include "client.h" -#include "debug.h" -#include "gettime.h" -#include "keycode.h" -#include "settings.h" -#include "porting.h" -#include "client/tile.h" -#include "fontengine.h" -#include "log.h" -#include "gettext.h" -#include - -#if USE_FREETYPE - #include "xCGUITTFont.h" -#endif - -inline u32 clamp_u8(s32 value) -{ - return (u32) MYMIN(MYMAX(value, 0), 255); -} - - -GUIChatConsole::GUIChatConsole( - gui::IGUIEnvironment* env, - gui::IGUIElement* parent, - s32 id, - ChatBackend* backend, - Client* client, - IMenuManager* menumgr -): - IGUIElement(gui::EGUIET_ELEMENT, env, parent, id, - core::rect(0,0,100,100)), - m_chat_backend(backend), - m_client(client), - m_menumgr(menumgr), - m_screensize(v2u32(0,0)), - m_animate_time_old(0), - m_open(false), - m_close_on_enter(false), - m_height(0), - m_desired_height(0), - m_desired_height_fraction(0.0), - m_height_speed(5.0), - m_open_inhibited(0), - m_cursor_blink(0.0), - m_cursor_blink_speed(0.0), - m_cursor_height(0.0), - m_background(NULL), - m_background_color(255, 0, 0, 0), - m_font(NULL), - m_fontsize(0, 0) -{ - m_animate_time_old = getTimeMs(); - - // load background settings - s32 console_alpha = g_settings->getS32("console_alpha"); - m_background_color.setAlpha(clamp_u8(console_alpha)); - - // load the background texture depending on settings - ITextureSource *tsrc = client->getTextureSource(); - if (tsrc->isKnownSourceImage("background_chat.jpg")) { - m_background = tsrc->getTexture("background_chat.jpg"); - m_background_color.setRed(255); - m_background_color.setGreen(255); - m_background_color.setBlue(255); - } else { - v3f console_color = g_settings->getV3F("console_color"); - m_background_color.setRed(clamp_u8(myround(console_color.X))); - m_background_color.setGreen(clamp_u8(myround(console_color.Y))); - m_background_color.setBlue(clamp_u8(myround(console_color.Z))); - } - - m_font = g_fontengine->getFont(FONT_SIZE_UNSPECIFIED, FM_Mono); - - if (m_font == NULL) - { - errorstream << "GUIChatConsole: Unable to load mono font "; - } - else - { - core::dimension2d dim = m_font->getDimension(L"M"); - m_fontsize = v2u32(dim.Width, dim.Height); - m_font->grab(); - } - m_fontsize.X = MYMAX(m_fontsize.X, 1); - m_fontsize.Y = MYMAX(m_fontsize.Y, 1); - - // set default cursor options - setCursor(true, true, 2.0, 0.1); -} - -GUIChatConsole::~GUIChatConsole() -{ - if (m_font) - m_font->drop(); -} - -void GUIChatConsole::openConsole(f32 height) -{ - m_open = true; - m_desired_height_fraction = height; - m_desired_height = height * m_screensize.Y; - reformatConsole(); - m_animate_time_old = getTimeMs(); - IGUIElement::setVisible(true); - Environment->setFocus(this); - m_menumgr->createdMenu(this); -} - -bool GUIChatConsole::isOpen() const -{ - return m_open; -} - -bool GUIChatConsole::isOpenInhibited() const -{ - return m_open_inhibited > 0; -} - -void GUIChatConsole::closeConsole() -{ - m_open = false; - Environment->removeFocus(this); - m_menumgr->deletingMenu(this); -} - -void GUIChatConsole::closeConsoleAtOnce() -{ - closeConsole(); - m_height = 0; - recalculateConsolePosition(); -} - -f32 GUIChatConsole::getDesiredHeight() const -{ - return m_desired_height_fraction; -} - -void GUIChatConsole::replaceAndAddToHistory(std::wstring line) -{ - ChatPrompt& prompt = m_chat_backend->getPrompt(); - prompt.addToHistory(prompt.getLine()); - prompt.replace(line); -} - - -void GUIChatConsole::setCursor( - bool visible, bool blinking, f32 blink_speed, f32 relative_height) -{ - if (visible) - { - if (blinking) - { - // leave m_cursor_blink unchanged - m_cursor_blink_speed = blink_speed; - } - else - { - m_cursor_blink = 0x8000; // on - m_cursor_blink_speed = 0.0; - } - } - else - { - m_cursor_blink = 0; // off - m_cursor_blink_speed = 0.0; - } - m_cursor_height = relative_height; -} - -void GUIChatConsole::draw() -{ - if(!IsVisible) - return; - - video::IVideoDriver* driver = Environment->getVideoDriver(); - - // Check screen size - v2u32 screensize = driver->getScreenSize(); - if (screensize != m_screensize) - { - // screen size has changed - // scale current console height to new window size - if (m_screensize.Y != 0) - m_height = m_height * screensize.Y / m_screensize.Y; - m_desired_height = m_desired_height_fraction * m_screensize.Y; - m_screensize = screensize; - reformatConsole(); - } - - // Animation - u32 now = getTimeMs(); - animate(now - m_animate_time_old); - m_animate_time_old = now; - - // Draw console elements if visible - if (m_height > 0) - { - drawBackground(); - drawText(); - drawPrompt(); - } - - gui::IGUIElement::draw(); -} - -void GUIChatConsole::reformatConsole() -{ - s32 cols = m_screensize.X / m_fontsize.X - 2; // make room for a margin (looks better) - s32 rows = m_desired_height / m_fontsize.Y - 1; // make room for the input prompt - if (cols <= 0 || rows <= 0) - cols = rows = 0; - m_chat_backend->reformat(cols, rows); -} - -void GUIChatConsole::recalculateConsolePosition() -{ - core::rect rect(0, 0, m_screensize.X, m_height); - DesiredRect = rect; - recalculateAbsolutePosition(false); -} - -void GUIChatConsole::animate(u32 msec) -{ - // animate the console height - s32 goal = m_open ? m_desired_height : 0; - - // Set invisible if close animation finished (reset by openConsole) - // This function (animate()) is never called once its visibility becomes false so do not - // actually set visible to false before the inhibited period is over - if (!m_open && m_height == 0 && m_open_inhibited == 0) - IGUIElement::setVisible(false); - - if (m_height != goal) - { - s32 max_change = msec * m_screensize.Y * (m_height_speed / 1000.0); - if (max_change == 0) - max_change = 1; - - if (m_height < goal) - { - // increase height - if (m_height + max_change < goal) - m_height += max_change; - else - m_height = goal; - } - else - { - // decrease height - if (m_height > goal + max_change) - m_height -= max_change; - else - m_height = goal; - } - - recalculateConsolePosition(); - } - - // blink the cursor - if (m_cursor_blink_speed != 0.0) - { - u32 blink_increase = 0x10000 * msec * (m_cursor_blink_speed / 1000.0); - if (blink_increase == 0) - blink_increase = 1; - m_cursor_blink = ((m_cursor_blink + blink_increase) & 0xffff); - } - - // decrease open inhibit counter - if (m_open_inhibited > msec) - m_open_inhibited -= msec; - else - m_open_inhibited = 0; -} - -void GUIChatConsole::drawBackground() -{ - video::IVideoDriver* driver = Environment->getVideoDriver(); - if (m_background != NULL) - { - core::rect sourcerect(0, -m_height, m_screensize.X, 0); - driver->draw2DImage( - m_background, - v2s32(0, 0), - sourcerect, - &AbsoluteClippingRect, - m_background_color, - false); - } - else - { - driver->draw2DRectangle( - m_background_color, - core::rect(0, 0, m_screensize.X, m_height), - &AbsoluteClippingRect); - } -} - -void GUIChatConsole::drawText() -{ - if (m_font == NULL) - return; - - ChatBuffer& buf = m_chat_backend->getConsoleBuffer(); - for (u32 row = 0; row < buf.getRows(); ++row) - { - const ChatFormattedLine& line = buf.getFormattedLine(row); - if (line.fragments.empty()) - continue; - - s32 line_height = m_fontsize.Y; - s32 y = row * line_height + m_height - m_desired_height; - if (y + line_height < 0) - continue; - - for (u32 i = 0; i < line.fragments.size(); ++i) - { - const ChatFormattedFragment& fragment = line.fragments[i]; - s32 x = (fragment.column + 1) * m_fontsize.X; - core::rect destrect( - x, y, x + m_fontsize.X * fragment.text.size(), y + m_fontsize.Y); - - - #if USE_FREETYPE - // Draw colored text if FreeType is enabled - irr::gui::CGUITTFont *tmp = static_cast(m_font); - tmp->draw( - fragment.text.c_str(), - destrect, - fragment.text.getColors(), - false, - false, - &AbsoluteClippingRect); - #else - // Otherwise use standard text - m_font->draw( - fragment.text.c_str(), - destrect, - video::SColor(255, 255, 255, 255), - false, - false, - &AbsoluteClippingRect); - #endif - } - } -} - -void GUIChatConsole::drawPrompt() -{ - if (m_font == NULL) - return; - - u32 row = m_chat_backend->getConsoleBuffer().getRows(); - s32 line_height = m_fontsize.Y; - s32 y = row * line_height + m_height - m_desired_height; - - ChatPrompt& prompt = m_chat_backend->getPrompt(); - std::wstring prompt_text = prompt.getVisiblePortion(); - - // FIXME Draw string at once, not character by character - // That will only work with the cursor once we have a monospace font - for (u32 i = 0; i < prompt_text.size(); ++i) - { - wchar_t ws[2] = {prompt_text[i], 0}; - s32 x = (1 + i) * m_fontsize.X; - core::rect destrect( - x, y, x + m_fontsize.X, y + m_fontsize.Y); - m_font->draw( - ws, - destrect, - video::SColor(255, 255, 255, 255), - false, - false, - &AbsoluteClippingRect); - } - - // Draw the cursor during on periods - if ((m_cursor_blink & 0x8000) != 0) - { - s32 cursor_pos = prompt.getVisibleCursorPosition(); - if (cursor_pos >= 0) - { - s32 cursor_len = prompt.getCursorLength(); - video::IVideoDriver* driver = Environment->getVideoDriver(); - s32 x = (1 + cursor_pos) * m_fontsize.X; - core::rect destrect( - x, - y + m_fontsize.Y * (1.0 - m_cursor_height), - x + m_fontsize.X * MYMAX(cursor_len, 1), - y + m_fontsize.Y * (cursor_len ? m_cursor_height+1 : 1) - ); - video::SColor cursor_color(255,255,255,255); - driver->draw2DRectangle( - cursor_color, - destrect, - &AbsoluteClippingRect); - } - } - -} - -bool GUIChatConsole::OnEvent(const SEvent& event) -{ - - ChatPrompt &prompt = m_chat_backend->getPrompt(); - - if(event.EventType == EET_KEY_INPUT_EVENT && event.KeyInput.PressedDown) - { - // Key input - if(KeyPress(event.KeyInput) == getKeySetting("keymap_console")) - { - closeConsole(); - - // inhibit open so the_game doesn't reopen immediately - m_open_inhibited = 50; - m_close_on_enter = false; - return true; - } - else if(event.KeyInput.Key == KEY_ESCAPE) - { - closeConsoleAtOnce(); - m_close_on_enter = false; - // inhibit open so the_game doesn't reopen immediately - m_open_inhibited = 1; // so the ESCAPE button doesn't open the "pause menu" - return true; - } - else if(event.KeyInput.Key == KEY_PRIOR) - { - m_chat_backend->scrollPageUp(); - return true; - } - else if(event.KeyInput.Key == KEY_NEXT) - { - m_chat_backend->scrollPageDown(); - return true; - } - else if(event.KeyInput.Key == KEY_RETURN) - { - prompt.addToHistory(prompt.getLine()); - std::wstring text = prompt.replace(L""); - m_client->typeChatMessage(text); - if (m_close_on_enter) { - closeConsoleAtOnce(); - m_close_on_enter = false; - } - return true; - } - else if(event.KeyInput.Key == KEY_UP) - { - // Up pressed - // Move back in history - prompt.historyPrev(); - return true; - } - else if(event.KeyInput.Key == KEY_DOWN) - { - // Down pressed - // Move forward in history - prompt.historyNext(); - return true; - } - else if(event.KeyInput.Key == KEY_LEFT || event.KeyInput.Key == KEY_RIGHT) - { - // Left/right pressed - // Move/select character/word to the left depending on control and shift keys - ChatPrompt::CursorOp op = event.KeyInput.Shift ? - ChatPrompt::CURSOROP_SELECT : - ChatPrompt::CURSOROP_MOVE; - ChatPrompt::CursorOpDir dir = event.KeyInput.Key == KEY_LEFT ? - ChatPrompt::CURSOROP_DIR_LEFT : - ChatPrompt::CURSOROP_DIR_RIGHT; - ChatPrompt::CursorOpScope scope = event.KeyInput.Control ? - ChatPrompt::CURSOROP_SCOPE_WORD : - ChatPrompt::CURSOROP_SCOPE_CHARACTER; - prompt.cursorOperation(op, dir, scope); - return true; - } - else if(event.KeyInput.Key == KEY_HOME) - { - // Home pressed - // move to beginning of line - prompt.cursorOperation( - ChatPrompt::CURSOROP_MOVE, - ChatPrompt::CURSOROP_DIR_LEFT, - ChatPrompt::CURSOROP_SCOPE_LINE); - return true; - } - else if(event.KeyInput.Key == KEY_END) - { - // End pressed - // move to end of line - prompt.cursorOperation( - ChatPrompt::CURSOROP_MOVE, - ChatPrompt::CURSOROP_DIR_RIGHT, - ChatPrompt::CURSOROP_SCOPE_LINE); - return true; - } - else if(event.KeyInput.Key == KEY_BACK) - { - // Backspace or Ctrl-Backspace pressed - // delete character / word to the left - ChatPrompt::CursorOpScope scope = - event.KeyInput.Control ? - ChatPrompt::CURSOROP_SCOPE_WORD : - ChatPrompt::CURSOROP_SCOPE_CHARACTER; - prompt.cursorOperation( - ChatPrompt::CURSOROP_DELETE, - ChatPrompt::CURSOROP_DIR_LEFT, - scope); - return true; - } - else if(event.KeyInput.Key == KEY_DELETE) - { - // Delete or Ctrl-Delete pressed - // delete character / word to the right - ChatPrompt::CursorOpScope scope = - event.KeyInput.Control ? - ChatPrompt::CURSOROP_SCOPE_WORD : - ChatPrompt::CURSOROP_SCOPE_CHARACTER; - prompt.cursorOperation( - ChatPrompt::CURSOROP_DELETE, - ChatPrompt::CURSOROP_DIR_RIGHT, - scope); - return true; - } - else if(event.KeyInput.Key == KEY_KEY_A && event.KeyInput.Control) - { - // Ctrl-A pressed - // Select all text - prompt.cursorOperation( - ChatPrompt::CURSOROP_SELECT, - ChatPrompt::CURSOROP_DIR_LEFT, // Ignored - ChatPrompt::CURSOROP_SCOPE_LINE); - return true; - } - else if(event.KeyInput.Key == KEY_KEY_C && event.KeyInput.Control) - { - // Ctrl-C pressed - // Copy text to clipboard - if (prompt.getCursorLength() <= 0) - return true; - std::wstring wselected = prompt.getSelection(); - std::string selected(wselected.begin(), wselected.end()); - Environment->getOSOperator()->copyToClipboard(selected.c_str()); - return true; - } - else if(event.KeyInput.Key == KEY_KEY_V && event.KeyInput.Control) - { - // Ctrl-V pressed - // paste text from clipboard - if (prompt.getCursorLength() > 0) { - // Delete selected section of text - prompt.cursorOperation( - ChatPrompt::CURSOROP_DELETE, - ChatPrompt::CURSOROP_DIR_LEFT, // Ignored - ChatPrompt::CURSOROP_SCOPE_SELECTION); - } - IOSOperator *os_operator = Environment->getOSOperator(); - const c8 *text = os_operator->getTextFromClipboard(); - if (!text) - return true; - std::basic_string str((const unsigned char*)text); - prompt.input(std::wstring(str.begin(), str.end())); - return true; - } - else if(event.KeyInput.Key == KEY_KEY_X && event.KeyInput.Control) - { - // Ctrl-X pressed - // Cut text to clipboard - if (prompt.getCursorLength() <= 0) - return true; - std::wstring wselected = prompt.getSelection(); - std::string selected(wselected.begin(), wselected.end()); - Environment->getOSOperator()->copyToClipboard(selected.c_str()); - prompt.cursorOperation( - ChatPrompt::CURSOROP_DELETE, - ChatPrompt::CURSOROP_DIR_LEFT, // Ignored - ChatPrompt::CURSOROP_SCOPE_SELECTION); - return true; - } - else if(event.KeyInput.Key == KEY_KEY_U && event.KeyInput.Control) - { - // Ctrl-U pressed - // kill line to left end - prompt.cursorOperation( - ChatPrompt::CURSOROP_DELETE, - ChatPrompt::CURSOROP_DIR_LEFT, - ChatPrompt::CURSOROP_SCOPE_LINE); - return true; - } - else if(event.KeyInput.Key == KEY_KEY_K && event.KeyInput.Control) - { - // Ctrl-K pressed - // kill line to right end - prompt.cursorOperation( - ChatPrompt::CURSOROP_DELETE, - ChatPrompt::CURSOROP_DIR_RIGHT, - ChatPrompt::CURSOROP_SCOPE_LINE); - return true; - } - else if(event.KeyInput.Key == KEY_TAB) - { - // Tab or Shift-Tab pressed - // Nick completion - std::list names = m_client->getConnectedPlayerNames(); - bool backwards = event.KeyInput.Shift; - prompt.nickCompletion(names, backwards); - return true; - } - else if(event.KeyInput.Char != 0 && !event.KeyInput.Control) - { - #if (defined(linux) || defined(__linux)) - wchar_t wc = L'_'; - mbtowc( &wc, (char *) &event.KeyInput.Char, sizeof(event.KeyInput.Char) ); - prompt.input(wc); - #else - prompt.input(event.KeyInput.Char); - #endif - return true; - } - } - else if(event.EventType == EET_MOUSE_INPUT_EVENT) - { - if(event.MouseInput.Event == EMIE_MOUSE_WHEEL) - { - s32 rows = myround(-3.0 * event.MouseInput.Wheel); - m_chat_backend->scroll(rows); - } - } - - return Parent ? Parent->OnEvent(event) : false; -} - -void GUIChatConsole::setVisible(bool visible) -{ - m_open = visible; - IGUIElement::setVisible(visible); - if (!visible) { - m_height = 0; - recalculateConsolePosition(); - } -} - diff --git a/src/client/guiChatConsole.h b/src/client/guiChatConsole.h deleted file mode 100644 index 3013a1d31..000000000 --- a/src/client/guiChatConsole.h +++ /dev/null @@ -1,138 +0,0 @@ -/* -Minetest -Copyright (C) 2013 celeron55, Perttu Ahola - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation; either version 2.1 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License along -with this program; if not, write to the Free Software Foundation, Inc., -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -*/ - -#ifndef GUICHATCONSOLE_HEADER -#define GUICHATCONSOLE_HEADER - -#include "irrlichttypes_extrabloated.h" -#include "modalMenu.h" -#include "chat.h" -#include "config.h" - -class Client; - -class GUIChatConsole : public gui::IGUIElement -{ -public: - GUIChatConsole(gui::IGUIEnvironment* env, - gui::IGUIElement* parent, - s32 id, - ChatBackend* backend, - Client* client, - IMenuManager* menumgr); - virtual ~GUIChatConsole(); - - // Open the console (height = desired fraction of screen size) - // This doesn't open immediately but initiates an animation. - // You should call isOpenInhibited() before this. - void openConsole(f32 height); - - bool isOpen() const; - - // Check if the console should not be opened at the moment - // This is to avoid reopening the console immediately after closing - bool isOpenInhibited() const; - // Close the console, equivalent to openConsole(0). - // This doesn't close immediately but initiates an animation. - void closeConsole(); - // Close the console immediately, without animation. - void closeConsoleAtOnce(); - // Set whether to close the console after the user presses enter. - void setCloseOnEnter(bool close) { m_close_on_enter = close; } - - // Return the desired height (fraction of screen size) - // Zero if the console is closed or getting closed - f32 getDesiredHeight() const; - - // Replace actual line when adding the actual to the history (if there is any) - void replaceAndAddToHistory(std::wstring line); - - // Change how the cursor looks - void setCursor( - bool visible, - bool blinking = false, - f32 blink_speed = 1.0, - f32 relative_height = 1.0); - - // Irrlicht draw method - virtual void draw(); - - bool canTakeFocus(gui::IGUIElement* element) { return false; } - - virtual bool OnEvent(const SEvent& event); - - virtual void setVisible(bool visible); - -private: - void reformatConsole(); - void recalculateConsolePosition(); - - // These methods are called by draw - void animate(u32 msec); - void drawBackground(); - void drawText(); - void drawPrompt(); - -private: - ChatBackend* m_chat_backend; - Client* m_client; - IMenuManager* m_menumgr; - - // current screen size - v2u32 m_screensize; - - // used to compute how much time passed since last animate() - u32 m_animate_time_old; - - // should the console be opened or closed? - bool m_open; - // should it close after you press enter? - bool m_close_on_enter; - // current console height [pixels] - s32 m_height; - // desired height [pixels] - f32 m_desired_height; - // desired height [screen height fraction] - f32 m_desired_height_fraction; - // console open/close animation speed [screen height fraction / second] - f32 m_height_speed; - // if nonzero, opening the console is inhibited [milliseconds] - u32 m_open_inhibited; - - // cursor blink frame (16-bit value) - // cursor is off during [0,32767] and on during [32768,65535] - u32 m_cursor_blink; - // cursor blink speed [on/off toggles / second] - f32 m_cursor_blink_speed; - // cursor height [line height] - f32 m_cursor_height; - - // background texture - video::ITexture* m_background; - // background color (including alpha) - video::SColor m_background_color; - - // font - gui::IGUIFont* m_font; - v2u32 m_fontsize; -}; - - -#endif - diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index e8b652c17..632ec0df9 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -202,6 +202,8 @@ void set_default_settings(Settings *settings) settings->setDefault("server_name", ""); settings->setDefault("server_description", ""); + settings->setDefault("disable_escape_sequences", "false"); + #if USE_FREETYPE settings->setDefault("freetype", "true"); settings->setDefault("font_path", porting::getDataPath("fonts" DIR_DELIM "liberationsans.ttf")); diff --git a/src/game.cpp b/src/game.cpp index 71a04aef5..def202fe5 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -34,7 +34,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "log.h" #include "filesys.h" #include "gettext.h" -#include "client/guiChatConsole.h" +#include "guiChatConsole.h" #include "guiFormSpecMenu.h" #include "guiKeyChangeMenu.h" #include "guiPasswordChange.h" @@ -55,14 +55,11 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "tool.h" #include "util/directiontables.h" #include "util/pointedthing.h" +#include "irrlicht_changes/static_text.h" #include "version.h" #include "minimap.h" #include "mapblock_mesh.h" -#if USE_FREETYPE - #include "util/statictext.h" -#endif - #include "sound.h" #if USE_SOUND @@ -541,7 +538,7 @@ void update_profiler_gui(gui::IGUIStaticText *guitext_profiler, FontEngine *fe, std::ostringstream os(std::ios_base::binary); g_profiler->printPage(os, show_profiler, show_profiler_max); std::wstring text = utf8_to_wide(os.str()); - guitext_profiler->setText(text.c_str()); + setStaticText(guitext_profiler, text.c_str()); guitext_profiler->setVisible(true); s32 w = fe->getTextWidth(text.c_str()); @@ -1244,7 +1241,11 @@ static void updateChat(Client &client, f32 dtime, bool show_debug, // Get new messages from error log buffer while (!chat_log_error_buf.empty()) { - chat_backend.addMessage(L"", utf8_to_wide(chat_log_error_buf.get())); + std::wstring error_message = utf8_to_wide(chat_log_error_buf.get()); + if (!g_settings->getBool("disable_escape_sequences")) { + error_message = L"\x1b(c@red)" + error_message + L"\x1b(c@white)"; + } + chat_backend.addMessage(L"", error_message); } // Get new messages from client @@ -1259,10 +1260,10 @@ static void updateChat(Client &client, f32 dtime, bool show_debug, // Display all messages in a static text element unsigned int recent_chat_count = chat_backend.getRecentBuffer().getLineCount(); - std::wstring recent_chat = chat_backend.getRecentChat(); + EnrichedString recent_chat = chat_backend.getRecentChat(); unsigned int line_height = g_fontengine->getLineHeight(); - guitext_chat->setText(recent_chat.c_str()); + setStaticText(guitext_chat, recent_chat); // Update gui element size and position s32 chat_y = 5 + line_height; @@ -1271,7 +1272,7 @@ static void updateChat(Client &client, f32 dtime, bool show_debug, chat_y += line_height; // first pass to calculate height of text to be set - s32 width = std::min(g_fontengine->getTextWidth(recent_chat) + 10, + s32 width = std::min(g_fontengine->getTextWidth(recent_chat.c_str()) + 10, porting::getWindowSize().X - 20); core::rect rect(10, chat_y, width, chat_y + porting::getWindowSize().Y); guitext_chat->setRelativePosition(rect); @@ -2218,45 +2219,39 @@ bool Game::createClient(const std::string &playername, bool Game::initGui() { // First line of debug text - guitext = guienv->addStaticText( + guitext = addStaticText(guienv, utf8_to_wide(PROJECT_NAME_C).c_str(), core::rect(0, 0, 0, 0), false, false, guiroot); // Second line of debug text - guitext2 = guienv->addStaticText( + guitext2 = addStaticText(guienv, L"", core::rect(0, 0, 0, 0), false, false, guiroot); // At the middle of the screen // Object infos are shown in this - guitext_info = guienv->addStaticText( + guitext_info = addStaticText(guienv, L"", core::rect(0, 0, 400, g_fontengine->getTextHeight() * 5 + 5) + v2s32(100, 200), false, true, guiroot); // Status text (displays info when showing and hiding GUI stuff, etc.) - guitext_status = guienv->addStaticText( + guitext_status = addStaticText(guienv, L"", core::rect(0, 0, 0, 0), false, false, guiroot); guitext_status->setVisible(false); -#if USE_FREETYPE - // Colored chat support when using FreeType - guitext_chat = new gui::StaticText(L"", false, guienv, guiroot, -1, core::rect(0, 0, 0, 0), false); - guitext_chat->setWordWrap(true); - guitext_chat->drop(); -#else - // Standard chat when FreeType is disabled // Chat text - guitext_chat = guienv->addStaticText( + guitext_chat = addStaticText( + guienv, L"", core::rect(0, 0, 0, 0), //false, false); // Disable word wrap as of now false, true, guiroot); -#endif + // Remove stale "recent" chat messages from previous connections chat_backend->clearRecentChat(); @@ -2270,7 +2265,7 @@ bool Game::initGui() } // Profiler text (size is updated when text is updated) - guitext_profiler = guienv->addStaticText( + guitext_profiler = addStaticText(guienv, L"", core::rect(0, 0, 0, 0), false, false, guiroot); @@ -4308,12 +4303,12 @@ void Game::updateGui(float *statustext_time, const RunStats &stats, << ", v_range = " << draw_control->wanted_range << std::setprecision(3) << ", RTT = " << client->getRTT(); - guitext->setText(utf8_to_wide(os.str()).c_str()); + setStaticText(guitext, utf8_to_wide(os.str()).c_str()); guitext->setVisible(true); } else if (flags.show_hud || flags.show_chat) { std::ostringstream os(std::ios_base::binary); os << PROJECT_NAME_C " " << g_version_hash; - guitext->setText(utf8_to_wide(os.str()).c_str()); + setStaticText(guitext, utf8_to_wide(os.str()).c_str()); guitext->setVisible(true); } else { guitext->setVisible(false); @@ -4350,7 +4345,7 @@ void Game::updateGui(float *statustext_time, const RunStats &stats, } } - guitext2->setText(utf8_to_wide(os.str()).c_str()); + setStaticText(guitext2, utf8_to_wide(os.str()).c_str()); guitext2->setVisible(true); core::rect rect( @@ -4362,7 +4357,7 @@ void Game::updateGui(float *statustext_time, const RunStats &stats, guitext2->setVisible(false); } - guitext_info->setText(infotext.c_str()); + setStaticText(guitext_info, infotext.c_str()); guitext_info->setVisible(flags.show_hud && g_menumgr.menuCount() == 0); float statustext_time_max = 1.5; @@ -4376,7 +4371,7 @@ void Game::updateGui(float *statustext_time, const RunStats &stats, } } - guitext_status->setText(statustext.c_str()); + setStaticText(guitext_status, statustext.c_str()); guitext_status->setVisible(!statustext.empty()); if (!statustext.empty()) { diff --git a/src/guiChatConsole.cpp b/src/guiChatConsole.cpp new file mode 100644 index 000000000..bb58d1305 --- /dev/null +++ b/src/guiChatConsole.cpp @@ -0,0 +1,664 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "guiChatConsole.h" +#include "chat.h" +#include "client.h" +#include "debug.h" +#include "gettime.h" +#include "keycode.h" +#include "settings.h" +#include "porting.h" +#include "client/tile.h" +#include "fontengine.h" +#include "log.h" +#include "gettext.h" +#include + +#if USE_FREETYPE + #include "xCGUITTFont.h" +#endif + +inline u32 clamp_u8(s32 value) +{ + return (u32) MYMIN(MYMAX(value, 0), 255); +} + + +GUIChatConsole::GUIChatConsole( + gui::IGUIEnvironment* env, + gui::IGUIElement* parent, + s32 id, + ChatBackend* backend, + Client* client, + IMenuManager* menumgr +): + IGUIElement(gui::EGUIET_ELEMENT, env, parent, id, + core::rect(0,0,100,100)), + m_chat_backend(backend), + m_client(client), + m_menumgr(menumgr), + m_screensize(v2u32(0,0)), + m_animate_time_old(0), + m_open(false), + m_close_on_enter(false), + m_height(0), + m_desired_height(0), + m_desired_height_fraction(0.0), + m_height_speed(5.0), + m_open_inhibited(0), + m_cursor_blink(0.0), + m_cursor_blink_speed(0.0), + m_cursor_height(0.0), + m_background(NULL), + m_background_color(255, 0, 0, 0), + m_font(NULL), + m_fontsize(0, 0) +{ + m_animate_time_old = getTimeMs(); + + // load background settings + s32 console_alpha = g_settings->getS32("console_alpha"); + m_background_color.setAlpha(clamp_u8(console_alpha)); + + // load the background texture depending on settings + ITextureSource *tsrc = client->getTextureSource(); + if (tsrc->isKnownSourceImage("background_chat.jpg")) { + m_background = tsrc->getTexture("background_chat.jpg"); + m_background_color.setRed(255); + m_background_color.setGreen(255); + m_background_color.setBlue(255); + } else { + v3f console_color = g_settings->getV3F("console_color"); + m_background_color.setRed(clamp_u8(myround(console_color.X))); + m_background_color.setGreen(clamp_u8(myround(console_color.Y))); + m_background_color.setBlue(clamp_u8(myround(console_color.Z))); + } + + m_font = g_fontengine->getFont(FONT_SIZE_UNSPECIFIED, FM_Mono); + + if (m_font == NULL) + { + errorstream << "GUIChatConsole: Unable to load mono font "; + } + else + { + core::dimension2d dim = m_font->getDimension(L"M"); + m_fontsize = v2u32(dim.Width, dim.Height); + m_font->grab(); + } + m_fontsize.X = MYMAX(m_fontsize.X, 1); + m_fontsize.Y = MYMAX(m_fontsize.Y, 1); + + // set default cursor options + setCursor(true, true, 2.0, 0.1); +} + +GUIChatConsole::~GUIChatConsole() +{ + if (m_font) + m_font->drop(); +} + +void GUIChatConsole::openConsole(f32 height) +{ + m_open = true; + m_desired_height_fraction = height; + m_desired_height = height * m_screensize.Y; + reformatConsole(); + m_animate_time_old = getTimeMs(); + IGUIElement::setVisible(true); + Environment->setFocus(this); + m_menumgr->createdMenu(this); +} + +bool GUIChatConsole::isOpen() const +{ + return m_open; +} + +bool GUIChatConsole::isOpenInhibited() const +{ + return m_open_inhibited > 0; +} + +void GUIChatConsole::closeConsole() +{ + m_open = false; + Environment->removeFocus(this); + m_menumgr->deletingMenu(this); +} + +void GUIChatConsole::closeConsoleAtOnce() +{ + closeConsole(); + m_height = 0; + recalculateConsolePosition(); +} + +f32 GUIChatConsole::getDesiredHeight() const +{ + return m_desired_height_fraction; +} + +void GUIChatConsole::replaceAndAddToHistory(std::wstring line) +{ + ChatPrompt& prompt = m_chat_backend->getPrompt(); + prompt.addToHistory(prompt.getLine()); + prompt.replace(line); +} + + +void GUIChatConsole::setCursor( + bool visible, bool blinking, f32 blink_speed, f32 relative_height) +{ + if (visible) + { + if (blinking) + { + // leave m_cursor_blink unchanged + m_cursor_blink_speed = blink_speed; + } + else + { + m_cursor_blink = 0x8000; // on + m_cursor_blink_speed = 0.0; + } + } + else + { + m_cursor_blink = 0; // off + m_cursor_blink_speed = 0.0; + } + m_cursor_height = relative_height; +} + +void GUIChatConsole::draw() +{ + if(!IsVisible) + return; + + video::IVideoDriver* driver = Environment->getVideoDriver(); + + // Check screen size + v2u32 screensize = driver->getScreenSize(); + if (screensize != m_screensize) + { + // screen size has changed + // scale current console height to new window size + if (m_screensize.Y != 0) + m_height = m_height * screensize.Y / m_screensize.Y; + m_desired_height = m_desired_height_fraction * m_screensize.Y; + m_screensize = screensize; + reformatConsole(); + } + + // Animation + u32 now = getTimeMs(); + animate(now - m_animate_time_old); + m_animate_time_old = now; + + // Draw console elements if visible + if (m_height > 0) + { + drawBackground(); + drawText(); + drawPrompt(); + } + + gui::IGUIElement::draw(); +} + +void GUIChatConsole::reformatConsole() +{ + s32 cols = m_screensize.X / m_fontsize.X - 2; // make room for a margin (looks better) + s32 rows = m_desired_height / m_fontsize.Y - 1; // make room for the input prompt + if (cols <= 0 || rows <= 0) + cols = rows = 0; + m_chat_backend->reformat(cols, rows); +} + +void GUIChatConsole::recalculateConsolePosition() +{ + core::rect rect(0, 0, m_screensize.X, m_height); + DesiredRect = rect; + recalculateAbsolutePosition(false); +} + +void GUIChatConsole::animate(u32 msec) +{ + // animate the console height + s32 goal = m_open ? m_desired_height : 0; + + // Set invisible if close animation finished (reset by openConsole) + // This function (animate()) is never called once its visibility becomes false so do not + // actually set visible to false before the inhibited period is over + if (!m_open && m_height == 0 && m_open_inhibited == 0) + IGUIElement::setVisible(false); + + if (m_height != goal) + { + s32 max_change = msec * m_screensize.Y * (m_height_speed / 1000.0); + if (max_change == 0) + max_change = 1; + + if (m_height < goal) + { + // increase height + if (m_height + max_change < goal) + m_height += max_change; + else + m_height = goal; + } + else + { + // decrease height + if (m_height > goal + max_change) + m_height -= max_change; + else + m_height = goal; + } + + recalculateConsolePosition(); + } + + // blink the cursor + if (m_cursor_blink_speed != 0.0) + { + u32 blink_increase = 0x10000 * msec * (m_cursor_blink_speed / 1000.0); + if (blink_increase == 0) + blink_increase = 1; + m_cursor_blink = ((m_cursor_blink + blink_increase) & 0xffff); + } + + // decrease open inhibit counter + if (m_open_inhibited > msec) + m_open_inhibited -= msec; + else + m_open_inhibited = 0; +} + +void GUIChatConsole::drawBackground() +{ + video::IVideoDriver* driver = Environment->getVideoDriver(); + if (m_background != NULL) + { + core::rect sourcerect(0, -m_height, m_screensize.X, 0); + driver->draw2DImage( + m_background, + v2s32(0, 0), + sourcerect, + &AbsoluteClippingRect, + m_background_color, + false); + } + else + { + driver->draw2DRectangle( + m_background_color, + core::rect(0, 0, m_screensize.X, m_height), + &AbsoluteClippingRect); + } +} + +void GUIChatConsole::drawText() +{ + if (m_font == NULL) + return; + + ChatBuffer& buf = m_chat_backend->getConsoleBuffer(); + for (u32 row = 0; row < buf.getRows(); ++row) + { + const ChatFormattedLine& line = buf.getFormattedLine(row); + if (line.fragments.empty()) + continue; + + s32 line_height = m_fontsize.Y; + s32 y = row * line_height + m_height - m_desired_height; + if (y + line_height < 0) + continue; + + for (u32 i = 0; i < line.fragments.size(); ++i) + { + const ChatFormattedFragment& fragment = line.fragments[i]; + s32 x = (fragment.column + 1) * m_fontsize.X; + core::rect destrect( + x, y, x + m_fontsize.X * fragment.text.size(), y + m_fontsize.Y); + + + #if USE_FREETYPE + // Draw colored text if FreeType is enabled + irr::gui::CGUITTFont *tmp = static_cast(m_font); + tmp->draw( + fragment.text, + destrect, + video::SColor(255, 255, 255, 255), + false, + false, + &AbsoluteClippingRect); + #else + // Otherwise use standard text + m_font->draw( + fragment.text.c_str(), + destrect, + video::SColor(255, 255, 255, 255), + false, + false, + &AbsoluteClippingRect); + #endif + } + } +} + +void GUIChatConsole::drawPrompt() +{ + if (m_font == NULL) + return; + + u32 row = m_chat_backend->getConsoleBuffer().getRows(); + s32 line_height = m_fontsize.Y; + s32 y = row * line_height + m_height - m_desired_height; + + ChatPrompt& prompt = m_chat_backend->getPrompt(); + std::wstring prompt_text = prompt.getVisiblePortion(); + + // FIXME Draw string at once, not character by character + // That will only work with the cursor once we have a monospace font + for (u32 i = 0; i < prompt_text.size(); ++i) + { + wchar_t ws[2] = {prompt_text[i], 0}; + s32 x = (1 + i) * m_fontsize.X; + core::rect destrect( + x, y, x + m_fontsize.X, y + m_fontsize.Y); + m_font->draw( + ws, + destrect, + video::SColor(255, 255, 255, 255), + false, + false, + &AbsoluteClippingRect); + } + + // Draw the cursor during on periods + if ((m_cursor_blink & 0x8000) != 0) + { + s32 cursor_pos = prompt.getVisibleCursorPosition(); + if (cursor_pos >= 0) + { + s32 cursor_len = prompt.getCursorLength(); + video::IVideoDriver* driver = Environment->getVideoDriver(); + s32 x = (1 + cursor_pos) * m_fontsize.X; + core::rect destrect( + x, + y + m_fontsize.Y * (1.0 - m_cursor_height), + x + m_fontsize.X * MYMAX(cursor_len, 1), + y + m_fontsize.Y * (cursor_len ? m_cursor_height+1 : 1) + ); + video::SColor cursor_color(255,255,255,255); + driver->draw2DRectangle( + cursor_color, + destrect, + &AbsoluteClippingRect); + } + } + +} + +bool GUIChatConsole::OnEvent(const SEvent& event) +{ + + ChatPrompt &prompt = m_chat_backend->getPrompt(); + + if(event.EventType == EET_KEY_INPUT_EVENT && event.KeyInput.PressedDown) + { + // Key input + if(KeyPress(event.KeyInput) == getKeySetting("keymap_console")) + { + closeConsole(); + + // inhibit open so the_game doesn't reopen immediately + m_open_inhibited = 50; + m_close_on_enter = false; + return true; + } + else if(event.KeyInput.Key == KEY_ESCAPE) + { + closeConsoleAtOnce(); + m_close_on_enter = false; + // inhibit open so the_game doesn't reopen immediately + m_open_inhibited = 1; // so the ESCAPE button doesn't open the "pause menu" + return true; + } + else if(event.KeyInput.Key == KEY_PRIOR) + { + m_chat_backend->scrollPageUp(); + return true; + } + else if(event.KeyInput.Key == KEY_NEXT) + { + m_chat_backend->scrollPageDown(); + return true; + } + else if(event.KeyInput.Key == KEY_RETURN) + { + prompt.addToHistory(prompt.getLine()); + std::wstring text = prompt.replace(L""); + m_client->typeChatMessage(text); + if (m_close_on_enter) { + closeConsoleAtOnce(); + m_close_on_enter = false; + } + return true; + } + else if(event.KeyInput.Key == KEY_UP) + { + // Up pressed + // Move back in history + prompt.historyPrev(); + return true; + } + else if(event.KeyInput.Key == KEY_DOWN) + { + // Down pressed + // Move forward in history + prompt.historyNext(); + return true; + } + else if(event.KeyInput.Key == KEY_LEFT || event.KeyInput.Key == KEY_RIGHT) + { + // Left/right pressed + // Move/select character/word to the left depending on control and shift keys + ChatPrompt::CursorOp op = event.KeyInput.Shift ? + ChatPrompt::CURSOROP_SELECT : + ChatPrompt::CURSOROP_MOVE; + ChatPrompt::CursorOpDir dir = event.KeyInput.Key == KEY_LEFT ? + ChatPrompt::CURSOROP_DIR_LEFT : + ChatPrompt::CURSOROP_DIR_RIGHT; + ChatPrompt::CursorOpScope scope = event.KeyInput.Control ? + ChatPrompt::CURSOROP_SCOPE_WORD : + ChatPrompt::CURSOROP_SCOPE_CHARACTER; + prompt.cursorOperation(op, dir, scope); + return true; + } + else if(event.KeyInput.Key == KEY_HOME) + { + // Home pressed + // move to beginning of line + prompt.cursorOperation( + ChatPrompt::CURSOROP_MOVE, + ChatPrompt::CURSOROP_DIR_LEFT, + ChatPrompt::CURSOROP_SCOPE_LINE); + return true; + } + else if(event.KeyInput.Key == KEY_END) + { + // End pressed + // move to end of line + prompt.cursorOperation( + ChatPrompt::CURSOROP_MOVE, + ChatPrompt::CURSOROP_DIR_RIGHT, + ChatPrompt::CURSOROP_SCOPE_LINE); + return true; + } + else if(event.KeyInput.Key == KEY_BACK) + { + // Backspace or Ctrl-Backspace pressed + // delete character / word to the left + ChatPrompt::CursorOpScope scope = + event.KeyInput.Control ? + ChatPrompt::CURSOROP_SCOPE_WORD : + ChatPrompt::CURSOROP_SCOPE_CHARACTER; + prompt.cursorOperation( + ChatPrompt::CURSOROP_DELETE, + ChatPrompt::CURSOROP_DIR_LEFT, + scope); + return true; + } + else if(event.KeyInput.Key == KEY_DELETE) + { + // Delete or Ctrl-Delete pressed + // delete character / word to the right + ChatPrompt::CursorOpScope scope = + event.KeyInput.Control ? + ChatPrompt::CURSOROP_SCOPE_WORD : + ChatPrompt::CURSOROP_SCOPE_CHARACTER; + prompt.cursorOperation( + ChatPrompt::CURSOROP_DELETE, + ChatPrompt::CURSOROP_DIR_RIGHT, + scope); + return true; + } + else if(event.KeyInput.Key == KEY_KEY_A && event.KeyInput.Control) + { + // Ctrl-A pressed + // Select all text + prompt.cursorOperation( + ChatPrompt::CURSOROP_SELECT, + ChatPrompt::CURSOROP_DIR_LEFT, // Ignored + ChatPrompt::CURSOROP_SCOPE_LINE); + return true; + } + else if(event.KeyInput.Key == KEY_KEY_C && event.KeyInput.Control) + { + // Ctrl-C pressed + // Copy text to clipboard + if (prompt.getCursorLength() <= 0) + return true; + std::wstring wselected = prompt.getSelection(); + std::string selected(wselected.begin(), wselected.end()); + Environment->getOSOperator()->copyToClipboard(selected.c_str()); + return true; + } + else if(event.KeyInput.Key == KEY_KEY_V && event.KeyInput.Control) + { + // Ctrl-V pressed + // paste text from clipboard + if (prompt.getCursorLength() > 0) { + // Delete selected section of text + prompt.cursorOperation( + ChatPrompt::CURSOROP_DELETE, + ChatPrompt::CURSOROP_DIR_LEFT, // Ignored + ChatPrompt::CURSOROP_SCOPE_SELECTION); + } + IOSOperator *os_operator = Environment->getOSOperator(); + const c8 *text = os_operator->getTextFromClipboard(); + if (!text) + return true; + std::basic_string str((const unsigned char*)text); + prompt.input(std::wstring(str.begin(), str.end())); + return true; + } + else if(event.KeyInput.Key == KEY_KEY_X && event.KeyInput.Control) + { + // Ctrl-X pressed + // Cut text to clipboard + if (prompt.getCursorLength() <= 0) + return true; + std::wstring wselected = prompt.getSelection(); + std::string selected(wselected.begin(), wselected.end()); + Environment->getOSOperator()->copyToClipboard(selected.c_str()); + prompt.cursorOperation( + ChatPrompt::CURSOROP_DELETE, + ChatPrompt::CURSOROP_DIR_LEFT, // Ignored + ChatPrompt::CURSOROP_SCOPE_SELECTION); + return true; + } + else if(event.KeyInput.Key == KEY_KEY_U && event.KeyInput.Control) + { + // Ctrl-U pressed + // kill line to left end + prompt.cursorOperation( + ChatPrompt::CURSOROP_DELETE, + ChatPrompt::CURSOROP_DIR_LEFT, + ChatPrompt::CURSOROP_SCOPE_LINE); + return true; + } + else if(event.KeyInput.Key == KEY_KEY_K && event.KeyInput.Control) + { + // Ctrl-K pressed + // kill line to right end + prompt.cursorOperation( + ChatPrompt::CURSOROP_DELETE, + ChatPrompt::CURSOROP_DIR_RIGHT, + ChatPrompt::CURSOROP_SCOPE_LINE); + return true; + } + else if(event.KeyInput.Key == KEY_TAB) + { + // Tab or Shift-Tab pressed + // Nick completion + std::list names = m_client->getConnectedPlayerNames(); + bool backwards = event.KeyInput.Shift; + prompt.nickCompletion(names, backwards); + return true; + } + else if(event.KeyInput.Char != 0 && !event.KeyInput.Control) + { + #if (defined(linux) || defined(__linux)) + wchar_t wc = L'_'; + mbtowc( &wc, (char *) &event.KeyInput.Char, sizeof(event.KeyInput.Char) ); + prompt.input(wc); + #else + prompt.input(event.KeyInput.Char); + #endif + return true; + } + } + else if(event.EventType == EET_MOUSE_INPUT_EVENT) + { + if(event.MouseInput.Event == EMIE_MOUSE_WHEEL) + { + s32 rows = myround(-3.0 * event.MouseInput.Wheel); + m_chat_backend->scroll(rows); + } + } + + return Parent ? Parent->OnEvent(event) : false; +} + +void GUIChatConsole::setVisible(bool visible) +{ + m_open = visible; + IGUIElement::setVisible(visible); + if (!visible) { + m_height = 0; + recalculateConsolePosition(); + } +} + diff --git a/src/guiChatConsole.h b/src/guiChatConsole.h new file mode 100644 index 000000000..3013a1d31 --- /dev/null +++ b/src/guiChatConsole.h @@ -0,0 +1,138 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef GUICHATCONSOLE_HEADER +#define GUICHATCONSOLE_HEADER + +#include "irrlichttypes_extrabloated.h" +#include "modalMenu.h" +#include "chat.h" +#include "config.h" + +class Client; + +class GUIChatConsole : public gui::IGUIElement +{ +public: + GUIChatConsole(gui::IGUIEnvironment* env, + gui::IGUIElement* parent, + s32 id, + ChatBackend* backend, + Client* client, + IMenuManager* menumgr); + virtual ~GUIChatConsole(); + + // Open the console (height = desired fraction of screen size) + // This doesn't open immediately but initiates an animation. + // You should call isOpenInhibited() before this. + void openConsole(f32 height); + + bool isOpen() const; + + // Check if the console should not be opened at the moment + // This is to avoid reopening the console immediately after closing + bool isOpenInhibited() const; + // Close the console, equivalent to openConsole(0). + // This doesn't close immediately but initiates an animation. + void closeConsole(); + // Close the console immediately, without animation. + void closeConsoleAtOnce(); + // Set whether to close the console after the user presses enter. + void setCloseOnEnter(bool close) { m_close_on_enter = close; } + + // Return the desired height (fraction of screen size) + // Zero if the console is closed or getting closed + f32 getDesiredHeight() const; + + // Replace actual line when adding the actual to the history (if there is any) + void replaceAndAddToHistory(std::wstring line); + + // Change how the cursor looks + void setCursor( + bool visible, + bool blinking = false, + f32 blink_speed = 1.0, + f32 relative_height = 1.0); + + // Irrlicht draw method + virtual void draw(); + + bool canTakeFocus(gui::IGUIElement* element) { return false; } + + virtual bool OnEvent(const SEvent& event); + + virtual void setVisible(bool visible); + +private: + void reformatConsole(); + void recalculateConsolePosition(); + + // These methods are called by draw + void animate(u32 msec); + void drawBackground(); + void drawText(); + void drawPrompt(); + +private: + ChatBackend* m_chat_backend; + Client* m_client; + IMenuManager* m_menumgr; + + // current screen size + v2u32 m_screensize; + + // used to compute how much time passed since last animate() + u32 m_animate_time_old; + + // should the console be opened or closed? + bool m_open; + // should it close after you press enter? + bool m_close_on_enter; + // current console height [pixels] + s32 m_height; + // desired height [pixels] + f32 m_desired_height; + // desired height [screen height fraction] + f32 m_desired_height_fraction; + // console open/close animation speed [screen height fraction / second] + f32 m_height_speed; + // if nonzero, opening the console is inhibited [milliseconds] + u32 m_open_inhibited; + + // cursor blink frame (16-bit value) + // cursor is off during [0,32767] and on during [32768,65535] + u32 m_cursor_blink; + // cursor blink speed [on/off toggles / second] + f32 m_cursor_blink_speed; + // cursor height [line height] + f32 m_cursor_height; + + // background texture + video::ITexture* m_background; + // background color (including alpha) + video::SColor m_background_color; + + // font + gui::IGUIFont* m_font; + v2u32 m_fontsize; +}; + + +#endif + diff --git a/src/guiEngine.cpp b/src/guiEngine.cpp index ba286a91c..96de7a4f7 100644 --- a/src/guiEngine.cpp +++ b/src/guiEngine.cpp @@ -37,6 +37,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "log.h" #include "fontengine.h" #include "guiscalingfilter.h" +#include "irrlicht_changes/static_text.h" #ifdef __ANDROID__ #include "client/tile.h" @@ -172,15 +173,16 @@ GUIEngine::GUIEngine( irr::IrrlichtDevice* dev, m_sound_manager = &dummySoundManager; //create topleft header - std::wstring t = utf8_to_wide(std::string(PROJECT_NAME_C " ") + + m_toplefttext = utf8_to_wide(std::string(PROJECT_NAME_C " ") + g_version_hash); - core::rect rect(0, 0, g_fontengine->getTextWidth(t), g_fontengine->getTextHeight()); + core::rect rect(0, 0, g_fontengine->getTextWidth(m_toplefttext.c_str()), + g_fontengine->getTextHeight()); rect += v2s32(4, 0); m_irr_toplefttext = - m_device->getGUIEnvironment()->addStaticText(t.c_str(), - rect,false,true,0,-1); + addStaticText(m_device->getGUIEnvironment(), m_toplefttext, + rect, false, true, 0, -1); //create formspecsource m_formspecgui = new FormspecFormSource(""); @@ -578,7 +580,7 @@ void GUIEngine::setTopleftText(std::string append) toset += utf8_to_wide(append); } - m_irr_toplefttext->setText(toset.c_str()); + m_toplefttext = toset; updateTopLeftTextSize(); } @@ -586,15 +588,14 @@ void GUIEngine::setTopleftText(std::string append) /******************************************************************************/ void GUIEngine::updateTopLeftTextSize() { - std::wstring text = m_irr_toplefttext->getText(); - - core::rect rect(0, 0, g_fontengine->getTextWidth(text), g_fontengine->getTextHeight()); - rect += v2s32(4, 0); + core::rect rect(0, 0, g_fontengine->getTextWidth(m_toplefttext.c_str()), + g_fontengine->getTextHeight()); + rect += v2s32(4, 0); m_irr_toplefttext->remove(); m_irr_toplefttext = - m_device->getGUIEnvironment()->addStaticText(text.c_str(), - rect,false,true,0,-1); + addStaticText(m_device->getGUIEnvironment(), m_toplefttext, + rect, false, true, 0, -1); } /******************************************************************************/ diff --git a/src/guiEngine.h b/src/guiEngine.h index d527f7222..fa98a21e4 100644 --- a/src/guiEngine.h +++ b/src/guiEngine.h @@ -28,6 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "guiFormSpecMenu.h" #include "sound.h" #include "client/tile.h" +#include "util/enriched_string.h" /******************************************************************************/ /* Typedefs and macros */ @@ -275,6 +276,8 @@ private: /** pointer to gui element shown at topleft corner */ irr::gui::IGUIStaticText* m_irr_toplefttext; + /** and text that is in it */ + EnrichedString m_toplefttext; /** initialize cloud subsystem */ void cloudInit(); diff --git a/src/guiFormSpecMenu.cpp b/src/guiFormSpecMenu.cpp index 99b1153c1..cf01dc38c 100644 --- a/src/guiFormSpecMenu.cpp +++ b/src/guiFormSpecMenu.cpp @@ -50,6 +50,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/hex.h" #include "util/numeric.h" #include "util/string.h" // for parseColorString() +#include "irrlicht_changes/static_text.h" #include "guiscalingfilter.h" #if USE_FREETYPE && IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 9 @@ -249,37 +250,6 @@ std::vector* GUIFormSpecMenu::getDropDownValues(const std::string & return NULL; } -static std::vector split(const std::string &s, char delim) -{ - std::vector tokens; - - std::string current = ""; - bool last_was_escape = false; - for (unsigned int i = 0; i < s.size(); i++) { - char si = s.c_str()[i]; - if (last_was_escape) { - current += '\\'; - current += si; - last_was_escape = false; - } else { - if (si == delim) { - tokens.push_back(current); - current = ""; - last_was_escape = false; - } else if (si == '\\') { - last_was_escape = true; - } else { - current += si; - last_was_escape = false; - } - } - } - //push last element - tokens.push_back(current); - - return tokens; -} - void GUIFormSpecMenu::parseSize(parserData* data,std::string element) { std::vector parts = split(element,','); @@ -966,7 +936,7 @@ void GUIFormSpecMenu::parsePwdField(parserData* data,std::string element) int font_height = g_fontengine->getTextHeight(); rect.UpperLeftCorner.Y -= font_height; rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + font_height; - Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, 0); + addStaticText(Environment, spec.flabel.c_str(), rect, false, true, this, 0); } e->setPasswordBox(true,L'*'); @@ -1021,7 +991,7 @@ void GUIFormSpecMenu::parseSimpleField(parserData* data, if (name == "") { // spec field id to 0, this stops submit searching for a value that isn't there - Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, spec.fid); + addStaticText(Environment, spec.flabel.c_str(), rect, false, true, this, spec.fid); } else { @@ -1056,7 +1026,7 @@ void GUIFormSpecMenu::parseSimpleField(parserData* data, int font_height = g_fontengine->getTextHeight(); rect.UpperLeftCorner.Y -= font_height; rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + font_height; - Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, 0); + addStaticText(Environment, spec.flabel.c_str(), rect, false, true, this, 0); } } @@ -1117,7 +1087,7 @@ void GUIFormSpecMenu::parseTextArea(parserData* data, if (name == "") { // spec field id to 0, this stops submit searching for a value that isn't there - Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, spec.fid); + addStaticText(Environment, spec.flabel.c_str(), rect, false, true, this, spec.fid); } else { @@ -1161,7 +1131,7 @@ void GUIFormSpecMenu::parseTextArea(parserData* data, int font_height = g_fontengine->getTextHeight(); rect.UpperLeftCorner.Y -= font_height; rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + font_height; - Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, 0); + addStaticText(Environment, spec.flabel.c_str(), rect, false, true, this, 0); } } m_fields.push_back(spec); @@ -1230,7 +1200,7 @@ void GUIFormSpecMenu::parseLabel(parserData* data,std::string element) 258+m_fields.size() ); gui::IGUIStaticText *e = - Environment->addStaticText(spec.flabel.c_str(), + addStaticText(Environment, spec.flabel.c_str(), rect, false, false, this, spec.fid); e->setTextAlignment(gui::EGUIA_UPPERLEFT, gui::EGUIA_CENTER); @@ -1284,7 +1254,7 @@ void GUIFormSpecMenu::parseVertLabel(parserData* data,std::string element) 258+m_fields.size() ); gui::IGUIStaticText *t = - Environment->addStaticText(spec.flabel.c_str(), rect, false, false, this, spec.fid); + addStaticText(Environment, spec.flabel.c_str(), rect, false, false, this, spec.fid); t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER); m_fields.push_back(spec); return; @@ -1910,7 +1880,7 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) { assert(m_tooltip_element == NULL); // Note: parent != this so that the tooltip isn't clipped by the menu rectangle - m_tooltip_element = Environment->addStaticText(L"",core::rect(0,0,110,18)); + m_tooltip_element = addStaticText(Environment, L"",core::rect(0,0,110,18)); m_tooltip_element->enableOverrideColor(true); m_tooltip_element->setBackgroundColor(m_default_tooltip_bgcolor); m_tooltip_element->setDrawBackground(true); @@ -2255,7 +2225,6 @@ void GUIFormSpecMenu::drawList(const ListDrawSpec &s, int phase, std::wstring tooltip_text = L""; if (hovering && !m_selected_item) { tooltip_text = utf8_to_wide(item.getDefinition(m_gamedef->idef()).description); - tooltip_text = unescape_enriched(tooltip_text); } if (tooltip_text != L"") { std::vector tt_rows = str_split(tooltip_text, L'\n'); @@ -2263,7 +2232,7 @@ void GUIFormSpecMenu::drawList(const ListDrawSpec &s, int phase, m_tooltip_element->setOverrideColor(m_default_tooltip_color); m_tooltip_element->setVisible(true); this->bringToFront(m_tooltip_element); - m_tooltip_element->setText(tooltip_text.c_str()); + setStaticText(m_tooltip_element, tooltip_text.c_str()); s32 tooltip_width = m_tooltip_element->getTextWidth() + m_btn_height; #if IRRLICHT_VERSION_MAJOR <= 1 && IRRLICHT_VERSION_MINOR <= 8 && IRRLICHT_VERSION_REVISION < 2 s32 tooltip_height = m_tooltip_element->getTextHeight() * tt_rows.size() + 5; @@ -2535,8 +2504,10 @@ void GUIFormSpecMenu::drawMenu() iter != m_fields.end(); ++iter) { if (iter->fid == id && m_tooltips[iter->fname].tooltip != L"") { if (m_old_tooltip != m_tooltips[iter->fname].tooltip) { + m_tooltip_element->setBackgroundColor(m_tooltips[iter->fname].bgcolor); + m_tooltip_element->setOverrideColor(m_tooltips[iter->fname].color); m_old_tooltip = m_tooltips[iter->fname].tooltip; - m_tooltip_element->setText(m_tooltips[iter->fname].tooltip.c_str()); + setStaticText(m_tooltip_element, m_tooltips[iter->fname].tooltip.c_str()); std::vector tt_rows = str_split(m_tooltips[iter->fname].tooltip, L'\n'); s32 tooltip_width = m_tooltip_element->getTextWidth() + m_btn_height; s32 tooltip_height = m_tooltip_element->getTextHeight() * tt_rows.size() + 5; @@ -2558,8 +2529,6 @@ void GUIFormSpecMenu::drawMenu() core::position2d(tooltip_x, tooltip_y), core::dimension2d(tooltip_width, tooltip_height))); } - m_tooltip_element->setBackgroundColor(m_tooltips[iter->fname].bgcolor); - m_tooltip_element->setOverrideColor(m_tooltips[iter->fname].color); m_tooltip_element->setVisible(true); this->bringToFront(m_tooltip_element); break; @@ -2568,6 +2537,8 @@ void GUIFormSpecMenu::drawMenu() } } + m_tooltip_element->draw(); + /* Draw dragged item stack */ diff --git a/src/guiFormSpecMenu.h b/src/guiFormSpecMenu.h index ef230c81c..4122b1f56 100644 --- a/src/guiFormSpecMenu.h +++ b/src/guiFormSpecMenu.h @@ -30,6 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "guiTable.h" #include "network/networkprotocol.h" #include "util/string.h" +#include "util/enriched_string.h" class IGameDef; class InventoryManager; @@ -202,7 +203,8 @@ class GUIFormSpecMenu : public GUIModalMenu fname(name), fid(id) { - flabel = unescape_enriched(label); + //flabel = unescape_enriched(label); + flabel = label; fdefault = unescape_enriched(default_text); send = false; ftype = f_Unknown; @@ -239,7 +241,8 @@ class GUIFormSpecMenu : public GUIModalMenu bgcolor(a_bgcolor), color(a_color) { - tooltip = unescape_enriched(utf8_to_wide(a_tooltip)); + //tooltip = unescape_enriched(utf8_to_wide(a_tooltip)); + tooltip = utf8_to_wide(a_tooltip); } std::wstring tooltip; irr::video::SColor bgcolor; @@ -256,7 +259,8 @@ class GUIFormSpecMenu : public GUIModalMenu rect(a_rect), parent_button(NULL) { - text = unescape_enriched(a_text); + //text = unescape_enriched(a_text); + text = a_text; } StaticTextSpec(const std::wstring &a_text, const core::rect &a_rect, @@ -264,7 +268,8 @@ class GUIFormSpecMenu : public GUIModalMenu rect(a_rect), parent_button(a_parent_button) { - text = unescape_enriched(a_text); + //text = unescape_enriched(a_text); + text = a_text; } std::wstring text; core::rect rect; diff --git a/src/irrlicht_changes/CMakeLists.txt b/src/irrlicht_changes/CMakeLists.txt new file mode 100644 index 000000000..3a265c99d --- /dev/null +++ b/src/irrlicht_changes/CMakeLists.txt @@ -0,0 +1,7 @@ +if (BUILD_CLIENT) + set(client_irrlicht_changes_SRCS + ${CMAKE_CURRENT_SOURCE_DIR}/static_text.cpp + PARENT_SCOPE + ) +endif() + diff --git a/src/irrlicht_changes/static_text.cpp b/src/irrlicht_changes/static_text.cpp new file mode 100644 index 000000000..703287eb3 --- /dev/null +++ b/src/irrlicht_changes/static_text.cpp @@ -0,0 +1,679 @@ +// Copyright (C) 2002-2012 Nikolaus Gebhardt +// Copyright (C) 2016 Nathanaël Courant: +// Modified the functions to use EnrichedText instead of string. +// This file is part of the "Irrlicht Engine". +// For conditions of distribution and use, see copyright notice in irrlicht.h + +#include "static_text.h" +#ifdef _IRR_COMPILE_WITH_GUI_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if USE_FREETYPE + #include "cguittfont/xCGUITTFont.h" +#endif + +#include "util/string.h" + +namespace irr +{ + +#if USE_FREETYPE + +namespace gui +{ +//! constructor +StaticText::StaticText(const EnrichedString &text, bool border, + IGUIEnvironment* environment, IGUIElement* parent, + s32 id, const core::rect& rectangle, + bool background) +: IGUIStaticText(environment, parent, id, rectangle), + HAlign(EGUIA_UPPERLEFT), VAlign(EGUIA_UPPERLEFT), + Border(border), OverrideColorEnabled(false), OverrideBGColorEnabled(false), WordWrap(false), Background(background), + RestrainTextInside(true), RightToLeft(false), + OverrideColor(video::SColor(101,255,255,255)), BGColor(video::SColor(101,210,210,210)), + OverrideFont(0), LastBreakFont(0) +{ + #ifdef _DEBUG + setDebugName("StaticText"); + #endif + + Text = text.c_str(); + cText = text; + if (environment && environment->getSkin()) + { + BGColor = environment->getSkin()->getColor(gui::EGDC_3D_FACE); + } +} + + +//! destructor +StaticText::~StaticText() +{ + if (OverrideFont) + OverrideFont->drop(); +} + +//! draws the element and its children +void StaticText::draw() +{ + if (!IsVisible) + return; + + IGUISkin* skin = Environment->getSkin(); + if (!skin) + return; + video::IVideoDriver* driver = Environment->getVideoDriver(); + + core::rect frameRect(AbsoluteRect); + + // draw background + + if (Background) + { + if ( !OverrideBGColorEnabled ) // skin-colors can change + BGColor = skin->getColor(gui::EGDC_3D_FACE); + + driver->draw2DRectangle(BGColor, frameRect, &AbsoluteClippingRect); + } + + // draw the border + + if (Border) + { + skin->draw3DSunkenPane(this, 0, true, false, frameRect, &AbsoluteClippingRect); + frameRect.UpperLeftCorner.X += skin->getSize(EGDS_TEXT_DISTANCE_X); + } + + // draw the text + if (cText.size()) + { + IGUIFont* font = getActiveFont(); + + if (font) + { + if (!WordWrap) + { + // TODO: add colors here + if (VAlign == EGUIA_LOWERRIGHT) + { + frameRect.UpperLeftCorner.Y = frameRect.LowerRightCorner.Y - + font->getDimension(L"A").Height - font->getKerningHeight(); + } + if (HAlign == EGUIA_LOWERRIGHT) + { + frameRect.UpperLeftCorner.X = frameRect.LowerRightCorner.X - + font->getDimension(cText.c_str()).Width; + } + + irr::gui::CGUITTFont *tmp = static_cast(font); + tmp->draw(cText, frameRect, + OverrideColorEnabled ? OverrideColor : skin->getColor(isEnabled() ? EGDC_BUTTON_TEXT : EGDC_GRAY_TEXT), + HAlign == EGUIA_CENTER, VAlign == EGUIA_CENTER, (RestrainTextInside ? &AbsoluteClippingRect : NULL)); + } + else + { + if (font != LastBreakFont) + breakText(); + + core::rect r = frameRect; + s32 height = font->getDimension(L"A").Height + font->getKerningHeight(); + s32 totalHeight = height * BrokenText.size(); + if (VAlign == EGUIA_CENTER) + { + r.UpperLeftCorner.Y = r.getCenter().Y - (totalHeight / 2); + } + else if (VAlign == EGUIA_LOWERRIGHT) + { + r.UpperLeftCorner.Y = r.LowerRightCorner.Y - totalHeight; + } + + irr::video::SColor previous_color(255, 255, 255, 255); + for (u32 i=0; igetDimension(BrokenText[i].c_str()).Width; + } + + //std::vector colors; + //std::wstring str; + EnrichedString str = BrokenText[i]; + + //str = colorizeText(BrokenText[i].c_str(), colors, previous_color); + //if (!colors.empty()) + // previous_color = colors[colors.size() - 1]; + + irr::gui::CGUITTFont *tmp = static_cast(font); + tmp->draw(str, r, + previous_color, // FIXME + HAlign == EGUIA_CENTER, false, (RestrainTextInside ? &AbsoluteClippingRect : NULL)); + + r.LowerRightCorner.Y += height; + r.UpperLeftCorner.Y += height; + } + } + } + } + + IGUIElement::draw(); +} + + +//! Sets another skin independent font. +void StaticText::setOverrideFont(IGUIFont* font) +{ + if (OverrideFont == font) + return; + + if (OverrideFont) + OverrideFont->drop(); + + OverrideFont = font; + + if (OverrideFont) + OverrideFont->grab(); + + breakText(); +} + +//! Gets the override font (if any) +IGUIFont * StaticText::getOverrideFont() const +{ + return OverrideFont; +} + +//! Get the font which is used right now for drawing +IGUIFont* StaticText::getActiveFont() const +{ + if ( OverrideFont ) + return OverrideFont; + IGUISkin* skin = Environment->getSkin(); + if (skin) + return skin->getFont(); + return 0; +} + +//! Sets another color for the text. +void StaticText::setOverrideColor(video::SColor color) +{ + OverrideColor = color; + OverrideColorEnabled = true; +} + + +//! Sets another color for the text. +void StaticText::setBackgroundColor(video::SColor color) +{ + BGColor = color; + OverrideBGColorEnabled = true; + Background = true; +} + + +//! Sets whether to draw the background +void StaticText::setDrawBackground(bool draw) +{ + Background = draw; +} + + +//! Gets the background color +video::SColor StaticText::getBackgroundColor() const +{ + return BGColor; +} + + +//! Checks if background drawing is enabled +bool StaticText::isDrawBackgroundEnabled() const +{ + _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX; + return Background; +} + + +//! Sets whether to draw the border +void StaticText::setDrawBorder(bool draw) +{ + Border = draw; +} + + +//! Checks if border drawing is enabled +bool StaticText::isDrawBorderEnabled() const +{ + _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX; + return Border; +} + + +void StaticText::setTextRestrainedInside(bool restrainTextInside) +{ + RestrainTextInside = restrainTextInside; +} + + +bool StaticText::isTextRestrainedInside() const +{ + return RestrainTextInside; +} + + +void StaticText::setTextAlignment(EGUI_ALIGNMENT horizontal, EGUI_ALIGNMENT vertical) +{ + HAlign = horizontal; + VAlign = vertical; +} + + +#if IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR <= 7 +const video::SColor& StaticText::getOverrideColor() const +#else +video::SColor StaticText::getOverrideColor() const +#endif +{ + return OverrideColor; +} + + +//! Sets if the static text should use the overide color or the +//! color in the gui skin. +void StaticText::enableOverrideColor(bool enable) +{ + OverrideColorEnabled = enable; +} + + +bool StaticText::isOverrideColorEnabled() const +{ + _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX; + return OverrideColorEnabled; +} + + +//! Enables or disables word wrap for using the static text as +//! multiline text control. +void StaticText::setWordWrap(bool enable) +{ + WordWrap = enable; + breakText(); +} + + +bool StaticText::isWordWrapEnabled() const +{ + _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX; + return WordWrap; +} + + +void StaticText::setRightToLeft(bool rtl) +{ + if (RightToLeft != rtl) + { + RightToLeft = rtl; + breakText(); + } +} + + +bool StaticText::isRightToLeft() const +{ + return RightToLeft; +} + + +//! Breaks the single text line. +void StaticText::breakText() +{ + if (!WordWrap) + return; + + BrokenText.clear(); + + IGUISkin* skin = Environment->getSkin(); + IGUIFont* font = getActiveFont(); + if (!font) + return; + + LastBreakFont = font; + + EnrichedString line; + EnrichedString word; + EnrichedString whitespace; + s32 size = cText.size(); + s32 length = 0; + s32 elWidth = RelativeRect.getWidth(); + if (Border) + elWidth -= 2*skin->getSize(EGDS_TEXT_DISTANCE_X); + wchar_t c; + + //std::vector colors; + + // We have to deal with right-to-left and left-to-right differently + // However, most parts of the following code is the same, it's just + // some order and boundaries which change. + if (!RightToLeft) + { + // regular (left-to-right) + for (s32 i=0; igetDimension(whitespace.c_str()).Width; + //const std::wstring sanitized = removeEscapes(word.c_str()); + const s32 wordlgth = font->getDimension(word.c_str()).Width; + + if (wordlgth > elWidth) + { + // This word is too long to fit in the available space, look for + // the Unicode Soft HYphen (SHY / 00AD) character for a place to + // break the word at + int where = core::stringw(word.c_str()).findFirst( wchar_t(0x00AD) ); + if (where != -1) + { + EnrichedString first = word.substr(0, where); + EnrichedString second = word.substr(where, word.size() - where); + first.addCharNoColor(L'-'); + BrokenText.push_back(line + first); + const s32 secondLength = font->getDimension(second.c_str()).Width; + + length = secondLength; + line = second; + } + else + { + // No soft hyphen found, so there's nothing more we can do + // break to next line + if (length) + BrokenText.push_back(line); + length = wordlgth; + line = word; + } + } + else if (length && (length + wordlgth + whitelgth > elWidth)) + { + // break to next line + BrokenText.push_back(line); + length = wordlgth; + line = word; + } + else + { + // add word to line + line += whitespace; + line += word; + length += whitelgth + wordlgth; + } + + word.clear(); + whitespace.clear(); + } + + if ( isWhitespace && c != 0) + { + whitespace.addChar(cText, i); + } + + // compute line break + if (lineBreak) + { + line += whitespace; + line += word; + BrokenText.push_back(line); + line.clear(); + word.clear(); + whitespace.clear(); + length = 0; + } + } + } + + line += whitespace; + line += word; + BrokenText.push_back(line); + } + else + { + // right-to-left + for (s32 i=size; i>=0; --i) + { + c = cText.getString()[i]; + bool lineBreak = false; + + if (c == L'\r') // Mac or Windows breaks + { + lineBreak = true; + //if ((i>0) && Text[i-1] == L'\n') // Windows breaks + //{ + // Text.erase(i-1); + // --size; + //} + c = '\0'; + } + else if (c == L'\n') // Unix breaks + { + lineBreak = true; + c = '\0'; + } + + if (c==L' ' || c==0 || i==0) + { + if (word.size()) + { + // here comes the next whitespace, look if + // we must break the last word to the next line. + const s32 whitelgth = font->getDimension(whitespace.c_str()).Width; + const s32 wordlgth = font->getDimension(word.c_str()).Width; + + if (length && (length + wordlgth + whitelgth > elWidth)) + { + // break to next line + BrokenText.push_back(line); + length = wordlgth; + line = word; + } + else + { + // add word to line + line = whitespace + line; + line = word + line; + length += whitelgth + wordlgth; + } + + word.clear(); + whitespace.clear(); + } + + if (c != 0) + // whitespace = core::stringw(&c, 1) + whitespace; + whitespace = cText.substr(i, 1) + whitespace; + + // compute line break + if (lineBreak) + { + line = whitespace + line; + line = word + line; + BrokenText.push_back(line); + line.clear(); + word.clear(); + whitespace.clear(); + length = 0; + } + } + else + { + // yippee this is a word.. + //word = core::stringw(&c, 1) + word; + word = cText.substr(i, 1) + word; + } + } + + line = whitespace + line; + line = word + line; + BrokenText.push_back(line); + } +} + + +//! Sets the new caption of this element. +void StaticText::setText(const wchar_t* text) +{ + setText(EnrichedString(text)); +} + +//! Sets the new caption of this element. +void StaticText::setText(const EnrichedString &text) +{ + IGUIElement::setText(text.c_str()); + cText = text; + if (text.hasBackground()) { + setBackgroundColor(text.getBackground()); + } + breakText(); +} + + +void StaticText::updateAbsolutePosition() +{ + IGUIElement::updateAbsolutePosition(); + breakText(); +} + + +//! Returns the height of the text in pixels when it is drawn. +s32 StaticText::getTextHeight() const +{ + IGUIFont* font = getActiveFont(); + if (!font) + return 0; + + s32 height = font->getDimension(L"A").Height + font->getKerningHeight(); + + if (WordWrap) + height *= BrokenText.size(); + + return height; +} + + +s32 StaticText::getTextWidth() const +{ + IGUIFont * font = getActiveFont(); + if(!font) + return 0; + + if(WordWrap) + { + s32 widest = 0; + + for(u32 line = 0; line < BrokenText.size(); ++line) + { + s32 width = font->getDimension(BrokenText[line].c_str()).Width; + + if(width > widest) + widest = width; + } + + return widest; + } + else + { + return font->getDimension(cText.c_str()).Width; + } +} + + +//! Writes attributes of the element. +//! Implement this to expose the attributes of your element for +//! scripting languages, editors, debuggers or xml serialization purposes. +void StaticText::serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options=0) const +{ + IGUIStaticText::serializeAttributes(out,options); + + out->addBool ("Border", Border); + out->addBool ("OverrideColorEnabled",OverrideColorEnabled); + out->addBool ("OverrideBGColorEnabled",OverrideBGColorEnabled); + out->addBool ("WordWrap", WordWrap); + out->addBool ("Background", Background); + out->addBool ("RightToLeft", RightToLeft); + out->addBool ("RestrainTextInside", RestrainTextInside); + out->addColor ("OverrideColor", OverrideColor); + out->addColor ("BGColor", BGColor); + out->addEnum ("HTextAlign", HAlign, GUIAlignmentNames); + out->addEnum ("VTextAlign", VAlign, GUIAlignmentNames); + + // out->addFont ("OverrideFont", OverrideFont); +} + + +//! Reads attributes of the element +void StaticText::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options=0) +{ + IGUIStaticText::deserializeAttributes(in,options); + + Border = in->getAttributeAsBool("Border"); + enableOverrideColor(in->getAttributeAsBool("OverrideColorEnabled")); + OverrideBGColorEnabled = in->getAttributeAsBool("OverrideBGColorEnabled"); + setWordWrap(in->getAttributeAsBool("WordWrap")); + Background = in->getAttributeAsBool("Background"); + RightToLeft = in->getAttributeAsBool("RightToLeft"); + RestrainTextInside = in->getAttributeAsBool("RestrainTextInside"); + OverrideColor = in->getAttributeAsColor("OverrideColor"); + BGColor = in->getAttributeAsColor("BGColor"); + + setTextAlignment( (EGUI_ALIGNMENT) in->getAttributeAsEnumeration("HTextAlign", GUIAlignmentNames), + (EGUI_ALIGNMENT) in->getAttributeAsEnumeration("VTextAlign", GUIAlignmentNames)); + + // OverrideFont = in->getAttributeAsFont("OverrideFont"); +} + +} // end namespace gui + +#endif // USE_FREETYPE + +} // end namespace irr + + +#endif // _IRR_COMPILE_WITH_GUI_ diff --git a/src/irrlicht_changes/static_text.h b/src/irrlicht_changes/static_text.h new file mode 100644 index 000000000..408a12784 --- /dev/null +++ b/src/irrlicht_changes/static_text.h @@ -0,0 +1,268 @@ +// Copyright (C) 2002-2012 Nikolaus Gebhardt +// Copyright (C) 2016 Nathanaël Courant +// Modified this class to work with EnrichedStrings too +// This file is part of the "Irrlicht Engine". +// For conditions of distribution and use, see copyright notice in irrlicht.h + +#ifndef __C_GUI_STATIC_TEXT_H_INCLUDED__ +#define __C_GUI_STATIC_TEXT_H_INCLUDED__ + +#include "IrrCompileConfig.h" +#ifdef _IRR_COMPILE_WITH_GUI_ + +#include "IGUIStaticText.h" +#include "irrArray.h" + +#include "log.h" + +#include + +#include "util/enriched_string.h" +#include "config.h" +#include + +#if USE_FREETYPE + +namespace irr +{ + +namespace gui +{ + + const EGUI_ELEMENT_TYPE EGUIET_ENRICHED_STATIC_TEXT = (EGUI_ELEMENT_TYPE)(0x1000); + + class StaticText : public IGUIStaticText + { + public: + + //! constructor + StaticText(const EnrichedString &text, bool border, IGUIEnvironment* environment, + IGUIElement* parent, s32 id, const core::rect& rectangle, + bool background = false); + + //! destructor + virtual ~StaticText(); + + //! draws the element and its children + virtual void draw(); + + //! Sets another skin independent font. + virtual void setOverrideFont(IGUIFont* font=0); + + //! Gets the override font (if any) + virtual IGUIFont* getOverrideFont() const; + + //! Get the font which is used right now for drawing + virtual IGUIFont* getActiveFont() const; + + //! Sets another color for the text. + virtual void setOverrideColor(video::SColor color); + + //! Sets another color for the background. + virtual void setBackgroundColor(video::SColor color); + + //! Sets whether to draw the background + virtual void setDrawBackground(bool draw); + + //! Gets the background color + virtual video::SColor getBackgroundColor() const; + + //! Checks if background drawing is enabled + virtual bool isDrawBackgroundEnabled() const; + + //! Sets whether to draw the border + virtual void setDrawBorder(bool draw); + + //! Checks if border drawing is enabled + virtual bool isDrawBorderEnabled() const; + + //! Sets alignment mode for text + virtual void setTextAlignment(EGUI_ALIGNMENT horizontal, EGUI_ALIGNMENT vertical); + + //! Gets the override color + #if IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR <= 7 + virtual const video::SColor& getOverrideColor() const; + #else + virtual video::SColor getOverrideColor() const; + #endif + + //! Sets if the static text should use the overide color or the + //! color in the gui skin. + virtual void enableOverrideColor(bool enable); + + //! Checks if an override color is enabled + virtual bool isOverrideColorEnabled() const; + + //! Set whether the text in this label should be clipped if it goes outside bounds + virtual void setTextRestrainedInside(bool restrainedInside); + + //! Checks if the text in this label should be clipped if it goes outside bounds + virtual bool isTextRestrainedInside() const; + + //! Enables or disables word wrap for using the static text as + //! multiline text control. + virtual void setWordWrap(bool enable); + + //! Checks if word wrap is enabled + virtual bool isWordWrapEnabled() const; + + //! Sets the new caption of this element. + virtual void setText(const wchar_t* text); + + //! Returns the height of the text in pixels when it is drawn. + virtual s32 getTextHeight() const; + + //! Returns the width of the current text, in the current font + virtual s32 getTextWidth() const; + + //! Updates the absolute position, splits text if word wrap is enabled + virtual void updateAbsolutePosition(); + + //! Set whether the string should be interpreted as right-to-left (RTL) text + /** \note This component does not implement the Unicode bidi standard, the + text of the component should be already RTL if you call this. The + main difference when RTL is enabled is that the linebreaks for multiline + elements are performed starting from the end. + */ + virtual void setRightToLeft(bool rtl); + + //! Checks if the text should be interpreted as right-to-left text + virtual bool isRightToLeft() const; + + //! Writes attributes of the element. + virtual void serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options) const; + + //! Reads attributes of the element + virtual void deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options); + + virtual bool hasType(EGUI_ELEMENT_TYPE t) const { + return (t == EGUIET_ENRICHED_STATIC_TEXT) || (t == EGUIET_STATIC_TEXT); + }; + + virtual bool hasType(EGUI_ELEMENT_TYPE t) { + return (t == EGUIET_ENRICHED_STATIC_TEXT) || (t == EGUIET_STATIC_TEXT); + }; + + void setText(const EnrichedString &text); + + private: + + //! Breaks the single text line. + void breakText(); + + EGUI_ALIGNMENT HAlign, VAlign; + bool Border; + bool OverrideColorEnabled; + bool OverrideBGColorEnabled; + bool WordWrap; + bool Background; + bool RestrainTextInside; + bool RightToLeft; + + video::SColor OverrideColor, BGColor; + gui::IGUIFont* OverrideFont; + gui::IGUIFont* LastBreakFont; // stored because: if skin changes, line break must be recalculated. + + EnrichedString cText; + core::array< EnrichedString > BrokenText; + }; + + +} // end namespace gui + +} // end namespace irr + +inline irr::gui::IGUIStaticText *addStaticText( + irr::gui::IGUIEnvironment *guienv, + const EnrichedString &text, + const core::rect< s32 > &rectangle, + bool border = false, + bool wordWrap = true, + irr::gui::IGUIElement *parent = NULL, + s32 id = -1, + bool fillBackground = false) +{ + if (parent == NULL) { + // parent is NULL, so we must find one, or we need not to drop + // result, but then there will be a memory leak. + // + // What Irrlicht does is to use guienv as a parent, but the problem + // is that guienv is here only an IGUIEnvironment, while it is a + // CGUIEnvironment in Irrlicht, which inherits from both IGUIElement + // and IGUIEnvironment. + // + // A solution would be to dynamic_cast guienv to a + // IGUIElement*, but Irrlicht is shipped without rtti support + // in some distributions, causing the dymanic_cast to segfault. + // + // Thus, to find the parent, we create a dummy StaticText and ask + // for its parent, and then remove it. + irr::gui::IGUIStaticText *dummy_text = + guienv->addStaticText(L"", rectangle, border, wordWrap, + parent, id, fillBackground); + parent = dummy_text->getParent(); + dummy_text->remove(); + } + irr::gui::IGUIStaticText *result = new irr::gui::StaticText( + text, border, guienv, parent, + id, rectangle, fillBackground); + + result->setWordWrap(wordWrap); + result->drop(); + return result; +} + +inline void setStaticText(irr::gui::IGUIStaticText *static_text, const EnrichedString &text) +{ + // dynamic_cast not possible due to some distributions shipped + // without rtti support in irrlicht + if (static_text->hasType(irr::gui::EGUIET_ENRICHED_STATIC_TEXT)) { + irr::gui::StaticText* stext = static_cast(static_text); + stext->setText(text); + } else { + static_text->setText(text.c_str()); + } +} + +#else // USE_FREETYPE + +inline irr::gui::IGUIStaticText *addStaticText( + irr::gui::IGUIEnvironment *guienv, + const EnrichedString &text, + const core::rect< s32 > &rectangle, + bool border = false, + bool wordWrap = true, + irr::gui::IGUIElement *parent = NULL, + s32 id = -1, + bool fillBackground = false) +{ + return guienv->addStaticText(text.c_str(), rectangle, border, wordWrap, parent, id, fillBackground); +} + +inline void setStaticText(irr::gui::IGUIStaticText *static_text, const EnrichedString &text) +{ + static_text->setText(text.c_str()); +} + +#endif + +inline irr::gui::IGUIStaticText *addStaticText( + irr::gui::IGUIEnvironment *guienv, + const wchar_t *text, + const core::rect< s32 > &rectangle, + bool border = false, + bool wordWrap = true, + irr::gui::IGUIElement *parent = NULL, + s32 id = -1, + bool fillBackground = false) { + return addStaticText(guienv, EnrichedString(text), rectangle, border, wordWrap, parent, id, fillBackground); +} + +inline void setStaticText(irr::gui::IGUIStaticText *static_text, const wchar_t *text) +{ + setStaticText(static_text, EnrichedString(text)); +} + +#endif // _IRR_COMPILE_WITH_GUI_ + +#endif // C_GUI_STATIC_TEXT_H_INCLUDED diff --git a/src/terminal_chat_console.cpp b/src/terminal_chat_console.cpp index c86a960fa..a8c4ebaef 100644 --- a/src/terminal_chat_console.cpp +++ b/src/terminal_chat_console.cpp @@ -345,9 +345,11 @@ void TerminalChatConsole::step(int ch) if (p.first > m_log_level) continue; - m_chat_backend.addMessage( - utf8_to_wide(Logger::getLevelLabel(p.first)), - utf8_to_wide(p.second)); + std::wstring error_message = utf8_to_wide(Logger::getLevelLabel(p.first)); + if (!g_settings->getBool("disable_escape_sequences")) { + error_message = L"\x1b(c@red)" + error_message + L"\x1b(c@white)"; + } + m_chat_backend.addMessage(error_message, utf8_to_wide(p.second)); } // handle input @@ -438,7 +440,7 @@ void TerminalChatConsole::draw_text() continue; for (u32 i = 0; i < line.fragments.size(); ++i) { const ChatFormattedFragment& fragment = line.fragments[i]; - addstr(wide_to_utf8(fragment.text).c_str()); + addstr(wide_to_utf8(fragment.text.getString()).c_str()); } } } diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt index e028a0435..f571ab22c 100644 --- a/src/util/CMakeLists.txt +++ b/src/util/CMakeLists.txt @@ -1,17 +1,9 @@ -if(USE_FREETYPE) - set(UTIL_FREETYPEDEP_SRCS - ${CMAKE_CURRENT_SOURCE_DIR}/statictext.cpp - ) -else() - set(UTIL_FREETYPEDEP_SRCS ) -endif(USE_FREETYPE) - set(UTIL_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/areastore.cpp ${CMAKE_CURRENT_SOURCE_DIR}/auth.cpp ${CMAKE_CURRENT_SOURCE_DIR}/base64.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/coloredstring.cpp ${CMAKE_CURRENT_SOURCE_DIR}/directiontables.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/enriched_string.cpp ${CMAKE_CURRENT_SOURCE_DIR}/numeric.cpp ${CMAKE_CURRENT_SOURCE_DIR}/pointedthing.cpp ${CMAKE_CURRENT_SOURCE_DIR}/serialize.cpp @@ -20,6 +12,5 @@ set(UTIL_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/string.cpp ${CMAKE_CURRENT_SOURCE_DIR}/srp.cpp ${CMAKE_CURRENT_SOURCE_DIR}/timetaker.cpp - ${UTIL_FREETYPEDEP_SRCS} PARENT_SCOPE) diff --git a/src/util/coloredstring.cpp b/src/util/coloredstring.cpp deleted file mode 100644 index 7db586550..000000000 --- a/src/util/coloredstring.cpp +++ /dev/null @@ -1,68 +0,0 @@ -/* -Copyright (C) 2013 xyz, Ilya Zhuravlev - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation; either version 2.1 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License along -with this program; if not, write to the Free Software Foundation, Inc., -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -*/ - -#include "coloredstring.h" -#include "util/string.h" - -ColoredString::ColoredString() -{} - -ColoredString::ColoredString(const std::wstring &string, const std::vector &colors): - m_string(string), - m_colors(colors) -{} - -ColoredString::ColoredString(const std::wstring &s) { - m_string = colorizeText(s, m_colors, SColor(255, 255, 255, 255)); -} - -void ColoredString::operator=(const wchar_t *str) { - m_string = colorizeText(str, m_colors, SColor(255, 255, 255, 255)); -} - -size_t ColoredString::size() const { - return m_string.size(); -} - -ColoredString ColoredString::substr(size_t pos, size_t len) const { - if (pos == m_string.length()) - return ColoredString(); - if (len == std::string::npos || pos + len > m_string.length()) { - return ColoredString( - m_string.substr(pos, std::string::npos), - std::vector(m_colors.begin() + pos, m_colors.end()) - ); - } else { - return ColoredString( - m_string.substr(pos, len), - std::vector(m_colors.begin() + pos, m_colors.begin() + pos + len) - ); - } -} - -const wchar_t *ColoredString::c_str() const { - return m_string.c_str(); -} - -const std::vector &ColoredString::getColors() const { - return m_colors; -} - -const std::wstring &ColoredString::getString() const { - return m_string; -} diff --git a/src/util/coloredstring.h b/src/util/coloredstring.h deleted file mode 100644 index a6d98db30..000000000 --- a/src/util/coloredstring.h +++ /dev/null @@ -1,44 +0,0 @@ -/* -Copyright (C) 2013 xyz, Ilya Zhuravlev - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation; either version 2.1 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License along -with this program; if not, write to the Free Software Foundation, Inc., -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -*/ - -#ifndef COLOREDSTRING_HEADER -#define COLOREDSTRING_HEADER - -#include -#include -#include - -using namespace irr::video; - -class ColoredString { -public: - ColoredString(); - ColoredString(const std::wstring &s); - ColoredString(const std::wstring &string, const std::vector &colors); - void operator=(const wchar_t *str); - size_t size() const; - ColoredString substr(size_t pos = 0, size_t len = std::string::npos) const; - const wchar_t *c_str() const; - const std::vector &getColors() const; - const std::wstring &getString() const; -private: - std::wstring m_string; - std::vector m_colors; -}; - -#endif diff --git a/src/util/enriched_string.cpp b/src/util/enriched_string.cpp new file mode 100644 index 000000000..a7fc3a828 --- /dev/null +++ b/src/util/enriched_string.cpp @@ -0,0 +1,166 @@ +/* +Copyright (C) 2013 xyz, Ilya Zhuravlev +Copyright (C) 2016 Nore, Nathanaël Courant + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "enriched_string.h" +#include "util/string.h" +#include "log.h" +using namespace irr::video; + +EnrichedString::EnrichedString() +{ + clear(); +} + +EnrichedString::EnrichedString(const std::wstring &string, + const std::vector &colors): + m_string(string), + m_colors(colors), + m_has_background(false) +{} + +EnrichedString::EnrichedString(const std::wstring &s, const SColor &color) +{ + clear(); + addAtEnd(s, color); +} + +EnrichedString::EnrichedString(const wchar_t *str, const SColor &color) +{ + clear(); + addAtEnd(std::wstring(str), color); +} + +void EnrichedString::operator=(const wchar_t *str) +{ + clear(); + addAtEnd(std::wstring(str), SColor(255, 255, 255, 255)); +} + +void EnrichedString::addAtEnd(const std::wstring &s, const SColor &initial_color) +{ + SColor color(initial_color); + size_t i = 0; + while (i < s.length()) { + if (s[i] != L'\x1b') { + m_string += s[i]; + m_colors.push_back(color); + ++i; + continue; + } + ++i; + size_t start_index = i; + size_t length; + if (i == s.length()) { + break; + } + if (s[i] == L'(') { + ++i; + ++start_index; + while (i < s.length() && s[i] != L')') { + if (s[i] == L'\\') { + ++i; + } + ++i; + } + length = i - start_index; + ++i; + } else { + ++i; + length = 1; + } + std::wstring escape_sequence(s, start_index, length); + std::vector parts = split(escape_sequence, L'@'); + if (parts[0] == L"c") { + if (parts.size() < 2) { + continue; + } + parseColorString(wide_to_utf8(parts[1]), color, true); + } else if (parts[0] == L"b") { + if (parts.size() < 2) { + continue; + } + parseColorString(wide_to_utf8(parts[1]), m_background, true); + m_has_background = true; + } + continue; + } +} + +void EnrichedString::addChar(const EnrichedString &source, size_t i) +{ + m_string += source.m_string[i]; + m_colors.push_back(source.m_colors[i]); +} + +void EnrichedString::addCharNoColor(wchar_t c) +{ + m_string += c; + if (m_colors.empty()) { + m_colors.push_back(SColor(255, 255, 255, 255)); + } else { + m_colors.push_back(m_colors[m_colors.size() - 1]); + } +} + +EnrichedString EnrichedString::operator+(const EnrichedString &other) const +{ + std::vector result; + result.insert(result.end(), m_colors.begin(), m_colors.end()); + result.insert(result.end(), other.m_colors.begin(), other.m_colors.end()); + return EnrichedString(m_string + other.m_string, result); +} + +void EnrichedString::operator+=(const EnrichedString &other) +{ + m_string += other.m_string; + m_colors.insert(m_colors.end(), other.m_colors.begin(), other.m_colors.end()); +} + +EnrichedString EnrichedString::substr(size_t pos, size_t len) const +{ + if (pos == m_string.length()) { + return EnrichedString(); + } + if (len == std::string::npos || pos + len > m_string.length()) { + return EnrichedString( + m_string.substr(pos, std::string::npos), + std::vector(m_colors.begin() + pos, m_colors.end()) + ); + } else { + return EnrichedString( + m_string.substr(pos, len), + std::vector(m_colors.begin() + pos, m_colors.begin() + pos + len) + ); + } +} + +const wchar_t *EnrichedString::c_str() const +{ + return m_string.c_str(); +} + +const std::vector &EnrichedString::getColors() const +{ + return m_colors; +} + +const std::wstring &EnrichedString::getString() const +{ + return m_string; +} diff --git a/src/util/enriched_string.h b/src/util/enriched_string.h new file mode 100644 index 000000000..1aca8948a --- /dev/null +++ b/src/util/enriched_string.h @@ -0,0 +1,91 @@ +/* +Copyright (C) 2013 xyz, Ilya Zhuravlev +Copyright (C) 2016 Nore, Nathanaël Courant + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef ENRICHEDSTRING_HEADER +#define ENRICHEDSTRING_HEADER + +#include +#include +#include + +class EnrichedString { +public: + EnrichedString(); + EnrichedString(const std::wstring &s, + const irr::video::SColor &color = irr::video::SColor(255, 255, 255, 255)); + EnrichedString(const wchar_t *str, + const irr::video::SColor &color = irr::video::SColor(255, 255, 255, 255)); + EnrichedString(const std::wstring &string, + const std::vector &colors); + void operator=(const wchar_t *str); + void addAtEnd(const std::wstring &s, const irr::video::SColor &color); + + // Adds the character source[i] at the end. + // An EnrichedString should always be able to be copied + // to the end of an existing EnrichedString that way. + void addChar(const EnrichedString &source, size_t i); + + // Adds a single character at the end, without specifying its + // color. The color used will be the one from the last character. + void addCharNoColor(wchar_t c); + + EnrichedString substr(size_t pos = 0, size_t len = std::string::npos) const; + EnrichedString operator+(const EnrichedString &other) const; + void operator+=(const EnrichedString &other); + const wchar_t *c_str() const; + const std::vector &getColors() const; + const std::wstring &getString() const; + inline bool operator==(const EnrichedString &other) const + { + return (m_string == other.m_string && m_colors == other.m_colors); + } + inline bool operator!=(const EnrichedString &other) const + { + return !(*this == other); + } + inline void clear() + { + m_string.clear(); + m_colors.clear(); + m_has_background = false; + } + inline bool empty() const + { + return m_string.empty(); + } + inline size_t size() const + { + return m_string.size(); + } + inline bool hasBackground() const + { + return m_has_background; + } + inline irr::video::SColor getBackground() const + { + return m_background; + } +private: + std::wstring m_string; + std::vector m_colors; + bool m_has_background; + irr::video::SColor m_background; +}; + +#endif diff --git a/src/util/statictext.cpp b/src/util/statictext.cpp deleted file mode 100644 index b534b560e..000000000 --- a/src/util/statictext.cpp +++ /dev/null @@ -1,654 +0,0 @@ -// Copyright (C) 2002-2012 Nikolaus Gebhardt -// This file is part of the "Irrlicht Engine". -// For conditions of distribution and use, see copyright notice in irrlicht.h - -#include "statictext.h" -#ifdef _IRR_COMPILE_WITH_GUI_ - -//Only compile this if freetype is enabled. - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "cguittfont/xCGUITTFont.h" -#include "util/string.h" - -namespace irr -{ -namespace gui -{ -//! constructor -StaticText::StaticText(const wchar_t* text, bool border, - IGUIEnvironment* environment, IGUIElement* parent, - s32 id, const core::rect& rectangle, - bool background) -: IGUIStaticText(environment, parent, id, rectangle), - HAlign(EGUIA_UPPERLEFT), VAlign(EGUIA_UPPERLEFT), - Border(border), OverrideColorEnabled(false), OverrideBGColorEnabled(false), WordWrap(false), Background(background), - RestrainTextInside(true), RightToLeft(false), - OverrideColor(video::SColor(101,255,255,255)), BGColor(video::SColor(101,210,210,210)), - OverrideFont(0), LastBreakFont(0) -{ - #ifdef _DEBUG - setDebugName("StaticText"); - #endif - - Text = text; - if (environment && environment->getSkin()) - { - BGColor = environment->getSkin()->getColor(gui::EGDC_3D_FACE); - } -} - - -//! destructor -StaticText::~StaticText() -{ - if (OverrideFont) - OverrideFont->drop(); -} - - -//! draws the element and its children -void StaticText::draw() -{ - if (!IsVisible) - return; - - IGUISkin* skin = Environment->getSkin(); - if (!skin) - return; - video::IVideoDriver* driver = Environment->getVideoDriver(); - - core::rect frameRect(AbsoluteRect); - - // draw background - - if (Background) - { - if ( !OverrideBGColorEnabled ) // skin-colors can change - BGColor = skin->getColor(gui::EGDC_3D_FACE); - - driver->draw2DRectangle(BGColor, frameRect, &AbsoluteClippingRect); - } - - // draw the border - - if (Border) - { - skin->draw3DSunkenPane(this, 0, true, false, frameRect, &AbsoluteClippingRect); - frameRect.UpperLeftCorner.X += skin->getSize(EGDS_TEXT_DISTANCE_X); - } - - // draw the text - if (Text.size()) - { - IGUIFont* font = getActiveFont(); - - if (font) - { - if (!WordWrap) - { - // TODO: add colors here - if (VAlign == EGUIA_LOWERRIGHT) - { - frameRect.UpperLeftCorner.Y = frameRect.LowerRightCorner.Y - - font->getDimension(L"A").Height - font->getKerningHeight(); - } - if (HAlign == EGUIA_LOWERRIGHT) - { - frameRect.UpperLeftCorner.X = frameRect.LowerRightCorner.X - - font->getDimension(Text.c_str()).Width; - } - - font->draw(Text.c_str(), frameRect, - OverrideColorEnabled ? OverrideColor : skin->getColor(isEnabled() ? EGDC_BUTTON_TEXT : EGDC_GRAY_TEXT), - HAlign == EGUIA_CENTER, VAlign == EGUIA_CENTER, (RestrainTextInside ? &AbsoluteClippingRect : NULL)); - } - else - { - if (font != LastBreakFont) - breakText(); - - core::rect r = frameRect; - s32 height = font->getDimension(L"A").Height + font->getKerningHeight(); - s32 totalHeight = height * BrokenText.size(); - if (VAlign == EGUIA_CENTER) - { - r.UpperLeftCorner.Y = r.getCenter().Y - (totalHeight / 2); - } - else if (VAlign == EGUIA_LOWERRIGHT) - { - r.UpperLeftCorner.Y = r.LowerRightCorner.Y - totalHeight; - } - - irr::video::SColor previous_color(255, 255, 255, 255); - for (u32 i=0; igetDimension(BrokenText[i].c_str()).Width; - } - - std::vector colors; - std::wstring str; - - str = colorizeText(BrokenText[i].c_str(), colors, previous_color); - if (!colors.empty()) - previous_color = colors[colors.size() - 1]; - - irr::gui::CGUITTFont *tmp = static_cast(font); - tmp->draw(str.c_str(), r, - colors, - HAlign == EGUIA_CENTER, false, (RestrainTextInside ? &AbsoluteClippingRect : NULL)); - - r.LowerRightCorner.Y += height; - r.UpperLeftCorner.Y += height; - } - } - } - } - - IGUIElement::draw(); -} - - -//! Sets another skin independent font. -void StaticText::setOverrideFont(IGUIFont* font) -{ - if (OverrideFont == font) - return; - - if (OverrideFont) - OverrideFont->drop(); - - OverrideFont = font; - - if (OverrideFont) - OverrideFont->grab(); - - breakText(); -} - -//! Gets the override font (if any) -IGUIFont * StaticText::getOverrideFont() const -{ - return OverrideFont; -} - -//! Get the font which is used right now for drawing -IGUIFont* StaticText::getActiveFont() const -{ - if ( OverrideFont ) - return OverrideFont; - IGUISkin* skin = Environment->getSkin(); - if (skin) - return skin->getFont(); - return 0; -} - -//! Sets another color for the text. -void StaticText::setOverrideColor(video::SColor color) -{ - OverrideColor = color; - OverrideColorEnabled = true; -} - - -//! Sets another color for the text. -void StaticText::setBackgroundColor(video::SColor color) -{ - BGColor = color; - OverrideBGColorEnabled = true; - Background = true; -} - - -//! Sets whether to draw the background -void StaticText::setDrawBackground(bool draw) -{ - Background = draw; -} - - -//! Gets the background color -video::SColor StaticText::getBackgroundColor() const -{ - return BGColor; -} - - -//! Checks if background drawing is enabled -bool StaticText::isDrawBackgroundEnabled() const -{ - _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX; - return Background; -} - - -//! Sets whether to draw the border -void StaticText::setDrawBorder(bool draw) -{ - Border = draw; -} - - -//! Checks if border drawing is enabled -bool StaticText::isDrawBorderEnabled() const -{ - _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX; - return Border; -} - - -void StaticText::setTextRestrainedInside(bool restrainTextInside) -{ - RestrainTextInside = restrainTextInside; -} - - -bool StaticText::isTextRestrainedInside() const -{ - return RestrainTextInside; -} - - -void StaticText::setTextAlignment(EGUI_ALIGNMENT horizontal, EGUI_ALIGNMENT vertical) -{ - HAlign = horizontal; - VAlign = vertical; -} - - -#if IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR <= 7 -const video::SColor& StaticText::getOverrideColor() const -#else -video::SColor StaticText::getOverrideColor() const -#endif -{ - return OverrideColor; -} - - -//! Sets if the static text should use the overide color or the -//! color in the gui skin. -void StaticText::enableOverrideColor(bool enable) -{ - OverrideColorEnabled = enable; -} - - -bool StaticText::isOverrideColorEnabled() const -{ - _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX; - return OverrideColorEnabled; -} - - -//! Enables or disables word wrap for using the static text as -//! multiline text control. -void StaticText::setWordWrap(bool enable) -{ - WordWrap = enable; - breakText(); -} - - -bool StaticText::isWordWrapEnabled() const -{ - _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX; - return WordWrap; -} - - -void StaticText::setRightToLeft(bool rtl) -{ - if (RightToLeft != rtl) - { - RightToLeft = rtl; - breakText(); - } -} - - -bool StaticText::isRightToLeft() const -{ - return RightToLeft; -} - - -//! Breaks the single text line. -void StaticText::breakText() -{ - if (!WordWrap) - return; - - BrokenText.clear(); - - IGUISkin* skin = Environment->getSkin(); - IGUIFont* font = getActiveFont(); - if (!font) - return; - - LastBreakFont = font; - - core::stringw line; - core::stringw word; - core::stringw whitespace; - s32 size = Text.size(); - s32 length = 0; - s32 elWidth = RelativeRect.getWidth(); - if (Border) - elWidth -= 2*skin->getSize(EGDS_TEXT_DISTANCE_X); - wchar_t c; - - std::vector colors; - - // We have to deal with right-to-left and left-to-right differently - // However, most parts of the following code is the same, it's just - // some order and boundaries which change. - if (!RightToLeft) - { - // regular (left-to-right) - for (s32 i=0; igetDimension(whitespace.c_str()).Width; - const std::wstring sanitized = removeEscapes(word.c_str()); - const s32 wordlgth = font->getDimension(sanitized.c_str()).Width; - - if (wordlgth > elWidth) - { - // This word is too long to fit in the available space, look for - // the Unicode Soft HYphen (SHY / 00AD) character for a place to - // break the word at - int where = word.findFirst( wchar_t(0x00AD) ); - if (where != -1) - { - core::stringw first = word.subString(0, where); - core::stringw second = word.subString(where, word.size() - where); - BrokenText.push_back(line + first + L"-"); - const s32 secondLength = font->getDimension(second.c_str()).Width; - - length = secondLength; - line = second; - } - else - { - // No soft hyphen found, so there's nothing more we can do - // break to next line - if (length) - BrokenText.push_back(line); - length = wordlgth; - line = word; - } - } - else if (length && (length + wordlgth + whitelgth > elWidth)) - { - // break to next line - BrokenText.push_back(line); - length = wordlgth; - line = word; - } - else - { - // add word to line - line += whitespace; - line += word; - length += whitelgth + wordlgth; - } - - word = L""; - whitespace = L""; - } - - if ( isWhitespace ) - { - whitespace += c; - } - - // compute line break - if (lineBreak) - { - line += whitespace; - line += word; - BrokenText.push_back(line); - line = L""; - word = L""; - whitespace = L""; - length = 0; - } - } - } - - line += whitespace; - line += word; - BrokenText.push_back(line); - } - else - { - // right-to-left - for (s32 i=size; i>=0; --i) - { - c = Text[i]; - bool lineBreak = false; - - if (c == L'\r') // Mac or Windows breaks - { - lineBreak = true; - if ((i>0) && Text[i-1] == L'\n') // Windows breaks - { - Text.erase(i-1); - --size; - } - c = '\0'; - } - else if (c == L'\n') // Unix breaks - { - lineBreak = true; - c = '\0'; - } - - if (c==L' ' || c==0 || i==0) - { - if (word.size()) - { - // here comes the next whitespace, look if - // we must break the last word to the next line. - const s32 whitelgth = font->getDimension(whitespace.c_str()).Width; - const s32 wordlgth = font->getDimension(word.c_str()).Width; - - if (length && (length + wordlgth + whitelgth > elWidth)) - { - // break to next line - BrokenText.push_back(line); - length = wordlgth; - line = word; - } - else - { - // add word to line - line = whitespace + line; - line = word + line; - length += whitelgth + wordlgth; - } - - word = L""; - whitespace = L""; - } - - if (c != 0) - whitespace = core::stringw(&c, 1) + whitespace; - - // compute line break - if (lineBreak) - { - line = whitespace + line; - line = word + line; - BrokenText.push_back(line); - line = L""; - word = L""; - whitespace = L""; - length = 0; - } - } - else - { - // yippee this is a word.. - word = core::stringw(&c, 1) + word; - } - } - - line = whitespace + line; - line = word + line; - BrokenText.push_back(line); - } -} - - -//! Sets the new caption of this element. -void StaticText::setText(const wchar_t* text) -{ - IGUIElement::setText(text); - breakText(); -} - - -void StaticText::updateAbsolutePosition() -{ - IGUIElement::updateAbsolutePosition(); - breakText(); -} - - -//! Returns the height of the text in pixels when it is drawn. -s32 StaticText::getTextHeight() const -{ - IGUIFont* font = getActiveFont(); - if (!font) - return 0; - - s32 height = font->getDimension(L"A").Height + font->getKerningHeight(); - - if (WordWrap) - height *= BrokenText.size(); - - return height; -} - - -s32 StaticText::getTextWidth() const -{ - IGUIFont * font = getActiveFont(); - if(!font) - return 0; - - if(WordWrap) - { - s32 widest = 0; - - for(u32 line = 0; line < BrokenText.size(); ++line) - { - s32 width = font->getDimension(BrokenText[line].c_str()).Width; - - if(width > widest) - widest = width; - } - - return widest; - } - else - { - return font->getDimension(Text.c_str()).Width; - } -} - - -//! Writes attributes of the element. -//! Implement this to expose the attributes of your element for -//! scripting languages, editors, debuggers or xml serialization purposes. -void StaticText::serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options=0) const -{ - IGUIStaticText::serializeAttributes(out,options); - - out->addBool ("Border", Border); - out->addBool ("OverrideColorEnabled",OverrideColorEnabled); - out->addBool ("OverrideBGColorEnabled",OverrideBGColorEnabled); - out->addBool ("WordWrap", WordWrap); - out->addBool ("Background", Background); - out->addBool ("RightToLeft", RightToLeft); - out->addBool ("RestrainTextInside", RestrainTextInside); - out->addColor ("OverrideColor", OverrideColor); - out->addColor ("BGColor", BGColor); - out->addEnum ("HTextAlign", HAlign, GUIAlignmentNames); - out->addEnum ("VTextAlign", VAlign, GUIAlignmentNames); - - // out->addFont ("OverrideFont", OverrideFont); -} - - -//! Reads attributes of the element -void StaticText::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options=0) -{ - IGUIStaticText::deserializeAttributes(in,options); - - Border = in->getAttributeAsBool("Border"); - enableOverrideColor(in->getAttributeAsBool("OverrideColorEnabled")); - OverrideBGColorEnabled = in->getAttributeAsBool("OverrideBGColorEnabled"); - setWordWrap(in->getAttributeAsBool("WordWrap")); - Background = in->getAttributeAsBool("Background"); - RightToLeft = in->getAttributeAsBool("RightToLeft"); - RestrainTextInside = in->getAttributeAsBool("RestrainTextInside"); - OverrideColor = in->getAttributeAsColor("OverrideColor"); - BGColor = in->getAttributeAsColor("BGColor"); - - setTextAlignment( (EGUI_ALIGNMENT) in->getAttributeAsEnumeration("HTextAlign", GUIAlignmentNames), - (EGUI_ALIGNMENT) in->getAttributeAsEnumeration("VTextAlign", GUIAlignmentNames)); - - // OverrideFont = in->getAttributeAsFont("OverrideFont"); -} - -} // end namespace gui -} // end namespace irr - - -#endif // _IRR_COMPILE_WITH_GUI_ diff --git a/src/util/statictext.h b/src/util/statictext.h deleted file mode 100644 index 8d2f879e7..000000000 --- a/src/util/statictext.h +++ /dev/null @@ -1,150 +0,0 @@ -// Copyright (C) 2002-2012 Nikolaus Gebhardt -// This file is part of the "Irrlicht Engine". -// For conditions of distribution and use, see copyright notice in irrlicht.h - -#ifndef __C_GUI_STATIC_TEXT_H_INCLUDED__ -#define __C_GUI_STATIC_TEXT_H_INCLUDED__ - -#include "IrrCompileConfig.h" -#ifdef _IRR_COMPILE_WITH_GUI_ - -#include "IGUIStaticText.h" -#include "irrArray.h" - -#include - -namespace irr -{ -namespace gui -{ - class StaticText : public IGUIStaticText - { - public: - - //! constructor - StaticText(const wchar_t* text, bool border, IGUIEnvironment* environment, - IGUIElement* parent, s32 id, const core::rect& rectangle, - bool background = false); - - //! destructor - virtual ~StaticText(); - - //! draws the element and its children - virtual void draw(); - - //! Sets another skin independent font. - virtual void setOverrideFont(IGUIFont* font=0); - - //! Gets the override font (if any) - virtual IGUIFont* getOverrideFont() const; - - //! Get the font which is used right now for drawing - virtual IGUIFont* getActiveFont() const; - - //! Sets another color for the text. - virtual void setOverrideColor(video::SColor color); - - //! Sets another color for the background. - virtual void setBackgroundColor(video::SColor color); - - //! Sets whether to draw the background - virtual void setDrawBackground(bool draw); - - //! Gets the background color - virtual video::SColor getBackgroundColor() const; - - //! Checks if background drawing is enabled - virtual bool isDrawBackgroundEnabled() const; - - //! Sets whether to draw the border - virtual void setDrawBorder(bool draw); - - //! Checks if border drawing is enabled - virtual bool isDrawBorderEnabled() const; - - //! Sets alignment mode for text - virtual void setTextAlignment(EGUI_ALIGNMENT horizontal, EGUI_ALIGNMENT vertical); - - //! Gets the override color - #if IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR <= 7 - virtual const video::SColor& getOverrideColor() const; - #else - virtual video::SColor getOverrideColor() const; - #endif - - //! Sets if the static text should use the overide color or the - //! color in the gui skin. - virtual void enableOverrideColor(bool enable); - - //! Checks if an override color is enabled - virtual bool isOverrideColorEnabled() const; - - //! Set whether the text in this label should be clipped if it goes outside bounds - virtual void setTextRestrainedInside(bool restrainedInside); - - //! Checks if the text in this label should be clipped if it goes outside bounds - virtual bool isTextRestrainedInside() const; - - //! Enables or disables word wrap for using the static text as - //! multiline text control. - virtual void setWordWrap(bool enable); - - //! Checks if word wrap is enabled - virtual bool isWordWrapEnabled() const; - - //! Sets the new caption of this element. - virtual void setText(const wchar_t* text); - - //! Returns the height of the text in pixels when it is drawn. - virtual s32 getTextHeight() const; - - //! Returns the width of the current text, in the current font - virtual s32 getTextWidth() const; - - //! Updates the absolute position, splits text if word wrap is enabled - virtual void updateAbsolutePosition(); - - //! Set whether the string should be interpreted as right-to-left (RTL) text - /** \note This component does not implement the Unicode bidi standard, the - text of the component should be already RTL if you call this. The - main difference when RTL is enabled is that the linebreaks for multiline - elements are performed starting from the end. - */ - virtual void setRightToLeft(bool rtl); - - //! Checks if the text should be interpreted as right-to-left text - virtual bool isRightToLeft() const; - - //! Writes attributes of the element. - virtual void serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options) const; - - //! Reads attributes of the element - virtual void deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options); - - private: - - //! Breaks the single text line. - void breakText(); - - EGUI_ALIGNMENT HAlign, VAlign; - bool Border; - bool OverrideColorEnabled; - bool OverrideBGColorEnabled; - bool WordWrap; - bool Background; - bool RestrainTextInside; - bool RightToLeft; - - video::SColor OverrideColor, BGColor; - gui::IGUIFont* OverrideFont; - gui::IGUIFont* LastBreakFont; // stored because: if skin changes, line break must be recalculated. - - core::array< core::stringw > BrokenText; - }; - -} // end namespace gui -} // end namespace irr - -#endif // _IRR_COMPILE_WITH_GUI_ - -#endif // C_GUI_STATIC_TEXT_H_INCLUDED diff --git a/src/util/string.h b/src/util/string.h index 40ef3e4d3..c77c5a6f9 100644 --- a/src/util/string.h +++ b/src/util/string.h @@ -519,6 +519,38 @@ std::basic_string unescape_enriched(const std::basic_string &s) return output; } +template +std::vector > split(const std::basic_string &s, T delim) +{ + std::vector > tokens; + + std::basic_string current; + bool last_was_escape = false; + for (size_t i = 0; i < s.length(); i++) { + T si = s[i]; + if (last_was_escape) { + current += '\\'; + current += si; + last_was_escape = false; + } else { + if (si == delim) { + tokens.push_back(current); + current = std::basic_string(); + last_was_escape = false; + } else if (si == '\\') { + last_was_escape = true; + } else { + current += si; + last_was_escape = false; + } + } + } + //push last element + tokens.push_back(current); + + return tokens; +} + /** * Checks that all characters in \p to_check are a decimal digits. * -- cgit v1.2.3 From 569998011e0a9cb6179f1e79fb15b7a3ce80977f Mon Sep 17 00:00:00 2001 From: paramat Date: Mon, 30 May 2016 13:15:14 +0100 Subject: Mgv7: Always carve river channels in mountain terrain Previously, rivers were sometimes blocked by vertical walls of mountain terrain due to river carving being disabled when base terrain height was below water_level - 16 Remove now unused base terrain heightmap created in generateTerrain() --- src/mapgen_v7.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/mapgen_v7.cpp b/src/mapgen_v7.cpp index 9ff2fbe66..7327facae 100644 --- a/src/mapgen_v7.cpp +++ b/src/mapgen_v7.cpp @@ -206,13 +206,15 @@ void MapgenV7::makeChunk(BlockMakeData *data) blockseed = getBlockSeed2(full_node_min, seed); - // Generate terrain and ridges with initial heightmaps + // Generate base and mountain terrain + // An initial heightmap is no longer created here for use in generateRidgeTerrain() s16 stone_surface_max_y = generateTerrain(); + // Generate rivers if (spflags & MGV7_RIDGES) generateRidgeTerrain(); - // Update heightmap to include mountain terrain + // Create heightmap updateHeightmap(node_min, node_max); // Init biome generator, place biome-specific nodes, and build biomemap @@ -331,7 +333,6 @@ int MapgenV7::generateTerrain() for (s16 z = node_min.Z; z <= node_max.Z; z++) for (s16 x = node_min.X; x <= node_max.X; x++, index2d++) { s16 surface_y = baseTerrainLevelFromMap(index2d); - heightmap[index2d] = surface_y; // Create base terrain heightmap if (surface_y > stone_surface_max_y) stone_surface_max_y = surface_y; @@ -382,9 +383,6 @@ void MapgenV7::generateRidgeTerrain() for (s16 x = node_min.X; x <= node_max.X; x++, index++, vi++) { int j = (z - node_min.Z) * csize.X + (x - node_min.X); - if (heightmap[j] < water_level - 16) // Use base terrain heightmap - continue; - float uwatern = noise_ridge_uwater->result[j] * 2; if (fabs(uwatern) > width) continue; -- cgit v1.2.3 From 7ea4a03c835d68a6fb58aa55aa6a6315ec80b79f Mon Sep 17 00:00:00 2001 From: Robert Kiraly Date: Fri, 3 Jun 2016 00:50:21 -0700 Subject: Sapier's fix for the RESEND RELIABLE problem (#4170) --- src/network/connection.cpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'src') diff --git a/src/network/connection.cpp b/src/network/connection.cpp index 02c0aa165..b711cae11 100644 --- a/src/network/connection.cpp +++ b/src/network/connection.cpp @@ -71,6 +71,9 @@ static inline float CALC_DTIME(unsigned int lasttime, unsigned int curtime) { #define PING_TIMEOUT 5.0 +/* maximum number of retries for reliable packets */ +#define MAX_RELIABLE_RETRY 5 + static u16 readPeerId(u8 *packetdata) { return readU16(&packetdata[4]); @@ -1399,6 +1402,7 @@ void ConnectionSendThread::runTimeouts(float dtime) } float resend_timeout = dynamic_cast(&peer)->getResendTimeout(); + bool retry_count_exceeded = false; for(u16 i=0; i timed_outs; @@ -1438,6 +1442,13 @@ void ConnectionSendThread::runTimeouts(float dtime) channel->UpdateBytesLost(k->data.getSize()); k->resend_count++; + if (k-> resend_count > MAX_RELIABLE_RETRY) { + retry_count_exceeded = true; + timeouted_peers.push_back(peer->id); + /* no need to check additional packets if a single one did timeout*/ + break; + } + LOG(derr_con<getDesc() <<"RE-SENDING timed-out RELIABLE to " << k->address.serializeString() @@ -1452,9 +1463,18 @@ void ConnectionSendThread::runTimeouts(float dtime) // do not handle rtt here as we can't decide if this packet was // lost or really takes more time to transmit } + + if (retry_count_exceeded) { + break; /* no need to check other channels if we already did timeout */ + } + channel->UpdateTimers(dtime,dynamic_cast(&peer)->getLegacyPeer()); } + /* skip to next peer if we did timeout */ + if (retry_count_exceeded) + continue; + /* send ping if necessary */ if (dynamic_cast(&peer)->Ping(dtime,data)) { LOG(dout_con<getDesc() -- cgit v1.2.3 From 1e86c89f3614cf298916149a8f13d44ea671da64 Mon Sep 17 00:00:00 2001 From: est31 Date: Wed, 25 May 2016 09:22:20 +0200 Subject: Input related generalisations * Move key types into own file * Use Generalized input methods in game.cpp --- src/client/keys.h | 79 +++++++++++++++ src/game.cpp | 293 ++++++++++++++++++++++++++---------------------------- src/game.h | 1 + 3 files changed, 223 insertions(+), 150 deletions(-) create mode 100644 src/client/keys.h (limited to 'src') diff --git a/src/client/keys.h b/src/client/keys.h new file mode 100644 index 000000000..08b1c4123 --- /dev/null +++ b/src/client/keys.h @@ -0,0 +1,79 @@ +/* +Minetest +Copyright (C) 2016 est31, + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef KEYS_HEADER +#define KEYS_HEADER + +#include + +class KeyType { +public: + enum T { + // Player movement + FORWARD, + BACKWARD, + LEFT, + RIGHT, + JUMP, + SPECIAL1, + SNEAK, + AUTORUN, + + ESC, + + // Other + DROP, + INVENTORY, + CHAT, + CMD, + CONSOLE, + MINIMAP, + FREEMOVE, + FASTMOVE, + NOCLIP, + CINEMATIC, + SCREENSHOT, + TOGGLE_HUD, + TOGGLE_CHAT, + TOGGLE_FORCE_FOG_OFF, + TOGGLE_UPDATE_CAMERA, + TOGGLE_DEBUG, + TOGGLE_PROFILER, + CAMERA_MODE, + INCREASE_VIEWING_RANGE, + DECREASE_VIEWING_RANGE, + RANGESELECT, + + QUICKTUNE_NEXT, + QUICKTUNE_PREV, + QUICKTUNE_INC, + QUICKTUNE_DEC, + + DEBUG_STACKS, + + // Fake keycode for array size and internal checks + INTERNAL_ENUM_COUNT + + }; +}; + +typedef KeyType::T GameKeyType; + + +#endif diff --git a/src/game.cpp b/src/game.cpp index def202fe5..ac1c0fe6b 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "camera.h" #include "client.h" #include "client/tile.h" // For TextureSource +#include "client/keys.h" #include "clientmap.h" #include "clouds.h" #include "config.h" @@ -1306,114 +1307,76 @@ struct KeyCache { { handler = NULL; populate(); + populate_nonchanging(); } - enum { - // Player movement - KEYMAP_ID_FORWARD, - KEYMAP_ID_BACKWARD, - KEYMAP_ID_LEFT, - KEYMAP_ID_RIGHT, - KEYMAP_ID_JUMP, - KEYMAP_ID_SPECIAL1, - KEYMAP_ID_SNEAK, - KEYMAP_ID_AUTORUN, - - // Other - KEYMAP_ID_DROP, - KEYMAP_ID_INVENTORY, - KEYMAP_ID_CHAT, - KEYMAP_ID_CMD, - KEYMAP_ID_CONSOLE, - KEYMAP_ID_MINIMAP, - KEYMAP_ID_FREEMOVE, - KEYMAP_ID_FASTMOVE, - KEYMAP_ID_NOCLIP, - KEYMAP_ID_CINEMATIC, - KEYMAP_ID_SCREENSHOT, - KEYMAP_ID_TOGGLE_HUD, - KEYMAP_ID_TOGGLE_CHAT, - KEYMAP_ID_TOGGLE_FORCE_FOG_OFF, - KEYMAP_ID_TOGGLE_UPDATE_CAMERA, - KEYMAP_ID_TOGGLE_DEBUG, - KEYMAP_ID_TOGGLE_PROFILER, - KEYMAP_ID_CAMERA_MODE, - KEYMAP_ID_INCREASE_VIEWING_RANGE, - KEYMAP_ID_DECREASE_VIEWING_RANGE, - KEYMAP_ID_RANGESELECT, - - KEYMAP_ID_QUICKTUNE_NEXT, - KEYMAP_ID_QUICKTUNE_PREV, - KEYMAP_ID_QUICKTUNE_INC, - KEYMAP_ID_QUICKTUNE_DEC, - - KEYMAP_ID_DEBUG_STACKS, - - // Fake keycode for array size and internal checks - KEYMAP_INTERNAL_ENUM_COUNT - - - }; - void populate(); - KeyPress key[KEYMAP_INTERNAL_ENUM_COUNT]; + // Keys that are not settings dependent + void populate_nonchanging(); + + KeyPress key[KeyType::INTERNAL_ENUM_COUNT]; InputHandler *handler; }; +void KeyCache::populate_nonchanging() +{ + key[KeyType::ESC] = EscapeKey; +} + void KeyCache::populate() { - key[KEYMAP_ID_FORWARD] = getKeySetting("keymap_forward"); - key[KEYMAP_ID_BACKWARD] = getKeySetting("keymap_backward"); - key[KEYMAP_ID_LEFT] = getKeySetting("keymap_left"); - key[KEYMAP_ID_RIGHT] = getKeySetting("keymap_right"); - key[KEYMAP_ID_JUMP] = getKeySetting("keymap_jump"); - key[KEYMAP_ID_SPECIAL1] = getKeySetting("keymap_special1"); - key[KEYMAP_ID_SNEAK] = getKeySetting("keymap_sneak"); - - key[KEYMAP_ID_AUTORUN] = getKeySetting("keymap_autorun"); - - key[KEYMAP_ID_DROP] = getKeySetting("keymap_drop"); - key[KEYMAP_ID_INVENTORY] = getKeySetting("keymap_inventory"); - key[KEYMAP_ID_CHAT] = getKeySetting("keymap_chat"); - key[KEYMAP_ID_CMD] = getKeySetting("keymap_cmd"); - key[KEYMAP_ID_CONSOLE] = getKeySetting("keymap_console"); - key[KEYMAP_ID_MINIMAP] = getKeySetting("keymap_minimap"); - key[KEYMAP_ID_FREEMOVE] = getKeySetting("keymap_freemove"); - key[KEYMAP_ID_FASTMOVE] = getKeySetting("keymap_fastmove"); - key[KEYMAP_ID_NOCLIP] = getKeySetting("keymap_noclip"); - key[KEYMAP_ID_CINEMATIC] = getKeySetting("keymap_cinematic"); - key[KEYMAP_ID_SCREENSHOT] = getKeySetting("keymap_screenshot"); - key[KEYMAP_ID_TOGGLE_HUD] = getKeySetting("keymap_toggle_hud"); - key[KEYMAP_ID_TOGGLE_CHAT] = getKeySetting("keymap_toggle_chat"); - key[KEYMAP_ID_TOGGLE_FORCE_FOG_OFF] + key[KeyType::FORWARD] = getKeySetting("keymap_forward"); + key[KeyType::BACKWARD] = getKeySetting("keymap_backward"); + key[KeyType::LEFT] = getKeySetting("keymap_left"); + key[KeyType::RIGHT] = getKeySetting("keymap_right"); + key[KeyType::JUMP] = getKeySetting("keymap_jump"); + key[KeyType::SPECIAL1] = getKeySetting("keymap_special1"); + key[KeyType::SNEAK] = getKeySetting("keymap_sneak"); + + key[KeyType::AUTORUN] = getKeySetting("keymap_autorun"); + + key[KeyType::DROP] = getKeySetting("keymap_drop"); + key[KeyType::INVENTORY] = getKeySetting("keymap_inventory"); + key[KeyType::CHAT] = getKeySetting("keymap_chat"); + key[KeyType::CMD] = getKeySetting("keymap_cmd"); + key[KeyType::CONSOLE] = getKeySetting("keymap_console"); + key[KeyType::MINIMAP] = getKeySetting("keymap_minimap"); + key[KeyType::FREEMOVE] = getKeySetting("keymap_freemove"); + key[KeyType::FASTMOVE] = getKeySetting("keymap_fastmove"); + key[KeyType::NOCLIP] = getKeySetting("keymap_noclip"); + key[KeyType::CINEMATIC] = getKeySetting("keymap_cinematic"); + key[KeyType::SCREENSHOT] = getKeySetting("keymap_screenshot"); + key[KeyType::TOGGLE_HUD] = getKeySetting("keymap_toggle_hud"); + key[KeyType::TOGGLE_CHAT] = getKeySetting("keymap_toggle_chat"); + key[KeyType::TOGGLE_FORCE_FOG_OFF] = getKeySetting("keymap_toggle_force_fog_off"); - key[KEYMAP_ID_TOGGLE_UPDATE_CAMERA] + key[KeyType::TOGGLE_UPDATE_CAMERA] = getKeySetting("keymap_toggle_update_camera"); - key[KEYMAP_ID_TOGGLE_DEBUG] + key[KeyType::TOGGLE_DEBUG] = getKeySetting("keymap_toggle_debug"); - key[KEYMAP_ID_TOGGLE_PROFILER] + key[KeyType::TOGGLE_PROFILER] = getKeySetting("keymap_toggle_profiler"); - key[KEYMAP_ID_CAMERA_MODE] + key[KeyType::CAMERA_MODE] = getKeySetting("keymap_camera_mode"); - key[KEYMAP_ID_INCREASE_VIEWING_RANGE] + key[KeyType::INCREASE_VIEWING_RANGE] = getKeySetting("keymap_increase_viewing_range_min"); - key[KEYMAP_ID_DECREASE_VIEWING_RANGE] + key[KeyType::DECREASE_VIEWING_RANGE] = getKeySetting("keymap_decrease_viewing_range_min"); - key[KEYMAP_ID_RANGESELECT] + key[KeyType::RANGESELECT] = getKeySetting("keymap_rangeselect"); - key[KEYMAP_ID_QUICKTUNE_NEXT] = getKeySetting("keymap_quicktune_next"); - key[KEYMAP_ID_QUICKTUNE_PREV] = getKeySetting("keymap_quicktune_prev"); - key[KEYMAP_ID_QUICKTUNE_INC] = getKeySetting("keymap_quicktune_inc"); - key[KEYMAP_ID_QUICKTUNE_DEC] = getKeySetting("keymap_quicktune_dec"); + key[KeyType::QUICKTUNE_NEXT] = getKeySetting("keymap_quicktune_next"); + key[KeyType::QUICKTUNE_PREV] = getKeySetting("keymap_quicktune_prev"); + key[KeyType::QUICKTUNE_INC] = getKeySetting("keymap_quicktune_inc"); + key[KeyType::QUICKTUNE_DEC] = getKeySetting("keymap_quicktune_dec"); - key[KEYMAP_ID_DEBUG_STACKS] = getKeySetting("keymap_print_debug_stacks"); + key[KeyType::DEBUG_STACKS] = getKeySetting("keymap_print_debug_stacks"); if (handler) { // First clear all keys, then re-add the ones we listen for handler->dontListenForKeys(); - for (size_t i = 0; i < KEYMAP_INTERNAL_ENUM_COUNT; i++) { + for (size_t i = 0; i < KeyType::INTERNAL_ENUM_COUNT; i++) { handler->listenForKey(key[i]); } handler->listenForKey(EscapeKey); @@ -1575,9 +1538,10 @@ protected: f32 dtime); void updateStats(RunStats *stats, const FpsControl &draw_times, f32 dtime); + // Input related void processUserInput(VolatileRunFlags *flags, GameRunData *runData, f32 dtime); - void processKeyboardInput(VolatileRunFlags *flags, + void processKeyInput(VolatileRunFlags *flags, float *statustext_time, float *jump_timer, bool *reset_jump_timer, @@ -1647,6 +1611,36 @@ protected: static void settingChangedCallback(const std::string &setting_name, void *data); void readSettings(); + bool getLeftClicked() + { + return input->getLeftClicked(); + } + bool getRightClicked() + { + return input->getRightClicked(); + } + bool isLeftPressed() + { + return input->getLeftState(); + } + bool isRightPressed() + { + return input->getRightState(); + } + bool getLeftReleased() + { + return input->getLeftReleased(); + } + + bool isKeyDown(GameKeyType k) + { + return input->isKeyDown(keycache.key[k]); + } + bool wasKeyDown(GameKeyType k) + { + return input->wasKeyDown(keycache.key[k]); + } + #ifdef __ANDROID__ void handleAndroidChatInput(); #endif @@ -2379,7 +2373,7 @@ bool Game::connectToServer(const std::string &playername, break; } - if (input->wasKeyDown(EscapeKey) || input->wasKeyDown(CancelKey)) { + if (wasKeyDown(KeyType::ESC) || input->wasKeyDown(CancelKey)) { *aborted = true; infostream << "Connect aborted [Escape]" << std::endl; break; @@ -2440,7 +2434,7 @@ bool Game::getServerContent(bool *aborted) return false; } - if (input->wasKeyDown(EscapeKey) || input->wasKeyDown(CancelKey)) { + if (wasKeyDown(KeyType::ESC) || input->wasKeyDown(CancelKey)) { *aborted = true; infostream << "Connect aborted [Escape]" << std::endl; return false; @@ -2702,7 +2696,7 @@ void Game::processUserInput(VolatileRunFlags *flags, if (m_cache_doubletap_jump && runData->jump_timer <= 0.2) runData->jump_timer += dtime; - processKeyboardInput( + processKeyInput( flags, &runData->statustext_time, &runData->jump_timer, @@ -2714,7 +2708,7 @@ void Game::processUserInput(VolatileRunFlags *flags, } -void Game::processKeyboardInput(VolatileRunFlags *flags, +void Game::processKeyInput(VolatileRunFlags *flags, float *statustext_time, float *jump_timer, bool *reset_jump_timer, @@ -2724,66 +2718,66 @@ void Game::processKeyboardInput(VolatileRunFlags *flags, //TimeTaker tt("process kybd input", NULL, PRECISION_NANO); - if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_DROP])) { + if (wasKeyDown(KeyType::DROP)) { dropSelectedItem(); - } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_AUTORUN])) { + } else if (wasKeyDown(KeyType::AUTORUN)) { toggleAutorun(statustext_time); - } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_INVENTORY])) { + } else if (wasKeyDown(KeyType::INVENTORY)) { openInventory(); - } else if (input->wasKeyDown(EscapeKey) || input->wasKeyDown(CancelKey)) { + } else if (wasKeyDown(KeyType::ESC) || input->wasKeyDown(CancelKey)) { if (!gui_chat_console->isOpenInhibited()) { show_pause_menu(¤t_formspec, client, gamedef, texture_src, device, simple_singleplayer_mode); } - } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_CHAT])) { + } else if (wasKeyDown(KeyType::CHAT)) { openConsole(0.2, L""); - } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_CMD])) { + } else if (wasKeyDown(KeyType::CMD)) { openConsole(0.2, L"/"); - } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_CONSOLE])) { + } else if (wasKeyDown(KeyType::CONSOLE)) { openConsole(1); - } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_FREEMOVE])) { + } else if (wasKeyDown(KeyType::FREEMOVE)) { toggleFreeMove(statustext_time); - } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_JUMP])) { + } else if (wasKeyDown(KeyType::JUMP)) { toggleFreeMoveAlt(statustext_time, jump_timer); *reset_jump_timer = true; - } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_FASTMOVE])) { + } else if (wasKeyDown(KeyType::FASTMOVE)) { toggleFast(statustext_time); - } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_NOCLIP])) { + } else if (wasKeyDown(KeyType::NOCLIP)) { toggleNoClip(statustext_time); - } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_CINEMATIC])) { + } else if (wasKeyDown(KeyType::CINEMATIC)) { toggleCinematic(statustext_time); - } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_SCREENSHOT])) { + } else if (wasKeyDown(KeyType::SCREENSHOT)) { client->makeScreenshot(device); - } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_TOGGLE_HUD])) { + } else if (wasKeyDown(KeyType::TOGGLE_HUD)) { toggleHud(statustext_time, &flags->show_hud); - } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_MINIMAP])) { + } else if (wasKeyDown(KeyType::MINIMAP)) { toggleMinimap(statustext_time, &flags->show_minimap, flags->show_hud, - input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_SNEAK])); - } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_TOGGLE_CHAT])) { + isKeyDown(KeyType::SNEAK)); + } else if (wasKeyDown(KeyType::TOGGLE_CHAT)) { toggleChat(statustext_time, &flags->show_chat); - } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_TOGGLE_FORCE_FOG_OFF])) { + } else if (wasKeyDown(KeyType::TOGGLE_FORCE_FOG_OFF)) { toggleFog(statustext_time, &flags->force_fog_off); - } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_TOGGLE_UPDATE_CAMERA])) { + } else if (wasKeyDown(KeyType::TOGGLE_UPDATE_CAMERA)) { toggleUpdateCamera(statustext_time, &flags->disable_camera_update); - } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_TOGGLE_DEBUG])) { + } else if (wasKeyDown(KeyType::TOGGLE_DEBUG)) { toggleDebug(statustext_time, &flags->show_debug, &flags->show_profiler_graph); - } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_TOGGLE_PROFILER])) { + } else if (wasKeyDown(KeyType::TOGGLE_PROFILER)) { toggleProfiler(statustext_time, profiler_current_page, profiler_max_page); - } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_INCREASE_VIEWING_RANGE])) { + } else if (wasKeyDown(KeyType::INCREASE_VIEWING_RANGE)) { increaseViewRange(statustext_time); - } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_DECREASE_VIEWING_RANGE])) { + } else if (wasKeyDown(KeyType::DECREASE_VIEWING_RANGE)) { decreaseViewRange(statustext_time); - } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_RANGESELECT])) { + } else if (wasKeyDown(KeyType::RANGESELECT)) { toggleFullViewRange(statustext_time); - } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_QUICKTUNE_NEXT])) { + } else if (wasKeyDown(KeyType::QUICKTUNE_NEXT)) { quicktune->next(); - } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_QUICKTUNE_PREV])) { + } else if (wasKeyDown(KeyType::QUICKTUNE_PREV)) { quicktune->prev(); - } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_QUICKTUNE_INC])) { + } else if (wasKeyDown(KeyType::QUICKTUNE_INC)) { quicktune->inc(); - } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_QUICKTUNE_DEC])) { + } else if (wasKeyDown(KeyType::QUICKTUNE_DEC)) { quicktune->dec(); - } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_DEBUG_STACKS])) { + } else if (wasKeyDown(KeyType::DEBUG_STACKS)) { // Print debug stacks dstream << "-----------------------------------------" << std::endl; @@ -2793,7 +2787,7 @@ void Game::processKeyboardInput(VolatileRunFlags *flags, debug_stacks_print(); } - if (!input->isKeyDown(getKeySetting("keymap_jump")) && *reset_jump_timer) { + if (!isKeyDown(KeyType::JUMP) && *reset_jump_timer) { *reset_jump_timer = false; *jump_timer = 0.0; } @@ -2807,7 +2801,6 @@ void Game::processKeyboardInput(VolatileRunFlags *flags, } } - void Game::processItemSelection(u16 *new_playeritem) { LocalPlayer *player = client->getEnv().getLocalPlayer(); @@ -3228,13 +3221,13 @@ void Game::updatePlayerControl(const CameraOrientation &cam) //TimeTaker tt("update player control", NULL, PRECISION_NANO); PlayerControl control( - input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_FORWARD]), - input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_BACKWARD]), - input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_LEFT]), - input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_RIGHT]), - input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_JUMP]), - input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_SPECIAL1]), - input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_SNEAK]), + input->isKeyDown(keycache.key[KeyType::FORWARD]), + input->isKeyDown(keycache.key[KeyType::BACKWARD]), + input->isKeyDown(keycache.key[KeyType::LEFT]), + input->isKeyDown(keycache.key[KeyType::RIGHT]), + input->isKeyDown(keycache.key[KeyType::JUMP]), + input->isKeyDown(keycache.key[KeyType::SPECIAL1]), + input->isKeyDown(keycache.key[KeyType::SNEAK]), input->getLeftState(), input->getRightState(), cam.camera_pitch, @@ -3242,13 +3235,13 @@ void Game::updatePlayerControl(const CameraOrientation &cam) ); u32 keypress_bits = - ( (u32)(input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_FORWARD]) & 0x1) << 0) | - ( (u32)(input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_BACKWARD]) & 0x1) << 1) | - ( (u32)(input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_LEFT]) & 0x1) << 2) | - ( (u32)(input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_RIGHT]) & 0x1) << 3) | - ( (u32)(input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_JUMP]) & 0x1) << 4) | - ( (u32)(input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_SPECIAL1]) & 0x1) << 5) | - ( (u32)(input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_SNEAK]) & 0x1) << 6) | + ( (u32)(input->isKeyDown(keycache.key[KeyType::FORWARD]) & 0x1) << 0) | + ( (u32)(input->isKeyDown(keycache.key[KeyType::BACKWARD]) & 0x1) << 1) | + ( (u32)(input->isKeyDown(keycache.key[KeyType::LEFT]) & 0x1) << 2) | + ( (u32)(input->isKeyDown(keycache.key[KeyType::RIGHT]) & 0x1) << 3) | + ( (u32)(input->isKeyDown(keycache.key[KeyType::JUMP]) & 0x1) << 4) | + ( (u32)(input->isKeyDown(keycache.key[KeyType::SPECIAL1]) & 0x1) << 5) | + ( (u32)(input->isKeyDown(keycache.key[KeyType::SNEAK]) & 0x1) << 6) | ( (u32)(input->getLeftState() & 0x1) << 7) | ( (u32)(input->getRightState() & 0x1) << 8 ); @@ -3522,7 +3515,7 @@ void Game::updateCamera(VolatileRunFlags *flags, u32 busy_time, v3s16 old_camera_offset = camera->getOffset(); - if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_CAMERA_MODE])) { + if (wasKeyDown(KeyType::CAMERA_MODE)) { GenericCAO *playercao = player->getCAO(); // If playercao not loaded, don't change camera @@ -3665,7 +3658,7 @@ void Game::processPlayerInteraction(GameRunData *runData, - pointing away from node */ if (runData->digging) { - if (input->getLeftReleased()) { + if (getLeftReleased()) { infostream << "Left button released" << " (stopped digging)" << std::endl; runData->digging = false; @@ -3691,7 +3684,7 @@ void Game::processPlayerInteraction(GameRunData *runData, } } - if (!runData->digging && runData->ldown_for_dig && !input->getLeftState()) { + if (!runData->digging && runData->ldown_for_dig && !isLeftPressed()) { runData->ldown_for_dig = false; } @@ -3699,13 +3692,13 @@ void Game::processPlayerInteraction(GameRunData *runData, soundmaker->m_player_leftpunch_sound.name = ""; - if (input->getRightState()) + if (isRightPressed()) runData->repeat_rightclick_timer += dtime; else runData->repeat_rightclick_timer = 0; - if (playeritem_def.usable && input->getLeftState()) { - if (input->getLeftClicked()) + if (playeritem_def.usable && isLeftPressed()) { + if (getLeftClicked()) client->interact(4, pointed); } else if (pointed.type == POINTEDTHING_NODE) { ToolCapabilities playeritem_toolcap = @@ -3715,16 +3708,16 @@ void Game::processPlayerInteraction(GameRunData *runData, } else if (pointed.type == POINTEDTHING_OBJECT) { handlePointingAtObject(runData, pointed, playeritem, player_position, show_debug); - } else if (input->getLeftState()) { + } else if (isLeftPressed()) { // When button is held down in air, show continuous animation runData->left_punch = true; - } else if (input->getRightClicked()) { + } else if (getRightClicked()) { handlePointingAtNothing(runData, playeritem); } runData->pointed_old = pointed; - if (runData->left_punch || input->getLeftClicked()) + if (runData->left_punch || getLeftClicked()) camera->setDigging(0); // left click animation input->resetLeftClicked(); @@ -3769,19 +3762,19 @@ void Game::handlePointingAtNode(GameRunData *runData, } } - if (runData->nodig_delay_timer <= 0.0 && input->getLeftState() + if (runData->nodig_delay_timer <= 0.0 && isLeftPressed() && client->checkPrivilege("interact")) { handleDigging(runData, pointed, nodepos, playeritem_toolcap, dtime); } - if ((input->getRightClicked() || + if ((getRightClicked() || runData->repeat_rightclick_timer >= m_repeat_right_click_time) && client->checkPrivilege("interact")) { runData->repeat_rightclick_timer = 0; infostream << "Ground right-clicked" << std::endl; if (meta && meta->getString("formspec") != "" && !random_input - && !input->isKeyDown(getKeySetting("keymap_sneak"))) { + && !isKeyDown(KeyType::SNEAK)) { infostream << "Launching custom inventory view" << std::endl; InventoryLocation inventoryloc; @@ -3846,7 +3839,7 @@ void Game::handlePointingAtObject(GameRunData *runData, runData->selected_object->debugInfoText())); } - if (input->getLeftState()) { + if (isLeftPressed()) { bool do_punch = false; bool do_punch_damage = false; @@ -3856,7 +3849,7 @@ void Game::handlePointingAtObject(GameRunData *runData, runData->object_hit_delay_timer = object_hit_delay; } - if (input->getLeftClicked()) + if (getLeftClicked()) do_punch = true; if (do_punch) { @@ -3876,7 +3869,7 @@ void Game::handlePointingAtObject(GameRunData *runData, if (!disable_send) client->interact(0, pointed); } - } else if (input->getRightClicked()) { + } else if (getRightClicked()) { infostream << "Right-clicked object" << std::endl; client->interact(3, pointed); // place } diff --git a/src/game.h b/src/game.h index 5465ecdc6..d8c2f78fc 100644 --- a/src/game.h +++ b/src/game.h @@ -22,6 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "irrlichttypes_extrabloated.h" #include +#include "client/keys.h" #include "keycode.h" #include -- cgit v1.2.3 From 2060fd9cbe587d7e8ffe0cecdd67925f13a56c05 Mon Sep 17 00:00:00 2001 From: est31 Date: Fri, 27 May 2016 08:35:07 +0200 Subject: Initial Gamepad support Adds initial ingame gamepad support to minetest. Full Formspec support is not implemented yet and can be added by a later change. --- builtin/settingtypes.txt | 11 +++ src/client/CMakeLists.txt | 1 + src/client/clientlauncher.cpp | 20 ++++- src/client/inputhandler.h | 13 +++ src/client/joystick_controller.cpp | 179 +++++++++++++++++++++++++++++++++++++ src/client/joystick_controller.h | 163 +++++++++++++++++++++++++++++++++ src/client/keys.h | 6 ++ src/content_cao.cpp | 4 +- src/defaultsettings.cpp | 3 + src/game.cpp | 165 ++++++++++++++++++++++------------ src/game.h | 3 + src/guiEngine.cpp | 2 + src/guiEngine.h | 3 +- src/guiFormSpecMenu.cpp | 42 +++++++-- src/guiFormSpecMenu.h | 13 ++- src/localplayer.cpp | 17 ++-- src/player.h | 10 ++- 17 files changed, 576 insertions(+), 79 deletions(-) create mode 100644 src/client/joystick_controller.cpp create mode 100644 src/client/joystick_controller.h (limited to 'src') diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index 538a04f33..b53e35a85 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -104,6 +104,17 @@ random_input (Random input) bool false # Continuous forward movement (only used for testing). continuous_forward (Continuous forward) bool false +# Enable Joysticks +enable_joysticks (Enable Joysticks) bool true + +# The time in seconds it takes between repeated events +# when holding down a joystick button combination. +repeat_joystick_button_time (Joystick button repetition invterval) float 0.17 + +# The sensitivity of the joystick axes for moving the +# ingame view frustum around. +joystick_frustum_sensitivity (Joystick frustum sensitivity) float 170 + # Key for moving the player forward. # See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 keymap_forward (Forward key) key KEY_KEY_W diff --git a/src/client/CMakeLists.txt b/src/client/CMakeLists.txt index a1ec37fe3..5faa186a7 100644 --- a/src/client/CMakeLists.txt +++ b/src/client/CMakeLists.txt @@ -1,6 +1,7 @@ set(client_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/clientlauncher.cpp ${CMAKE_CURRENT_SOURCE_DIR}/tile.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/joystick_controller.cpp PARENT_SCOPE ) diff --git a/src/client/clientlauncher.cpp b/src/client/clientlauncher.cpp index 404a16310..ee8662ed6 100644 --- a/src/client/clientlauncher.cpp +++ b/src/client/clientlauncher.cpp @@ -32,6 +32,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "guiEngine.h" #include "player.h" #include "fontengine.h" +#include "joystick_controller.h" #include "clientlauncher.h" /* mainmenumanager.h @@ -499,7 +500,8 @@ void ClientLauncher::main_menu(MainMenuData *menudata) #endif /* show main menu */ - GUIEngine mymenu(device, guiroot, &g_menumgr, smgr, menudata, *kill); + GUIEngine mymenu(device, &input->joystick, guiroot, + &g_menumgr, smgr, menudata, *kill); smgr->clear(); /* leave scene manager in a clean state */ } @@ -558,6 +560,22 @@ bool ClientLauncher::create_engine_device() device = createDeviceEx(params); if (device) { + if (g_settings->getBool("enable_joysticks")) { + irr::core::array infos; + std::vector joystick_infos; + // Make sure this is called maximum once per + // irrlicht device, otherwise it will give you + // multiple events for the same joystick. + if (device->activateJoysticks(infos)) { + infostream << "Joystick support enabled" << std::endl; + joystick_infos.reserve(infos.size()); + for (u32 i = 0; i < infos.size(); i++) { + joystick_infos.push_back(infos[i]); + } + } else { + errorstream << "Could not activate joystick support." << std::endl; + } + } porting::initIrrlicht(device); } diff --git a/src/client/inputhandler.h b/src/client/inputhandler.h index 73e507fdc..824b0da2e 100644 --- a/src/client/inputhandler.h +++ b/src/client/inputhandler.h @@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #define INPUT_HANDLER_H #include "irrlichttypes_extrabloated.h" +#include "joystick_controller.h" class MyEventReceiver : public IEventReceiver { @@ -62,6 +63,14 @@ public: return true; } #endif + + if (event.EventType == irr::EET_JOYSTICK_INPUT_EVENT) { + /* TODO add a check like: + if (event.JoystickEvent != joystick_we_listen_for) + return false; + */ + return joystick->handleEvent(event.JoystickEvent); + } // handle mouse events if (event.EventType == irr::EET_MOUSE_INPUT_EVENT) { if (noMenuActive() == false) { @@ -172,6 +181,8 @@ public: s32 mouse_wheel; + JoystickController *joystick; + #ifdef HAVE_TOUCHSCREENGUI TouchScreenGUI* m_touchscreengui; #endif @@ -202,6 +213,7 @@ public: m_receiver(receiver), m_mousepos(0,0) { + m_receiver->joystick = &joystick; } virtual bool isKeyDown(const KeyPress &keyCode) { @@ -288,6 +300,7 @@ public: void clear() { + joystick.clear(); m_receiver->clearInput(); } private: diff --git a/src/client/joystick_controller.cpp b/src/client/joystick_controller.cpp new file mode 100644 index 000000000..ef8d18ab0 --- /dev/null +++ b/src/client/joystick_controller.cpp @@ -0,0 +1,179 @@ +/* +Minetest +Copyright (C) 2016 est31, + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "joystick_controller.h" +#include "irrlichttypes_extrabloated.h" +#include "keys.h" +#include "settings.h" +#include "gettime.h" + +bool JoystickButtonCmb::isTriggered(const irr::SEvent::SJoystickEvent &ev) const +{ + u32 buttons = ev.ButtonStates; + + buttons &= filter_mask; + return buttons == compare_mask; +} + +bool JoystickAxisCmb::isTriggered(const irr::SEvent::SJoystickEvent &ev) const +{ + s16 ax_val = ev.Axis[axis_to_compare]; + + return (ax_val * direction < 0) && (thresh * direction > ax_val * direction); +} + +// spares many characters +#define JLO_B_PB(A, B, C) jlo.button_keys.push_back(JoystickButtonCmb(A, B, C)) +#define JLO_A_PB(A, B, C, D) jlo.axis_keys.push_back(JoystickAxisCmb(A, B, C, D)) + +static JoystickLayout create_default_layout() +{ + JoystickLayout jlo; + + jlo.axes_dead_border = 1024; + + const JoystickAxisLayout axes[JA_COUNT] = { + {0, 1}, // JA_SIDEWARD_MOVE + {1, 1}, // JA_FORWARD_MOVE + {3, 1}, // JA_FRUSTUM_HORIZONTAL + {4, 1}, // JA_FRUSTUM_VERTICAL + }; + memcpy(jlo.axes, axes, sizeof(jlo.axes)); + + u32 sb = 1 << 7; // START button mask + u32 fb = 1 << 3; // FOUR button mask + u32 bm = sb | fb; // Mask for Both Modifiers + + // The back button means "ESC". + JLO_B_PB(KeyType::ESC, 1 << 6, 1 << 6); + + // The start button counts as modifier as well as use key. + // JLO_B_PB(KeyType::USE, sb, sb)); + + // Accessible without start modifier button pressed + // regardless whether four is pressed or not + JLO_B_PB(KeyType::SNEAK, sb | 1 << 2, 1 << 2); + + // Accessible without four modifier button pressed + // regardless whether start is pressed or not + JLO_B_PB(KeyType::MOUSE_L, fb | 1 << 4, 1 << 4); + JLO_B_PB(KeyType::MOUSE_R, fb | 1 << 5, 1 << 5); + + // Accessible without any modifier pressed + JLO_B_PB(KeyType::JUMP, bm | 1 << 0, 1 << 0); + JLO_B_PB(KeyType::SPECIAL1, bm | 1 << 1, 1 << 1); + + // Accessible with start button not pressed, but four pressed + // TODO find usage for button 0 + JLO_B_PB(KeyType::DROP, bm | 1 << 1, fb | 1 << 1); + JLO_B_PB(KeyType::SCROLL_UP, bm | 1 << 4, fb | 1 << 4); + JLO_B_PB(KeyType::SCROLL_DOWN,bm | 1 << 5, fb | 1 << 5); + + // Accessible with start button and four pressed + // TODO find usage for buttons 0, 1 and 4, 5 + + // Now about the buttons simulated by the axes + + // Movement buttons, important for vessels + JLO_A_PB(KeyType::FORWARD, 1, 1, 1024); + JLO_A_PB(KeyType::BACKWARD, 1, -1, 1024); + JLO_A_PB(KeyType::LEFT, 0, 1, 1024); + JLO_A_PB(KeyType::RIGHT, 0, -1, 1024); + + // Scroll buttons + JLO_A_PB(KeyType::SCROLL_UP, 2, -1, 1024); + JLO_A_PB(KeyType::SCROLL_DOWN, 5, -1, 1024); + + return jlo; +} + +static const JoystickLayout default_layout = create_default_layout(); + +JoystickController::JoystickController() +{ + m_layout = &default_layout; + doubling_dtime = g_settings->getFloat("repeat_joystick_button_time"); + + for (size_t i = 0; i < KeyType::INTERNAL_ENUM_COUNT; i++) { + m_past_pressed_time[i] = 0; + } + clear(); +} + +bool JoystickController::handleEvent(const irr::SEvent::SJoystickEvent &ev) +{ + m_internal_time = getTimeMs() / 1000.f; + + std::bitset keys_pressed; + + // First generate a list of keys pressed + + for (size_t i = 0; i < m_layout->button_keys.size(); i++) { + if (m_layout->button_keys[i].isTriggered(ev)) { + keys_pressed.set(m_layout->button_keys[i].key); + } + } + + for (size_t i = 0; i < m_layout->axis_keys.size(); i++) { + if (m_layout->axis_keys[i].isTriggered(ev)) { + keys_pressed.set(m_layout->axis_keys[i].key); + } + } + + // Then update the values + + for (size_t i = 0; i < KeyType::INTERNAL_ENUM_COUNT; i++) { + if (keys_pressed[i]) { + if (!m_past_pressed_keys[i] && + m_past_pressed_time[i] < m_internal_time - doubling_dtime) { + m_past_pressed_keys[i] = true; + m_past_pressed_time[i] = m_internal_time; + } + } else if (m_pressed_keys[i]) { + m_past_released_keys[i] = true; + } + + m_pressed_keys[i] = keys_pressed[i]; + } + + for (size_t i = 0; i < JA_COUNT; i++) { + const JoystickAxisLayout &ax_la = m_layout->axes[i]; + m_axes_vals[i] = ax_la.invert * ev.Axis[ax_la.axis_id]; + } + + + return true; +} + +void JoystickController::clear() +{ + m_pressed_keys.reset(); + m_past_pressed_keys.reset(); + m_past_released_keys.reset(); + memset(m_axes_vals, 0, sizeof(m_axes_vals)); +} + +s16 JoystickController::getAxisWithoutDead(JoystickAxis axis) +{ + s16 v = m_axes_vals[axis]; + if (((v > 0) && (v < m_layout->axes_dead_border)) || + ((v < 0) && (v > -m_layout->axes_dead_border))) + return 0; + return v; +} diff --git a/src/client/joystick_controller.h b/src/client/joystick_controller.h new file mode 100644 index 000000000..ed0ee4068 --- /dev/null +++ b/src/client/joystick_controller.h @@ -0,0 +1,163 @@ +/* +Minetest +Copyright (C) 2016 est31, + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef JOYSTICK_HEADER +#define JOYSTICK_HEADER + +#include "irrlichttypes_extrabloated.h" +#include "keys.h" +#include +#include + +enum JoystickAxis { + JA_SIDEWARD_MOVE, + JA_FORWARD_MOVE, + + JA_FRUSTUM_HORIZONTAL, + JA_FRUSTUM_VERTICAL, + + // To know the count of enum values + JA_COUNT, +}; + +struct JoystickAxisLayout { + u16 axis_id; + // -1 if to invert, 1 if to keep it. + int invert; +}; + + +struct JoystickCombination { + + virtual bool isTriggered(const irr::SEvent::SJoystickEvent &ev) const=0; + + GameKeyType key; +}; + +struct JoystickButtonCmb : public JoystickCombination { + + JoystickButtonCmb() {} + JoystickButtonCmb(GameKeyType key, u32 filter_mask, u32 compare_mask) : + filter_mask(filter_mask), + compare_mask(compare_mask) + { + this->key = key; + } + + virtual bool isTriggered(const irr::SEvent::SJoystickEvent &ev) const; + + u32 filter_mask; + u32 compare_mask; +}; + +struct JoystickAxisCmb : public JoystickCombination { + + JoystickAxisCmb() {} + JoystickAxisCmb(GameKeyType key, u16 axis_to_compare, int direction, s16 thresh) : + axis_to_compare(axis_to_compare), + direction(direction), + thresh(thresh) + { + this->key = key; + } + + virtual bool isTriggered(const irr::SEvent::SJoystickEvent &ev) const; + + u16 axis_to_compare; + + // if -1, thresh must be smaller than the axis value in order to trigger + // if 1, thresh must be bigger than the axis value in order to trigger + int direction; + s16 thresh; +}; + +struct JoystickLayout { + std::vector button_keys; + std::vector axis_keys; + JoystickAxisLayout axes[JA_COUNT]; + s16 axes_dead_border; +}; + +class JoystickController { + +public: + JoystickController(); + bool handleEvent(const irr::SEvent::SJoystickEvent &ev); + void clear(); + + bool wasKeyDown(GameKeyType b) + { + bool r = m_past_pressed_keys[b]; + m_past_pressed_keys[b] = false; + return r; + } + bool getWasKeyDown(GameKeyType b) + { + return m_past_pressed_keys[b]; + } + void clearWasKeyDown(GameKeyType b) + { + m_past_pressed_keys[b] = false; + } + + bool wasKeyReleased(GameKeyType b) + { + bool r = m_past_released_keys[b]; + m_past_released_keys[b] = false; + return r; + } + bool getWasKeyReleased(GameKeyType b) + { + return m_past_pressed_keys[b]; + } + void clearWasKeyReleased(GameKeyType b) + { + m_past_pressed_keys[b] = false; + } + + bool isKeyDown(GameKeyType b) + { + return m_pressed_keys[b]; + } + + s16 getAxis(JoystickAxis axis) + { + return m_axes_vals[axis]; + } + + s16 getAxisWithoutDead(JoystickAxis axis); + + f32 doubling_dtime; + +private: + const JoystickLayout *m_layout; + + s16 m_axes_vals[JA_COUNT]; + + std::bitset m_pressed_keys; + + f32 m_internal_time; + + f32 m_past_pressed_time[KeyType::INTERNAL_ENUM_COUNT]; + + std::bitset m_past_pressed_keys; + std::bitset m_past_released_keys; +}; + +#endif diff --git a/src/client/keys.h b/src/client/keys.h index 08b1c4123..0921bc166 100644 --- a/src/client/keys.h +++ b/src/client/keys.h @@ -67,6 +67,12 @@ public: DEBUG_STACKS, + // joystick specific keys + MOUSE_L, + MOUSE_R, + SCROLL_UP, + SCROLL_DOWN, + // Fake keycode for array size and internal checks INTERNAL_ENUM_COUNT diff --git a/src/content_cao.cpp b/src/content_cao.cpp index 35ab1c508..eeb85c8a6 100644 --- a/src/content_cao.cpp +++ b/src/content_cao.cpp @@ -1056,7 +1056,9 @@ void GenericCAO::step(float dtime, ClientEnvironment *env) PlayerControl controls = player->getPlayerControl(); bool walking = false; - if(controls.up || controls.down || controls.left || controls.right) + if (controls.up || controls.down || controls.left || controls.right || + controls.forw_move_joystick_axis != 0.f || + controls.sidew_move_joystick_axis != 0.f) walking = true; f32 new_speed = player->local_animation_speed; diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index 632ec0df9..43a7a7c00 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -116,6 +116,9 @@ void set_default_settings(Settings *settings) settings->setDefault("free_move", "false"); settings->setDefault("noclip", "false"); settings->setDefault("continuous_forward", "false"); + settings->setDefault("enable_joysticks", "true"); + settings->setDefault("repeat_joystick_button_time", "0.17"); + settings->setDefault("joystick_frustum_sensitivity", "170"); settings->setDefault("cinematic", "false"); settings->setDefault("camera_smoothing", "0"); settings->setDefault("cinematic_camera_smoothing", "0.7"); diff --git a/src/game.cpp b/src/game.cpp index ac1c0fe6b..ba77d299a 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -24,6 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "client.h" #include "client/tile.h" // For TextureSource #include "client/keys.h" +#include "client/joystick_controller.h" #include "clientmap.h" #include "clouds.h" #include "config.h" @@ -1108,12 +1109,14 @@ bool nodePlacementPrediction(Client &client, static inline void create_formspec_menu(GUIFormSpecMenu **cur_formspec, InventoryManager *invmgr, IGameDef *gamedef, IWritableTextureSource *tsrc, IrrlichtDevice *device, + JoystickController *joystick, IFormSource *fs_src, TextDest *txt_dest, Client *client) { if (*cur_formspec == 0) { - *cur_formspec = new GUIFormSpecMenu(device, guiroot, -1, &g_menumgr, - invmgr, gamedef, tsrc, fs_src, txt_dest, client); + *cur_formspec = new GUIFormSpecMenu(device, joystick, + guiroot, -1, &g_menumgr, invmgr, gamedef, tsrc, + fs_src, txt_dest, client); (*cur_formspec)->doPause = false; /* @@ -1138,7 +1141,8 @@ static inline void create_formspec_menu(GUIFormSpecMenu **cur_formspec, static void show_deathscreen(GUIFormSpecMenu **cur_formspec, InventoryManager *invmgr, IGameDef *gamedef, - IWritableTextureSource *tsrc, IrrlichtDevice *device, Client *client) + IWritableTextureSource *tsrc, IrrlichtDevice *device, + JoystickController *joystick, Client *client) { std::string formspec = std::string(FORMSPEC_VERSION_STRING) + @@ -1154,14 +1158,15 @@ static void show_deathscreen(GUIFormSpecMenu **cur_formspec, FormspecFormSource *fs_src = new FormspecFormSource(formspec); LocalFormspecHandler *txt_dst = new LocalFormspecHandler("MT_DEATH_SCREEN", client); - create_formspec_menu(cur_formspec, invmgr, gamedef, tsrc, device, fs_src, txt_dst, NULL); + create_formspec_menu(cur_formspec, invmgr, gamedef, tsrc, device, + joystick, fs_src, txt_dst, NULL); } /******************************************************************************/ static void show_pause_menu(GUIFormSpecMenu **cur_formspec, InventoryManager *invmgr, IGameDef *gamedef, IWritableTextureSource *tsrc, IrrlichtDevice *device, - bool singleplayermode) + JoystickController *joystick, bool singleplayermode) { #ifdef __ANDROID__ std::string control_text = strgettext("Default Controls:\n" @@ -1226,7 +1231,8 @@ static void show_pause_menu(GUIFormSpecMenu **cur_formspec, FormspecFormSource *fs_src = new FormspecFormSource(os.str()); LocalFormspecHandler *txt_dst = new LocalFormspecHandler("MT_PAUSE_MENU"); - create_formspec_menu(cur_formspec, invmgr, gamedef, tsrc, device, fs_src, txt_dst, NULL); + create_formspec_menu(cur_formspec, invmgr, gamedef, tsrc, device, + joystick, fs_src, txt_dst, NULL); std::string con("btn_continue"); (*cur_formspec)->setFocus(con); (*cur_formspec)->doPause = true; @@ -1574,9 +1580,10 @@ protected: void decreaseViewRange(float *statustext_time); void toggleFullViewRange(float *statustext_time); - void updateCameraDirection(CameraOrientation *cam, VolatileRunFlags *flags); + void updateCameraDirection(CameraOrientation *cam, VolatileRunFlags *flags, + float dtime); void updateCameraOrientation(CameraOrientation *cam, - const VolatileRunFlags &flags); + const VolatileRunFlags &flags, float dtime); void updatePlayerControl(const CameraOrientation &cam); void step(f32 *dtime); void processClientEvents(CameraOrientation *cam, float *damage_flash); @@ -1611,34 +1618,39 @@ protected: static void settingChangedCallback(const std::string &setting_name, void *data); void readSettings(); - bool getLeftClicked() + inline bool getLeftClicked() { - return input->getLeftClicked(); + return input->getLeftClicked() || + input->joystick.getWasKeyDown(KeyType::MOUSE_L); } - bool getRightClicked() + inline bool getRightClicked() { - return input->getRightClicked(); + return input->getRightClicked() || + input->joystick.getWasKeyDown(KeyType::MOUSE_R); } - bool isLeftPressed() + inline bool isLeftPressed() { - return input->getLeftState(); + return input->getLeftState() || + input->joystick.isKeyDown(KeyType::MOUSE_L); } - bool isRightPressed() + inline bool isRightPressed() { - return input->getRightState(); + return input->getRightState() || + input->joystick.isKeyDown(KeyType::MOUSE_R); } - bool getLeftReleased() + inline bool getLeftReleased() { - return input->getLeftReleased(); + return input->getLeftReleased() || + input->joystick.wasKeyReleased(KeyType::MOUSE_L); } - bool isKeyDown(GameKeyType k) + inline bool isKeyDown(GameKeyType k) { - return input->isKeyDown(keycache.key[k]); + return input->isKeyDown(keycache.key[k]) || input->joystick.isKeyDown(k); } - bool wasKeyDown(GameKeyType k) + inline bool wasKeyDown(GameKeyType k) { - return input->wasKeyDown(keycache.key[k]); + return input->wasKeyDown(keycache.key[k]) || input->joystick.wasKeyDown(k); } #ifdef __ANDROID__ @@ -1724,9 +1736,11 @@ private: */ bool m_cache_doubletap_jump; bool m_cache_enable_clouds; + bool m_cache_enable_joysticks; bool m_cache_enable_particles; bool m_cache_enable_fog; f32 m_cache_mouse_sensitivity; + f32 m_cache_joystick_frustum_sensitivity; f32 m_repeat_right_click_time; #ifdef __ANDROID__ @@ -1762,12 +1776,16 @@ Game::Game() : &settingChangedCallback, this); g_settings->registerChangedCallback("enable_clouds", &settingChangedCallback, this); + g_settings->registerChangedCallback("doubletap_joysticks", + &settingChangedCallback, this); g_settings->registerChangedCallback("enable_particles", &settingChangedCallback, this); g_settings->registerChangedCallback("enable_fog", &settingChangedCallback, this); g_settings->registerChangedCallback("mouse_sensitivity", &settingChangedCallback, this); + g_settings->registerChangedCallback("joystick_frustum_sensitivity", + &settingChangedCallback, this); g_settings->registerChangedCallback("repeat_rightclick_time", &settingChangedCallback, this); @@ -1930,7 +1948,7 @@ void Game::run() updateProfilers(runData, stats, draw_times, dtime); processUserInput(&flags, &runData, dtime); // Update camera before player movement to avoid camera lag of one frame - updateCameraDirection(&cam_view_target, &flags); + updateCameraDirection(&cam_view_target, &flags, dtime); float cam_smoothing = 0; if (g_settings->getBool("cinematic")) cam_smoothing = 1 - g_settings->getFloat("cinematic_camera_smoothing"); @@ -2727,7 +2745,8 @@ void Game::processKeyInput(VolatileRunFlags *flags, } else if (wasKeyDown(KeyType::ESC) || input->wasKeyDown(CancelKey)) { if (!gui_chat_console->isOpenInhibited()) { show_pause_menu(¤t_formspec, client, gamedef, - texture_src, device, simple_singleplayer_mode); + texture_src, device, &input->joystick, + simple_singleplayer_mode); } } else if (wasKeyDown(KeyType::CHAT)) { openConsole(0.2, L""); @@ -2813,12 +2832,21 @@ void Game::processItemSelection(u16 *new_playeritem) u16 max_item = MYMIN(PLAYER_INVENTORY_SIZE - 1, player->hud_hotbar_itemcount - 1); - if (wheel < 0) + s32 dir = wheel; + + if (input->joystick.wasKeyDown(KeyType::SCROLL_DOWN)) { + dir = -1; + } + + if (input->joystick.wasKeyDown(KeyType::SCROLL_UP)) { + dir = 1; + } + + if (dir < 0) *new_playeritem = *new_playeritem < max_item ? *new_playeritem + 1 : 0; - else if (wheel > 0) + else if (dir > 0) *new_playeritem = *new_playeritem > 0 ? *new_playeritem - 1 : max_item; - // else wheel == 0 - + // else dir == 0 /* Item selection using keyboard */ @@ -2868,7 +2896,7 @@ void Game::openInventory() TextDest *txt_dst = new TextDestPlayerInventory(client); create_formspec_menu(¤t_formspec, client, gamedef, texture_src, - device, fs_src, txt_dst, client); + device, &input->joystick, fs_src, txt_dst, client); InventoryLocation inventoryloc; inventoryloc.setCurrentPlayer(); @@ -3154,7 +3182,7 @@ void Game::toggleFullViewRange(float *statustext_time) void Game::updateCameraDirection(CameraOrientation *cam, - VolatileRunFlags *flags) + VolatileRunFlags *flags, float dtime) { if ((device->isWindowActive() && noMenuActive()) || random_input) { @@ -3169,7 +3197,7 @@ void Game::updateCameraDirection(CameraOrientation *cam, if (flags->first_loop_after_window_activation) flags->first_loop_after_window_activation = false; else - updateCameraOrientation(cam, *flags); + updateCameraOrientation(cam, *flags, dtime); input->setMousePos((driver->getScreenSize().Width / 2), (driver->getScreenSize().Height / 2)); @@ -3187,9 +3215,8 @@ void Game::updateCameraDirection(CameraOrientation *cam, } } - void Game::updateCameraOrientation(CameraOrientation *cam, - const VolatileRunFlags &flags) + const VolatileRunFlags &flags, float dtime) { #ifdef HAVE_TOUCHSCREENGUI if (g_touchscreengui) { @@ -3197,6 +3224,7 @@ void Game::updateCameraOrientation(CameraOrientation *cam, cam->camera_pitch = g_touchscreengui->getPitch(); } else { #endif + s32 dx = input->getMousePos().X - (driver->getScreenSize().Width / 2); s32 dy = input->getMousePos().Y - (driver->getScreenSize().Height / 2); @@ -3212,6 +3240,14 @@ void Game::updateCameraOrientation(CameraOrientation *cam, } #endif + if (m_cache_enable_joysticks) { + f32 c = m_cache_joystick_frustum_sensitivity * (1.f / 32767.f) * dtime; + cam->camera_yaw -= input->joystick.getAxisWithoutDead(JA_FRUSTUM_HORIZONTAL) * + c; + cam->camera_pitch += input->joystick.getAxisWithoutDead(JA_FRUSTUM_VERTICAL) * + c; + } + cam->camera_pitch = rangelim(cam->camera_pitch, -89.5, 89.5); } @@ -3220,30 +3256,36 @@ void Game::updatePlayerControl(const CameraOrientation &cam) { //TimeTaker tt("update player control", NULL, PRECISION_NANO); + // DO NOT use the isKeyDown method for the forward, backward, left, right + // buttons, as the code that uses the controls needs to be able to + // distinguish between the two in order to know when to use joysticks. + PlayerControl control( input->isKeyDown(keycache.key[KeyType::FORWARD]), input->isKeyDown(keycache.key[KeyType::BACKWARD]), input->isKeyDown(keycache.key[KeyType::LEFT]), input->isKeyDown(keycache.key[KeyType::RIGHT]), - input->isKeyDown(keycache.key[KeyType::JUMP]), - input->isKeyDown(keycache.key[KeyType::SPECIAL1]), - input->isKeyDown(keycache.key[KeyType::SNEAK]), - input->getLeftState(), - input->getRightState(), + isKeyDown(KeyType::JUMP), + isKeyDown(KeyType::SPECIAL1), + isKeyDown(KeyType::SNEAK), + isLeftPressed(), + isRightPressed(), cam.camera_pitch, - cam.camera_yaw + cam.camera_yaw, + input->joystick.getAxisWithoutDead(JA_SIDEWARD_MOVE), + input->joystick.getAxisWithoutDead(JA_FORWARD_MOVE) ); u32 keypress_bits = - ( (u32)(input->isKeyDown(keycache.key[KeyType::FORWARD]) & 0x1) << 0) | - ( (u32)(input->isKeyDown(keycache.key[KeyType::BACKWARD]) & 0x1) << 1) | - ( (u32)(input->isKeyDown(keycache.key[KeyType::LEFT]) & 0x1) << 2) | - ( (u32)(input->isKeyDown(keycache.key[KeyType::RIGHT]) & 0x1) << 3) | - ( (u32)(input->isKeyDown(keycache.key[KeyType::JUMP]) & 0x1) << 4) | - ( (u32)(input->isKeyDown(keycache.key[KeyType::SPECIAL1]) & 0x1) << 5) | - ( (u32)(input->isKeyDown(keycache.key[KeyType::SNEAK]) & 0x1) << 6) | - ( (u32)(input->getLeftState() & 0x1) << 7) | - ( (u32)(input->getRightState() & 0x1) << 8 + ( (u32)(isKeyDown(KeyType::FORWARD) & 0x1) << 0) | + ( (u32)(isKeyDown(KeyType::BACKWARD) & 0x1) << 1) | + ( (u32)(isKeyDown(KeyType::LEFT) & 0x1) << 2) | + ( (u32)(isKeyDown(KeyType::RIGHT) & 0x1) << 3) | + ( (u32)(isKeyDown(KeyType::JUMP) & 0x1) << 4) | + ( (u32)(isKeyDown(KeyType::SPECIAL1) & 0x1) << 5) | + ( (u32)(isKeyDown(KeyType::SNEAK) & 0x1) << 6) | + ( (u32)(isLeftPressed() & 0x1) << 7) | + ( (u32)(isRightPressed() & 0x1) << 8 ); #ifdef ANDROID @@ -3312,7 +3354,7 @@ void Game::processClientEvents(CameraOrientation *cam, float *damage_flash) cam->camera_pitch = event.player_force_move.pitch; } else if (event.type == CE_DEATHSCREEN) { show_deathscreen(¤t_formspec, client, gamedef, texture_src, - device, client); + device, &input->joystick, client); chat_backend->addMessage(L"", L"You died."); @@ -3328,7 +3370,8 @@ void Game::processClientEvents(CameraOrientation *cam, float *damage_flash) new TextDestPlayerInventory(client, *(event.show_formspec.formname)); create_formspec_menu(¤t_formspec, client, gamedef, - texture_src, device, fs_src, txt_dst, client); + texture_src, device, &input->joystick, + fs_src, txt_dst, client); delete(event.show_formspec.formspec); delete(event.show_formspec.formname); @@ -3723,8 +3766,14 @@ void Game::processPlayerInteraction(GameRunData *runData, input->resetLeftClicked(); input->resetRightClicked(); + input->joystick.clearWasKeyDown(KeyType::MOUSE_L); + input->joystick.clearWasKeyDown(KeyType::MOUSE_R); + input->resetLeftReleased(); input->resetRightReleased(); + + input->joystick.clearWasKeyReleased(KeyType::MOUSE_L); + input->joystick.clearWasKeyReleased(KeyType::MOUSE_R); } @@ -3785,7 +3834,7 @@ void Game::handlePointingAtNode(GameRunData *runData, TextDest *txt_dst = new TextDestNodeMetadata(nodepos, client); create_formspec_menu(¤t_formspec, client, gamedef, - texture_src, device, fs_src, txt_dst, client); + texture_src, device, &input->joystick, fs_src, txt_dst, client); current_formspec->setFormSpec(meta->getString("formspec"), inventoryloc); } else { @@ -4468,12 +4517,14 @@ void Game::settingChangedCallback(const std::string &setting_name, void *data) void Game::readSettings() { - m_cache_doubletap_jump = g_settings->getBool("doubletap_jump"); - m_cache_enable_clouds = g_settings->getBool("enable_clouds"); - m_cache_enable_particles = g_settings->getBool("enable_particles"); - m_cache_enable_fog = g_settings->getBool("enable_fog"); - m_cache_mouse_sensitivity = g_settings->getFloat("mouse_sensitivity"); - m_repeat_right_click_time = g_settings->getFloat("repeat_rightclick_time"); + m_cache_doubletap_jump = g_settings->getBool("doubletap_jump"); + m_cache_enable_clouds = g_settings->getBool("enable_clouds"); + m_cache_enable_joysticks = g_settings->getBool("enable_joysticks"); + m_cache_enable_particles = g_settings->getBool("enable_particles"); + m_cache_enable_fog = g_settings->getBool("enable_fog"); + m_cache_mouse_sensitivity = g_settings->getFloat("mouse_sensitivity"); + m_cache_joystick_frustum_sensitivity = g_settings->getFloat("joystick_frustum_sensitivity"); + m_repeat_right_click_time = g_settings->getFloat("repeat_rightclick_time"); m_cache_mouse_sensitivity = rangelim(m_cache_mouse_sensitivity, 0.001, 100.0); } diff --git a/src/game.h b/src/game.h index d8c2f78fc..df32e3397 100644 --- a/src/game.h +++ b/src/game.h @@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "irrlichttypes_extrabloated.h" #include #include "client/keys.h" +#include "client/joystick_controller.h" #include "keycode.h" #include @@ -135,6 +136,8 @@ public: virtual void step(float dtime) {} virtual void clear() {} + + JoystickController joystick; }; class ChatBackend; /* to avoid having to include chat.h */ diff --git a/src/guiEngine.cpp b/src/guiEngine.cpp index 96de7a4f7..b9d796ccb 100644 --- a/src/guiEngine.cpp +++ b/src/guiEngine.cpp @@ -131,6 +131,7 @@ void MenuMusicFetcher::fetchSounds(const std::string &name, /** GUIEngine */ /******************************************************************************/ GUIEngine::GUIEngine( irr::IrrlichtDevice* dev, + JoystickController *joystick, gui::IGUIElement* parent, IMenuManager *menumgr, scene::ISceneManager* smgr, @@ -189,6 +190,7 @@ GUIEngine::GUIEngine( irr::IrrlichtDevice* dev, /* Create menu */ m_menu = new GUIFormSpecMenu(m_device, + joystick, m_parent, -1, m_menumanager, diff --git a/src/guiEngine.h b/src/guiEngine.h index fa98a21e4..a59436953 100644 --- a/src/guiEngine.h +++ b/src/guiEngine.h @@ -149,7 +149,8 @@ public: * @param smgr scene manager to add scene elements to * @param data struct to transfer data to main game handling */ - GUIEngine( irr::IrrlichtDevice* dev, + GUIEngine(irr::IrrlichtDevice* dev, + JoystickController *joystick, gui::IGUIElement* parent, IMenuManager *menumgr, scene::ISceneManager* smgr, diff --git a/src/guiFormSpecMenu.cpp b/src/guiFormSpecMenu.cpp index cf01dc38c..a9cbb6254 100644 --- a/src/guiFormSpecMenu.cpp +++ b/src/guiFormSpecMenu.cpp @@ -79,6 +79,7 @@ static unsigned int font_line_height(gui::IGUIFont *font) } GUIFormSpecMenu::GUIFormSpecMenu(irr::IrrlichtDevice* dev, + JoystickController *joystick, gui::IGUIElement* parent, s32 id, IMenuManager *menumgr, InventoryManager *invmgr, IGameDef *gamedef, ISimpleTextureSource *tsrc, IFormSource* fsrc, TextDest* tdst, @@ -102,6 +103,7 @@ GUIFormSpecMenu::GUIFormSpecMenu(irr::IrrlichtDevice* dev, m_text_dst(tdst), m_formspec_version(0), m_focused_element(""), + m_joystick(joystick), m_font(NULL), m_remap_dbl_click(remap_dbl_click) #ifdef __ANDROID__ @@ -2459,7 +2461,7 @@ void GUIFormSpecMenu::drawMenu() Draw static text elements */ for (u32 i = 0; i < m_static_texts.size(); i++) { - const StaticTextSpec &spec = m_static_texts[i]; + const StaticTextSpec &spec = m_static_texts[i]; core::rect rect = spec.rect; if (spec.parent_button && spec.parent_button->isPressed()) { #if (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 8) @@ -3024,6 +3026,25 @@ bool GUIFormSpecMenu::preprocessEvent(const SEvent& event) } #endif + if (event.EventType == irr::EET_JOYSTICK_INPUT_EVENT) { + /* TODO add a check like: + if (event.JoystickEvent != joystick_we_listen_for) + return false; + */ + bool handled = m_joystick->handleEvent(event.JoystickEvent); + if (handled) { + if (m_joystick->wasKeyDown(KeyType::ESC)) { + tryClose(); + } else if (m_joystick->wasKeyDown(KeyType::JUMP)) { + if (m_allowclose) { + acceptInput(quit_mode_accept); + quitMenu(); + } + } + } + return handled; + } + return false; } @@ -3085,19 +3106,24 @@ bool GUIFormSpecMenu::DoubleClickDetection(const SEvent event) return false; } +void GUIFormSpecMenu::tryClose() +{ + if (m_allowclose) { + doPause = false; + acceptInput(quit_mode_cancel); + quitMenu(); + } else { + m_text_dst->gotText(L"MenuQuit"); + } +} + bool GUIFormSpecMenu::OnEvent(const SEvent& event) { if (event.EventType==EET_KEY_INPUT_EVENT) { KeyPress kp(event.KeyInput); if (event.KeyInput.PressedDown && ( (kp == EscapeKey) || (kp == getKeySetting("keymap_inventory")) || (kp == CancelKey))) { - if (m_allowclose) { - doPause = false; - acceptInput(quit_mode_cancel); - quitMenu(); - } else { - m_text_dst->gotText(L"MenuQuit"); - } + tryClose(); return true; } else if (m_client != NULL && event.KeyInput.PressedDown && (kp == getKeySetting("keymap_screenshot"))) { diff --git a/src/guiFormSpecMenu.h b/src/guiFormSpecMenu.h index 4122b1f56..2fb55070d 100644 --- a/src/guiFormSpecMenu.h +++ b/src/guiFormSpecMenu.h @@ -29,6 +29,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "modalMenu.h" #include "guiTable.h" #include "network/networkprotocol.h" +#include "client/joystick_controller.h" #include "util/string.h" #include "util/enriched_string.h" @@ -278,6 +279,7 @@ class GUIFormSpecMenu : public GUIModalMenu public: GUIFormSpecMenu(irr::IrrlichtDevice* dev, + JoystickController *joystick, gui::IGUIElement* parent, s32 id, IMenuManager *menumgr, InventoryManager *invmgr, @@ -433,10 +435,11 @@ protected: video::SColor m_default_tooltip_color; private: - IFormSource *m_form_src; - TextDest *m_text_dst; - unsigned int m_formspec_version; - std::string m_focused_element; + IFormSource *m_form_src; + TextDest *m_text_dst; + unsigned int m_formspec_version; + std::string m_focused_element; + JoystickController *m_joystick; typedef struct { bool explicit_size; @@ -494,6 +497,8 @@ private: bool parseSizeDirect(parserData* data, std::string element); void parseScrollBar(parserData* data, std::string element); + void tryClose(); + /** * check if event is part of a double click * @param event event to evaluate diff --git a/src/localplayer.cpp b/src/localplayer.cpp index 507f31980..732ca8acf 100644 --- a/src/localplayer.cpp +++ b/src/localplayer.cpp @@ -528,18 +528,23 @@ void LocalPlayer::applyControl(float dtime) speedH += move_direction; } } - if(control.down) - { + if (control.down) { speedH -= move_direction; } - if(control.left) - { + if (!control.up && !control.down) { + speedH -= move_direction * + (control.forw_move_joystick_axis / 32767.f); + } + if (control.left) { speedH += move_direction.crossProduct(v3f(0,1,0)); } - if(control.right) - { + if (control.right) { speedH += move_direction.crossProduct(v3f(0,-1,0)); } + if (!control.left && !control.right) { + speedH -= move_direction.crossProduct(v3f(0,1,0)) * + (control.sidew_move_joystick_axis / 32767.f); + } if(control.jump) { if (free_move) { diff --git a/src/player.h b/src/player.h index b317cda4f..6687ca86e 100644 --- a/src/player.h +++ b/src/player.h @@ -46,6 +46,8 @@ struct PlayerControl RMB = false; pitch = 0; yaw = 0; + sidew_move_joystick_axis = .0f; + forw_move_joystick_axis = .0f; } PlayerControl( bool a_up, @@ -58,7 +60,9 @@ struct PlayerControl bool a_LMB, bool a_RMB, float a_pitch, - float a_yaw + float a_yaw, + float a_sidew_move_joystick_axis, + float a_forw_move_joystick_axis ) { up = a_up; @@ -72,6 +76,8 @@ struct PlayerControl RMB = a_RMB; pitch = a_pitch; yaw = a_yaw; + sidew_move_joystick_axis = a_sidew_move_joystick_axis; + forw_move_joystick_axis = a_forw_move_joystick_axis; } bool up; bool down; @@ -84,6 +90,8 @@ struct PlayerControl bool RMB; float pitch; float yaw; + float sidew_move_joystick_axis; + float forw_move_joystick_axis; }; class Map; -- cgit v1.2.3 From dfbdb5bcd7bc48efb21d585d5c22454a9d5f0f1e Mon Sep 17 00:00:00 2001 From: kwolekr Date: Sat, 4 Jun 2016 01:35:37 -0400 Subject: Change internal type for seeds to s32 This fixes value truncation (and therefore incompatibility) on platforms with an LP32 data model, such as VAX or MS-DOS. --- src/cavegen.cpp | 4 ++-- src/cavegen.h | 6 +++--- src/dungeongen.h | 2 +- src/mapgen.cpp | 20 +++++++++++++++++--- src/mapgen.h | 6 +++--- src/mg_biome.h | 2 +- src/noise.cpp | 26 +++++++++++++------------- src/noise.h | 32 ++++++++++++++++---------------- src/script/lua_api/l_env.cpp | 2 +- src/script/lua_api/l_noise.cpp | 2 +- src/script/lua_api/l_noise.h | 4 ++-- src/treegen.cpp | 8 ++++---- src/treegen.h | 8 ++++---- 13 files changed, 68 insertions(+), 54 deletions(-) (limited to 'src') diff --git a/src/cavegen.cpp b/src/cavegen.cpp index 1c25ce711..dc7355fe0 100644 --- a/src/cavegen.cpp +++ b/src/cavegen.cpp @@ -35,7 +35,7 @@ static NoiseParams nparams_caveliquids(0, 1, v3f(150.0, 150.0, 150.0), 776, 3, 0 CavesNoiseIntersection::CavesNoiseIntersection( INodeDefManager *nodedef, BiomeManager *biomemgr, v3s16 chunksize, - NoiseParams *np_cave1, NoiseParams *np_cave2, int seed, float cave_width) + NoiseParams *np_cave1, NoiseParams *np_cave2, s32 seed, float cave_width) { assert(nodedef); assert(biomemgr); @@ -130,7 +130,7 @@ void CavesNoiseIntersection::generateCaves(MMVManip *vm, CavesRandomWalk::CavesRandomWalk( INodeDefManager *ndef, GenerateNotifier *gennotify, - int seed, + s32 seed, int water_level, content_t water_source, content_t lava_source) diff --git a/src/cavegen.h b/src/cavegen.h index da0894635..2bf503d47 100644 --- a/src/cavegen.h +++ b/src/cavegen.h @@ -41,7 +41,7 @@ class CavesNoiseIntersection { public: CavesNoiseIntersection(INodeDefManager *nodedef, BiomeManager *biomemgr, v3s16 chunksize, NoiseParams *np_cave1, NoiseParams *np_cave2, - int seed, float cave_width); + s32 seed, float cave_width); ~CavesNoiseIntersection(); void generateCaves(MMVManip *vm, v3s16 nmin, v3s16 nmax, u8 *biomemap); @@ -83,7 +83,7 @@ public: s16 *heightmap; // configurable parameters - int seed; + s32 seed; int water_level; int lava_depth; NoiseParams *np_caveliquids; @@ -122,7 +122,7 @@ public: // If gennotify is NULL, generation events are not logged. CavesRandomWalk(INodeDefManager *ndef, GenerateNotifier *gennotify = NULL, - int seed = 0, + s32 seed = 0, int water_level = 1, content_t water_source = CONTENT_IGNORE, content_t lava_source = CONTENT_IGNORE); diff --git a/src/dungeongen.h b/src/dungeongen.h index f0a5e5ba8..898627483 100644 --- a/src/dungeongen.h +++ b/src/dungeongen.h @@ -39,7 +39,7 @@ int dir_to_facedir(v3s16 d); struct DungeonParams { - int seed; + s32 seed; content_t c_water; content_t c_river_water; diff --git a/src/mapgen.cpp b/src/mapgen.cpp index 041356e3d..32f7e29eb 100644 --- a/src/mapgen.cpp +++ b/src/mapgen.cpp @@ -89,11 +89,25 @@ Mapgen::Mapgen(int mapgenid, MapgenParams *params, EmergeManager *emerge) : { generating = false; id = mapgenid; - seed = (int)params->seed; water_level = params->water_level; flags = params->flags; csize = v3s16(1, 1, 1) * (params->chunksize * MAP_BLOCKSIZE); + /* + We are losing half our entropy by doing this, but it is necessary to + preserve reverse compatibility. If the top half of our current 64 bit + seeds ever starts getting used, existing worlds will break due to a + different hash outcome and no way to differentiate between versions. + + A solution could be to add a new bit to designate that the top half of + the seed value should be used, essentially a 1-bit version code, but + this would require increasing the total size of a seed to 9 bytes (yuck) + + It's probably okay if this never gets fixed. 4.2 billion possibilities + ought to be enough for anyone. + */ + seed = (s32)params->seed; + vm = NULL; ndef = emerge->ndef; biomegen = NULL; @@ -107,7 +121,7 @@ Mapgen::~Mapgen() } -u32 Mapgen::getBlockSeed(v3s16 p, int seed) +u32 Mapgen::getBlockSeed(v3s16 p, s32 seed) { return (u32)seed + p.Z * 38134234 + @@ -116,7 +130,7 @@ u32 Mapgen::getBlockSeed(v3s16 p, int seed) } -u32 Mapgen::getBlockSeed2(v3s16 p, int seed) +u32 Mapgen::getBlockSeed2(v3s16 p, s32 seed) { u32 n = 1619 * p.X + 31337 * p.Y + 52591 * p.Z + 1013 * seed; n = (n >> 13) ^ n; diff --git a/src/mapgen.h b/src/mapgen.h index 10595fafc..0342fd46e 100644 --- a/src/mapgen.h +++ b/src/mapgen.h @@ -150,7 +150,7 @@ struct MapgenParams { */ class Mapgen { public: - int seed; + s32 seed; int water_level; u32 flags; bool generating; @@ -171,8 +171,8 @@ public: Mapgen(int mapgenid, MapgenParams *params, EmergeManager *emerge); virtual ~Mapgen(); - static u32 getBlockSeed(v3s16 p, int seed); - static u32 getBlockSeed2(v3s16 p, int seed); + static u32 getBlockSeed(v3s16 p, s32 seed); + static u32 getBlockSeed2(v3s16 p, s32 seed); s16 findGroundLevelFull(v2s16 p2d); s16 findGroundLevel(v2s16 p2d, s16 ymin, s16 ymax); s16 findLiquidSurface(v2s16 p2d, s16 ymin, s16 ymax); diff --git a/src/mg_biome.h b/src/mg_biome.h index e78e90e5f..389b7664f 100644 --- a/src/mg_biome.h +++ b/src/mg_biome.h @@ -80,7 +80,7 @@ struct BiomeParams { virtual void writeParams(Settings *settings) const = 0; virtual ~BiomeParams() {} - int seed; + s32 seed; }; class BiomeGen { diff --git a/src/noise.cpp b/src/noise.cpp index 2ddc3926f..c57c98ccb 100644 --- a/src/noise.cpp +++ b/src/noise.cpp @@ -156,7 +156,7 @@ s32 PcgRandom::randNormalDist(s32 min, s32 max, int num_trials) /////////////////////////////////////////////////////////////////////////////// -float noise2d(int x, int y, int seed) +float noise2d(int x, int y, s32 seed) { unsigned int n = (NOISE_MAGIC_X * x + NOISE_MAGIC_Y * y + NOISE_MAGIC_SEED * seed) & 0x7fffffff; @@ -166,7 +166,7 @@ float noise2d(int x, int y, int seed) } -float noise3d(int x, int y, int z, int seed) +float noise3d(int x, int y, int z, s32 seed) { unsigned int n = (NOISE_MAGIC_X * x + NOISE_MAGIC_Y * y + NOISE_MAGIC_Z * z + NOISE_MAGIC_SEED * seed) & 0x7fffffff; @@ -235,7 +235,7 @@ float triLinearInterpolationNoEase( return linearInterpolation(u, v, z); } -float noise2d_gradient(float x, float y, int seed, bool eased) +float noise2d_gradient(float x, float y, s32 seed, bool eased) { // Calculate the integer coordinates int x0 = myfloor(x); @@ -256,7 +256,7 @@ float noise2d_gradient(float x, float y, int seed, bool eased) } -float noise3d_gradient(float x, float y, float z, int seed, bool eased) +float noise3d_gradient(float x, float y, float z, s32 seed, bool eased) { // Calculate the integer coordinates int x0 = myfloor(x); @@ -290,7 +290,7 @@ float noise3d_gradient(float x, float y, float z, int seed, bool eased) } -float noise2d_perlin(float x, float y, int seed, +float noise2d_perlin(float x, float y, s32 seed, int octaves, float persistence, bool eased) { float a = 0; @@ -306,7 +306,7 @@ float noise2d_perlin(float x, float y, int seed, } -float noise2d_perlin_abs(float x, float y, int seed, +float noise2d_perlin_abs(float x, float y, s32 seed, int octaves, float persistence, bool eased) { float a = 0; @@ -321,7 +321,7 @@ float noise2d_perlin_abs(float x, float y, int seed, } -float noise3d_perlin(float x, float y, float z, int seed, +float noise3d_perlin(float x, float y, float z, s32 seed, int octaves, float persistence, bool eased) { float a = 0; @@ -336,7 +336,7 @@ float noise3d_perlin(float x, float y, float z, int seed, } -float noise3d_perlin_abs(float x, float y, float z, int seed, +float noise3d_perlin_abs(float x, float y, float z, s32 seed, int octaves, float persistence, bool eased) { float a = 0; @@ -363,7 +363,7 @@ float contour(float v) ///////////////////////// [ New noise ] //////////////////////////// -float NoisePerlin2D(NoiseParams *np, float x, float y, int seed) +float NoisePerlin2D(NoiseParams *np, float x, float y, s32 seed) { float a = 0; float f = 1.0; @@ -389,7 +389,7 @@ float NoisePerlin2D(NoiseParams *np, float x, float y, int seed) } -float NoisePerlin3D(NoiseParams *np, float x, float y, float z, int seed) +float NoisePerlin3D(NoiseParams *np, float x, float y, float z, s32 seed) { float a = 0; float f = 1.0; @@ -416,7 +416,7 @@ float NoisePerlin3D(NoiseParams *np, float x, float y, float z, int seed) } -Noise::Noise(NoiseParams *np_, int seed, u32 sx, u32 sy, u32 sz) +Noise::Noise(NoiseParams *np_, s32 seed, u32 sx, u32 sy, u32 sz) { memcpy(&np, np_, sizeof(np)); this->seed = seed; @@ -543,7 +543,7 @@ void Noise::resizeNoiseBuf(bool is3d) void Noise::gradientMap2D( float x, float y, float step_x, float step_y, - int seed) + s32 seed) { float v00, v01, v10, v11, u, v, orig_u; u32 index, i, j, noisex, noisey; @@ -607,7 +607,7 @@ void Noise::gradientMap2D( void Noise::gradientMap3D( float x, float y, float z, float step_x, float step_y, float step_z, - int seed) + s32 seed) { float v000, v010, v100, v110; float v001, v011, v101, v111; diff --git a/src/noise.h b/src/noise.h index 0e4252dd4..41b93ae01 100644 --- a/src/noise.h +++ b/src/noise.h @@ -148,7 +148,7 @@ struct NoiseParams { class Noise { public: NoiseParams np; - int seed; + s32 seed; u32 sx; u32 sy; u32 sz; @@ -157,7 +157,7 @@ public: float *persist_buf; float *result; - Noise(NoiseParams *np, int seed, u32 sx, u32 sy, u32 sz=1); + Noise(NoiseParams *np, s32 seed, u32 sx, u32 sy, u32 sz=1); ~Noise(); void setSize(u32 sx, u32 sy, u32 sz=1); @@ -167,11 +167,11 @@ public: void gradientMap2D( float x, float y, float step_x, float step_y, - int seed); + s32 seed); void gradientMap3D( float x, float y, float z, float step_x, float step_y, float step_z, - int seed); + s32 seed); float *perlinMap2D(float x, float y, float *persistence_map=NULL); float *perlinMap3D(float x, float y, float z, float *persistence_map=NULL); @@ -202,11 +202,11 @@ private: }; -float NoisePerlin2D(NoiseParams *np, float x, float y, int seed); -float NoisePerlin3D(NoiseParams *np, float x, float y, float z, int seed); +float NoisePerlin2D(NoiseParams *np, float x, float y, s32 seed); +float NoisePerlin3D(NoiseParams *np, float x, float y, float z, s32 seed); inline float NoisePerlin2D_PO(NoiseParams *np, float x, float xoff, - float y, float yoff, int seed) + float y, float yoff, s32 seed) { return NoisePerlin2D(np, x + xoff * np->spread.X, @@ -215,7 +215,7 @@ inline float NoisePerlin2D_PO(NoiseParams *np, float x, float xoff, } inline float NoisePerlin3D_PO(NoiseParams *np, float x, float xoff, - float y, float yoff, float z, float zoff, int seed) + float y, float yoff, float z, float zoff, s32 seed) { return NoisePerlin3D(np, x + xoff * np->spread.X, @@ -225,22 +225,22 @@ inline float NoisePerlin3D_PO(NoiseParams *np, float x, float xoff, } // Return value: -1 ... 1 -float noise2d(int x, int y, int seed); -float noise3d(int x, int y, int z, int seed); +float noise2d(int x, int y, s32 seed); +float noise3d(int x, int y, int z, s32 seed); -float noise2d_gradient(float x, float y, int seed, bool eased=true); -float noise3d_gradient(float x, float y, float z, int seed, bool eased=false); +float noise2d_gradient(float x, float y, s32 seed, bool eased=true); +float noise3d_gradient(float x, float y, float z, s32 seed, bool eased=false); -float noise2d_perlin(float x, float y, int seed, +float noise2d_perlin(float x, float y, s32 seed, int octaves, float persistence, bool eased=true); -float noise2d_perlin_abs(float x, float y, int seed, +float noise2d_perlin_abs(float x, float y, s32 seed, int octaves, float persistence, bool eased=true); -float noise3d_perlin(float x, float y, float z, int seed, +float noise3d_perlin(float x, float y, float z, s32 seed, int octaves, float persistence, bool eased=false); -float noise3d_perlin_abs(float x, float y, float z, int seed, +float noise3d_perlin_abs(float x, float y, float z, s32 seed, int octaves, float persistence, bool eased=false); inline float easeCurve(float t) diff --git a/src/script/lua_api/l_env.cpp b/src/script/lua_api/l_env.cpp index 8284c3fcb..ebdea09e4 100644 --- a/src/script/lua_api/l_env.cpp +++ b/src/script/lua_api/l_env.cpp @@ -758,7 +758,7 @@ int ModApiEnvMod::l_get_perlin_map(lua_State *L) return 0; v3s16 size = read_v3s16(L, 2); - int seed = (int)(env->getServerMap().getSeed()); + s32 seed = (s32)(env->getServerMap().getSeed()); LuaPerlinNoiseMap *n = new LuaPerlinNoiseMap(&np, seed, size); *(void **)(lua_newuserdata(L, sizeof(void *))) = n; luaL_getmetatable(L, "PerlinNoiseMap"); diff --git a/src/script/lua_api/l_noise.cpp b/src/script/lua_api/l_noise.cpp index 04dc6048f..e0039371f 100644 --- a/src/script/lua_api/l_noise.cpp +++ b/src/script/lua_api/l_noise.cpp @@ -146,7 +146,7 @@ const luaL_reg LuaPerlinNoise::methods[] = { LuaPerlinNoiseMap */ -LuaPerlinNoiseMap::LuaPerlinNoiseMap(NoiseParams *params, int seed, v3s16 size) +LuaPerlinNoiseMap::LuaPerlinNoiseMap(NoiseParams *params, s32 seed, v3s16 size) { m_is3d = size.Z > 1; np = *params; diff --git a/src/script/lua_api/l_noise.h b/src/script/lua_api/l_noise.h index 492eb7550..40bfd1315 100644 --- a/src/script/lua_api/l_noise.h +++ b/src/script/lua_api/l_noise.h @@ -79,7 +79,7 @@ class LuaPerlinNoiseMap : public ModApiBase { static int l_getMapSlice(lua_State *L); public: - LuaPerlinNoiseMap(NoiseParams *np, int seed, v3s16 size); + LuaPerlinNoiseMap(NoiseParams *np, s32 seed, v3s16 size); ~LuaPerlinNoiseMap(); @@ -111,7 +111,7 @@ private: static int l_next(lua_State *L); public: - LuaPseudoRandom(int seed) : + LuaPseudoRandom(s32 seed) : m_pseudo(seed) {} // LuaPseudoRandom(seed) diff --git a/src/treegen.cpp b/src/treegen.cpp index 208f34552..36d387c57 100644 --- a/src/treegen.cpp +++ b/src/treegen.cpp @@ -31,7 +31,7 @@ namespace treegen { void make_tree(MMVManip &vmanip, v3s16 p0, - bool is_apple_tree, INodeDefManager *ndef, int seed) + bool is_apple_tree, INodeDefManager *ndef, s32 seed) { /* NOTE: Tree-placing code is currently duplicated in the engine @@ -149,7 +149,7 @@ treegen::error make_ltree(MMVManip &vmanip, v3s16 p0, INodeDefManager *ndef, TreeDef tree_definition) { MapNode dirtnode(ndef->getId("mapgen_dirt")); - int seed; + s32 seed; if (tree_definition.explicit_seed) seed = tree_definition.seed + 14002; else @@ -649,7 +649,7 @@ v3f transposeMatrix(irr::core::matrix4 M, v3f v) } -void make_jungletree(MMVManip &vmanip, v3s16 p0, INodeDefManager *ndef, int seed) +void make_jungletree(MMVManip &vmanip, v3s16 p0, INodeDefManager *ndef, s32 seed) { /* NOTE: Tree-placing code is currently duplicated in the engine @@ -748,7 +748,7 @@ void make_jungletree(MMVManip &vmanip, v3s16 p0, INodeDefManager *ndef, int seed } -void make_pine_tree(MMVManip &vmanip, v3s16 p0, INodeDefManager *ndef, int seed) +void make_pine_tree(MMVManip &vmanip, v3s16 p0, INodeDefManager *ndef, s32 seed) { /* NOTE: Tree-placing code is currently duplicated in the engine diff --git a/src/treegen.h b/src/treegen.h index 4b0089d1e..4e6f95e67 100644 --- a/src/treegen.h +++ b/src/treegen.h @@ -54,19 +54,19 @@ namespace treegen { bool thin_branches; MapNode fruitnode; int fruit_chance; - int seed; + s32 seed; bool explicit_seed; }; // Add default tree void make_tree(MMVManip &vmanip, v3s16 p0, - bool is_apple_tree, INodeDefManager *ndef, int seed); + bool is_apple_tree, INodeDefManager *ndef, s32 seed); // Add jungle tree void make_jungletree(MMVManip &vmanip, v3s16 p0, - INodeDefManager *ndef, int seed); + INodeDefManager *ndef, s32 seed); // Add pine tree void make_pine_tree(MMVManip &vmanip, v3s16 p0, - INodeDefManager *ndef, int seed); + INodeDefManager *ndef, s32 seed); // Add L-Systems tree (used by engine) treegen::error make_ltree(MMVManip &vmanip, v3s16 p0, INodeDefManager *ndef, -- cgit v1.2.3 From 8ed467d438634ffe45806a6a6a325bb00774d651 Mon Sep 17 00:00:00 2001 From: kwolekr Date: Sat, 4 Jun 2016 02:16:06 -0400 Subject: PcgRandom: Fix/improve documentation --- doc/lua_api.txt | 4 +++- src/noise.cpp | 31 ++++++++++++++++++++----------- 2 files changed, 23 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index aa0d7e45d..f348f5103 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -2865,7 +2865,9 @@ It can be created via `PcgRandom(seed)` or `PcgRandom(seed, sequence)`. * `next()`: return next integer random number [`-2147483648`...`2147483647`] * `next(min, max)`: return next integer random number [`min`...`max`] * `rand_normal_dist(min, max, num_trials=6)`: return normally distributed random number [`min`...`max`] - * This is only a rough approximation of a normal distribution with mean=(max-min)/2 and variance=1 + * This is only a rough approximation of a normal distribution with: + * mean = (max - min) / 2, and + * variance = (((max - min + 1) ^ 2) - 1) / (12 * num_trials) * Increasing num_trials improves accuracy of the approximation ### `SecureRandom` diff --git a/src/noise.cpp b/src/noise.cpp index c57c98ccb..b918c9936 100644 --- a/src/noise.cpp +++ b/src/noise.cpp @@ -93,22 +93,31 @@ u32 PcgRandom::range(u32 bound) // If the bound is 0, we cover the whole RNG's range if (bound == 0) return next(); + /* - If the bound is not a multiple of the RNG's range, it may cause bias, - e.g. a RNG has a range from 0 to 3 and we take want a number 0 to 2. - Using rand() % 3, the number 0 would be twice as likely to appear. - With a very large RNG range, the effect becomes less prevalent but - still present. This can be solved by modifying the range of the RNG - to become a multiple of bound by dropping values above the a threshold. - In our example, threshold == 4 - 3 = 1 % 3 == 1, so reject 0, thus - making the range 3 with no bias. - - This loop looks dangerous, but will always terminate due to the - RNG's property of uniformity. + This is an optimization of the expression: + 0x100000000ull % bound + since 64-bit modulo operations typically much slower than 32. */ u32 threshold = -bound % bound; u32 r; + /* + If the bound is not a multiple of the RNG's range, it may cause bias, + e.g. a RNG has a range from 0 to 3 and we take want a number 0 to 2. + Using rand() % 3, the number 0 would be twice as likely to appear. + With a very large RNG range, the effect becomes less prevalent but + still present. + + This can be solved by modifying the range of the RNG to become a + multiple of bound by dropping values above the a threshold. + + In our example, threshold == 4 % 3 == 1, so reject values < 1 + (that is, 0), thus making the range == 3 with no bias. + + This loop may look dangerous, but will always terminate due to the + RNG's property of uniformity. + */ while ((r = next()) < threshold) ; -- cgit v1.2.3 From 109c7e334920f859068aeda31463f644e6b69895 Mon Sep 17 00:00:00 2001 From: kwolekr Date: Sat, 4 Jun 2016 03:00:45 -0400 Subject: Biomes: Define and use biome_t for biome IDs --- src/mapgen.h | 4 +++- src/mg_biome.cpp | 4 ++-- src/mg_biome.h | 13 ++++++------- 3 files changed, 11 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/mapgen.h b/src/mapgen.h index 0342fd46e..f673007b5 100644 --- a/src/mapgen.h +++ b/src/mapgen.h @@ -36,6 +36,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #define MG_LIGHT 0x10 #define MG_DECORATIONS 0x20 +typedef u8 biome_t; // copy from mg_biome.h to avoid an unnecessary include + class Settings; class MMVManip; class INodeDefManager; @@ -161,7 +163,7 @@ public: u32 blockseed; s16 *heightmap; - u8 *biomemap; + biome_t *biomemap; v3s16 csize; BiomeGen *biomegen; diff --git a/src/mg_biome.cpp b/src/mg_biome.cpp index dac0f7acc..df728af33 100644 --- a/src/mg_biome.cpp +++ b/src/mg_biome.cpp @@ -128,7 +128,7 @@ BiomeGenOriginal::BiomeGenOriginal(BiomeManager *biomemgr, heatmap = noise_heat->result; humidmap = noise_humidity->result; - biomemap = new u8[m_csize.X * m_csize.Z]; + biomemap = new biome_t[m_csize.X * m_csize.Z]; } BiomeGenOriginal::~BiomeGenOriginal() @@ -171,7 +171,7 @@ void BiomeGenOriginal::calcBiomeNoise(v3s16 pmin) } -u8 *BiomeGenOriginal::getBiomes(s16 *heightmap) +biome_t *BiomeGenOriginal::getBiomes(s16 *heightmap) { for (s32 i = 0; i != m_csize.X * m_csize.Z; i++) { Biome *biome = calcBiomeFromNoise( diff --git a/src/mg_biome.h b/src/mg_biome.h index 389b7664f..568d0b1ac 100644 --- a/src/mg_biome.h +++ b/src/mg_biome.h @@ -31,7 +31,9 @@ class BiomeManager; //// Biome //// -#define BIOME_NONE ((u8)0) +typedef u8 biome_t; + +#define BIOME_NONE ((biome_t)0) // TODO(hmmmm): Decide whether this is obsolete or will be used in the future enum BiomeType { @@ -101,7 +103,7 @@ public: // Gets all biomes in current chunk using each corresponding element of // heightmap as the y position, then stores the results by biome index in // biomemap (also returned) - virtual u8 *getBiomes(s16 *heightmap) = 0; + virtual biome_t *getBiomes(s16 *heightmap) = 0; // Gets a single biome at the specified position, which must be contained // in the region formed by m_pmin and (m_pmin + m_csize - 1). @@ -111,7 +113,7 @@ public: virtual Biome *getBiomeAtIndex(size_t index, s16 y) const = 0; // Result of calcBiomes bulk computation. - u8 *biomemap; + biome_t *biomemap; protected: BiomeManager *m_bmgr; @@ -157,7 +159,7 @@ public: Biome *calcBiomeAtPoint(v3s16 pos) const; void calcBiomeNoise(v3s16 pmin); - u8 *getBiomes(s16 *heightmap); + biome_t *getBiomes(s16 *heightmap); Biome *getBiomeAtPoint(v3s16 pos) const; Biome *getBiomeAtIndex(size_t index, s16 y) const; @@ -218,9 +220,6 @@ public: virtual void clear(); - // Looks for pos in the biome cache, and if non-existent, looks up by noise - u8 getBiomeAtPoint(v3s16 pos); - private: IGameDef *m_gamedef; -- cgit v1.2.3 From 7841f1c5098d73b7538623cf898ca1e32ac6773d Mon Sep 17 00:00:00 2001 From: MillersMan Date: Sun, 5 Jun 2016 01:30:36 +0200 Subject: Mapgen: Performance improvement and fixes for updateLiquid (#4065) - Adds only ~100 nodes per chunk to trans_liquid with similar processing time - Adds liquid nodes themselves instead of potential solid nodes below them - CONTENT_IGNORE nodes are interpreted as if they continue their neighborhood - This allows liquid columns to span multiple chunks without being interrupted - NOTE: Expects an one-node border in generation chunk without liquid changes --- src/mapgen.cpp | 87 ++++++++++++++++++++++++++++++++++++++++++++++++---------- src/mapgen.h | 4 +++ 2 files changed, 77 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/mapgen.cpp b/src/mapgen.cpp index 32f7e29eb..b5c48a471 100644 --- a/src/mapgen.cpp +++ b/src/mapgen.cpp @@ -213,27 +213,86 @@ void Mapgen::updateHeightmap(v3s16 nmin, v3s16 nmax) //printf("updateHeightmap: %dus\n", t.stop()); } +inline bool Mapgen::isLiquidHorizontallyFlowable(u32 vi, v3s16 em) +{ + u32 vi_neg_x = vi; + vm->m_area.add_x(em, vi_neg_x, -1); + if (vm->m_data[vi_neg_x].getContent() != CONTENT_IGNORE) { + const ContentFeatures &c_nx = ndef->get(vm->m_data[vi_neg_x]); + if (c_nx.floodable && !c_nx.isLiquid()) + return true; + } + u32 vi_pos_x = vi; + vm->m_area.add_x(em, vi_pos_x, +1); + if (vm->m_data[vi_pos_x].getContent() != CONTENT_IGNORE) { + const ContentFeatures &c_px = ndef->get(vm->m_data[vi_pos_x]); + if (c_px.floodable && !c_px.isLiquid()) + return true; + } + u32 vi_neg_z = vi; + vm->m_area.add_z(em, vi_neg_z, -1); + if (vm->m_data[vi_neg_z].getContent() != CONTENT_IGNORE) { + const ContentFeatures &c_nz = ndef->get(vm->m_data[vi_neg_z]); + if (c_nz.floodable && !c_nz.isLiquid()) + return true; + } + u32 vi_pos_z = vi; + vm->m_area.add_z(em, vi_pos_z, +1); + if (vm->m_data[vi_pos_z].getContent() != CONTENT_IGNORE) { + const ContentFeatures &c_pz = ndef->get(vm->m_data[vi_pos_z]); + if (c_pz.floodable && !c_pz.isLiquid()) + return true; + } + return false; +} void Mapgen::updateLiquid(UniqueQueue *trans_liquid, v3s16 nmin, v3s16 nmax) { - bool isliquid, wasliquid; + bool isignored, isliquid, wasignored, wasliquid, waschecked, waspushed; v3s16 em = vm->m_area.getExtent(); - for (s16 z = nmin.Z; z <= nmax.Z; z++) { - for (s16 x = nmin.X; x <= nmax.X; x++) { - wasliquid = true; - - u32 i = vm->m_area.index(x, nmax.Y, z); - for (s16 y = nmax.Y; y >= nmin.Y; y--) { - isliquid = ndef->get(vm->m_data[i]).isLiquid(); - - // there was a change between liquid and nonliquid, add to queue. - if (isliquid != wasliquid) + for (s16 z = nmin.Z + 1; z <= nmax.Z - 1; z++) + for (s16 x = nmin.X + 1; x <= nmax.X - 1; x++) { + wasignored = true; + wasliquid = false; + waschecked = false; + waspushed = false; + + u32 vi = vm->m_area.index(x, nmax.Y, z); + for (s16 y = nmax.Y; y >= nmin.Y; y--) { + isignored = vm->m_data[vi].getContent() == CONTENT_IGNORE; + isliquid = ndef->get(vm->m_data[vi]).isLiquid(); + + if (isignored || wasignored || isliquid == wasliquid) { + // Neither topmost node of liquid column nor topmost node below column + waschecked = false; + waspushed = false; + } else if (isliquid) { + // This is the topmost node in the column + bool ispushed = false; + if (isLiquidHorizontallyFlowable(vi, em)) { trans_liquid->push_back(v3s16(x, y, z)); - - wasliquid = isliquid; - vm->m_area.add_y(em, i, -1); + ispushed = true; + } + // Remember waschecked and waspushed to avoid repeated + // checks/pushes in case the column consists of only this node + waschecked = true; + waspushed = ispushed; + } else { + // This is the topmost node below a liquid column + u32 vi_above = vi; + vm->m_area.add_y(em, vi_above, 1); + if (!waspushed && (ndef->get(vm->m_data[vi]).floodable || + (!waschecked && isLiquidHorizontallyFlowable(vi_above, em)))) { + // Push back the lowest node in the column which is one + // node above this one + trans_liquid->push_back(v3s16(x, y + 1, z)); + } } + + wasliquid = isliquid; + wasignored = isignored; + vm->m_area.add_y(em, vi, -1); } } } diff --git a/src/mapgen.h b/src/mapgen.h index f673007b5..90ac84bd8 100644 --- a/src/mapgen.h +++ b/src/mapgen.h @@ -199,6 +199,10 @@ public: virtual int getSpawnLevelAtPoint(v2s16 p) { return 0; } private: + // isLiquidHorizontallyFlowable() is a helper function for updateLiquid() + // that checks whether there are floodable nodes without liquid beneath + // the node at index vi. + inline bool isLiquidHorizontallyFlowable(u32 vi, v3s16 em); DISABLE_CLASS_COPY(Mapgen); }; -- cgit v1.2.3 From d24f3841740b471eff384c8bd6e8bbfdfd03a3e2 Mon Sep 17 00:00:00 2001 From: paramat Date: Fri, 3 Jun 2016 12:58:50 +0100 Subject: Biome API: Add per-biome riverbed material and depth Mgvalleys: Remove riverbed sand placement from base terrain generation Riverbed material placement moved to MapgenBasic::generateBiomes() Document fields and add note that the biome API is still unstable --- doc/lua_api.txt | 6 ++++++ src/mapgen.cpp | 24 ++++++++++++++++++------ src/mapgen_valleys.cpp | 13 ++----------- src/mg_biome.cpp | 3 +++ src/mg_biome.h | 2 ++ src/script/lua_api/l_mapgen.cpp | 2 ++ 6 files changed, 33 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index f348f5103..30f4d87df 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -3723,6 +3723,9 @@ Definition tables ### Biome definition (`register_biome`) +**Note** +The biome API is still in an experimental phase and subject to change. + { name = "tundra", node_dust = "default:snow", @@ -3742,6 +3745,9 @@ Definition tables -- ^ Node that replaces all seawater nodes not in the defined surface layer. node_river_water = "default:ice", -- ^ Node that replaces river water in mapgens that use default:river_water. + node_riverbed = "default:gravel", + depth_riverbed = 2, + -- ^ Node placed under river water and thickness of this layer. y_min = 1, y_max = 31000, -- ^ Lower and upper limits for biome. diff --git a/src/mapgen.cpp b/src/mapgen.cpp index b5c48a471..66892a574 100644 --- a/src/mapgen.cpp +++ b/src/mapgen.cpp @@ -536,13 +536,15 @@ MgStoneType MapgenBasic::generateBiomes() u16 depth_top = 0; u16 base_filler = 0; u16 depth_water_top = 0; + u16 depth_riverbed = 0; u32 vi = vm->m_area.index(x, node_max.Y, z); // Check node at base of mapchunk above, either a node of a previously // generated mapchunk or if not, a node of overgenerated base terrain. content_t c_above = vm->m_data[vi + em.X].getContent(); bool air_above = c_above == CONTENT_AIR; - bool water_above = (c_above == c_water_source || c_above == c_river_water_source); + bool river_water_above = c_above == c_river_water_source; + bool water_above = c_above == c_water_source || river_water_above; // If there is air or water above enable top/filler placement, otherwise force // nplaced to stone level by setting a number exceeding any possible filler depth. @@ -564,10 +566,11 @@ MgStoneType MapgenBasic::generateBiomes() biome = biomegen->getBiomeAtIndex(index, y); depth_top = biome->depth_top; - base_filler = MYMAX(depth_top - + biome->depth_filler - + noise_filler_depth->result[index], 0.f); + base_filler = MYMAX(depth_top + + biome->depth_filler + + noise_filler_depth->result[index], 0.f); depth_water_top = biome->depth_water_top; + depth_riverbed = biome->depth_riverbed; // Detect stone type for dungeons during every biome calculation. // This is more efficient than detecting per-node and will not @@ -590,7 +593,15 @@ MgStoneType MapgenBasic::generateBiomes() || c_below == c_river_water_source) nplaced = U16_MAX; - if (nplaced < depth_top) { + if (river_water_above) { + if (nplaced < depth_riverbed) { + vm->m_data[vi] = MapNode(biome->c_riverbed); + nplaced++; + } else { + nplaced = U16_MAX; // Disable top/filler placement + river_water_above = false; + } + } else if (nplaced < depth_top) { vm->m_data[vi] = MapNode(biome->c_top); nplaced++; } else if (nplaced < base_filler) { @@ -610,9 +621,10 @@ MgStoneType MapgenBasic::generateBiomes() water_above = true; } else if (c == c_river_water_source) { vm->m_data[vi] = MapNode(biome->c_river_water); - nplaced = depth_top; // Enable filler placement for next surface + nplaced = 0; // Enable riverbed placement for next surface air_above = false; water_above = true; + river_water_above = true; } else if (c == CONTENT_AIR) { nplaced = 0; // Enable top/filler placement for next surface air_above = true; diff --git a/src/mapgen_valleys.cpp b/src/mapgen_valleys.cpp index 2cd733d36..6581b792f 100644 --- a/src/mapgen_valleys.cpp +++ b/src/mapgen_valleys.cpp @@ -114,11 +114,6 @@ MapgenValleys::MapgenValleys(int mapgenid, MapgenParams *params, EmergeManager * // Resolve content to be used c_lava_source = ndef->getId("mapgen_lava_source"); - c_sand = ndef->getId("mapgen_sand"); - - // Fall back to more basic content if not defined - if (c_sand == CONTENT_IGNORE) - c_sand = c_stone; } @@ -493,7 +488,6 @@ int MapgenValleys::generateTerrain() MapNode n_air(CONTENT_AIR); MapNode n_river_water(c_river_water_source); - MapNode n_sand(c_sand); MapNode n_stone(c_stone); MapNode n_water(c_water_source); @@ -537,10 +531,7 @@ int MapgenValleys::generateTerrain() float surface_delta = (float)y - surface_y; bool river = y + 1 < river_y; - if (fabs(surface_delta) <= 0.5f && y > water_level && river) { - // river bottom - vm->m_data[index_data] = n_sand; - } else if (slope * fill > surface_delta) { + if (slope * fill > surface_delta) { // ground vm->m_data[index_data] = n_stone; if (y > heightmap[index_2d]) @@ -553,7 +544,7 @@ int MapgenValleys::generateTerrain() } else if (river) { // river vm->m_data[index_data] = n_river_water; - } else { + } else { // air vm->m_data[index_data] = n_air; } } diff --git a/src/mg_biome.cpp b/src/mg_biome.cpp index df728af33..78034bf6c 100644 --- a/src/mg_biome.cpp +++ b/src/mg_biome.cpp @@ -46,6 +46,7 @@ BiomeManager::BiomeManager(IGameDef *gamedef) : b->depth_top = 0; b->depth_filler = -MAX_MAP_GENERATION_LIMIT; b->depth_water_top = 0; + b->depth_riverbed = 0; b->y_min = -MAX_MAP_GENERATION_LIMIT; b->y_max = MAX_MAP_GENERATION_LIMIT; b->heat_point = 0.0; @@ -57,6 +58,7 @@ BiomeManager::BiomeManager(IGameDef *gamedef) : b->m_nodenames.push_back("mapgen_water_source"); b->m_nodenames.push_back("mapgen_water_source"); b->m_nodenames.push_back("mapgen_river_water_source"); + b->m_nodenames.push_back("mapgen_stone"); b->m_nodenames.push_back("ignore"); m_ndef->pendNodeResolve(b); @@ -237,5 +239,6 @@ void Biome::resolveNodeNames() getIdFromNrBacklog(&c_water_top, "mapgen_water_source", CONTENT_AIR); getIdFromNrBacklog(&c_water, "mapgen_water_source", CONTENT_AIR); getIdFromNrBacklog(&c_river_water, "mapgen_river_water_source", CONTENT_AIR); + getIdFromNrBacklog(&c_riverbed, "mapgen_stone", CONTENT_AIR); getIdFromNrBacklog(&c_dust, "ignore", CONTENT_IGNORE); } diff --git a/src/mg_biome.h b/src/mg_biome.h index 568d0b1ac..eb0a18a2f 100644 --- a/src/mg_biome.h +++ b/src/mg_biome.h @@ -54,11 +54,13 @@ public: content_t c_water_top; content_t c_water; content_t c_river_water; + content_t c_riverbed; content_t c_dust; s16 depth_top; s16 depth_filler; s16 depth_water_top; + s16 depth_riverbed; s16 y_min; s16 y_max; diff --git a/src/script/lua_api/l_mapgen.cpp b/src/script/lua_api/l_mapgen.cpp index 6baf217af..dc188f8a4 100644 --- a/src/script/lua_api/l_mapgen.cpp +++ b/src/script/lua_api/l_mapgen.cpp @@ -378,6 +378,7 @@ Biome *read_biome_def(lua_State *L, int index, INodeDefManager *ndef) b->depth_top = getintfield_default(L, index, "depth_top", 0); b->depth_filler = getintfield_default(L, index, "depth_filler", -31000); b->depth_water_top = getintfield_default(L, index, "depth_water_top", 0); + b->depth_riverbed = getintfield_default(L, index, "depth_riverbed", 0); b->y_min = getintfield_default(L, index, "y_min", -31000); b->y_max = getintfield_default(L, index, "y_max", 31000); b->heat_point = getfloatfield_default(L, index, "heat_point", 0.f); @@ -391,6 +392,7 @@ Biome *read_biome_def(lua_State *L, int index, INodeDefManager *ndef) nn.push_back(getstringfield_default(L, index, "node_water_top", "")); nn.push_back(getstringfield_default(L, index, "node_water", "")); nn.push_back(getstringfield_default(L, index, "node_river_water", "")); + nn.push_back(getstringfield_default(L, index, "node_riverbed", "")); nn.push_back(getstringfield_default(L, index, "node_dust", "")); ndef->pendNodeResolve(b); -- cgit v1.2.3 From d4457ef42061435a1ccf6f5e056f37bac9b30c02 Mon Sep 17 00:00:00 2001 From: kwolekr Date: Sat, 4 Jun 2016 20:24:07 -0400 Subject: Mapgen: Optimize biomemap creation by combining with generateBiomes --- src/mapgen.cpp | 20 +++++++++++++++++--- src/mapgen_flat.cpp | 1 - src/mapgen_fractal.cpp | 1 - src/mapgen_v5.cpp | 1 - src/mapgen_v7.cpp | 1 - src/mapgen_valleys.cpp | 5 +---- 6 files changed, 18 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/mapgen.cpp b/src/mapgen.cpp index 66892a574..576fb219a 100644 --- a/src/mapgen.cpp +++ b/src/mapgen.cpp @@ -524,6 +524,10 @@ MapgenBasic::~MapgenBasic() MgStoneType MapgenBasic::generateBiomes() { + // can't generate biomes without a biome generator! + assert(biomegen); + assert(biomemap); + v3s16 em = vm->m_area.getExtent(); u32 index = 0; MgStoneType stone_type = MGSTONE_STONE; @@ -546,6 +550,8 @@ MgStoneType MapgenBasic::generateBiomes() bool river_water_above = c_above == c_river_water_source; bool water_above = c_above == c_water_source || river_water_above; + biomemap[index] = BIOME_NONE; + // If there is air or water above enable top/filler placement, otherwise force // nplaced to stone level by setting a number exceeding any possible filler depth. u16 nplaced = (air_above || water_above) ? 0 : U16_MAX; @@ -560,11 +566,19 @@ MgStoneType MapgenBasic::generateBiomes() // 1. At the surface of stone below air or water. // 2. At the surface of water below air. // 3. When stone or water is detected but biome has not yet been calculated. - if ((c == c_stone && (air_above || water_above || !biome)) - || ((c == c_water_source || c == c_river_water_source) - && (air_above || !biome))) { + bool is_stone_surface = (c == c_stone) && + (air_above || water_above || !biome); + + bool is_water_surface = + (c == c_water_source || c == c_river_water_source) && + (air_above || !biome); + + if (is_stone_surface || is_water_surface) { biome = biomegen->getBiomeAtIndex(index, y); + if (biomemap[index] == BIOME_NONE && is_stone_surface) + biomemap[index] = biome->index; + depth_top = biome->depth_top; base_filler = MYMAX(depth_top + biome->depth_filler + diff --git a/src/mapgen_flat.cpp b/src/mapgen_flat.cpp index 724f06825..7cc6aad5c 100644 --- a/src/mapgen_flat.cpp +++ b/src/mapgen_flat.cpp @@ -192,7 +192,6 @@ void MapgenFlat::makeChunk(BlockMakeData *data) // Init biome generator, place biome-specific nodes, and build biomemap biomegen->calcBiomeNoise(node_min); - biomegen->getBiomes(heightmap); MgStoneType stone_type = generateBiomes(); if (flags & MG_CAVES) diff --git a/src/mapgen_fractal.cpp b/src/mapgen_fractal.cpp index b613bf358..c47a7bfdc 100644 --- a/src/mapgen_fractal.cpp +++ b/src/mapgen_fractal.cpp @@ -208,7 +208,6 @@ void MapgenFractal::makeChunk(BlockMakeData *data) // Init biome generator, place biome-specific nodes, and build biomemap biomegen->calcBiomeNoise(node_min); - biomegen->getBiomes(heightmap); MgStoneType stone_type = generateBiomes(); if (flags & MG_CAVES) diff --git a/src/mapgen_v5.cpp b/src/mapgen_v5.cpp index 3e10ec2a5..74d5e1ee3 100644 --- a/src/mapgen_v5.cpp +++ b/src/mapgen_v5.cpp @@ -190,7 +190,6 @@ void MapgenV5::makeChunk(BlockMakeData *data) // Init biome generator, place biome-specific nodes, and build biomemap biomegen->calcBiomeNoise(node_min); - biomegen->getBiomes(heightmap); MgStoneType stone_type = generateBiomes(); // Generate caves diff --git a/src/mapgen_v7.cpp b/src/mapgen_v7.cpp index 7327facae..35dcdcd94 100644 --- a/src/mapgen_v7.cpp +++ b/src/mapgen_v7.cpp @@ -219,7 +219,6 @@ void MapgenV7::makeChunk(BlockMakeData *data) // Init biome generator, place biome-specific nodes, and build biomemap biomegen->calcBiomeNoise(node_min); - biomegen->getBiomes(heightmap); MgStoneType stone_type = generateBiomes(); if (flags & MG_CAVES) diff --git a/src/mapgen_valleys.cpp b/src/mapgen_valleys.cpp index 6581b792f..bbf20719d 100644 --- a/src/mapgen_valleys.cpp +++ b/src/mapgen_valleys.cpp @@ -250,10 +250,7 @@ void MapgenValleys::makeChunk(BlockMakeData *data) // Generate base terrain with initial heightmaps s16 stone_surface_max_y = generateTerrain(); - // Build biomemap - m_bgen->getBiomes(heightmap); - - // Place biome-specific nodes + // Place biome-specific nodes and build biomemap MgStoneType stone_type = generateBiomes(); // Cave creation. -- cgit v1.2.3 From 3ef71edd818b15ed2ba2f184b8a2dc4ee6198042 Mon Sep 17 00:00:00 2001 From: Paramat Date: Wed, 8 Jun 2016 07:43:27 +0100 Subject: Sky: Fix sunrisebg texture motion (#4199) Top of texture no longer shows above horizon during night and day --- src/sky.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/sky.cpp b/src/sky.cpp index 682ff05e3..4ee750c55 100644 --- a/src/sky.cpp +++ b/src/sky.cpp @@ -239,7 +239,7 @@ void Sky::render() float a = easeCurve(MYMAX(0, MYMIN(1, a_))); //std::cerr<<"a_="<getPlayerSAO(); assert(playersao); - m_script->on_leaveplayer(playersao); + m_script->on_leaveplayer(playersao, reason == CDR_TIMEOUT); playersao->disconnected(); } -- cgit v1.2.3 From 33b874fea3948dfcbcc2d6761c28b325ca4aa367 Mon Sep 17 00:00:00 2001 From: kwolekr Date: Sat, 11 Jun 2016 00:10:25 -0400 Subject: Settings: Clean up settings changed callback code --- src/settings.cpp | 45 ++++++++++++++++++++++----------------------- src/settings.h | 31 ++++++++++++++++++++++--------- 2 files changed, 44 insertions(+), 32 deletions(-) (limited to 'src') diff --git a/src/settings.cpp b/src/settings.cpp index 56afa6133..91ea9c58b 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -985,39 +985,38 @@ void Settings::clearDefaultsNoLock() } -void Settings::registerChangedCallback(std::string name, - setting_changed_callback cbf, void *userdata) +void Settings::registerChangedCallback(const std::string &name, + SettingsChangedCallback cbf, void *userdata) { - MutexAutoLock lock(m_callbackMutex); + MutexAutoLock lock(m_callback_mutex); m_callbacks[name].push_back(std::make_pair(cbf, userdata)); } -void Settings::deregisterChangedCallback(std::string name, setting_changed_callback cbf, void *userdata) +void Settings::deregisterChangedCallback(const std::string &name, + SettingsChangedCallback cbf, void *userdata) { - MutexAutoLock lock(m_callbackMutex); - std::map > >::iterator iterToVector = m_callbacks.find(name); - if (iterToVector != m_callbacks.end()) - { - std::vector > &vector = iterToVector->second; + MutexAutoLock lock(m_callback_mutex); + SettingsCallbackMap::iterator it_cbks = m_callbacks.find(name); + + if (it_cbks != m_callbacks.end()) { + SettingsCallbackList &cbks = it_cbks->second; - std::vector >::iterator position = - std::find(vector.begin(), vector.end(), std::make_pair(cbf, userdata)); + SettingsCallbackList::iterator position = + std::find(cbks.begin(), cbks.end(), std::make_pair(cbf, userdata)); - if (position != vector.end()) - vector.erase(position); + if (position != cbks.end()) + cbks.erase(position); } } -void Settings::doCallbacks(const std::string name) +void Settings::doCallbacks(const std::string &name) const { - MutexAutoLock lock(m_callbackMutex); - std::map > >::iterator iterToVector = m_callbacks.find(name); - if (iterToVector != m_callbacks.end()) - { - std::vector >::iterator iter; - for (iter = iterToVector->second.begin(); iter != iterToVector->second.end(); ++iter) - { - (iter->first)(name, iter->second); - } + MutexAutoLock lock(m_callback_mutex); + + SettingsCallbackMap::const_iterator it_cbks = m_callbacks.find(name); + if (it_cbks != m_callbacks.end()) { + SettingsCallbackList::const_iterator it; + for (it = it_cbks->second.begin(); it != it_cbks->second.end(); ++it) + (it->first)(name, it->second); } } diff --git a/src/settings.h b/src/settings.h index 80d41fd79..0af861a58 100644 --- a/src/settings.h +++ b/src/settings.h @@ -35,8 +35,17 @@ struct NoiseParams; extern Settings *g_settings; extern std::string g_settings_path; -/** function type to register a changed callback */ -typedef void (*setting_changed_callback)(const std::string &name, void *data); +// Type for a settings changed callback function +typedef void (*SettingsChangedCallback)(const std::string &name, void *data); + +typedef std::vector< + std::pair< + SettingsChangedCallback, + void * + > +> SettingsCallbackList; + +typedef std::map SettingsCallbackMap; enum ValueType { VALUETYPE_STRING, @@ -209,24 +218,28 @@ public: void clearDefaults(); void updateValue(const Settings &other, const std::string &name); void update(const Settings &other); - void registerChangedCallback(std::string name, setting_changed_callback cbf, void *userdata = NULL); - void deregisterChangedCallback(std::string name, setting_changed_callback cbf, void *userdata = NULL); -private: + void registerChangedCallback(const std::string &name, + SettingsChangedCallback cbf, void *userdata = NULL); + void deregisterChangedCallback(const std::string &name, + SettingsChangedCallback cbf, void *userdata = NULL); +private: void updateNoLock(const Settings &other); void clearNoLock(); void clearDefaultsNoLock(); - void doCallbacks(std::string name); + void doCallbacks(const std::string &name) const; std::map m_settings; std::map m_defaults; - std::map > > m_callbacks; + SettingsCallbackMap m_callbacks; + + mutable Mutex m_callback_mutex; - mutable Mutex m_callbackMutex; - mutable Mutex m_mutex; // All methods that access m_settings/m_defaults directly should lock this. + // All methods that access m_settings/m_defaults directly should lock this. + mutable Mutex m_mutex; }; -- cgit v1.2.3 From 27aff22a9b68044d3ea51db731597834336effa3 Mon Sep 17 00:00:00 2001 From: kwolekr Date: Sat, 11 Jun 2016 03:23:53 -0400 Subject: Random misc. warning fixes and cleanups - Fix unused c_sand member warning in Valleys Mapgen - Fix some code style - Make some std::string params const ref --- src/mapgen_valleys.h | 1 - src/util/string.cpp | 19 +++++++++++-------- src/util/string.h | 4 ++-- 3 files changed, 13 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/mapgen_valleys.h b/src/mapgen_valleys.h index a7369ea92..faaffa905 100644 --- a/src/mapgen_valleys.h +++ b/src/mapgen_valleys.h @@ -125,7 +125,6 @@ private: Noise *noise_valley_profile; content_t c_lava_source; - content_t c_sand; float terrainLevelAtPoint(s16 x, s16 z); diff --git a/src/util/string.cpp b/src/util/string.cpp index 2c4143c76..94064ef93 100644 --- a/src/util/string.cpp +++ b/src/util/string.cpp @@ -314,7 +314,7 @@ std::string wide_to_narrow(const std::wstring &wcs) #endif -std::string urlencode(std::string str) +std::string urlencode(const std::string &str) { // Encodes non-unreserved URI characters by a percent sign // followed by two hex digits. See RFC 3986, section 2.3. @@ -322,17 +322,18 @@ std::string urlencode(std::string str) std::ostringstream oss(std::ios::binary); for (u32 i = 0; i < str.size(); i++) { unsigned char c = str[i]; - if (isalnum(c) || c == '-' || c == '.' || c == '_' || c == '~') + if (isalnum(c) || c == '-' || c == '.' || c == '_' || c == '~') { oss << c; - else + } else { oss << "%" << url_hex_chars[(c & 0xf0) >> 4] << url_hex_chars[c & 0x0f]; + } } return oss.str(); } -std::string urldecode(std::string str) +std::string urldecode(const std::string &str) { // Inverse of urlencode std::ostringstream oss(std::ios::binary); @@ -343,18 +344,20 @@ std::string urldecode(std::string str) hex_digit_decode(str[i+2], lowvalue)) { oss << (char) ((highvalue << 4) | lowvalue); i += 2; - } - else + } else { oss << str[i]; + } } return oss.str(); } u32 readFlagString(std::string str, const FlagDesc *flagdesc, u32 *flagmask) { - u32 result = 0, mask = 0; + u32 result = 0; + u32 mask = 0; char *s = &str[0]; - char *flagstr, *strpos = NULL; + char *flagstr; + char *strpos = NULL; while ((flagstr = strtok_r(s, ",", &strpos))) { s = NULL; diff --git a/src/util/string.h b/src/util/string.h index c77c5a6f9..8f4ef4711 100644 --- a/src/util/string.h +++ b/src/util/string.h @@ -77,8 +77,8 @@ wchar_t *narrow_to_wide_c(const char *str); std::wstring narrow_to_wide(const std::string &mbs); std::string wide_to_narrow(const std::wstring &wcs); -std::string urlencode(std::string str); -std::string urldecode(std::string str); +std::string urlencode(const std::string &str); +std::string urldecode(const std::string &str); u32 readFlagString(std::string str, const FlagDesc *flagdesc, u32 *flagmask); std::string writeFlagString(u32 flags, const FlagDesc *flagdesc, u32 flagmask); size_t mystrlcpy(char *dst, const char *src, size_t size); -- cgit v1.2.3 From 559dd9946988cd35a7c26bcafe7d0f8c42dc547a Mon Sep 17 00:00:00 2001 From: Ekdohibs Date: Mon, 21 Mar 2016 12:58:52 +0100 Subject: Make node timers more efficient --- src/content_nodemeta.cpp | 2 +- src/environment.cpp | 32 ++++++++-------- src/map.cpp | 9 +++-- src/map.h | 2 +- src/mapblock.h | 4 +- src/nodetimer.cpp | 72 +++++++++++++++++------------------ src/nodetimer.h | 77 ++++++++++++++++++++++++++++++-------- src/script/lua_api/l_nodetimer.cpp | 4 +- 8 files changed, 125 insertions(+), 77 deletions(-) (limited to 'src') diff --git a/src/content_nodemeta.cpp b/src/content_nodemeta.cpp index 7f4264d8e..79a32b6bf 100644 --- a/src/content_nodemeta.cpp +++ b/src/content_nodemeta.cpp @@ -186,7 +186,7 @@ void content_nodemeta_deserialize_legacy(std::istream &is, meta->set(p, data); if(need_timer) - timers->set(p, NodeTimer(1., 0.)); + timers->set(NodeTimer(1., 0., p)); } } diff --git a/src/environment.cpp b/src/environment.cpp index 413bc7ff1..eea264699 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -1030,17 +1030,17 @@ void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime) m_lbm_mgr.applyLBMs(this, block, stamp); // Run node timers - std::map elapsed_timers = + std::vector elapsed_timers = block->m_node_timers.step((float)dtime_s); - if(!elapsed_timers.empty()){ + if (!elapsed_timers.empty()) { MapNode n; - for(std::map::iterator + for (std::vector::iterator i = elapsed_timers.begin(); i != elapsed_timers.end(); ++i){ - n = block->getNodeNoEx(i->first); - v3s16 p = i->first + block->getPosRelative(); - if(m_script->node_on_timer(p,n,i->second.elapsed)) - block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0)); + n = block->getNodeNoEx(i->position); + v3s16 p = i->position + block->getPosRelative(); + if (m_script->node_on_timer(p, n, i->elapsed)) + block->setNodeTimer(NodeTimer(i->timeout, 0, i->position)); } } @@ -1434,17 +1434,19 @@ void ServerEnvironment::step(float dtime) MOD_REASON_BLOCK_EXPIRED); // Run node timers - std::map elapsed_timers = + std::vector elapsed_timers = block->m_node_timers.step((float)dtime); - if(!elapsed_timers.empty()){ + if (!elapsed_timers.empty()) { MapNode n; - for(std::map::iterator + for (std::vector::iterator i = elapsed_timers.begin(); - i != elapsed_timers.end(); ++i){ - n = block->getNodeNoEx(i->first); - p = i->first + block->getPosRelative(); - if(m_script->node_on_timer(p,n,i->second.elapsed)) - block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0)); + i != elapsed_timers.end(); ++i) { + n = block->getNodeNoEx(i->position); + p = i->position + block->getPosRelative(); + if (m_script->node_on_timer(p, n, i->elapsed)) { + block->setNodeTimer(NodeTimer( + i->timeout, 0, i->position)); + } } } } diff --git a/src/map.cpp b/src/map.cpp index 03daf4fa8..a1f2086ce 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -2087,11 +2087,13 @@ NodeTimer Map::getNodeTimer(v3s16 p) return NodeTimer(); } NodeTimer t = block->m_node_timers.get(p_rel); - return t; + NodeTimer nt(t.timeout, t.elapsed, p); + return nt; } -void Map::setNodeTimer(v3s16 p, NodeTimer t) +void Map::setNodeTimer(const NodeTimer &t) { + v3s16 p = t.position; v3s16 blockpos = getNodeBlockPos(p); v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE; MapBlock *block = getBlockNoCreateNoEx(blockpos); @@ -2105,7 +2107,8 @@ void Map::setNodeTimer(v3s16 p, NodeTimer t) <m_node_timers.set(p_rel, t); + NodeTimer nt(t.timeout, t.elapsed, p_rel); + block->m_node_timers.set(nt); } void Map::removeNodeTimer(v3s16 p) diff --git a/src/map.h b/src/map.h index 78614d228..23da56471 100644 --- a/src/map.h +++ b/src/map.h @@ -327,7 +327,7 @@ public: */ NodeTimer getNodeTimer(v3s16 p); - void setNodeTimer(v3s16 p, NodeTimer t); + void setNodeTimer(const NodeTimer &t); void removeNodeTimer(v3s16 p); /* diff --git a/src/mapblock.h b/src/mapblock.h index 73c17ee60..5adfcf3fb 100644 --- a/src/mapblock.h +++ b/src/mapblock.h @@ -488,9 +488,9 @@ public: m_node_timers.remove(p); } - inline void setNodeTimer(v3s16 p, NodeTimer t) + inline void setNodeTimer(const NodeTimer &t) { - m_node_timers.set(p,t); + m_node_timers.set(t); } inline void clearNodeTimers() diff --git a/src/nodetimer.cpp b/src/nodetimer.cpp index 350940546..003d08782 100644 --- a/src/nodetimer.cpp +++ b/src/nodetimer.cpp @@ -47,36 +47,38 @@ void NodeTimerList::serialize(std::ostream &os, u8 map_format_version) const { if (map_format_version == 24) { // Version 0 is a placeholder for "nothing to see here; go away." - if (m_data.empty()) { + if (m_timers.empty()) { writeU8(os, 0); // version return; } writeU8(os, 1); // version - writeU16(os, m_data.size()); + writeU16(os, m_timers.size()); } if (map_format_version >= 25) { writeU8(os, 2 + 4 + 4); // length of the data for a single timer - writeU16(os, m_data.size()); + writeU16(os, m_timers.size()); } - for (std::map::const_iterator - i = m_data.begin(); - i != m_data.end(); ++i) { - v3s16 p = i->first; + for (std::multimap::const_iterator + i = m_timers.begin(); + i != m_timers.end(); ++i) { NodeTimer t = i->second; + NodeTimer nt = NodeTimer(t.timeout, + t.timeout - (f32)(i->first - m_time), t.position); + v3s16 p = t.position; u16 p16 = p.Z * MAP_BLOCKSIZE * MAP_BLOCKSIZE + p.Y * MAP_BLOCKSIZE + p.X; writeU16(os, p16); - t.serialize(os); + nt.serialize(os); } } void NodeTimerList::deSerialize(std::istream &is, u8 map_format_version) { - m_data.clear(); + clear(); - if(map_format_version == 24){ + if (map_format_version == 24) { u8 timer_version = readU8(is); if(timer_version == 0) return; @@ -84,7 +86,7 @@ void NodeTimerList::deSerialize(std::istream &is, u8 map_format_version) throw SerializationError("unsupported NodeTimerList version"); } - if(map_format_version >= 25){ + if (map_format_version >= 25) { u8 timer_data_len = readU8(is); if(timer_data_len != 2+4+4) throw SerializationError("unsupported NodeTimer data length"); @@ -92,8 +94,7 @@ void NodeTimerList::deSerialize(std::istream &is, u8 map_format_version) u16 count = readU16(is); - for(u16 i=0; i NodeTimerList::step(float dtime) +std::vector NodeTimerList::step(float dtime) { - std::map elapsed_timers; - // Increment timers - for(std::map::iterator - i = m_data.begin(); - i != m_data.end(); ++i){ - v3s16 p = i->first; + std::vector elapsed_timers; + m_time += dtime; + if (m_next_trigger_time == -1. || m_time < m_next_trigger_time) { + return elapsed_timers; + } + std::multimap::iterator i = m_timers.begin(); + // Process timers + for (; i != m_timers.end() && i->first <= m_time; ++i) { NodeTimer t = i->second; - t.elapsed += dtime; - if(t.elapsed >= t.timeout) - elapsed_timers.insert(std::make_pair(p, t)); - else - i->second = t; + t.elapsed = t.timeout + (f32)(m_time - i->first); + elapsed_timers.push_back(t); + m_iterators.erase(t.position); } // Delete elapsed timers - for(std::map::const_iterator - i = elapsed_timers.begin(); - i != elapsed_timers.end(); ++i){ - v3s16 p = i->first; - m_data.erase(p); - } + m_timers.erase(m_timers.begin(), i); + if (m_timers.empty()) + m_next_trigger_time = -1.; + else + m_next_trigger_time = m_timers.begin()->first; return elapsed_timers; } diff --git a/src/nodetimer.h b/src/nodetimer.h index 9fb56edec..0fd43b2a8 100644 --- a/src/nodetimer.h +++ b/src/nodetimer.h @@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "irr_v3d.h" #include #include +#include /* NodeTimer provides per-node timed callback functionality. @@ -36,8 +37,10 @@ class NodeTimer { public: NodeTimer(): timeout(0.), elapsed(0.) {} - NodeTimer(f32 timeout_, f32 elapsed_): - timeout(timeout_), elapsed(elapsed_) {} + NodeTimer(const v3s16 &position_): + timeout(0.), elapsed(0.), position(position_) {} + NodeTimer(f32 timeout_, f32 elapsed_, v3s16 position_): + timeout(timeout_), elapsed(elapsed_), position(position_) {} ~NodeTimer() {} void serialize(std::ostream &os) const; @@ -45,6 +48,7 @@ public: f32 timeout; f32 elapsed; + v3s16 position; }; /* @@ -54,37 +58,78 @@ public: class NodeTimerList { public: - NodeTimerList() {} + NodeTimerList(): m_next_trigger_time(-1.), m_time(0.) {} ~NodeTimerList() {} void serialize(std::ostream &os, u8 map_format_version) const; void deSerialize(std::istream &is, u8 map_format_version); // Get timer - NodeTimer get(v3s16 p){ - std::map::iterator n = m_data.find(p); - if(n == m_data.end()) + NodeTimer get(const v3s16 &p) { + std::map::iterator>::iterator n = + m_iterators.find(p); + if (n == m_iterators.end()) return NodeTimer(); - return n->second; + NodeTimer t = n->second->second; + t.elapsed = t.timeout - (n->second->first - m_time); + return t; } // Deletes timer - void remove(v3s16 p){ - m_data.erase(p); + void remove(v3s16 p) { + std::map::iterator>::iterator n = + m_iterators.find(p); + if(n != m_iterators.end()) { + double removed_time = n->second->first; + m_timers.erase(n->second); + m_iterators.erase(n); + // Yes, this is float equality, but it is not a problem + // since we only test equality of floats as an ordered type + // and thus we never lose precision + if (removed_time == m_next_trigger_time) { + if (m_timers.empty()) + m_next_trigger_time = -1.; + else + m_next_trigger_time = m_timers.begin()->first; + } + } + } + // Undefined behaviour if there already is a timer + void insert(NodeTimer timer) { + v3s16 p = timer.position; + double trigger_time = m_time + (double)(timer.timeout - timer.elapsed); + std::multimap::iterator it = + m_timers.insert(std::pair( + trigger_time, timer + )); + m_iterators.insert( + std::pair::iterator>(p, it)); + if (m_next_trigger_time == -1. || trigger_time < m_next_trigger_time) + m_next_trigger_time = trigger_time; } // Deletes old timer and sets a new one - void set(v3s16 p, NodeTimer t){ - m_data[p] = t; + inline void set(const NodeTimer &timer) { + remove(timer.position); + insert(timer); } // Deletes all timers - void clear(){ - m_data.clear(); + void clear() { + m_timers.clear(); + m_iterators.clear(); + m_next_trigger_time = -1.; + } + + inline double getNextTriggerTime() { + return m_next_trigger_time; } - // A step in time. Returns map of elapsed timers. - std::map step(float dtime); + // Move forward in time, returns elapsed timers + std::vector step(float dtime); private: - std::map m_data; + std::multimap m_timers; + std::map::iterator> m_iterators; + double m_next_trigger_time; + double m_time; }; #endif diff --git a/src/script/lua_api/l_nodetimer.cpp b/src/script/lua_api/l_nodetimer.cpp index 601113516..3242d6ea5 100644 --- a/src/script/lua_api/l_nodetimer.cpp +++ b/src/script/lua_api/l_nodetimer.cpp @@ -45,7 +45,7 @@ int NodeTimerRef::l_set(lua_State *L) if(env == NULL) return 0; f32 t = luaL_checknumber(L,2); f32 e = luaL_checknumber(L,3); - env->getMap().setNodeTimer(o->m_p,NodeTimer(t,e)); + env->getMap().setNodeTimer(NodeTimer(t, e, o->m_p)); return 0; } @@ -56,7 +56,7 @@ int NodeTimerRef::l_start(lua_State *L) ServerEnvironment *env = o->m_env; if(env == NULL) return 0; f32 t = luaL_checknumber(L,2); - env->getMap().setNodeTimer(o->m_p,NodeTimer(t,0)); + env->getMap().setNodeTimer(NodeTimer(t, 0, o->m_p)); return 0; } -- cgit v1.2.3 From a39d53628283ecbb6d7fa617eadeabe9bc5ef127 Mon Sep 17 00:00:00 2001 From: paramat Date: Thu, 9 Jun 2016 03:15:41 +0100 Subject: Sky.cpp: Improve code style. Define sky colours as SColor --- src/sky.cpp | 527 +++++++++++++++++++++++++++++++----------------------------- 1 file changed, 268 insertions(+), 259 deletions(-) (limited to 'src') diff --git a/src/sky.cpp b/src/sky.cpp index 7757120bf..64f8cb5e7 100644 --- a/src/sky.cpp +++ b/src/sky.cpp @@ -4,37 +4,37 @@ #include "ICameraSceneNode.h" #include "S3DVertex.h" #include "client/tile.h" -#include "noise.h" // easeCurve +#include "noise.h" // easeCurve #include "profiler.h" #include "util/numeric.h" #include #include "settings.h" -#include "camera.h" // CameraModes +#include "camera.h" // CameraModes + -//! constructor Sky::Sky(scene::ISceneNode* parent, scene::ISceneManager* mgr, s32 id, ITextureSource *tsrc): scene::ISceneNode(parent, mgr, id), m_visible(true), - m_fallback_bg_color(255,255,255,255), + m_fallback_bg_color(255, 255, 255, 255), m_first_update(true), m_brightness(0.5), m_cloud_brightness(0.5), - m_bgcolor_bright_f(1,1,1,1), - m_skycolor_bright_f(1,1,1,1), - m_cloudcolor_bright_f(1,1,1,1) + m_bgcolor_bright_f(1, 1, 1, 1), + m_skycolor_bright_f(1, 1, 1, 1), + m_cloudcolor_bright_f(1, 1, 1, 1) { setAutomaticCulling(scene::EAC_OFF); - m_box.MaxEdge.set(0,0,0); - m_box.MinEdge.set(0,0,0); + m_box.MaxEdge.set(0, 0, 0); + m_box.MinEdge.set(0, 0, 0); - // create material + // Create material video::SMaterial mat; mat.Lighting = false; mat.ZBuffer = video::ECFN_NEVER; mat.ZWriteEnable = false; - mat.AntiAliasing=0; + mat.AntiAliasing = 0; mat.TextureLayer[0].TextureWrapU = video::ETC_CLAMP_TO_EDGE; mat.TextureLayer[0].TextureWrapV = video::ETC_CLAMP_TO_EDGE; mat.BackfaceCulling = false; @@ -59,14 +59,15 @@ Sky::Sky(scene::ISceneNode* parent, scene::ISceneManager* mgr, s32 id, m_moon_tonemap = tsrc->isKnownSourceImage("moon_tonemap.png") ? tsrc->getTexture("moon_tonemap.png") : NULL; - if (m_sun_texture){ + if (m_sun_texture) { m_materials[3] = mat; m_materials[3].setTexture(0, m_sun_texture); m_materials[3].MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; if (m_sun_tonemap) m_materials[3].Lighting = true; } - if (m_moon_texture){ + + if (m_moon_texture) { m_materials[4] = mat; m_materials[4].setTexture(0, m_moon_texture); m_materials[4].MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; @@ -74,11 +75,11 @@ Sky::Sky(scene::ISceneNode* parent, scene::ISceneManager* mgr, s32 id, m_materials[4].Lighting = true; } - for(u32 i=0; igetBool("directional_colored_fog"); } + void Sky::OnRegisterSceneNode() { if (IsVisible) @@ -94,10 +96,10 @@ void Sky::OnRegisterSceneNode() scene::ISceneNode::OnRegisterSceneNode(); } -//! renders the node. + void Sky::render() { - if(!m_visible) + if (!m_visible) return; video::IVideoDriver* driver = SceneManager->getVideoDriver(); @@ -108,7 +110,7 @@ void Sky::render() ScopeProfiler sp(g_profiler, "Sky::render()", SPT_AVG); - // draw perspective skybox + // Draw perspective skybox core::matrix4 translate(AbsoluteTransformation); translate.setTranslation(camera->getAbsolutePosition()); @@ -120,16 +122,15 @@ void Sky::render() driver->setTransform(video::ETS_WORLD, translate * scale); - if(m_sunlight_seen) - { + if (m_sunlight_seen) { float sunsize = 0.07; video::SColorf suncolor_f(1, 1, 0, 1); suncolor_f.r = 1; - suncolor_f.g = MYMAX(0.3, MYMIN(1.0, 0.7+m_time_brightness*(0.5))); - suncolor_f.b = MYMAX(0.0, m_brightness*0.95); + suncolor_f.g = MYMAX(0.3, MYMIN(1.0, 0.7 + m_time_brightness * 0.5)); + suncolor_f.b = MYMAX(0.0, m_brightness * 0.95); video::SColorf suncolor2_f(1, 1, 1, 1); suncolor_f.r = 1; - suncolor_f.g = MYMAX(0.3, MYMIN(1.0, 0.85+m_time_brightness*(0.5))); + suncolor_f.g = MYMAX(0.3, MYMIN(1.0, 0.85 + m_time_brightness * 0.5)); suncolor_f.b = MYMAX(0.0, m_brightness); float moonsize = 0.04; @@ -139,12 +140,12 @@ void Sky::render() float nightlength = 0.415; float wn = nightlength / 2; float wicked_time_of_day = 0; - if(m_time_of_day > wn && m_time_of_day < 1.0 - wn) - wicked_time_of_day = (m_time_of_day - wn)/(1.0-wn*2)*0.5 + 0.25; - else if(m_time_of_day < 0.5) + if (m_time_of_day > wn && m_time_of_day < 1.0 - wn) + wicked_time_of_day = (m_time_of_day - wn) / (1.0 - wn * 2) * 0.5 + 0.25; + else if (m_time_of_day < 0.5) wicked_time_of_day = m_time_of_day / wn * 0.25; else - wicked_time_of_day = 1.0 - ((1.0-m_time_of_day) / wn * 0.25); + wicked_time_of_day = 1.0 - ((1.0 - m_time_of_day) / wn * 0.25); /*std::cerr<<"time_of_day="< " <<"wicked_time_of_day="<lock(); video::SColor* texel = (video::SColor *)(texels + (u32)offset * 4); - video::SColor texel_color (255,texel->getRed(),texel->getGreen(), texel->getBlue()); + video::SColor texel_color (255, texel->getRed(), + texel->getGreen(), texel->getBlue()); m_sun_tonemap->unlock(); m_materials[3].EmissiveColor = texel_color; } - if (m_moon_tonemap){ + + if (m_moon_tonemap) { u8 * texels = (u8 *)m_moon_tonemap->lock(); video::SColor* texel = (video::SColor *)(texels + (u32)offset * 4); - video::SColor texel_color (255,texel->getRed(),texel->getGreen(), texel->getBlue()); + video::SColor texel_color (255, texel->getRed(), + texel->getGreen(), texel->getBlue()); m_moon_tonemap->unlock(); m_materials[4].EmissiveColor = texel_color; } const f32 t = 1.0f; const f32 o = 0.0f; - static const u16 indices[4] = {0,1,2,3}; + static const u16 indices[4] = {0, 1, 2, 3}; video::S3DVertex vertices[4]; driver->setMaterial(m_materials[1]); - //video::SColor cloudyfogcolor(255,255,255,255); video::SColor cloudyfogcolor = m_bgcolor; - //video::SColor cloudyfogcolor = m_bgcolor.getInterpolated(m_skycolor, 0.5); - // Draw far cloudy fog thing - for(u32 j=0; j<4; j++) - { + // Draw far cloudy fog thing blended with skycolor + for (u32 j = 0; j < 4; j++) { video::SColor c = cloudyfogcolor.getInterpolated(m_skycolor, 0.45); - vertices[0] = video::S3DVertex(-1, 0.08,-1, 0,0,1, c, t, t); - vertices[1] = video::S3DVertex( 1, 0.08,-1, 0,0,1, c, o, t); - vertices[2] = video::S3DVertex( 1, 0.12,-1, 0,0,1, c, o, o); - vertices[3] = video::S3DVertex(-1, 0.12,-1, 0,0,1, c, t, o); - for(u32 i=0; i<4; i++){ - if(j==0) + vertices[0] = video::S3DVertex(-1, 0.08, -1, 0, 0, 1, c, t, t); + vertices[1] = video::S3DVertex( 1, 0.08, -1, 0, 0, 1, c, o, t); + vertices[2] = video::S3DVertex( 1, 0.12, -1, 0, 0, 1, c, o, o); + vertices[3] = video::S3DVertex(-1, 0.12, -1, 0, 0, 1, c, t, o); + for (u32 i = 0; i < 4; i++) { + if (j == 0) // Don't switch {} - else if(j==1) + else if (j == 1) // Switch from -Z (south) to +X (east) vertices[i].Pos.rotateXZBy(90); - else if(j==2) + else if (j == 2) // Switch from -Z (south) to -X (west) vertices[i].Pos.rotateXZBy(-90); else - // Switch from -Z (south) to -Z (north) + // Switch from -Z (south) to +Z (north) vertices[i].Pos.rotateXZBy(-180); } driver->drawIndexedTriangleFan(&vertices[0], 4, indices, 2); } - for(u32 j=0; j<4; j++) - { + + // Draw far cloudy fog thing + for (u32 j = 0; j < 4; j++) { video::SColor c = cloudyfogcolor; - vertices[0] = video::S3DVertex(-1,-1.0,-1, 0,0,1, c, t, t); - vertices[1] = video::S3DVertex( 1,-1.0,-1, 0,0,1, c, o, t); - vertices[2] = video::S3DVertex( 1, 0.08,-1, 0,0,1, c, o, o); - vertices[3] = video::S3DVertex(-1, 0.08,-1, 0,0,1, c, t, o); - for(u32 i=0; i<4; i++){ - if(j==0) + vertices[0] = video::S3DVertex(-1, -1.0, -1, 0, 0, 1, c, t, t); + vertices[1] = video::S3DVertex( 1, -1.0, -1, 0, 0, 1, c, o, t); + vertices[2] = video::S3DVertex( 1, 0.08, -1, 0, 0, 1, c, o, o); + vertices[3] = video::S3DVertex(-1, 0.08, -1, 0, 0, 1, c, t, o); + for (u32 i = 0; i < 4; i++) { + if (j == 0) // Don't switch {} - else if(j==1) + else if (j == 1) // Switch from -Z (south) to +X (east) vertices[i].Pos.rotateXZBy(90); - else if(j==2) + else if (j == 2) // Switch from -Z (south) to -X (west) vertices[i].Pos.rotateXZBy(-90); else - // Switch from -Z (south) to -Z (north) + // Switch from -Z (south) to +Z (north) vertices[i].Pos.rotateXZBy(-180); } driver->drawIndexedTriangleFan(&vertices[0], 4, indices, 2); @@ -232,20 +234,21 @@ void Sky::render() driver->setMaterial(m_materials[2]); + // Draw sunrise/sunset horizon glow texture (textures/base/pack/sunrisebg.png) { float mid1 = 0.25; - float mid = (wicked_time_of_day < 0.5 ? mid1 : (1.0 - mid1)); + float mid = wicked_time_of_day < 0.5 ? mid1 : (1.0 - mid1); float a_ = 1.0 - fabs(wicked_time_of_day - mid) * 35.0; float a = easeCurve(MYMAX(0, MYMIN(1, a_))); //std::cerr<<"a_="< Date: Sun, 12 Jun 2016 03:11:26 +0100 Subject: Dungeons: Generalise use, add capabilities, various modifications - Generalise node names to c_wall and c_alt_wall - Remove 'mossratio' and instead disable alt_wall loop if c_alt_wall == CONTENT_IGNORE - Use one generalised 3D noise for alternative wall nodes and in mgv6 create moss distribution similar to the previous - Rename rarity noise to density noise and enable the option of multiple dungeons per chunk determined by the value. Recreate previous distribution - Add parameters for min and max rooms per dungeon - Add dungeon y limits - Integrate river water properly Generalisation is needed now that we have sandstone and desert stone dungeons by default and can choose any node for alternative structure. The current code is based around cobble dungeons with mossycobble alternative nodes, the 2 noises controlling the alternative nodes are based on wetness. Enabling multiple dungeons per chunk with definable number of rooms allows the option of very dense and complex underground structures that could interconnect to create megastructures. Y limits are added to be consistent with other mapgen elements, and enable locaton of dungeon or megastructure realms as part of our 'stacked realms' philosophy. --- src/dungeongen.cpp | 100 ++++++++++++++++++++++++++++------------------------- src/dungeongen.h | 17 ++++----- src/mapgen.cpp | 36 +++++++++---------- src/mapgen_v6.cpp | 26 +++++++------- 4 files changed, 93 insertions(+), 86 deletions(-) (limited to 'src') diff --git a/src/dungeongen.cpp b/src/dungeongen.cpp index 6be7e9086..78573da04 100644 --- a/src/dungeongen.cpp +++ b/src/dungeongen.cpp @@ -30,9 +30,8 @@ with this program; if not, write to the Free Software Foundation, Inc., //#define DGEN_USE_TORCHES -NoiseParams nparams_dungeon_rarity(0.0, 1.0, v3f(500.0, 500.0, 500.0), 0, 2, 0.8, 2.0); -NoiseParams nparams_dungeon_wetness(0.0, 1.0, v3f(40.0, 40.0, 40.0), 32474, 4, 1.1, 2.0); -NoiseParams nparams_dungeon_density(0.0, 1.0, v3f(2.5, 2.5, 2.5), 0, 2, 1.4, 2.0); +NoiseParams nparams_dungeon_density(0.9, 0.5, v3f(500.0, 500.0, 500.0), 0, 2, 0.8, 2.0); +NoiseParams nparams_dungeon_alt_wall(-0.4, 1.0, v3f(40.0, 40.0, 40.0), 32474, 6, 1.1, 2.0); /////////////////////////////////////////////////////////////////////////////// @@ -55,26 +54,27 @@ DungeonGen::DungeonGen(INodeDefManager *ndef, } else { dp.seed = 0; - dp.c_water = ndef->getId("mapgen_water_source"); - dp.c_cobble = ndef->getId("mapgen_cobble"); - dp.c_moss = ndef->getId("mapgen_mossycobble"); - dp.c_stair = ndef->getId("mapgen_stair_cobble"); + dp.c_water = ndef->getId("mapgen_water_source"); + dp.c_river_water = ndef->getId("mapgen_river_water_source"); + dp.c_wall = ndef->getId("mapgen_cobble"); + dp.c_alt_wall = ndef->getId("mapgen_mossycobble"); + dp.c_stair = ndef->getId("mapgen_stair_cobble"); + + if (dp.c_river_water == CONTENT_IGNORE) + dp.c_river_water = ndef->getId("mapgen_water_source"); dp.diagonal_dirs = false; - dp.mossratio = 3.0; dp.holesize = v3s16(1, 2, 1); dp.roomsize = v3s16(0, 0, 0); + dp.rooms_min = 2; + dp.rooms_max = 16; + dp.y_min = -MAX_MAP_GENERATION_LIMIT; + dp.y_max = MAX_MAP_GENERATION_LIMIT; dp.notifytype = GENNOTIFY_DUNGEON; - dp.np_rarity = nparams_dungeon_rarity; - dp.np_wetness = nparams_dungeon_wetness; - dp.np_density = nparams_dungeon_density; + dp.np_density = nparams_dungeon_density; + dp.np_alt_wall = nparams_dungeon_alt_wall; } - - // For mapgens using river water - dp.c_river_water = ndef->getId("mapgen_river_water_source"); - if (dp.c_river_water == CONTENT_IGNORE) - dp.c_river_water = ndef->getId("mapgen_water_source"); } @@ -83,7 +83,11 @@ void DungeonGen::generate(MMVManip *vm, u32 bseed, v3s16 nmin, v3s16 nmax) assert(vm); //TimeTaker t("gen dungeons"); - if (NoisePerlin3D(&dp.np_rarity, nmin.X, nmin.Y, nmin.Z, dp.seed) < 0.2) + if (nmin.Y < dp.y_min || nmax.Y > dp.y_max) + return; + + float nval_density = NoisePerlin3D(&dp.np_density, nmin.X, nmin.Y, nmin.Z, dp.seed); + if (nval_density < 1.0f) return; this->vm = vm; @@ -107,23 +111,23 @@ void DungeonGen::generate(MMVManip *vm, u32 bseed, v3s16 nmin, v3s16 nmax) } } - // Add it - makeDungeon(v3s16(1, 1, 1) * MAP_BLOCKSIZE); + // Add them + for (u32 i = 0; i < floor(nval_density); i++) + makeDungeon(v3s16(1, 1, 1) * MAP_BLOCKSIZE); - // Convert some cobble to mossy cobble - if (dp.mossratio != 0.0) { - for (s16 z = nmin.Z; z <= nmax.Z; z++) - for (s16 y = nmin.Y; y <= nmax.Y; y++) { - u32 i = vm->m_area.index(nmin.X, y, z); - for (s16 x = nmin.X; x <= nmax.X; x++) { - if (vm->m_data[i].getContent() == dp.c_cobble) { - float wetness = NoisePerlin3D(&dp.np_wetness, x, y, z, dp.seed); - float density = NoisePerlin3D(&dp.np_density, x, y, z, blockseed); - if (density < wetness / dp.mossratio) - vm->m_data[i].setContent(dp.c_moss); - } - i++; + // Optionally convert some structure to alternative structure + if (dp.c_alt_wall == CONTENT_IGNORE) + return; + + for (s16 z = nmin.Z; z <= nmax.Z; z++) + for (s16 y = nmin.Y; y <= nmax.Y; y++) { + u32 i = vm->m_area.index(nmin.X, y, z); + for (s16 x = nmin.X; x <= nmax.X; x++) { + if (vm->m_data[i].getContent() == dp.c_wall) { + if (NoisePerlin3D(&dp.np_alt_wall, x, y, z, blockseed) > 0.0f) + vm->m_data[i].setContent(dp.c_alt_wall); } + i++; } } @@ -189,7 +193,7 @@ void DungeonGen::makeDungeon(v3s16 start_padding) */ v3s16 last_room_center = roomplace + v3s16(roomsize.X / 2, 1, roomsize.Z / 2); - u32 room_count = random.range(2, 16); + u32 room_count = random.range(dp.rooms_min, dp.rooms_max); for (u32 i = 0; i < room_count; i++) { // Make a room to the determined place makeRoom(roomsize, roomplace); @@ -265,7 +269,7 @@ void DungeonGen::makeDungeon(v3s16 start_padding) void DungeonGen::makeRoom(v3s16 roomsize, v3s16 roomplace) { - MapNode n_cobble(dp.c_cobble); + MapNode n_wall(dp.c_wall); MapNode n_air(CONTENT_AIR); // Make +-X walls @@ -278,7 +282,7 @@ void DungeonGen::makeRoom(v3s16 roomsize, v3s16 roomplace) u32 vi = vm->m_area.index(p); if (vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE) continue; - vm->m_data[vi] = n_cobble; + vm->m_data[vi] = n_wall; } { v3s16 p = roomplace + v3s16(roomsize.X - 1, y, z); @@ -287,7 +291,7 @@ void DungeonGen::makeRoom(v3s16 roomsize, v3s16 roomplace) u32 vi = vm->m_area.index(p); if (vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE) continue; - vm->m_data[vi] = n_cobble; + vm->m_data[vi] = n_wall; } } @@ -301,7 +305,7 @@ void DungeonGen::makeRoom(v3s16 roomsize, v3s16 roomplace) u32 vi = vm->m_area.index(p); if (vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE) continue; - vm->m_data[vi] = n_cobble; + vm->m_data[vi] = n_wall; } { v3s16 p = roomplace + v3s16(x, y, roomsize.Z - 1); @@ -310,7 +314,7 @@ void DungeonGen::makeRoom(v3s16 roomsize, v3s16 roomplace) u32 vi = vm->m_area.index(p); if (vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE) continue; - vm->m_data[vi] = n_cobble; + vm->m_data[vi] = n_wall; } } @@ -324,7 +328,7 @@ void DungeonGen::makeRoom(v3s16 roomsize, v3s16 roomplace) u32 vi = vm->m_area.index(p); if (vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE) continue; - vm->m_data[vi] = n_cobble; + vm->m_data[vi] = n_wall; } { v3s16 p = roomplace + v3s16(x,roomsize. Y - 1, z); @@ -333,7 +337,7 @@ void DungeonGen::makeRoom(v3s16 roomsize, v3s16 roomplace) u32 vi = vm->m_area.index(p); if (vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE) continue; - vm->m_data[vi] = n_cobble; + vm->m_data[vi] = n_wall; } } @@ -417,7 +421,7 @@ void DungeonGen::makeCorridor(v3s16 doorplace, v3s16 doordir, makeFill(p + v3s16(-1, -1, -1), dp.holesize + v3s16(2, 3, 2), VMANIP_FLAG_DUNGEON_UNTOUCHABLE, - MapNode(dp.c_cobble), + MapNode(dp.c_wall), 0); makeHole(p); makeHole(p - dir); @@ -435,18 +439,18 @@ void DungeonGen::makeCorridor(v3s16 doorplace, v3s16 doordir, int facedir = dir_to_facedir(dir * make_stairs); u32 vi = vm->m_area.index(p.X - dir.X, p.Y - 1, p.Z - dir.Z); - if (vm->m_data[vi].getContent() == dp.c_cobble) + if (vm->m_data[vi].getContent() == dp.c_wall) vm->m_data[vi] = MapNode(dp.c_stair, 0, facedir); vi = vm->m_area.index(p.X, p.Y, p.Z); - if (vm->m_data[vi].getContent() == dp.c_cobble) + if (vm->m_data[vi].getContent() == dp.c_wall) vm->m_data[vi] = MapNode(dp.c_stair, 0, facedir); } } else { makeFill(p + v3s16(-1, -1, -1), dp.holesize + v3s16(2, 2, 2), VMANIP_FLAG_DUNGEON_UNTOUCHABLE, - MapNode(dp.c_cobble), + MapNode(dp.c_wall), 0); makeHole(p); } @@ -488,8 +492,8 @@ bool DungeonGen::findPlaceForDoor(v3s16 &result_place, v3s16 &result_dir) randomizeDir(); continue; } - if (vm->getNodeNoExNoEmerge(p).getContent() == dp.c_cobble && - vm->getNodeNoExNoEmerge(p1).getContent() == dp.c_cobble) { + if (vm->getNodeNoExNoEmerge(p).getContent() == dp.c_wall && + vm->getNodeNoExNoEmerge(p1).getContent() == dp.c_wall) { // Found wall, this is a good place! result_place = p; result_dir = m_dir; @@ -502,7 +506,7 @@ bool DungeonGen::findPlaceForDoor(v3s16 &result_place, v3s16 &result_dir) */ // Jump one up if the actual space is there if (vm->getNodeNoExNoEmerge(p + - v3s16(0, 0, 0)).getContent() == dp.c_cobble && + v3s16(0, 0, 0)).getContent() == dp.c_wall && vm->getNodeNoExNoEmerge(p + v3s16(0, 1, 0)).getContent() == CONTENT_AIR && vm->getNodeNoExNoEmerge(p + @@ -510,7 +514,7 @@ bool DungeonGen::findPlaceForDoor(v3s16 &result_place, v3s16 &result_dir) p += v3s16(0,1,0); // Jump one down if the actual space is there if (vm->getNodeNoExNoEmerge(p + - v3s16(0, 1, 0)).getContent() == dp.c_cobble && + v3s16(0, 1, 0)).getContent() == dp.c_wall && vm->getNodeNoExNoEmerge(p + v3s16(0, 0, 0)).getContent() == CONTENT_AIR && vm->getNodeNoExNoEmerge(p + diff --git a/src/dungeongen.h b/src/dungeongen.h index 898627483..30786b9f3 100644 --- a/src/dungeongen.h +++ b/src/dungeongen.h @@ -43,19 +43,21 @@ struct DungeonParams { content_t c_water; content_t c_river_water; - content_t c_cobble; - content_t c_moss; + content_t c_wall; + content_t c_alt_wall; content_t c_stair; - GenNotifyType notifytype; bool diagonal_dirs; - float mossratio; v3s16 holesize; v3s16 roomsize; + u16 rooms_min; + u16 rooms_max; + s16 y_min; + s16 y_max; + GenNotifyType notifytype; - NoiseParams np_rarity; - NoiseParams np_wetness; NoiseParams np_density; + NoiseParams np_alt_wall; }; class DungeonGen { @@ -99,8 +101,7 @@ public: } }; -extern NoiseParams nparams_dungeon_rarity; -extern NoiseParams nparams_dungeon_wetness; extern NoiseParams nparams_dungeon_density; +extern NoiseParams nparams_dungeon_alt_wall; #endif diff --git a/src/mapgen.cpp b/src/mapgen.cpp index 576fb219a..1e0e3aa44 100644 --- a/src/mapgen.cpp +++ b/src/mapgen.cpp @@ -738,44 +738,44 @@ void MapgenBasic::generateDungeons(s16 max_stone_y, MgStoneType stone_type) DungeonParams dp; - dp.seed = seed; - - dp.np_rarity = nparams_dungeon_rarity; - dp.np_density = nparams_dungeon_density; - dp.np_wetness = nparams_dungeon_wetness; - dp.c_water = c_water_source; + dp.seed = seed; + dp.c_water = c_water_source; + dp.c_river_water = c_river_water_source; + dp.rooms_min = 2; + dp.rooms_max = 16; + dp.y_min = -MAX_MAP_GENERATION_LIMIT; + dp.y_max = MAX_MAP_GENERATION_LIMIT; + dp.np_density = nparams_dungeon_density; + dp.np_alt_wall = nparams_dungeon_alt_wall; switch (stone_type) { default: case MGSTONE_STONE: - dp.c_cobble = c_cobble; - dp.c_moss = c_mossycobble; - dp.c_stair = c_stair_cobble; + dp.c_wall = c_cobble; + dp.c_alt_wall = c_mossycobble; + dp.c_stair = c_stair_cobble; dp.diagonal_dirs = false; - dp.mossratio = 3.0; dp.holesize = v3s16(1, 2, 1); dp.roomsize = v3s16(0, 0, 0); dp.notifytype = GENNOTIFY_DUNGEON; break; case MGSTONE_DESERT_STONE: - dp.c_cobble = c_desert_stone; - dp.c_moss = c_desert_stone; - dp.c_stair = c_desert_stone; + dp.c_wall = c_desert_stone; + dp.c_alt_wall = CONTENT_IGNORE; + dp.c_stair = c_desert_stone; dp.diagonal_dirs = true; - dp.mossratio = 0.0; dp.holesize = v3s16(2, 3, 2); dp.roomsize = v3s16(2, 5, 2); dp.notifytype = GENNOTIFY_TEMPLE; break; case MGSTONE_SANDSTONE: - dp.c_cobble = c_sandstonebrick; - dp.c_moss = c_sandstonebrick; - dp.c_stair = c_sandstonebrick; + dp.c_wall = c_sandstonebrick; + dp.c_alt_wall = CONTENT_IGNORE; + dp.c_stair = c_sandstonebrick; dp.diagonal_dirs = false; - dp.mossratio = 0.0; dp.holesize = v3s16(2, 2, 2); dp.roomsize = v3s16(2, 0, 2); dp.notifytype = GENNOTIFY_DUNGEON; diff --git a/src/mapgen_v6.cpp b/src/mapgen_v6.cpp index 103120b6a..caa64827e 100644 --- a/src/mapgen_v6.cpp +++ b/src/mapgen_v6.cpp @@ -560,28 +560,30 @@ void MapgenV6::makeChunk(BlockMakeData *data) DungeonParams dp; dp.seed = seed; + dp.c_water = c_water_source; + dp.c_river_water = c_water_source; + dp.rooms_min = 2; + dp.rooms_max = 16; + dp.y_min = -MAX_MAP_GENERATION_LIMIT; + dp.y_max = MAX_MAP_GENERATION_LIMIT; + dp.np_density = NoiseParams(0.9, 0.5, v3f(500.0, 500.0, 500.0), 0, 2, 0.8, 2.0); + dp.np_alt_wall = NoiseParams(-0.4, 1.0, v3f(40.0, 40.0, 40.0), 32474, 6, 1.1, 2.0); - dp.np_rarity = nparams_dungeon_rarity; - dp.np_density = nparams_dungeon_density; - dp.np_wetness = nparams_dungeon_wetness; - dp.c_water = c_water_source; if (getBiome(0, v2s16(node_min.X, node_min.Z)) == BT_DESERT) { - dp.c_cobble = c_desert_stone; - dp.c_moss = c_desert_stone; - dp.c_stair = c_desert_stone; + dp.c_wall = c_desert_stone; + dp.c_alt_wall = CONTENT_IGNORE; + dp.c_stair = c_desert_stone; dp.diagonal_dirs = true; - dp.mossratio = 0.0; dp.holesize = v3s16(2, 3, 2); dp.roomsize = v3s16(2, 5, 2); dp.notifytype = GENNOTIFY_TEMPLE; } else { - dp.c_cobble = c_cobble; - dp.c_moss = c_mossycobble; - dp.c_stair = c_stair_cobble; + dp.c_wall = c_cobble; + dp.c_alt_wall = c_mossycobble; + dp.c_stair = c_stair_cobble; dp.diagonal_dirs = false; - dp.mossratio = 3.0; dp.holesize = v3s16(1, 2, 1); dp.roomsize = v3s16(0, 0, 0); dp.notifytype = GENNOTIFY_DUNGEON; -- cgit v1.2.3 From fa0bbbf96df17f0d7911274ea85e5c049c20d07b Mon Sep 17 00:00:00 2001 From: raymoo Date: Fri, 17 Jun 2016 12:57:27 -0700 Subject: Player: New get_look, set_look API Deprecate get_look / set_look pitch / yaw --- doc/lua_api.txt | 18 +++++++--- src/player.h | 16 +++++++-- src/script/lua_api/l_object.cpp | 80 +++++++++++++++++++++++++++++++++++++++-- src/script/lua_api/l_object.h | 16 +++++++++ 4 files changed, 121 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index ed8f8504e..6d359379e 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -2679,10 +2679,20 @@ This is basically a reference to a C++ `ServerActiveObject` * `get_player_velocity()`: returns `nil` if is not a player, otherwise a table {x, y, z} representing the player's instantaneous velocity in nodes/s * `get_look_dir()`: get camera direction as a unit vector -* `get_look_pitch()`: pitch in radians -* `get_look_yaw()`: yaw in radians (wraps around pretty randomly as of now) -* `set_look_pitch(radians)`: sets look pitch -* `set_look_yaw(radians)`: sets look yaw +* `get_look_vertical()`: pitch in radians + * Angle ranges between -pi/2 and pi/2, which are straight up and down respectively. +* `get_look_horizontal()`: yaw in radians + * Angle is counter-clockwise from the +z direction. +* `set_look_vertical(radians)`: sets look pitch + * radians - Angle from looking forward, where positive is downwards. +* `set_look_horizontal(radians)`: sets look yaw + * radians - Angle from the +z direction, where positive is counter-clockwise. +* `get_look_pitch()`: pitch in radians - Deprecated as broken. Use get_look_vertical. + * Angle ranges between -pi/2 and pi/2, which are straight down and up respectively. +* `get_look_yaw()`: yaw in radians - Deprecated as broken. Use get_look_horizontal. + * Angle is counter-clockwise from the +x direction. +* `set_look_pitch(radians)`: sets look pitch - Deprecated. Use set_look_vertical. +* `set_look_yaw(radians)`: sets look yaw - Deprecated. Use set_look_horizontal. * `get_breath()`: returns players breath * `set_breath(value)`: sets players breath * values: diff --git a/src/player.h b/src/player.h index 6687ca86e..e6fcf388a 100644 --- a/src/player.h +++ b/src/player.h @@ -188,16 +188,28 @@ public: m_breath = breath; } - f32 getRadPitch() + // Deprecated + f32 getRadPitchDep() { return -1.0 * m_pitch * core::DEGTORAD; } - f32 getRadYaw() + // Deprecated + f32 getRadYawDep() { return (m_yaw + 90.) * core::DEGTORAD; } + f32 getRadPitch() + { + return m_pitch * core::DEGTORAD; + } + + f32 getRadYaw() + { + return m_yaw * core::DEGTORAD; + } + const char *getName() const { return m_name; diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index 6d6614e7d..befae4253 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -1016,27 +1016,61 @@ int ObjectRef::l_get_look_dir(lua_State *L) Player *player = getplayer(ref); if (player == NULL) return 0; // Do it - float pitch = player->getRadPitch(); - float yaw = player->getRadYaw(); + float pitch = player->getRadPitchDep(); + float yaw = player->getRadYawDep(); v3f v(cos(pitch)*cos(yaw), sin(pitch), cos(pitch)*sin(yaw)); push_v3f(L, v); return 1; } +// DEPRECATED // get_look_pitch(self) int ObjectRef::l_get_look_pitch(lua_State *L) { NO_MAP_LOCK_REQUIRED; + + log_deprecated(L, + "Deprecated call to get_look_pitch, use get_look_vertical instead"); + ObjectRef *ref = checkobject(L, 1); Player *player = getplayer(ref); if (player == NULL) return 0; // Do it - lua_pushnumber(L, player->getRadPitch()); + lua_pushnumber(L, player->getRadPitchDep()); return 1; } +// DEPRECATED // get_look_yaw(self) int ObjectRef::l_get_look_yaw(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + log_deprecated(L, + "Deprecated call to get_look_yaw, use get_look_horizontal instead"); + + ObjectRef *ref = checkobject(L, 1); + Player *player = getplayer(ref); + if (player == NULL) return 0; + // Do it + lua_pushnumber(L, player->getRadYawDep()); + return 1; +} + +// get_look_pitch2(self) +int ObjectRef::l_get_look_vertical(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + Player *player = getplayer(ref); + if (player == NULL) return 0; + // Do it + lua_pushnumber(L, player->getRadPitch()); + return 1; +} + +// get_look_yaw2(self) +int ObjectRef::l_get_look_horizontal(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); @@ -1047,10 +1081,41 @@ int ObjectRef::l_get_look_yaw(lua_State *L) return 1; } +// set_look_vertical(self, radians) +int ObjectRef::l_set_look_vertical(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + PlayerSAO* co = getplayersao(ref); + if (co == NULL) return 0; + float pitch = luaL_checknumber(L, 2) * core::RADTODEG; + // Do it + co->setPitch(pitch); + return 1; +} + +// set_look_horizontal(self, radians) +int ObjectRef::l_set_look_horizontal(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + PlayerSAO* co = getplayersao(ref); + if (co == NULL) return 0; + float yaw = luaL_checknumber(L, 2) * core::RADTODEG; + // Do it + co->setYaw(yaw); + return 1; +} + +// DEPRECATED // set_look_pitch(self, radians) int ObjectRef::l_set_look_pitch(lua_State *L) { NO_MAP_LOCK_REQUIRED; + + log_deprecated(L, + "Deprecated call to set_look_pitch, use set_look_vertical instead."); + ObjectRef *ref = checkobject(L, 1); PlayerSAO* co = getplayersao(ref); if (co == NULL) return 0; @@ -1060,10 +1125,15 @@ int ObjectRef::l_set_look_pitch(lua_State *L) return 1; } +// DEPRECATED // set_look_yaw(self, radians) int ObjectRef::l_set_look_yaw(lua_State *L) { NO_MAP_LOCK_REQUIRED; + + log_deprecated(L, + "Deprecated call to set_look_yaw, use set_look_horizontal instead."); + ObjectRef *ref = checkobject(L, 1); PlayerSAO* co = getplayersao(ref); if (co == NULL) return 0; @@ -1754,6 +1824,10 @@ const luaL_reg ObjectRef::methods[] = { luamethod(ObjectRef, get_look_dir), luamethod(ObjectRef, get_look_pitch), luamethod(ObjectRef, get_look_yaw), + luamethod(ObjectRef, get_look_vertical), + luamethod(ObjectRef, get_look_horizontal), + luamethod(ObjectRef, set_look_horizontal), + luamethod(ObjectRef, set_look_vertical), luamethod(ObjectRef, set_look_yaw), luamethod(ObjectRef, set_look_pitch), luamethod(ObjectRef, get_breath), diff --git a/src/script/lua_api/l_object.h b/src/script/lua_api/l_object.h index a4457cc05..a37d29535 100644 --- a/src/script/lua_api/l_object.h +++ b/src/script/lua_api/l_object.h @@ -189,15 +189,31 @@ private: // get_look_dir(self) static int l_get_look_dir(lua_State *L); + // DEPRECATED // get_look_pitch(self) static int l_get_look_pitch(lua_State *L); + // DEPRECATED // get_look_yaw(self) static int l_get_look_yaw(lua_State *L); + // get_look_pitch2(self) + static int l_get_look_vertical(lua_State *L); + + // get_look_yaw2(self) + static int l_get_look_horizontal(lua_State *L); + + // set_look_vertical(self, radians) + static int l_set_look_vertical(lua_State *L); + + // set_look_horizontal(self, radians) + static int l_set_look_horizontal(lua_State *L); + + // DEPRECATED // set_look_pitch(self, radians) static int l_set_look_pitch(lua_State *L); + // DEPRECATED // set_look_yaw(self, radians) static int l_set_look_yaw(lua_State *L); -- cgit v1.2.3 From 9997e2030c86b938d1889ee71522bc26d01226e6 Mon Sep 17 00:00:00 2001 From: paramat Date: Tue, 21 Jun 2016 03:48:24 +0100 Subject: Mgflat/fractal/v7/valleys: Denser 3D noise tunnels Reduce spread from 96 to primes 61 and 67 (either side of 64) Prime spreads help to keep 3D noise periodic features unaligned 'cave width' 0.2 to preserve tunnel width Reduce octaves to 3 to improve network structure --- builtin/settingtypes.txt | 24 ++++++++++++------------ minetest.conf.example | 24 ++++++++++++------------ src/mapgen_flat.cpp | 6 +++--- src/mapgen_fractal.cpp | 6 +++--- src/mapgen_v7.cpp | 6 +++--- src/mapgen_valleys.cpp | 6 +++--- 6 files changed, 36 insertions(+), 36 deletions(-) (limited to 'src') diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index b53e35a85..c751f34f2 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -954,7 +954,7 @@ mgv6_np_apple_trees (Mapgen v6 apple trees noise parameters) noise_params 0, 1, mgv7_spflags (Mapgen v7 flags) flags mountains,ridges mountains,ridges,nomountains,noridges # Controls width of tunnels, a smaller value creates wider tunnels. -mgv7_cave_width (Mapgen v7 cave width) float 0.3 +mgv7_cave_width (Mapgen v7 cave width) float 0.2 mgv7_np_terrain_base (Mapgen v7 terrain base noise parameters) noise_params 4, 70, (600, 600, 600), 82341, 5, 0.6, 2.0 mgv7_np_terrain_alt (Mapgen v7 terrain altitude noise parameters) noise_params 4, 25, (600, 600, 600), 5934, 5, 0.6, 2.0 @@ -965,8 +965,8 @@ mgv7_np_mount_height (Mapgen v7 mount height noise parameters) noise_params 256, mgv7_np_ridge_uwater (Mapgen v7 ridge water noise parameters) noise_params 0, 1, (1000, 1000, 1000), 85039, 5, 0.6, 2.0 mgv7_np_mountain (Mapgen v7 mountain noise parameters) noise_params -0.6, 1, (250, 350, 250), 5333, 5, 0.63, 2.0 mgv7_np_ridge (Mapgen v7 ridge noise parameters) noise_params 0, 1, (100, 100, 100), 6467, 4, 0.75, 2.0 -mgv7_np_cave1 (Mapgen v7 cave1 noise parameters) noise_params 0, 12, (100, 100, 100), 52534, 4, 0.5, 2.0 -mgv7_np_cave2 (Mapgen v7 cave2 noise parameters) noise_params 0, 12, (100, 100, 100), 10325, 4, 0.5, 2.0 +mgv7_np_cave1 (Mapgen v7 cave1 noise parameters) noise_params 0, 12, (61, 61, 61), 52534, 3, 0.5, 2.0 +mgv7_np_cave2 (Mapgen v7 cave2 noise parameters) noise_params 0, 12, (67, 67, 67), 10325, 3, 0.5, 2.0 [***Mapgen flat] @@ -983,7 +983,7 @@ mgflat_ground_level (Mapgen flat ground level) int 8 mgflat_large_cave_depth (Mapgen flat large cave depth) int -33 # Controls width of tunnels, a smaller value creates wider tunnels. -mgflat_cave_width (Mapgen flat cave width) float 0.3 +mgflat_cave_width (Mapgen flat cave width) float 0.2 # Terrain noise threshold for lakes. # Controls proportion of world area covered by lakes. @@ -1007,13 +1007,13 @@ mgflat_hill_steepness (Mapgen flat hill steepness) float 64.0 mgflat_np_terrain (Mapgen flat terrain noise parameters) noise_params 0, 1, (600, 600, 600), 7244, 5, 0.6, 2.0 mgflat_np_filler_depth (Mapgen flat filler depth noise parameters) noise_params 0, 1.2, (150, 150, 150), 261, 3, 0.7, 2.0 -mgflat_np_cave1 (Mapgen flat cave1 noise parameters) noise_params 0, 12, (128, 128, 128), 52534, 4, 0.5, 2.0 -mgflat_np_cave2 (Mapgen flat cave2 noise parameters) noise_params 0, 12, (128, 128, 128), 10325, 4, 0.5, 2.0 +mgflat_np_cave1 (Mapgen flat cave1 noise parameters) noise_params 0, 12, (61, 61, 61), 52534, 3, 0.5, 2.0 +mgflat_np_cave2 (Mapgen flat cave2 noise parameters) noise_params 0, 12, (67, 67, 67), 10325, 3, 0.5, 2.0 [***Mapgen fractal] # Controls width of tunnels, a smaller value creates wider tunnels. -mgfractal_cave_width (Mapgen fractal cave width) float 0.3 +mgfractal_cave_width (Mapgen fractal cave width) float 0.2 # Choice of 18 fractals from 9 formulas. # 1 = 4D "Roundy" mandelbrot set. @@ -1074,8 +1074,8 @@ mgfractal_julia_w (Mapgen fractal julia w) float 0.33 mgfractal_np_seabed (Mapgen fractal seabed noise parameters) noise_params -14, 9, (600, 600, 600), 41900, 5, 0.6, 2.0 mgfractal_np_filler_depth (Mapgen fractal filler depth noise parameters) noise_params 0, 1.2, (150, 150, 150), 261, 3, 0.7, 2.0 -mgfractal_np_cave1 (Mapgen fractal cave1 noise parameters) noise_params 0, 12, (128, 128, 128), 52534, 4, 0.5, 2.0 -mgfractal_np_cave2 (Mapgen fractal cave2 noise parameters) noise_params 0, 12, (128, 128, 128), 10325, 4, 0.5, 2.0 +mgfractal_np_cave1 (Mapgen fractal cave1 noise parameters) noise_params 0, 12, (61, 61, 61), 52534, 3, 0.5, 2.0 +mgfractal_np_cave2 (Mapgen fractal cave2 noise parameters) noise_params 0, 12, (67, 67, 67), 10325, 3, 0.5, 2.0 # Mapgen Valleys parameters [***Mapgen Valleys] @@ -1115,16 +1115,16 @@ mgvalleys_river_size (River Size) int 5 mgvalleys_water_features (Water Features) int 0 # Controls width of tunnels, a smaller value creates wider tunnels. -mgvalleys_cave_width (Cave width) float 0.3 +mgvalleys_cave_width (Cave width) float 0.2 # Noise parameters [****Noises] # Caves and tunnels form at the intersection of the two noises -mgvalleys_np_cave1 (Cave noise #1) noise_params 0, 12, (100, 100, 100), 52534, 4, 0.5, 2.0 +mgvalleys_np_cave1 (Cave noise #1) noise_params 0, 12, (61, 61, 61), 52534, 3, 0.5, 2.0 # Caves and tunnels form at the intersection of the two noises -mgvalleys_np_cave2 (Cave noise #2) noise_params 0, 12, (100, 100, 100), 10325, 4, 0.5, 2.0 +mgvalleys_np_cave2 (Cave noise #2) noise_params 0, 12, (67, 67, 67), 10325, 3, 0.5, 2.0 # The depth of dirt or other filler mgvalleys_np_filler_depth (Filler Depth) noise_params 0, 1.2, (256, 256, 256), 1605, 3, 0.5, 2.0 diff --git a/minetest.conf.example b/minetest.conf.example index a5ca16e51..35675dfe3 100644 --- a/minetest.conf.example +++ b/minetest.conf.example @@ -1176,7 +1176,7 @@ # Controls width of tunnels, a smaller value creates wider tunnels. # type: float -# mgv7_cave_width = 0.3 +# mgv7_cave_width = 0.2 # type: noise_params # mgv7_np_terrain_base = 4, 70, (600, 600, 600), 82341, 5, 0.6, 2.0 @@ -1206,10 +1206,10 @@ # mgv7_np_ridge = 0, 1, (100, 100, 100), 6467, 4, 0.75, 2.0 # type: noise_params -# mgv7_np_cave1 = 0, 12, (100, 100, 100), 52534, 4, 0.5, 2.0 +# mgv7_np_cave1 = 0, 12, (61, 61, 61), 52534, 3, 0.5, 2.0 # type: noise_params -# mgv7_np_cave2 = 0, 12, (100, 100, 100), 10325, 4, 0.5, 2.0 +# mgv7_np_cave2 = 0, 12, (67, 67, 67), 10325, 3, 0.5, 2.0 #### Mapgen flat @@ -1230,7 +1230,7 @@ # Controls width of tunnels, a smaller value creates wider tunnels. # type: float -# mgflat_cave_width = 0.3 +# mgflat_cave_width = 0.2 # Terrain noise threshold for lakes. # Controls proportion of world area covered by lakes. @@ -1262,16 +1262,16 @@ # mgflat_np_filler_depth = 0, 1.2, (150, 150, 150), 261, 3, 0.7, 2.0 # type: noise_params -# mgflat_np_cave1 = 0, 12, (128, 128, 128), 52534, 4, 0.5, 2.0 +# mgflat_np_cave1 = 0, 12, (61, 61, 61), 52534, 3, 0.5, 2.0 # type: noise_params -# mgflat_np_cave2 = 0, 12, (128, 128, 128), 10325, 4, 0.5, 2.0 +# mgflat_np_cave2 = 0, 12, (67, 67, 67), 10325, 3, 0.5, 2.0 #### Mapgen fractal # Controls width of tunnels, a smaller value creates wider tunnels. # type: float -# mgfractal_cave_width = 0.3 +# mgfractal_cave_width = 0.2 # Choice of 18 fractals from 9 formulas. # 1 = 4D "Roundy" mandelbrot set. @@ -1346,10 +1346,10 @@ # mgfractal_np_filler_depth = 0, 1.2, (150, 150, 150), 261, 3, 0.7, 2.0 # type: noise_params -# mgfractal_np_cave1 = 0, 12, (128, 128, 128), 52534, 4, 0.5, 2.0 +# mgfractal_np_cave1 = 0, 12, (61, 61, 61), 52534, 3, 0.5, 2.0 # type: noise_params -# mgfractal_np_cave2 = 0, 12, (128, 128, 128), 10325, 4, 0.5, 2.0 +# mgfractal_np_cave2 = 0, 12, (67, 67, 67), 10325, 3, 0.5, 2.0 #### Mapgen Valleys @@ -1396,17 +1396,17 @@ # Controls width of tunnels, a smaller value creates wider tunnels. # type: float -# mgvalleys_cave_width = 0.3 +# mgvalleys_cave_width = 0.2 ##### Noises # Caves and tunnels form at the intersection of the two noises # type: noise_params -# mgvalleys_np_cave1 = 0, 12, (100, 100, 100), 52534, 4, 0.5, 2.0 +# mgvalleys_np_cave1 = 0, 12, (61, 61, 61), 52534, 3, 0.5, 2.0 # Caves and tunnels form at the intersection of the two noises # type: noise_params -# mgvalleys_np_cave2 = 0, 12, (100, 100, 100), 10325, 4, 0.5, 2.0 +# mgvalleys_np_cave2 = 0, 12, (67, 67, 67), 10325, 3, 0.5, 2.0 # The depth of dirt or other filler # type: noise_params diff --git a/src/mapgen_flat.cpp b/src/mapgen_flat.cpp index 7cc6aad5c..956af999a 100644 --- a/src/mapgen_flat.cpp +++ b/src/mapgen_flat.cpp @@ -84,7 +84,7 @@ MapgenFlatParams::MapgenFlatParams() spflags = 0; ground_level = 8; large_cave_depth = -33; - cave_width = 0.3; + cave_width = 0.2; lake_threshold = -0.45; lake_steepness = 48.0; hill_threshold = 0.45; @@ -92,8 +92,8 @@ MapgenFlatParams::MapgenFlatParams() np_terrain = NoiseParams(0, 1, v3f(600, 600, 600), 7244, 5, 0.6, 2.0); np_filler_depth = NoiseParams(0, 1.2, v3f(150, 150, 150), 261, 3, 0.7, 2.0); - np_cave1 = NoiseParams(0, 12, v3f(96, 96, 96), 52534, 4, 0.5, 2.0); - np_cave2 = NoiseParams(0, 12, v3f(96, 96, 96), 10325, 4, 0.5, 2.0); + np_cave1 = NoiseParams(0, 12, v3f(61, 61, 61), 52534, 3, 0.5, 2.0); + np_cave2 = NoiseParams(0, 12, v3f(67, 67, 67), 10325, 3, 0.5, 2.0); } diff --git a/src/mapgen_fractal.cpp b/src/mapgen_fractal.cpp index c47a7bfdc..9e4c210dc 100644 --- a/src/mapgen_fractal.cpp +++ b/src/mapgen_fractal.cpp @@ -86,7 +86,7 @@ MapgenFractal::~MapgenFractal() MapgenFractalParams::MapgenFractalParams() { spflags = 0; - cave_width = 0.3; + cave_width = 0.2; fractal = 1; iterations = 11; scale = v3f(4096.0, 1024.0, 4096.0); @@ -99,8 +99,8 @@ MapgenFractalParams::MapgenFractalParams() np_seabed = NoiseParams(-14, 9, v3f(600, 600, 600), 41900, 5, 0.6, 2.0); np_filler_depth = NoiseParams(0, 1.2, v3f(150, 150, 150), 261, 3, 0.7, 2.0); - np_cave1 = NoiseParams(0, 12, v3f(96, 96, 96), 52534, 4, 0.5, 2.0); - np_cave2 = NoiseParams(0, 12, v3f(96, 96, 96), 10325, 4, 0.5, 2.0); + np_cave1 = NoiseParams(0, 12, v3f(61, 61, 61), 52534, 3, 0.5, 2.0); + np_cave2 = NoiseParams(0, 12, v3f(67, 67, 67), 10325, 3, 0.5, 2.0); } diff --git a/src/mapgen_v7.cpp b/src/mapgen_v7.cpp index 35dcdcd94..c24b3e8d1 100644 --- a/src/mapgen_v7.cpp +++ b/src/mapgen_v7.cpp @@ -94,7 +94,7 @@ MapgenV7::~MapgenV7() MapgenV7Params::MapgenV7Params() { spflags = MGV7_MOUNTAINS | MGV7_RIDGES; - cave_width = 0.3; + cave_width = 0.2; np_terrain_base = NoiseParams(4, 70, v3f(600, 600, 600), 82341, 5, 0.6, 2.0); np_terrain_alt = NoiseParams(4, 25, v3f(600, 600, 600), 5934, 5, 0.6, 2.0); @@ -105,8 +105,8 @@ MapgenV7Params::MapgenV7Params() np_ridge_uwater = NoiseParams(0, 1, v3f(1000, 1000, 1000), 85039, 5, 0.6, 2.0); np_mountain = NoiseParams(-0.6, 1, v3f(250, 350, 250), 5333, 5, 0.63, 2.0); np_ridge = NoiseParams(0, 1, v3f(100, 100, 100), 6467, 4, 0.75, 2.0); - np_cave1 = NoiseParams(0, 12, v3f(96, 96, 96), 52534, 4, 0.5, 2.0); - np_cave2 = NoiseParams(0, 12, v3f(96, 96, 96), 10325, 4, 0.5, 2.0); + np_cave1 = NoiseParams(0, 12, v3f(61, 61, 61), 52534, 3, 0.5, 2.0); + np_cave2 = NoiseParams(0, 12, v3f(67, 67, 67), 10325, 3, 0.5, 2.0); } diff --git a/src/mapgen_valleys.cpp b/src/mapgen_valleys.cpp index bbf20719d..02a8fbfe0 100644 --- a/src/mapgen_valleys.cpp +++ b/src/mapgen_valleys.cpp @@ -144,10 +144,10 @@ MapgenValleysParams::MapgenValleysParams() river_depth = 4; // How deep to carve river channels. river_size = 5; // How wide to make rivers. water_features = 0; // How often water will occur in caves. - cave_width = 0.3; + cave_width = 0.2; - np_cave1 = NoiseParams(0, 12, v3f(96, 96, 96), 52534, 4, 0.5, 2.0); - np_cave2 = NoiseParams(0, 12, v3f(96, 96, 96), 10325, 4, 0.5, 2.0); + np_cave1 = NoiseParams(0, 12, v3f(61, 61, 61), 52534, 3, 0.5, 2.0); + np_cave2 = NoiseParams(0, 12, v3f(67, 67, 67), 10325, 3, 0.5, 2.0); np_filler_depth = NoiseParams(0.f, 1.2f, v3f(256, 256, 256), 1605, 3, 0.5f, 2.f); np_inter_valley_fill = NoiseParams(0.f, 1.f, v3f(256, 512, 256), 1993, 6, 0.8f, 2.f); np_inter_valley_slope = NoiseParams(0.5f, 0.5f, v3f(128, 128, 128), 746, 1, 1.f, 2.f); -- cgit v1.2.3 From ab7a5c4ff138c39a2491592731d677c9f392caa0 Mon Sep 17 00:00:00 2001 From: est31 Date: Fri, 24 Jun 2016 20:43:29 +0200 Subject: Also shut down when SIGTERM was received Fixes #4251 --- src/porting.cpp | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/porting.cpp b/src/porting.cpp index 98b85b7d0..7ded58b3f 100644 --- a/src/porting.cpp +++ b/src/porting.cpp @@ -75,11 +75,16 @@ bool * signal_handler_killstatus(void) #if !defined(_WIN32) // POSIX #include -void sigint_handler(int sig) +void signal_handler(int sig) { if (!g_killed) { - dstream << "INFO: sigint_handler(): " - << "Ctrl-C pressed, shutting down." << std::endl; + if (sig == SIGINT) { + dstream << "INFO: signal_handler(): " + << "Ctrl-C pressed, shutting down." << std::endl; + } else if (sig == SIGTERM) { + dstream << "INFO: signal_handler(): " + << "got SIGTERM, shutting down." << std::endl; + } // Comment out for less clutter when testing scripts /*dstream << "INFO: sigint_handler(): " @@ -88,13 +93,14 @@ void sigint_handler(int sig) g_killed = true; } else { - (void)signal(SIGINT, SIG_DFL); + (void)signal(sig, SIG_DFL); } } void signal_handler_init(void) { - (void)signal(SIGINT, sigint_handler); + (void)signal(SIGINT, signal_handler); + (void)signal(SIGTERM, signal_handler); } #else // _WIN32 -- cgit v1.2.3 From b6eef1c0c2e76e56f148bf296a42fb3a4bae4a55 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sun, 19 Jun 2016 08:48:12 +0100 Subject: openbsd's port fix for non Linux systems w/o libRT X11 headers are not always in expected locations, add them to include list. Modifications by est31: indentation fixes, commit message improved --- src/CMakeLists.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f02812415..bffc1f7eb 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -336,7 +336,10 @@ else() if(APPLE) set(PLATFORM_LIBS "-framework CoreFoundation" ${PLATFORM_LIBS}) else() - set(PLATFORM_LIBS -lrt ${PLATFORM_LIBS}) + check_library_exists(rt clock_gettime "" HAVE_LIBRT) + if (HAVE_LIBRT) + set(PLATFORM_LIBS -lrt ${PLATFORM_LIBS}) + endif(HAVE_LIBRT) endif(APPLE) # This way Xxf86vm is found on OpenBSD too @@ -555,6 +558,7 @@ include_directories( ${LUA_INCLUDE_DIR} ${GMP_INCLUDE_DIR} ${JSON_INCLUDE_DIR} + ${X11_INCLUDE_DIR} ${PROJECT_SOURCE_DIR}/script ) -- cgit v1.2.3 From 92705306bfb4994107a43514f29997cea15d48dc Mon Sep 17 00:00:00 2001 From: kwolekr Date: Tue, 14 Jun 2016 00:10:55 -0400 Subject: Mapgen: Refactor mapgen creation and management - Move mapgen creation logic out of EmergeManager and into Mapgen - Internally represent mapgen type as an enum value, instead of a string - Remove the need for a MapgenFactory per mapgen --- src/emerge.cpp | 68 +++-------------------- src/emerge.h | 3 - src/mapgen.cpp | 112 +++++++++++++++++++++++++++++++++++++- src/mapgen.h | 33 ++++++++--- src/mapgen_flat.h | 14 +---- src/mapgen_fractal.h | 14 +---- src/mapgen_singlenode.h | 17 ++---- src/mapgen_v5.h | 15 +---- src/mapgen_v6.h | 16 +----- src/mapgen_v7.h | 14 +---- src/mapgen_valleys.h | 14 +---- src/script/lua_api/l_mainmenu.cpp | 4 +- 12 files changed, 160 insertions(+), 164 deletions(-) (limited to 'src') diff --git a/src/emerge.cpp b/src/emerge.cpp index a2a10a177..48de6cb1a 100644 --- a/src/emerge.cpp +++ b/src/emerge.cpp @@ -34,13 +34,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "log.h" #include "map.h" #include "mapblock.h" -#include "mapgen_flat.h" -#include "mapgen_fractal.h" -#include "mapgen_v5.h" -#include "mapgen_v6.h" -#include "mapgen_v7.h" -#include "mapgen_valleys.h" -#include "mapgen_singlenode.h" #include "mg_biome.h" #include "mg_ore.h" #include "mg_decoration.h" @@ -53,13 +46,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "settings.h" #include "voxel.h" - -struct MapgenDesc { - const char *name; - MapgenFactory *factory; - bool is_user_visible; -}; - class EmergeThread : public Thread { public: bool enable_mapgen_debug_info; @@ -99,20 +85,6 @@ private: friend class EmergeManager; }; -//// -//// Built-in mapgens -//// - -MapgenDesc g_reg_mapgens[] = { - {"v5", new MapgenFactoryV5, true}, - {"v6", new MapgenFactoryV6, true}, - {"v7", new MapgenFactoryV7, true}, - {"flat", new MapgenFactoryFlat, true}, - {"fractal", new MapgenFactoryFractal, true}, - {"valleys", new MapgenFactoryValleys, true}, - {"singlenode", new MapgenFactorySinglenode, false}, -}; - //// //// EmergeManager //// @@ -195,24 +167,24 @@ void EmergeManager::initMapgens() if (m_mapgens.size()) return; - MapgenFactory *mgfactory = getMapgenFactory(params.mg_name); - if (!mgfactory) { + MapgenType mgtype = Mapgen::getMapgenType(params.mg_name); + if (mgtype == MAPGEN_INVALID) { + const char *default_mapgen_name = Mapgen::getMapgenName(MAPGEN_DEFAULT); errorstream << "EmergeManager: mapgen " << params.mg_name << - " not registered; falling back to " << DEFAULT_MAPGEN << std::endl; - - params.mg_name = DEFAULT_MAPGEN; + " not registered; falling back to " << + default_mapgen_name << std::endl; - mgfactory = getMapgenFactory(params.mg_name); - FATAL_ERROR_IF(mgfactory == NULL, "Couldn't use any mapgen!"); + params.mg_name = default_mapgen_name; + mgtype = MAPGEN_DEFAULT; } if (!params.sparams) { - params.sparams = mgfactory->createMapgenParams(); + params.sparams = Mapgen::createMapgenParams(mgtype); params.sparams->readParams(g_settings); } for (u32 i = 0; i != m_threads.size(); i++) { - Mapgen *mg = mgfactory->createMapgen(i, ¶ms, this); + Mapgen *mg = Mapgen::createMapgen(mgtype, i, ¶ms, this); m_mapgens.push_back(mg); } } @@ -369,28 +341,6 @@ bool EmergeManager::isBlockUnderground(v3s16 blockpos) return blockpos.Y * (MAP_BLOCKSIZE + 1) <= params.water_level; } - -void EmergeManager::getMapgenNames( - std::vector *mgnames, bool include_hidden) -{ - for (u32 i = 0; i != ARRLEN(g_reg_mapgens); i++) { - if (include_hidden || g_reg_mapgens[i].is_user_visible) - mgnames->push_back(g_reg_mapgens[i].name); - } -} - - -MapgenFactory *EmergeManager::getMapgenFactory(const std::string &mgname) -{ - for (u32 i = 0; i != ARRLEN(g_reg_mapgens); i++) { - if (mgname == g_reg_mapgens[i].name) - return g_reg_mapgens[i].factory; - } - - return NULL; -} - - bool EmergeManager::pushBlockEmergeData( v3s16 pos, u16 peer_requested, diff --git a/src/emerge.h b/src/emerge.h index 825ac1c0f..303a35529 100644 --- a/src/emerge.h +++ b/src/emerge.h @@ -140,9 +140,6 @@ public: int getGroundLevelAtPoint(v2s16 p); bool isBlockUnderground(v3s16 blockpos); - static MapgenFactory *getMapgenFactory(const std::string &mgname); - static void getMapgenNames( - std::vector *mgnames, bool include_hidden); static v3s16 getContainingChunk(v3s16 blockpos, s16 chunksize); private: diff --git a/src/mapgen.cpp b/src/mapgen.cpp index 1e0e3aa44..e45233b33 100644 --- a/src/mapgen.cpp +++ b/src/mapgen.cpp @@ -39,6 +39,13 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/numeric.h" #include "filesys.h" #include "log.h" +#include "mapgen_flat.h" +#include "mapgen_fractal.h" +#include "mapgen_v5.h" +#include "mapgen_v6.h" +#include "mapgen_v7.h" +#include "mapgen_valleys.h" +#include "mapgen_singlenode.h" #include "cavegen.h" #include "dungeongen.h" @@ -63,6 +70,28 @@ FlagDesc flagdesc_gennotify[] = { {NULL, 0} }; +struct MapgenDesc { + const char *name; + bool is_user_visible; +}; + +//// +//// Built-in mapgens +//// + +static MapgenDesc g_reg_mapgens[] = { + {"v5", true}, + {"v6", true}, + {"v7", true}, + {"flat", true}, + {"fractal", true}, + {"valleys", true}, + {"singlenode", false}, +}; + +STATIC_ASSERT( + ARRLEN(g_reg_mapgens) == MAPGEN_INVALID, + registered_mapgens_is_wrong_size); //// //// Mapgen @@ -121,6 +150,83 @@ Mapgen::~Mapgen() } +MapgenType Mapgen::getMapgenType(const std::string &mgname) +{ + for (size_t i = 0; i != ARRLEN(g_reg_mapgens); i++) { + if (mgname == g_reg_mapgens[i].name) + return (MapgenType)i; + } + + return MAPGEN_INVALID; +} + + +const char *Mapgen::getMapgenName(MapgenType mgtype) +{ + size_t index = (size_t)mgtype; + if (index == MAPGEN_INVALID || index >= ARRLEN(g_reg_mapgens)) + return "invalid"; + + return g_reg_mapgens[index].name; +} + + +Mapgen *Mapgen::createMapgen(MapgenType mgtype, int mgid, + MapgenParams *params, EmergeManager *emerge) +{ + switch (mgtype) { + case MAPGEN_FLAT: + return new MapgenFlat(mgid, params, emerge); + case MAPGEN_FRACTAL: + return new MapgenFractal(mgid, params, emerge); + case MAPGEN_SINGLENODE: + return new MapgenSinglenode(mgid, params, emerge); + case MAPGEN_V5: + return new MapgenV5(mgid, params, emerge); + case MAPGEN_V6: + return new MapgenV6(mgid, params, emerge); + case MAPGEN_V7: + return new MapgenV7(mgid, params, emerge); + case MAPGEN_VALLEYS: + return new MapgenValleys(mgid, params, emerge); + default: + return NULL; + } +} + + +MapgenSpecificParams *Mapgen::createMapgenParams(MapgenType mgtype) +{ + switch (mgtype) { + case MAPGEN_FLAT: + return new MapgenFlatParams; + case MAPGEN_FRACTAL: + return new MapgenFractalParams; + case MAPGEN_SINGLENODE: + return new MapgenSinglenodeParams; + case MAPGEN_V5: + return new MapgenV5Params; + case MAPGEN_V6: + return new MapgenV6Params; + case MAPGEN_V7: + return new MapgenV7Params; + case MAPGEN_VALLEYS: + return new MapgenValleysParams; + default: + return NULL; + } +} + + +void Mapgen::getMapgenNames(std::vector *mgnames, bool include_hidden) +{ + for (u32 i = 0; i != ARRLEN(g_reg_mapgens); i++) { + if (include_hidden || g_reg_mapgens[i].is_user_visible) + mgnames->push_back(g_reg_mapgens[i].name); + } +} + + u32 Mapgen::getBlockSeed(v3s16 p, s32 seed) { return (u32)seed + @@ -891,9 +997,9 @@ void MapgenParams::load(const Settings &settings) } delete sparams; - MapgenFactory *mgfactory = EmergeManager::getMapgenFactory(mg_name); - if (mgfactory) { - sparams = mgfactory->createMapgenParams(); + MapgenType mgtype = Mapgen::getMapgenType(mg_name); + if (mgtype != MAPGEN_INVALID) { + sparams = Mapgen::createMapgenParams(mgtype); sparams->readParams(&settings); } } diff --git a/src/mapgen.h b/src/mapgen.h index 90ac84bd8..618a2f6b8 100644 --- a/src/mapgen.h +++ b/src/mapgen.h @@ -26,7 +26,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/string.h" #include "util/container.h" -#define DEFAULT_MAPGEN "v6" +#define MAPGEN_DEFAULT MAPGEN_V6 +#define MAPGEN_DEFAULT_NAME "v6" /////////////////// Mapgen flags #define MG_TREES 0x01 @@ -107,6 +108,17 @@ private: std::list m_notify_events; }; +enum MapgenType { + MAPGEN_V5, + MAPGEN_V6, + MAPGEN_V7, + MAPGEN_FLAT, + MAPGEN_FRACTAL, + MAPGEN_VALLEYS, + MAPGEN_SINGLENODE, + MAPGEN_INVALID, +}; + struct MapgenSpecificParams { virtual void readParams(const Settings *settings) = 0; virtual void writeParams(Settings *settings) const = 0; @@ -124,7 +136,7 @@ struct MapgenParams { MapgenSpecificParams *sparams; MapgenParams() : - mg_name(DEFAULT_MAPGEN), + mg_name(MAPGEN_DEFAULT_NAME), chunksize(5), seed(0), water_level(1), @@ -173,6 +185,8 @@ public: Mapgen(int mapgenid, MapgenParams *params, EmergeManager *emerge); virtual ~Mapgen(); + virtual MapgenType getType() const { return MAPGEN_INVALID; } + static u32 getBlockSeed(v3s16 p, s32 seed); static u32 getBlockSeed2(v3s16 p, s32 seed); s16 findGroundLevelFull(v2s16 p2d); @@ -198,6 +212,14 @@ public: // signify this and to cause Server::findSpawnPos() to try another (X, Z). virtual int getSpawnLevelAtPoint(v2s16 p) { return 0; } + // Mapgen management functions + static MapgenType getMapgenType(const std::string &mgname); + static const char *getMapgenName(MapgenType mgtype); + static Mapgen *createMapgen(MapgenType mgtype, int mgid, + MapgenParams *params, EmergeManager *emerge); + static MapgenSpecificParams *createMapgenParams(MapgenType mgtype); + static void getMapgenNames(std::vector *mgnames, bool include_hidden); + private: // isLiquidHorizontallyFlowable() is a helper function for updateLiquid() // that checks whether there are floodable nodes without liquid beneath @@ -267,11 +289,4 @@ protected: float cave_width; }; -struct MapgenFactory { - virtual Mapgen *createMapgen(int mgid, MapgenParams *params, - EmergeManager *emerge) = 0; - virtual MapgenSpecificParams *createMapgenParams() = 0; - virtual ~MapgenFactory() {} -}; - #endif diff --git a/src/mapgen_flat.h b/src/mapgen_flat.h index afe67b04e..39b6dc302 100644 --- a/src/mapgen_flat.h +++ b/src/mapgen_flat.h @@ -58,6 +58,8 @@ public: MapgenFlat(int mapgenid, MapgenParams *params, EmergeManager *emerge); ~MapgenFlat(); + virtual MapgenType getType() const { return MAPGEN_FLAT; } + virtual void makeChunk(BlockMakeData *data); int getSpawnLevelAtPoint(v2s16 p); s16 generateTerrain(); @@ -72,16 +74,4 @@ private: Noise *noise_terrain; }; -struct MapgenFactoryFlat : public MapgenFactory { - Mapgen *createMapgen(int mgid, MapgenParams *params, EmergeManager *emerge) - { - return new MapgenFlat(mgid, params, emerge); - }; - - MapgenSpecificParams *createMapgenParams() - { - return new MapgenFlatParams(); - }; -}; - #endif diff --git a/src/mapgen_fractal.h b/src/mapgen_fractal.h index 065142b84..cbd5567c4 100644 --- a/src/mapgen_fractal.h +++ b/src/mapgen_fractal.h @@ -62,6 +62,8 @@ public: MapgenFractal(int mapgenid, MapgenParams *params, EmergeManager *emerge); ~MapgenFractal(); + virtual MapgenType getType() const { return MAPGEN_FRACTAL; } + virtual void makeChunk(BlockMakeData *data); int getSpawnLevelAtPoint(v2s16 p); bool getFractalAtPoint(s16 x, s16 y, s16 z); @@ -83,16 +85,4 @@ private: Noise *noise_seabed; }; -struct MapgenFactoryFractal : public MapgenFactory { - Mapgen *createMapgen(int mgid, MapgenParams *params, EmergeManager *emerge) - { - return new MapgenFractal(mgid, params, emerge); - }; - - MapgenSpecificParams *createMapgenParams() - { - return new MapgenFractalParams(); - }; -}; - #endif diff --git a/src/mapgen_singlenode.h b/src/mapgen_singlenode.h index 2c6496c00..58672a0ed 100644 --- a/src/mapgen_singlenode.h +++ b/src/mapgen_singlenode.h @@ -23,10 +23,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "mapgen.h" struct MapgenSinglenodeParams : public MapgenSpecificParams { - MapgenSinglenodeParams() {} ~MapgenSinglenodeParams() {} - + void readParams(const Settings *settings) {} void writeParams(Settings *settings) const {} }; @@ -39,19 +38,11 @@ public: MapgenSinglenode(int mapgenid, MapgenParams *params, EmergeManager *emerge); ~MapgenSinglenode(); - + + virtual MapgenType getType() const { return MAPGEN_SINGLENODE; } + void makeChunk(BlockMakeData *data); int getSpawnLevelAtPoint(v2s16 p); }; -struct MapgenFactorySinglenode : public MapgenFactory { - Mapgen *createMapgen(int mgid, MapgenParams *params, EmergeManager *emerge) { - return new MapgenSinglenode(mgid, params, emerge); - }; - - MapgenSpecificParams *createMapgenParams() { - return new MapgenSinglenodeParams(); - }; -}; - #endif diff --git a/src/mapgen_v5.h b/src/mapgen_v5.h index 4e1772a64..5f6b10383 100644 --- a/src/mapgen_v5.h +++ b/src/mapgen_v5.h @@ -53,6 +53,8 @@ public: MapgenV5(int mapgenid, MapgenParams *params, EmergeManager *emerge); ~MapgenV5(); + virtual MapgenType getType() const { return MAPGEN_V5; } + virtual void makeChunk(BlockMakeData *data); int getSpawnLevelAtPoint(v2s16 p); int generateBaseTerrain(); @@ -63,17 +65,4 @@ private: Noise *noise_ground; }; - -struct MapgenFactoryV5 : public MapgenFactory { - Mapgen *createMapgen(int mgid, MapgenParams *params, EmergeManager *emerge) - { - return new MapgenV5(mgid, params, emerge); - }; - - MapgenSpecificParams *createMapgenParams() - { - return new MapgenV5Params(); - }; -}; - #endif diff --git a/src/mapgen_v6.h b/src/mapgen_v6.h index a55fc6d53..20b0bf92e 100644 --- a/src/mapgen_v6.h +++ b/src/mapgen_v6.h @@ -127,6 +127,8 @@ public: MapgenV6(int mapgenid, MapgenParams *params, EmergeManager *emerge); ~MapgenV6(); + virtual MapgenType getType() const { return MAPGEN_V6; } + void makeChunk(BlockMakeData *data); int getGroundLevelAtPoint(v2s16 p); int getSpawnLevelAtPoint(v2s16 p); @@ -162,18 +164,4 @@ public: virtual void generateCaves(int max_stone_y); }; - -struct MapgenFactoryV6 : public MapgenFactory { - Mapgen *createMapgen(int mgid, MapgenParams *params, EmergeManager *emerge) - { - return new MapgenV6(mgid, params, emerge); - }; - - MapgenSpecificParams *createMapgenParams() - { - return new MapgenV6Params(); - }; -}; - - #endif diff --git a/src/mapgen_v7.h b/src/mapgen_v7.h index 9ed89a511..c75f18a93 100644 --- a/src/mapgen_v7.h +++ b/src/mapgen_v7.h @@ -59,6 +59,8 @@ public: MapgenV7(int mapgenid, MapgenParams *params, EmergeManager *emerge); ~MapgenV7(); + virtual MapgenType getType() const { return MAPGEN_V7; } + virtual void makeChunk(BlockMakeData *data); int getSpawnLevelAtPoint(v2s16 p); @@ -80,16 +82,4 @@ private: Noise *noise_ridge; }; -struct MapgenFactoryV7 : public MapgenFactory { - Mapgen *createMapgen(int mgid, MapgenParams *params, EmergeManager *emerge) - { - return new MapgenV7(mgid, params, emerge); - }; - - MapgenSpecificParams *createMapgenParams() - { - return new MapgenV7Params(); - }; -}; - #endif diff --git a/src/mapgen_valleys.h b/src/mapgen_valleys.h index faaffa905..00f6aff07 100644 --- a/src/mapgen_valleys.h +++ b/src/mapgen_valleys.h @@ -91,6 +91,8 @@ public: MapgenValleys(int mapgenid, MapgenParams *params, EmergeManager *emerge); ~MapgenValleys(); + virtual MapgenType getType() const { return MAPGEN_VALLEYS; } + virtual void makeChunk(BlockMakeData *data); int getSpawnLevelAtPoint(v2s16 p); @@ -137,16 +139,4 @@ private: virtual void generateCaves(s16 max_stone_y, s16 large_cave_depth); }; -struct MapgenFactoryValleys : public MapgenFactory { - Mapgen *createMapgen(int mgid, MapgenParams *params, EmergeManager *emerge) - { - return new MapgenValleys(mgid, params, emerge); - }; - - MapgenSpecificParams *createMapgenParams() - { - return new MapgenValleysParams(); - }; -}; - #endif diff --git a/src/script/lua_api/l_mainmenu.cpp b/src/script/lua_api/l_mainmenu.cpp index 7b29db159..8b078eafd 100644 --- a/src/script/lua_api/l_mainmenu.cpp +++ b/src/script/lua_api/l_mainmenu.cpp @@ -31,7 +31,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "filesys.h" #include "convert_json.h" #include "serverlist.h" -#include "emerge.h" +#include "mapgen.h" #include "sound.h" #include "settings.h" #include "log.h" @@ -707,7 +707,7 @@ int ModApiMainMenu::l_set_topleft_text(lua_State *L) int ModApiMainMenu::l_get_mapgen_names(lua_State *L) { std::vector names; - EmergeManager::getMapgenNames(&names, lua_toboolean(L, 1)); + Mapgen::getMapgenNames(&names, lua_toboolean(L, 1)); lua_newtable(L); for (size_t i = 0; i != names.size(); i++) { -- cgit v1.2.3 From 3c63c3044d5e4ca36c2649c530f31622581d90fd Mon Sep 17 00:00:00 2001 From: kwolekr Date: Fri, 24 Jun 2016 18:15:56 -0400 Subject: Add MapSettingsManager and new mapgen setting script API functions This commit refactors the majority of the Mapgen settings system. - MapgenParams is now owned by MapSettingsManager, itself a part of ServerMap, instead of the EmergeManager. - New Script API functions added: core.get_mapgen_setting core.get_mapgen_setting_noiseparams, core.set_mapgen_setting, and core.set_mapgen_setting_noiseparams. - minetest.get/set_mapgen_params are deprecated by the above new functions. - It is now possible to view and modify any arbitrary mapgen setting from a mod, rather than the base MapgenParams structure. - MapgenSpecificParams has been removed. --- build/android/jni/Android.mk | 2 + doc/lua_api.txt | 19 +++ src/CMakeLists.txt | 1 + src/emerge.cpp | 40 ++--- src/emerge.h | 15 +- src/map.cpp | 100 +++-------- src/map.h | 10 +- src/map_settings_manager.cpp | 194 ++++++++++++++++++++++ src/map_settings_manager.h | 79 +++++++++ src/mapgen.cpp | 68 ++++---- src/mapgen.h | 20 +-- src/mapgen_flat.cpp | 28 ++-- src/mapgen_flat.h | 4 +- src/mapgen_fractal.cpp | 34 ++-- src/mapgen_fractal.h | 4 +- src/mapgen_singlenode.h | 2 +- src/mapgen_v5.cpp | 20 +-- src/mapgen_v5.h | 4 +- src/mapgen_v6.cpp | 33 ++-- src/mapgen_v6.h | 4 +- src/mapgen_v7.cpp | 30 ++-- src/mapgen_v7.h | 4 +- src/mapgen_valleys.cpp | 41 +++-- src/mapgen_valleys.h | 4 +- src/script/lua_api/l_mapgen.cpp | 154 +++++++++++++---- src/script/lua_api/l_mapgen.h | 12 ++ src/script/lua_api/l_vmanip.cpp | 2 +- src/server.cpp | 15 +- src/subgame.cpp | 4 +- src/unittest/CMakeLists.txt | 1 + src/unittest/test_map_settings_manager.cpp | 255 +++++++++++++++++++++++++++++ 31 files changed, 889 insertions(+), 314 deletions(-) create mode 100644 src/map_settings_manager.cpp create mode 100644 src/map_settings_manager.h create mode 100644 src/unittest/test_map_settings_manager.cpp (limited to 'src') diff --git a/build/android/jni/Android.mk b/build/android/jni/Android.mk index afd8c76b2..a42ab76b8 100644 --- a/build/android/jni/Android.mk +++ b/build/android/jni/Android.mk @@ -169,6 +169,7 @@ LOCAL_SRC_FILES := \ jni/src/log.cpp \ jni/src/main.cpp \ jni/src/map.cpp \ + jni/src/map_settings_manager.cpp \ jni/src/mapblock.cpp \ jni/src/mapblock_mesh.cpp \ jni/src/mapgen.cpp \ @@ -238,6 +239,7 @@ LOCAL_SRC_FILES := \ jni/src/unittest/test_connection.cpp \ jni/src/unittest/test_filepath.cpp \ jni/src/unittest/test_inventory.cpp \ + jni/src/unittest/test_map_settings_manager.cpp \ jni/src/unittest/test_mapnode.cpp \ jni/src/unittest/test_nodedef.cpp \ jni/src/unittest/test_noderesolver.cpp \ diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 6d359379e..297566068 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -2089,7 +2089,9 @@ and `minetest.auth_reload` call the authetification handler. given biome_name string. * `minetest.get_mapgen_params()` Returns mapgen parameters, a table containing `mgname`, `seed`, `chunksize`, `water_level`, and `flags`. + * Deprecated: use minetest.get_mapgen_setting(name) instead * `minetest.set_mapgen_params(MapgenParams)` + * Deprecated: use minetest.set_mapgen_setting(name, value, override) instead * Set map generation parameters * Function cannot be called after the registration period; only initialization and `on_mapgen_init` @@ -2099,6 +2101,23 @@ and `minetest.auth_reload` call the authetification handler. * `flags` contains a comma-delimited string of flags to set, or if the prefix `"no"` is attached, clears instead. * `flags` is in the same format and has the same options as `mg_flags` in `minetest.conf` +* `minetest.get_mapgen_setting(name)` + * Gets the *active* mapgen setting (or nil if none exists) in string format with the following + order of precedence: + 1) Settings loaded from map_meta.txt or overrides set during mod execution + 2) Settings set by mods without a metafile override + 3) Settings explicitly set in the user config file, minetest.conf + 4) Settings set as the user config default +* `minetest.get_mapgen_setting_noiseparams(name)` + * Same as above, but returns the value as a NoiseParams table if the setting `name` exists + and is a valid NoiseParams +* `minetest.set_mapgen_setting(name, value, [override_meta=false])` + * Sets a mapgen param to `value`, and will take effect if the corresponding mapgen setting + is not already present in map_meta.txt. If the optional boolean override_meta is set to true, + this setting will become the active setting regardless of the map metafile contents. + * Note: to set the seed, use "seed", not "fixed_map_seed" +* `minetest.set_mapgen_setting_noiseparams(name, value, [override_meta=false])` + * Same as above, except value is a NoiseParams table * `minetest.set_noiseparams(name, noiseparams, set_default)` * Sets the noiseparams setting of `name` to the noiseparams table specified in `noiseparams`. * `set_default` is an optional boolean (default: `true`) that specifies whether the setting diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index bffc1f7eb..bcd7a5984 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -414,6 +414,7 @@ set(common_SRCS light.cpp log.cpp map.cpp + map_settings_manager.cpp mapblock.cpp mapgen.cpp mapgen_flat.cpp diff --git a/src/emerge.cpp b/src/emerge.cpp index 48de6cb1a..daf42f5e2 100644 --- a/src/emerge.cpp +++ b/src/emerge.cpp @@ -156,37 +156,19 @@ EmergeManager::~EmergeManager() } -void EmergeManager::loadMapgenParams() -{ - params.load(*g_settings); -} - - -void EmergeManager::initMapgens() +bool EmergeManager::initMapgens(MapgenParams *params) { if (m_mapgens.size()) - return; - - MapgenType mgtype = Mapgen::getMapgenType(params.mg_name); - if (mgtype == MAPGEN_INVALID) { - const char *default_mapgen_name = Mapgen::getMapgenName(MAPGEN_DEFAULT); - errorstream << "EmergeManager: mapgen " << params.mg_name << - " not registered; falling back to " << - default_mapgen_name << std::endl; - - params.mg_name = default_mapgen_name; - mgtype = MAPGEN_DEFAULT; - } + return false; - if (!params.sparams) { - params.sparams = Mapgen::createMapgenParams(mgtype); - params.sparams->readParams(g_settings); - } + this->mgparams = params; for (u32 i = 0; i != m_threads.size(); i++) { - Mapgen *mg = Mapgen::createMapgen(mgtype, i, ¶ms, this); + Mapgen *mg = Mapgen::createMapgen(params->mgtype, i, params, this); m_mapgens.push_back(mg); } + + return true; } @@ -288,12 +270,14 @@ bool EmergeManager::enqueueBlockEmergeEx( // Mapgen-related helper functions // + +// TODO(hmmmm): Move this to ServerMap v3s16 EmergeManager::getContainingChunk(v3s16 blockpos) { - return getContainingChunk(blockpos, params.chunksize); + return getContainingChunk(blockpos, mgparams->chunksize); } - +// TODO(hmmmm): Move this to ServerMap v3s16 EmergeManager::getContainingChunk(v3s16 blockpos, s16 chunksize) { s16 coff = -chunksize / 2; @@ -327,7 +311,7 @@ int EmergeManager::getGroundLevelAtPoint(v2s16 p) return m_mapgens[0]->getGroundLevelAtPoint(p); } - +// TODO(hmmmm): Move this to ServerMap bool EmergeManager::isBlockUnderground(v3s16 blockpos) { #if 0 @@ -338,7 +322,7 @@ bool EmergeManager::isBlockUnderground(v3s16 blockpos) #endif // Use a simple heuristic; the above method is wildly inaccurate anyway. - return blockpos.Y * (MAP_BLOCKSIZE + 1) <= params.water_level; + return blockpos.Y * (MAP_BLOCKSIZE + 1) <= mgparams->water_level; } bool EmergeManager::pushBlockEmergeData( diff --git a/src/emerge.h b/src/emerge.h index 303a35529..cf0677145 100644 --- a/src/emerge.h +++ b/src/emerge.h @@ -97,8 +97,16 @@ public: u32 gen_notify_on; std::set gen_notify_on_deco_ids; - // Map generation parameters - MapgenParams params; + // Parameters passed to mapgens owned by ServerMap + // TODO(hmmmm): Remove this after mapgen helper methods using them + // are moved to ServerMap + MapgenParams *mgparams; + + // Hackish workaround: + // For now, EmergeManager must hold onto a ptr to the Map's setting manager + // since the Map can only be accessed through the Environment, and the + // Environment is not created until after script initialization. + MapSettingsManager *map_settings_mgr; // Managers of various map generation-related components BiomeManager *biomemgr; @@ -110,8 +118,7 @@ public: EmergeManager(IGameDef *gamedef); ~EmergeManager(); - void loadMapgenParams(); - void initMapgens(); + bool initMapgens(MapgenParams *mgparams); void startThreads(); void stopThreads(); diff --git a/src/map.cpp b/src/map.cpp index a1f2086ce..38f0fa37c 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -2130,11 +2130,15 @@ void Map::removeNodeTimer(v3s16 p) */ ServerMap::ServerMap(std::string savedir, IGameDef *gamedef, EmergeManager *emerge): Map(dout_server, gamedef), + settings_mgr(g_settings, savedir + DIR_DELIM + "map_meta.txt"), m_emerge(emerge), m_map_metadata_changed(true) { verbosestream<map_settings_mgr = &settings_mgr; + /* Try to load map; if not found, create a new one. */ @@ -2170,26 +2174,15 @@ ServerMap::ServerMap(std::string savedir, IGameDef *gamedef, EmergeManager *emer } else { - try{ - // Load map metadata (seed, chunksize) - loadMapMeta(); - } - catch(SettingNotFoundException &e){ - infostream<<"ServerMap: Some metadata not found." - <<" Using default settings."<params.seed; + return getMapgenParams()->seed; } s16 ServerMap::getWaterLevel() { - return m_emerge->params.water_level; + return getMapgenParams()->water_level; } bool ServerMap::initBlockMake(v3s16 blockpos, BlockMakeData *data) { - s16 csize = m_emerge->params.chunksize; + s16 csize = getMapgenParams()->chunksize; v3s16 bpmin = EmergeManager::getContainingChunk(blockpos, csize); v3s16 bpmax = bpmin + v3s16(1, 1, 1) * (csize - 1); @@ -2287,7 +2287,7 @@ bool ServerMap::initBlockMake(v3s16 blockpos, BlockMakeData *data) blockpos_over_limit(full_bpmax)) return false; - data->seed = m_emerge->params.seed; + data->seed = getSeed(); data->blockpos_min = bpmin; data->blockpos_max = bpmax; data->blockpos_requested = blockpos; @@ -2905,8 +2905,9 @@ void ServerMap::save(ModifiedState save_level) infostream<<"ServerMap: Saving whole map, this can take time." < &dst) } } -void ServerMap::saveMapMeta() -{ - DSTACK(FUNCTION_NAME); - - createDirs(m_savedir); - - std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt"; - std::ostringstream oss(std::ios_base::binary); - Settings conf; - - m_emerge->params.save(conf); - conf.writeLines(oss); - - oss << "[end_of_params]\n"; - - if(!fs::safeWriteToFile(fullpath, oss.str())) { - errorstream << "ServerMap::saveMapMeta(): " - << "could not write " << fullpath << std::endl; - throw FileNotGoodException("Cannot save chunk metadata"); - } - - m_map_metadata_changed = false; -} - -void ServerMap::loadMapMeta() -{ - DSTACK(FUNCTION_NAME); - - Settings conf; - std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt"; - - std::ifstream is(fullpath.c_str(), std::ios_base::binary); - if (!is.good()) { - errorstream << "ServerMap::loadMapMeta(): " - "could not open " << fullpath << std::endl; - throw FileNotGoodException("Cannot open map metadata"); - } - - if (!conf.parseConfigLines(is, "[end_of_params]")) { - throw SerializationError("ServerMap::loadMapMeta(): " - "[end_of_params] not found!"); - } - - m_emerge->params.load(conf); - - verbosestream << "ServerMap::loadMapMeta(): seed=" - << m_emerge->params.seed << std::endl; -} - void ServerMap::saveSectorMeta(ServerMapSector *sector) { DSTACK(FUNCTION_NAME); diff --git a/src/map.h b/src/map.h index 23da56471..13775fde1 100644 --- a/src/map.h +++ b/src/map.h @@ -33,6 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "modifiedstate.h" #include "util/container.h" #include "nodetimer.h" +#include "map_settings_manager.h" class Settings; class Database; @@ -46,8 +47,6 @@ class IRollbackManager; class EmergeManager; class ServerEnvironment; struct BlockMakeData; -struct MapgenParams; - /* MapEditEvent @@ -463,9 +462,8 @@ public: void save(ModifiedState save_level); void listAllLoadableBlocks(std::vector &dst); void listAllLoadedBlocks(std::vector &dst); - // Saves map seed and possibly other stuff - void saveMapMeta(); - void loadMapMeta(); + + MapgenParams *getMapgenParams(); /*void saveChunkMeta(); void loadChunkMeta();*/ @@ -506,6 +504,8 @@ public: u64 getSeed(); s16 getWaterLevel(); + MapSettingsManager settings_mgr; + private: // Emerge manager EmergeManager *m_emerge; diff --git a/src/map_settings_manager.cpp b/src/map_settings_manager.cpp new file mode 100644 index 000000000..53d17125c --- /dev/null +++ b/src/map_settings_manager.cpp @@ -0,0 +1,194 @@ +/* +Minetest +Copyright (C) 2010-2013 kwolekr, Ryan Kwolek + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "debug.h" +#include "filesys.h" +#include "log.h" +#include "mapgen.h" +#include "settings.h" + +#include "map_settings_manager.h" + +MapSettingsManager::MapSettingsManager( + Settings *user_settings, const std::string &map_meta_path) +{ + m_map_meta_path = map_meta_path; + m_user_settings = user_settings; + m_map_settings = new Settings; + mapgen_params = NULL; + + assert(m_user_settings != NULL); +} + + +MapSettingsManager::~MapSettingsManager() +{ + delete m_map_settings; + delete mapgen_params; +} + + +bool MapSettingsManager::getMapSetting( + const std::string &name, std::string *value_out) +{ + if (m_map_settings->getNoEx(name, *value_out)) + return true; + + // Compatibility kludge + if (m_user_settings == g_settings && name == "seed") + return m_user_settings->getNoEx("fixed_map_seed", *value_out); + + return m_user_settings->getNoEx(name, *value_out); +} + + +bool MapSettingsManager::getMapSettingNoiseParams( + const std::string &name, NoiseParams *value_out) +{ + return m_map_settings->getNoiseParams(name, *value_out) || + m_user_settings->getNoiseParams(name, *value_out); +} + + +bool MapSettingsManager::setMapSetting( + const std::string &name, const std::string &value, bool override_meta) +{ + if (mapgen_params) + return false; + + if (override_meta) + m_map_settings->set(name, value); + else + m_map_settings->setDefault(name, value); + + return true; +} + + +bool MapSettingsManager::setMapSettingNoiseParams( + const std::string &name, const NoiseParams *value, bool override_meta) +{ + if (mapgen_params) + return false; + + m_map_settings->setNoiseParams(name, *value, !override_meta); + return true; +} + + +bool MapSettingsManager::loadMapMeta() +{ + std::ifstream is(m_map_meta_path.c_str(), std::ios_base::binary); + + if (!is.good()) { + errorstream << "loadMapMeta: could not open " + << m_map_meta_path << std::endl; + return false; + } + + if (!m_map_settings->parseConfigLines(is, "[end_of_params]")) { + errorstream << "loadMapMeta: [end_of_params] not found!" << std::endl; + return false; + } + + return true; +} + + +bool MapSettingsManager::saveMapMeta() +{ + // If mapgen params haven't been created yet; abort + if (!mapgen_params) + return false; + + if (!fs::CreateAllDirs(fs::RemoveLastPathComponent(m_map_meta_path))) { + errorstream << "saveMapMeta: could not create dirs to " + << m_map_meta_path; + return false; + } + + std::ostringstream oss(std::ios_base::binary); + Settings conf; + + mapgen_params->MapgenParams::writeParams(&conf); + mapgen_params->writeParams(&conf); + conf.writeLines(oss); + + // NOTE: If there are ever types of map settings other than + // those relating to map generation, save them here + + oss << "[end_of_params]\n"; + + if (!fs::safeWriteToFile(m_map_meta_path, oss.str())) { + errorstream << "saveMapMeta: could not write " + << m_map_meta_path << std::endl; + return false; + } + + return true; +} + + +MapgenParams *MapSettingsManager::makeMapgenParams() +{ + if (mapgen_params) + return mapgen_params; + + assert(m_user_settings != NULL); + assert(m_map_settings != NULL); + + // At this point, we have (in order of precedence): + // 1). m_mapgen_settings->m_settings containing map_meta.txt settings or + // explicit overrides from scripts + // 2). m_mapgen_settings->m_defaults containing script-set mgparams without + // overrides + // 3). g_settings->m_settings containing all user-specified config file + // settings + // 4). g_settings->m_defaults containing any low-priority settings from + // scripts, e.g. mods using Lua as an enhanced config file) + + // Now, get the mapgen type so we can create the appropriate MapgenParams + std::string mg_name; + MapgenType mgtype = getMapSetting("mg_name", &mg_name) ? + Mapgen::getMapgenType(mg_name) : MAPGEN_DEFAULT; + if (mgtype == MAPGEN_INVALID) { + errorstream << "EmergeManager: mapgen '" << mg_name << + "' not valid; falling back to " << + Mapgen::getMapgenName(MAPGEN_DEFAULT) << std::endl; + mgtype = MAPGEN_DEFAULT; + } + + // Create our MapgenParams + MapgenParams *params = Mapgen::createMapgenParams(mgtype); + if (params == NULL) + return NULL; + + params->mgtype = mgtype; + + // Load the rest of the mapgen params from our active settings + params->MapgenParams::readParams(m_user_settings); + params->MapgenParams::readParams(m_map_settings); + params->readParams(m_user_settings); + params->readParams(m_map_settings); + + // Hold onto our params + mapgen_params = params; + + return params; +} diff --git a/src/map_settings_manager.h b/src/map_settings_manager.h new file mode 100644 index 000000000..9f766f1f0 --- /dev/null +++ b/src/map_settings_manager.h @@ -0,0 +1,79 @@ +/* +Minetest +Copyright (C) 2010-2013 kwolekr, Ryan Kwolek + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef MAP_SETTINGS_MANAGER_HEADER +#define MAP_SETTINGS_MANAGER_HEADER + +#include + +class Settings; +struct NoiseParams; +struct MapgenParams; + +/* + MapSettingsManager is a centralized object for management (creating, + loading, storing, saving, etc.) of config settings related to the Map. + + It has two phases: the initial r/w "gather and modify settings" state, and + the final r/o "read and save settings" state. + + The typical use case is, in order, as follows: + - Create a MapSettingsManager object + - Try to load map metadata into it from the metadata file + - Manually view and modify the current configuration as desired through a + Settings-like interface + - When all modifications are finished, create a 'Parameters' object + containing the finalized, active parameters. This could be passed along + to whichever Map-related objects that may require it. + - Save these active settings to the metadata file when requested +*/ +class MapSettingsManager { +public: + // Finalized map generation parameters + MapgenParams *mapgen_params; + + MapSettingsManager(Settings *user_settings, + const std::string &map_meta_path); + ~MapSettingsManager(); + + bool getMapSetting(const std::string &name, std::string *value_out); + + bool getMapSettingNoiseParams( + const std::string &name, NoiseParams *value_out); + + // Note: Map config becomes read-only after makeMapgenParams() gets called + // (i.e. mapgen_params is non-NULL). Attempts to set map config after + // params have been finalized will result in failure. + bool setMapSetting(const std::string &name, + const std::string &value, bool override_meta = false); + + bool setMapSettingNoiseParams(const std::string &name, + const NoiseParams *value, bool override_meta = false); + + bool loadMapMeta(); + bool saveMapMeta(); + MapgenParams *makeMapgenParams(); + +private: + std::string m_map_meta_path; + Settings *m_map_settings; + Settings *m_user_settings; +}; + +#endif diff --git a/src/mapgen.cpp b/src/mapgen.cpp index e45233b33..b6fda91ac 100644 --- a/src/mapgen.cpp +++ b/src/mapgen.cpp @@ -176,26 +176,26 @@ Mapgen *Mapgen::createMapgen(MapgenType mgtype, int mgid, { switch (mgtype) { case MAPGEN_FLAT: - return new MapgenFlat(mgid, params, emerge); + return new MapgenFlat(mgid, (MapgenFlatParams *)params, emerge); case MAPGEN_FRACTAL: - return new MapgenFractal(mgid, params, emerge); + return new MapgenFractal(mgid, (MapgenFractalParams *)params, emerge); case MAPGEN_SINGLENODE: - return new MapgenSinglenode(mgid, params, emerge); + return new MapgenSinglenode(mgid, (MapgenSinglenodeParams *)params, emerge); case MAPGEN_V5: - return new MapgenV5(mgid, params, emerge); + return new MapgenV5(mgid, (MapgenV5Params *)params, emerge); case MAPGEN_V6: - return new MapgenV6(mgid, params, emerge); + return new MapgenV6(mgid, (MapgenV6Params *)params, emerge); case MAPGEN_V7: - return new MapgenV7(mgid, params, emerge); + return new MapgenV7(mgid, (MapgenV7Params *)params, emerge); case MAPGEN_VALLEYS: - return new MapgenValleys(mgid, params, emerge); + return new MapgenValleys(mgid, (MapgenValleysParams *)params, emerge); default: return NULL; } } -MapgenSpecificParams *Mapgen::createMapgenParams(MapgenType mgtype) +MapgenParams *Mapgen::createMapgenParams(MapgenType mgtype) { switch (mgtype) { case MAPGEN_FLAT: @@ -970,52 +970,46 @@ void GenerateNotifier::getEvents( MapgenParams::~MapgenParams() { delete bparams; - delete sparams; } -void MapgenParams::load(const Settings &settings) +void MapgenParams::readParams(const Settings *settings) { std::string seed_str; - const char *seed_name = (&settings == g_settings) ? "fixed_map_seed" : "seed"; + const char *seed_name = (settings == g_settings) ? "fixed_map_seed" : "seed"; - if (settings.getNoEx(seed_name, seed_str) && !seed_str.empty()) - seed = read_seed(seed_str.c_str()); - else - myrand_bytes(&seed, sizeof(seed)); + if (settings->getNoEx(seed_name, seed_str)) { + if (!seed_str.empty()) + seed = read_seed(seed_str.c_str()); + else + myrand_bytes(&seed, sizeof(seed)); + } + + std::string mg_name; + if (settings->getNoEx("mg_name", mg_name)) + this->mgtype = Mapgen::getMapgenType(mg_name); - settings.getNoEx("mg_name", mg_name); - settings.getS16NoEx("water_level", water_level); - settings.getS16NoEx("chunksize", chunksize); - settings.getFlagStrNoEx("mg_flags", flags, flagdesc_mapgen); + settings->getS16NoEx("water_level", water_level); + settings->getS16NoEx("chunksize", chunksize); + settings->getFlagStrNoEx("mg_flags", flags, flagdesc_mapgen); delete bparams; bparams = BiomeManager::createBiomeParams(BIOMEGEN_ORIGINAL); if (bparams) { - bparams->readParams(&settings); + bparams->readParams(settings); bparams->seed = seed; } - - delete sparams; - MapgenType mgtype = Mapgen::getMapgenType(mg_name); - if (mgtype != MAPGEN_INVALID) { - sparams = Mapgen::createMapgenParams(mgtype); - sparams->readParams(&settings); - } } -void MapgenParams::save(Settings &settings) const +void MapgenParams::writeParams(Settings *settings) const { - settings.set("mg_name", mg_name); - settings.setU64("seed", seed); - settings.setS16("water_level", water_level); - settings.setS16("chunksize", chunksize); - settings.setFlagStr("mg_flags", flags, flagdesc_mapgen, U32_MAX); + settings->set("mg_name", Mapgen::getMapgenName(mgtype)); + settings->setU64("seed", seed); + settings->setS16("water_level", water_level); + settings->setS16("chunksize", chunksize); + settings->setFlagStr("mg_flags", flags, flagdesc_mapgen, U32_MAX); if (bparams) - bparams->writeParams(&settings); - - if (sparams) - sparams->writeParams(&settings); + bparams->writeParams(settings); } diff --git a/src/mapgen.h b/src/mapgen.h index 618a2f6b8..5fcf2a365 100644 --- a/src/mapgen.h +++ b/src/mapgen.h @@ -119,37 +119,29 @@ enum MapgenType { MAPGEN_INVALID, }; -struct MapgenSpecificParams { - virtual void readParams(const Settings *settings) = 0; - virtual void writeParams(Settings *settings) const = 0; - virtual ~MapgenSpecificParams() {} -}; - struct MapgenParams { - std::string mg_name; + MapgenType mgtype; s16 chunksize; u64 seed; s16 water_level; u32 flags; BiomeParams *bparams; - MapgenSpecificParams *sparams; MapgenParams() : - mg_name(MAPGEN_DEFAULT_NAME), + mgtype(MAPGEN_DEFAULT), chunksize(5), seed(0), water_level(1), flags(MG_CAVES | MG_LIGHT | MG_DECORATIONS), - bparams(NULL), - sparams(NULL) + bparams(NULL) { } virtual ~MapgenParams(); - void load(const Settings &settings); - void save(Settings &settings) const; + virtual void readParams(const Settings *settings); + virtual void writeParams(Settings *settings) const; }; @@ -217,7 +209,7 @@ public: static const char *getMapgenName(MapgenType mgtype); static Mapgen *createMapgen(MapgenType mgtype, int mgid, MapgenParams *params, EmergeManager *emerge); - static MapgenSpecificParams *createMapgenParams(MapgenType mgtype); + static MapgenParams *createMapgenParams(MapgenType mgtype); static void getMapgenNames(std::vector *mgnames, bool include_hidden); private: diff --git a/src/mapgen_flat.cpp b/src/mapgen_flat.cpp index 956af999a..2c1715e61 100644 --- a/src/mapgen_flat.cpp +++ b/src/mapgen_flat.cpp @@ -49,26 +49,24 @@ FlagDesc flagdesc_mapgen_flat[] = { /////////////////////////////////////////////////////////////////////////////////////// -MapgenFlat::MapgenFlat(int mapgenid, MapgenParams *params, EmergeManager *emerge) +MapgenFlat::MapgenFlat(int mapgenid, MapgenFlatParams *params, EmergeManager *emerge) : MapgenBasic(mapgenid, params, emerge) { - MapgenFlatParams *sp = (MapgenFlatParams *)params->sparams; - - this->spflags = sp->spflags; - this->ground_level = sp->ground_level; - this->large_cave_depth = sp->large_cave_depth; - this->cave_width = sp->cave_width; - this->lake_threshold = sp->lake_threshold; - this->lake_steepness = sp->lake_steepness; - this->hill_threshold = sp->hill_threshold; - this->hill_steepness = sp->hill_steepness; + this->spflags = params->spflags; + this->ground_level = params->ground_level; + this->large_cave_depth = params->large_cave_depth; + this->cave_width = params->cave_width; + this->lake_threshold = params->lake_threshold; + this->lake_steepness = params->lake_steepness; + this->hill_threshold = params->hill_threshold; + this->hill_steepness = params->hill_steepness; //// 2D noise - noise_terrain = new Noise(&sp->np_terrain, seed, csize.X, csize.Z); - noise_filler_depth = new Noise(&sp->np_filler_depth, seed, csize.X, csize.Z); + noise_terrain = new Noise(¶ms->np_terrain, seed, csize.X, csize.Z); + noise_filler_depth = new Noise(¶ms->np_filler_depth, seed, csize.X, csize.Z); - MapgenBasic::np_cave1 = sp->np_cave1; - MapgenBasic::np_cave2 = sp->np_cave2; + MapgenBasic::np_cave1 = params->np_cave1; + MapgenBasic::np_cave2 = params->np_cave2; } diff --git a/src/mapgen_flat.h b/src/mapgen_flat.h index 39b6dc302..8b3de2bcf 100644 --- a/src/mapgen_flat.h +++ b/src/mapgen_flat.h @@ -32,7 +32,7 @@ class BiomeManager; extern FlagDesc flagdesc_mapgen_flat[]; -struct MapgenFlatParams : public MapgenSpecificParams { +struct MapgenFlatParams : public MapgenParams { u32 spflags; s16 ground_level; s16 large_cave_depth; @@ -55,7 +55,7 @@ struct MapgenFlatParams : public MapgenSpecificParams { class MapgenFlat : public MapgenBasic { public: - MapgenFlat(int mapgenid, MapgenParams *params, EmergeManager *emerge); + MapgenFlat(int mapgenid, MapgenFlatParams *params, EmergeManager *emerge); ~MapgenFlat(); virtual MapgenType getType() const { return MAPGEN_FLAT; } diff --git a/src/mapgen_fractal.cpp b/src/mapgen_fractal.cpp index 9e4c210dc..0951a0afa 100644 --- a/src/mapgen_fractal.cpp +++ b/src/mapgen_fractal.cpp @@ -47,29 +47,27 @@ FlagDesc flagdesc_mapgen_fractal[] = { /////////////////////////////////////////////////////////////////////////////////////// -MapgenFractal::MapgenFractal(int mapgenid, MapgenParams *params, EmergeManager *emerge) +MapgenFractal::MapgenFractal(int mapgenid, MapgenFractalParams *params, EmergeManager *emerge) : MapgenBasic(mapgenid, params, emerge) { - MapgenFractalParams *sp = (MapgenFractalParams *)params->sparams; - - this->spflags = sp->spflags; - this->cave_width = sp->cave_width; - this->fractal = sp->fractal; - this->iterations = sp->iterations; - this->scale = sp->scale; - this->offset = sp->offset; - this->slice_w = sp->slice_w; - this->julia_x = sp->julia_x; - this->julia_y = sp->julia_y; - this->julia_z = sp->julia_z; - this->julia_w = sp->julia_w; + this->spflags = params->spflags; + this->cave_width = params->cave_width; + this->fractal = params->fractal; + this->iterations = params->iterations; + this->scale = params->scale; + this->offset = params->offset; + this->slice_w = params->slice_w; + this->julia_x = params->julia_x; + this->julia_y = params->julia_y; + this->julia_z = params->julia_z; + this->julia_w = params->julia_w; //// 2D terrain noise - noise_seabed = new Noise(&sp->np_seabed, seed, csize.X, csize.Z); - noise_filler_depth = new Noise(&sp->np_filler_depth, seed, csize.X, csize.Z); + noise_seabed = new Noise(¶ms->np_seabed, seed, csize.X, csize.Z); + noise_filler_depth = new Noise(¶ms->np_filler_depth, seed, csize.X, csize.Z); - MapgenBasic::np_cave1 = sp->np_cave1; - MapgenBasic::np_cave2 = sp->np_cave2; + MapgenBasic::np_cave1 = params->np_cave1; + MapgenBasic::np_cave2 = params->np_cave2; this->formula = fractal / 2 + fractal % 2; this->julia = fractal % 2 == 0; diff --git a/src/mapgen_fractal.h b/src/mapgen_fractal.h index cbd5567c4..3331848bc 100644 --- a/src/mapgen_fractal.h +++ b/src/mapgen_fractal.h @@ -33,7 +33,7 @@ class BiomeManager; extern FlagDesc flagdesc_mapgen_fractal[]; -struct MapgenFractalParams : public MapgenSpecificParams { +struct MapgenFractalParams : public MapgenParams { u32 spflags; float cave_width; u16 fractal; @@ -59,7 +59,7 @@ struct MapgenFractalParams : public MapgenSpecificParams { class MapgenFractal : public MapgenBasic { public: - MapgenFractal(int mapgenid, MapgenParams *params, EmergeManager *emerge); + MapgenFractal(int mapgenid, MapgenFractalParams *params, EmergeManager *emerge); ~MapgenFractal(); virtual MapgenType getType() const { return MAPGEN_FRACTAL; } diff --git a/src/mapgen_singlenode.h b/src/mapgen_singlenode.h index 58672a0ed..07520134d 100644 --- a/src/mapgen_singlenode.h +++ b/src/mapgen_singlenode.h @@ -22,7 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "mapgen.h" -struct MapgenSinglenodeParams : public MapgenSpecificParams { +struct MapgenSinglenodeParams : public MapgenParams { MapgenSinglenodeParams() {} ~MapgenSinglenodeParams() {} diff --git a/src/mapgen_v5.cpp b/src/mapgen_v5.cpp index 74d5e1ee3..9f189e253 100644 --- a/src/mapgen_v5.cpp +++ b/src/mapgen_v5.cpp @@ -45,25 +45,23 @@ FlagDesc flagdesc_mapgen_v5[] = { }; -MapgenV5::MapgenV5(int mapgenid, MapgenParams *params, EmergeManager *emerge) +MapgenV5::MapgenV5(int mapgenid, MapgenV5Params *params, EmergeManager *emerge) : MapgenBasic(mapgenid, params, emerge) { - MapgenV5Params *sp = (MapgenV5Params *)params->sparams; - - this->spflags = sp->spflags; - this->cave_width = sp->cave_width; + this->spflags = params->spflags; + this->cave_width = params->cave_width; // Terrain noise - noise_filler_depth = new Noise(&sp->np_filler_depth, seed, csize.X, csize.Z); - noise_factor = new Noise(&sp->np_factor, seed, csize.X, csize.Z); - noise_height = new Noise(&sp->np_height, seed, csize.X, csize.Z); + noise_filler_depth = new Noise(¶ms->np_filler_depth, seed, csize.X, csize.Z); + noise_factor = new Noise(¶ms->np_factor, seed, csize.X, csize.Z); + noise_height = new Noise(¶ms->np_height, seed, csize.X, csize.Z); // 3D terrain noise // 1-up 1-down overgeneration - noise_ground = new Noise(&sp->np_ground, seed, csize.X, csize.Y + 2, csize.Z); + noise_ground = new Noise(¶ms->np_ground, seed, csize.X, csize.Y + 2, csize.Z); - MapgenBasic::np_cave1 = sp->np_cave1; - MapgenBasic::np_cave2 = sp->np_cave2; + MapgenBasic::np_cave1 = params->np_cave1; + MapgenBasic::np_cave2 = params->np_cave2; } diff --git a/src/mapgen_v5.h b/src/mapgen_v5.h index 5f6b10383..ddb090a9c 100644 --- a/src/mapgen_v5.h +++ b/src/mapgen_v5.h @@ -30,7 +30,7 @@ class BiomeManager; extern FlagDesc flagdesc_mapgen_v5[]; -struct MapgenV5Params : public MapgenSpecificParams { +struct MapgenV5Params : public MapgenParams { u32 spflags; float cave_width; NoiseParams np_filler_depth; @@ -50,7 +50,7 @@ struct MapgenV5Params : public MapgenSpecificParams { class MapgenV5 : public MapgenBasic { public: - MapgenV5(int mapgenid, MapgenParams *params, EmergeManager *emerge); + MapgenV5(int mapgenid, MapgenV5Params *params, EmergeManager *emerge); ~MapgenV5(); virtual MapgenType getType() const { return MAPGEN_V5; } diff --git a/src/mapgen_v6.cpp b/src/mapgen_v6.cpp index caa64827e..e4444963f 100644 --- a/src/mapgen_v6.cpp +++ b/src/mapgen_v6.cpp @@ -53,7 +53,7 @@ FlagDesc flagdesc_mapgen_v6[] = { ///////////////////////////////////////////////////////////////////////////// -MapgenV6::MapgenV6(int mapgenid, MapgenParams *params, EmergeManager *emerge) +MapgenV6::MapgenV6(int mapgenid, MapgenV6Params *params, EmergeManager *emerge) : Mapgen(mapgenid, params, emerge) { this->m_emerge = emerge; @@ -61,26 +61,25 @@ MapgenV6::MapgenV6(int mapgenid, MapgenParams *params, EmergeManager *emerge) this->heightmap = new s16[csize.X * csize.Z]; - MapgenV6Params *sp = (MapgenV6Params *)params->sparams; - this->spflags = sp->spflags; - this->freq_desert = sp->freq_desert; - this->freq_beach = sp->freq_beach; + this->spflags = params->spflags; + this->freq_desert = params->freq_desert; + this->freq_beach = params->freq_beach; - np_cave = &sp->np_cave; - np_humidity = &sp->np_humidity; - np_trees = &sp->np_trees; - np_apple_trees = &sp->np_apple_trees; + np_cave = ¶ms->np_cave; + np_humidity = ¶ms->np_humidity; + np_trees = ¶ms->np_trees; + np_apple_trees = ¶ms->np_apple_trees; //// Create noise objects - noise_terrain_base = new Noise(&sp->np_terrain_base, seed, csize.X, csize.Y); - noise_terrain_higher = new Noise(&sp->np_terrain_higher, seed, csize.X, csize.Y); - noise_steepness = new Noise(&sp->np_steepness, seed, csize.X, csize.Y); - noise_height_select = new Noise(&sp->np_height_select, seed, csize.X, csize.Y); - noise_mud = new Noise(&sp->np_mud, seed, csize.X, csize.Y); - noise_beach = new Noise(&sp->np_beach, seed, csize.X, csize.Y); - noise_biome = new Noise(&sp->np_biome, seed, + noise_terrain_base = new Noise(¶ms->np_terrain_base, seed, csize.X, csize.Y); + noise_terrain_higher = new Noise(¶ms->np_terrain_higher, seed, csize.X, csize.Y); + noise_steepness = new Noise(¶ms->np_steepness, seed, csize.X, csize.Y); + noise_height_select = new Noise(¶ms->np_height_select, seed, csize.X, csize.Y); + noise_mud = new Noise(¶ms->np_mud, seed, csize.X, csize.Y); + noise_beach = new Noise(¶ms->np_beach, seed, csize.X, csize.Y); + noise_biome = new Noise(¶ms->np_biome, seed, csize.X + 2 * MAP_BLOCKSIZE, csize.Y + 2 * MAP_BLOCKSIZE); - noise_humidity = new Noise(&sp->np_humidity, seed, + noise_humidity = new Noise(¶ms->np_humidity, seed, csize.X + 2 * MAP_BLOCKSIZE, csize.Y + 2 * MAP_BLOCKSIZE); //// Resolve nodes to be used diff --git a/src/mapgen_v6.h b/src/mapgen_v6.h index 20b0bf92e..f018ffaca 100644 --- a/src/mapgen_v6.h +++ b/src/mapgen_v6.h @@ -53,7 +53,7 @@ enum BiomeV6Type }; -struct MapgenV6Params : public MapgenSpecificParams { +struct MapgenV6Params : public MapgenParams { u32 spflags; float freq_desert; float freq_beach; @@ -124,7 +124,7 @@ public: content_t c_mossycobble; content_t c_stair_cobble; - MapgenV6(int mapgenid, MapgenParams *params, EmergeManager *emerge); + MapgenV6(int mapgenid, MapgenV6Params *params, EmergeManager *emerge); ~MapgenV6(); virtual MapgenType getType() const { return MAPGEN_V6; } diff --git a/src/mapgen_v7.cpp b/src/mapgen_v7.cpp index c24b3e8d1..d14fdb97a 100644 --- a/src/mapgen_v7.cpp +++ b/src/mapgen_v7.cpp @@ -50,30 +50,28 @@ FlagDesc flagdesc_mapgen_v7[] = { /////////////////////////////////////////////////////////////////////////////// -MapgenV7::MapgenV7(int mapgenid, MapgenParams *params, EmergeManager *emerge) +MapgenV7::MapgenV7(int mapgenid, MapgenV7Params *params, EmergeManager *emerge) : MapgenBasic(mapgenid, params, emerge) { - MapgenV7Params *sp = (MapgenV7Params *)params->sparams; - - this->spflags = sp->spflags; - this->cave_width = sp->cave_width; + this->spflags = params->spflags; + this->cave_width = params->cave_width; //// Terrain noise - noise_terrain_base = new Noise(&sp->np_terrain_base, seed, csize.X, csize.Z); - noise_terrain_alt = new Noise(&sp->np_terrain_alt, seed, csize.X, csize.Z); - noise_terrain_persist = new Noise(&sp->np_terrain_persist, seed, csize.X, csize.Z); - noise_height_select = new Noise(&sp->np_height_select, seed, csize.X, csize.Z); - noise_filler_depth = new Noise(&sp->np_filler_depth, seed, csize.X, csize.Z); - noise_mount_height = new Noise(&sp->np_mount_height, seed, csize.X, csize.Z); - noise_ridge_uwater = new Noise(&sp->np_ridge_uwater, seed, csize.X, csize.Z); + noise_terrain_base = new Noise(¶ms->np_terrain_base, seed, csize.X, csize.Z); + noise_terrain_alt = new Noise(¶ms->np_terrain_alt, seed, csize.X, csize.Z); + noise_terrain_persist = new Noise(¶ms->np_terrain_persist, seed, csize.X, csize.Z); + noise_height_select = new Noise(¶ms->np_height_select, seed, csize.X, csize.Z); + noise_filler_depth = new Noise(¶ms->np_filler_depth, seed, csize.X, csize.Z); + noise_mount_height = new Noise(¶ms->np_mount_height, seed, csize.X, csize.Z); + noise_ridge_uwater = new Noise(¶ms->np_ridge_uwater, seed, csize.X, csize.Z); //// 3d terrain noise // 1-up 1-down overgeneration - noise_mountain = new Noise(&sp->np_mountain, seed, csize.X, csize.Y + 2, csize.Z); - noise_ridge = new Noise(&sp->np_ridge, seed, csize.X, csize.Y + 2, csize.Z); + noise_mountain = new Noise(¶ms->np_mountain, seed, csize.X, csize.Y + 2, csize.Z); + noise_ridge = new Noise(¶ms->np_ridge, seed, csize.X, csize.Y + 2, csize.Z); - MapgenBasic::np_cave1 = sp->np_cave1; - MapgenBasic::np_cave2 = sp->np_cave2; + MapgenBasic::np_cave1 = params->np_cave1; + MapgenBasic::np_cave2 = params->np_cave2; } diff --git a/src/mapgen_v7.h b/src/mapgen_v7.h index c75f18a93..3a6bc0801 100644 --- a/src/mapgen_v7.h +++ b/src/mapgen_v7.h @@ -32,7 +32,7 @@ class BiomeManager; extern FlagDesc flagdesc_mapgen_v7[]; -struct MapgenV7Params : public MapgenSpecificParams { +struct MapgenV7Params : public MapgenParams { u32 spflags; float cave_width; NoiseParams np_terrain_base; @@ -56,7 +56,7 @@ struct MapgenV7Params : public MapgenSpecificParams { class MapgenV7 : public MapgenBasic { public: - MapgenV7(int mapgenid, MapgenParams *params, EmergeManager *emerge); + MapgenV7(int mapgenid, MapgenV7Params *params, EmergeManager *emerge); ~MapgenV7(); virtual MapgenType getType() const { return MAPGEN_V7; } diff --git a/src/mapgen_valleys.cpp b/src/mapgen_valleys.cpp index 02a8fbfe0..a61f1b329 100644 --- a/src/mapgen_valleys.cpp +++ b/src/mapgen_valleys.cpp @@ -64,7 +64,7 @@ static FlagDesc flagdesc_mapgen_valleys[] = { /////////////////////////////////////////////////////////////////////////////// -MapgenValleys::MapgenValleys(int mapgenid, MapgenParams *params, EmergeManager *emerge) +MapgenValleys::MapgenValleys(int mapgenid, MapgenValleysParams *params, EmergeManager *emerge) : MapgenBasic(mapgenid, params, emerge) { // NOTE: MapgenValleys has a hard dependency on BiomeGenOriginal @@ -73,34 +73,33 @@ MapgenValleys::MapgenValleys(int mapgenid, MapgenParams *params, EmergeManager * this->map_gen_limit = MYMIN(MAX_MAP_GENERATION_LIMIT, g_settings->getU16("map_generation_limit")); - MapgenValleysParams *sp = (MapgenValleysParams *)params->sparams; BiomeParamsOriginal *bp = (BiomeParamsOriginal *)params->bparams; - this->spflags = sp->spflags; - this->altitude_chill = sp->altitude_chill; - this->large_cave_depth = sp->large_cave_depth; - this->lava_features_lim = rangelim(sp->lava_features, 0, 10); - this->massive_cave_depth = sp->massive_cave_depth; - this->river_depth_bed = sp->river_depth + 1.f; - this->river_size_factor = sp->river_size / 100.f; - this->water_features_lim = rangelim(sp->water_features, 0, 10); - this->cave_width = sp->cave_width; + this->spflags = params->spflags; + this->altitude_chill = params->altitude_chill; + this->large_cave_depth = params->large_cave_depth; + this->lava_features_lim = rangelim(params->lava_features, 0, 10); + this->massive_cave_depth = params->massive_cave_depth; + this->river_depth_bed = params->river_depth + 1.f; + this->river_size_factor = params->river_size / 100.f; + this->water_features_lim = rangelim(params->water_features, 0, 10); + this->cave_width = params->cave_width; //// 2D Terrain noise - noise_filler_depth = new Noise(&sp->np_filler_depth, seed, csize.X, csize.Z); - noise_inter_valley_slope = new Noise(&sp->np_inter_valley_slope, seed, csize.X, csize.Z); - noise_rivers = new Noise(&sp->np_rivers, seed, csize.X, csize.Z); - noise_terrain_height = new Noise(&sp->np_terrain_height, seed, csize.X, csize.Z); - noise_valley_depth = new Noise(&sp->np_valley_depth, seed, csize.X, csize.Z); - noise_valley_profile = new Noise(&sp->np_valley_profile, seed, csize.X, csize.Z); + noise_filler_depth = new Noise(¶ms->np_filler_depth, seed, csize.X, csize.Z); + noise_inter_valley_slope = new Noise(¶ms->np_inter_valley_slope, seed, csize.X, csize.Z); + noise_rivers = new Noise(¶ms->np_rivers, seed, csize.X, csize.Z); + noise_terrain_height = new Noise(¶ms->np_terrain_height, seed, csize.X, csize.Z); + noise_valley_depth = new Noise(¶ms->np_valley_depth, seed, csize.X, csize.Z); + noise_valley_profile = new Noise(¶ms->np_valley_profile, seed, csize.X, csize.Z); //// 3D Terrain noise // 1-up 1-down overgeneration - noise_inter_valley_fill = new Noise(&sp->np_inter_valley_fill, seed, csize.X, csize.Y + 2, csize.Z); + noise_inter_valley_fill = new Noise(¶ms->np_inter_valley_fill, seed, csize.X, csize.Y + 2, csize.Z); // 1-down overgeneraion - noise_cave1 = new Noise(&sp->np_cave1, seed, csize.X, csize.Y + 1, csize.Z); - noise_cave2 = new Noise(&sp->np_cave2, seed, csize.X, csize.Y + 1, csize.Z); - noise_massive_caves = new Noise(&sp->np_massive_caves, seed, csize.X, csize.Y + 1, csize.Z); + noise_cave1 = new Noise(¶ms->np_cave1, seed, csize.X, csize.Y + 1, csize.Z); + noise_cave2 = new Noise(¶ms->np_cave2, seed, csize.X, csize.Y + 1, csize.Z); + noise_massive_caves = new Noise(¶ms->np_massive_caves, seed, csize.X, csize.Y + 1, csize.Z); this->humid_rivers = (spflags & MGVALLEYS_HUMID_RIVERS); this->use_altitude_chill = (spflags & MGVALLEYS_ALT_CHILL); diff --git a/src/mapgen_valleys.h b/src/mapgen_valleys.h index 00f6aff07..6dd7ebc47 100644 --- a/src/mapgen_valleys.h +++ b/src/mapgen_valleys.h @@ -46,7 +46,7 @@ class BiomeGenOriginal; //extern Profiler *mapgen_profiler; -struct MapgenValleysParams : public MapgenSpecificParams { +struct MapgenValleysParams : public MapgenParams { u32 spflags; s16 large_cave_depth; s16 massive_cave_depth; @@ -88,7 +88,7 @@ struct TerrainNoise { class MapgenValleys : public MapgenBasic { public: - MapgenValleys(int mapgenid, MapgenParams *params, EmergeManager *emerge); + MapgenValleys(int mapgenid, MapgenValleysParams *params, EmergeManager *emerge); ~MapgenValleys(); virtual MapgenType getType() const { return MAPGEN_VALLEYS; } diff --git a/src/script/lua_api/l_mapgen.cpp b/src/script/lua_api/l_mapgen.cpp index dc188f8a4..9f14838ce 100644 --- a/src/script/lua_api/l_mapgen.cpp +++ b/src/script/lua_api/l_mapgen.cpp @@ -600,24 +600,37 @@ int ModApiMapgen::l_get_mapgen_params(lua_State *L) { NO_MAP_LOCK_REQUIRED; - MapgenParams *params = &getServer(L)->getEmergeManager()->params; + log_deprecated(L, "get_mapgen_params is deprecated; " + "use get_mapgen_setting instead"); + + std::string value; + + MapSettingsManager *settingsmgr = + getServer(L)->getEmergeManager()->map_settings_mgr; lua_newtable(L); - lua_pushstring(L, params->mg_name.c_str()); + settingsmgr->getMapSetting("mg_name", &value); + lua_pushstring(L, value.c_str()); lua_setfield(L, -2, "mgname"); - lua_pushinteger(L, params->seed); + settingsmgr->getMapSetting("seed", &value); + std::istringstream ss(value); + u64 seed; + ss >> seed; + lua_pushinteger(L, seed); lua_setfield(L, -2, "seed"); - lua_pushinteger(L, params->water_level); + settingsmgr->getMapSetting("water_level", &value); + lua_pushinteger(L, stoi(value, -32768, 32767)); lua_setfield(L, -2, "water_level"); - lua_pushinteger(L, params->chunksize); + settingsmgr->getMapSetting("chunksize", &value); + lua_pushinteger(L, stoi(value, -32768, 32767)); lua_setfield(L, -2, "chunksize"); - std::string flagstr = writeFlagString(params->flags, flagdesc_mapgen, U32_MAX); - lua_pushstring(L, flagstr.c_str()); + settingsmgr->getMapSetting("mg_flags", &value); + lua_pushstring(L, value.c_str()); lua_setfield(L, -2, "flags"); return 1; @@ -630,44 +643,120 @@ int ModApiMapgen::l_set_mapgen_params(lua_State *L) { NO_MAP_LOCK_REQUIRED; + log_deprecated(L, "set_mapgen_params is deprecated; " + "use set_mapgen_setting instead"); + if (!lua_istable(L, 1)) return 0; - EmergeManager *emerge = getServer(L)->getEmergeManager(); - if (emerge->isRunning()) - throw LuaError("Cannot set parameters while mapgen is running"); - - MapgenParams *params = &emerge->params; - u32 flags = 0, flagmask = 0; + MapSettingsManager *settingsmgr = + getServer(L)->getEmergeManager()->map_settings_mgr; lua_getfield(L, 1, "mgname"); - if (lua_isstring(L, -1)) { - params->mg_name = lua_tostring(L, -1); - delete params->sparams; - params->sparams = NULL; - } + if (lua_isstring(L, -1)) + settingsmgr->setMapSetting("mg_name", lua_tostring(L, -1), true); lua_getfield(L, 1, "seed"); if (lua_isnumber(L, -1)) - params->seed = lua_tointeger(L, -1); + settingsmgr->setMapSetting("seed", lua_tostring(L, -1), true); lua_getfield(L, 1, "water_level"); if (lua_isnumber(L, -1)) - params->water_level = lua_tointeger(L, -1); + settingsmgr->setMapSetting("water_level", lua_tostring(L, -1), true); lua_getfield(L, 1, "chunksize"); if (lua_isnumber(L, -1)) - params->chunksize = lua_tointeger(L, -1); + settingsmgr->setMapSetting("chunksize", lua_tostring(L, -1), true); warn_if_field_exists(L, 1, "flagmask", "Deprecated: flags field now includes unset flags."); - lua_getfield(L, 1, "flagmask"); + + lua_getfield(L, 1, "flags"); if (lua_isstring(L, -1)) - params->flags &= ~readFlagString(lua_tostring(L, -1), flagdesc_mapgen, NULL); + settingsmgr->setMapSetting("mg_flags", lua_tostring(L, -1), true); + + return 0; +} + +// get_mapgen_setting(name) +int ModApiMapgen::l_get_mapgen_setting(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; - if (getflagsfield(L, 1, "flags", flagdesc_mapgen, &flags, &flagmask)) { - params->flags &= ~flagmask; - params->flags |= flags; + std::string value; + MapSettingsManager *settingsmgr = + getServer(L)->getEmergeManager()->map_settings_mgr; + + const char *name = luaL_checkstring(L, 1); + if (!settingsmgr->getMapSetting(name, &value)) + return 0; + + lua_pushstring(L, value.c_str()); + return 1; +} + +// get_mapgen_setting_noiseparams(name) +int ModApiMapgen::l_get_mapgen_setting_noiseparams(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + NoiseParams np; + MapSettingsManager *settingsmgr = + getServer(L)->getEmergeManager()->map_settings_mgr; + + const char *name = luaL_checkstring(L, 1); + if (!settingsmgr->getMapSettingNoiseParams(name, &np)) + return 0; + + push_noiseparams(L, &np); + return 1; +} + +// set_mapgen_setting(name, value, override_meta) +// set mapgen config values +int ModApiMapgen::l_set_mapgen_setting(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + MapSettingsManager *settingsmgr = + getServer(L)->getEmergeManager()->map_settings_mgr; + + const char *name = luaL_checkstring(L, 1); + const char *value = luaL_checkstring(L, 2); + bool override_meta = lua_isboolean(L, 3) ? lua_toboolean(L, 3) : false; + + if (!settingsmgr->setMapSetting(name, value, override_meta)) { + errorstream << "set_mapgen_setting: cannot set '" + << name << "' after initialization" << std::endl; + } + + return 0; +} + + +// set_mapgen_setting_noiseparams(name, noiseparams, set_default) +// set mapgen config values for noise parameters +int ModApiMapgen::l_set_mapgen_setting_noiseparams(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + MapSettingsManager *settingsmgr = + getServer(L)->getEmergeManager()->map_settings_mgr; + + const char *name = luaL_checkstring(L, 1); + + NoiseParams np; + if (!read_noiseparams(L, 2, &np)) { + errorstream << "set_mapgen_setting_noiseparams: cannot set '" << name + << "'; invalid noiseparams table" << std::endl; + return 0; + } + + bool override_meta = lua_isboolean(L, 3) ? lua_toboolean(L, 3) : false; + + if (!settingsmgr->setMapSettingNoiseParams(name, &np, override_meta)) { + errorstream << "set_mapgen_setting_noiseparams: cannot set '" + << name << "' after initialization" << std::endl; } return 0; @@ -683,8 +772,11 @@ int ModApiMapgen::l_set_noiseparams(lua_State *L) const char *name = luaL_checkstring(L, 1); NoiseParams np; - if (!read_noiseparams(L, 2, &np)) + if (!read_noiseparams(L, 2, &np)) { + errorstream << "set_noiseparams: cannot set '" << name + << "'; invalid noiseparams table" << std::endl; return 0; + } bool set_default = lua_isboolean(L, 3) ? lua_toboolean(L, 3) : true; @@ -1143,7 +1235,7 @@ int ModApiMapgen::l_generate_ores(lua_State *L) EmergeManager *emerge = getServer(L)->getEmergeManager(); Mapgen mg; - mg.seed = emerge->params.seed; + mg.seed = emerge->mgparams->seed; mg.vm = LuaVoxelManip::checkobject(L, 1)->vm; mg.ndef = getServer(L)->getNodeDefManager(); @@ -1169,7 +1261,7 @@ int ModApiMapgen::l_generate_decorations(lua_State *L) EmergeManager *emerge = getServer(L)->getEmergeManager(); Mapgen mg; - mg.seed = emerge->params.seed; + mg.seed = emerge->mgparams->seed; mg.vm = LuaVoxelManip::checkobject(L, 1)->vm; mg.ndef = getServer(L)->getNodeDefManager(); @@ -1393,6 +1485,10 @@ void ModApiMapgen::Initialize(lua_State *L, int top) API_FCT(get_mapgen_params); API_FCT(set_mapgen_params); + API_FCT(get_mapgen_setting); + API_FCT(set_mapgen_setting); + API_FCT(get_mapgen_setting_noiseparams); + API_FCT(set_mapgen_setting_noiseparams); API_FCT(set_noiseparams); API_FCT(get_noiseparams); API_FCT(set_gen_notify); diff --git a/src/script/lua_api/l_mapgen.h b/src/script/lua_api/l_mapgen.h index 9751c0db6..bb94575c7 100644 --- a/src/script/lua_api/l_mapgen.h +++ b/src/script/lua_api/l_mapgen.h @@ -40,6 +40,18 @@ private: // set mapgen parameters static int l_set_mapgen_params(lua_State *L); + // get_mapgen_setting(name) + static int l_get_mapgen_setting(lua_State *L); + + // set_mapgen_setting(name, value, override_meta) + static int l_set_mapgen_setting(lua_State *L); + + // get_mapgen_setting_noiseparams(name) + static int l_get_mapgen_setting_noiseparams(lua_State *L); + + // set_mapgen_setting_noiseparams(name, value, override_meta) + static int l_set_mapgen_setting_noiseparams(lua_State *L); + // set_noiseparam_defaults(name, noiseparams, set_default) static int l_set_noiseparams(lua_State *L); diff --git a/src/script/lua_api/l_vmanip.cpp b/src/script/lua_api/l_vmanip.cpp index f13866408..0d8123acd 100644 --- a/src/script/lua_api/l_vmanip.cpp +++ b/src/script/lua_api/l_vmanip.cpp @@ -190,7 +190,7 @@ int LuaVoxelManip::l_calc_lighting(lua_State *L) Mapgen mg; mg.vm = vm; mg.ndef = ndef; - mg.water_level = emerge->params.water_level; + mg.water_level = emerge->mgparams->water_level; mg.calcLighting(pmin, pmax, fpmin, fpmax, propagate_shadow); diff --git a/src/server.cpp b/src/server.cpp index f7f698d50..97a53f189 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -266,9 +266,6 @@ Server::Server( //lock environment MutexAutoLock envlock(m_env_mutex); - // Load mapgen params from Settings - m_emerge->loadMapgenParams(); - // Create the Map (loads map_meta.txt, overriding configured mapgen params) ServerMap *servermap = new ServerMap(path_world, this, m_emerge); @@ -331,8 +328,11 @@ Server::Server( m_clients.setEnv(m_env); + if (!servermap->settings_mgr.makeMapgenParams()) + FATAL_ERROR("Couldn't create any mapgen type"); + // Initialize mapgens - m_emerge->initMapgens(); + m_emerge->initMapgens(servermap->getMapgenParams()); m_enable_rollback_recording = g_settings->getBool("enable_rollback_recording"); if (m_enable_rollback_recording) { @@ -402,11 +402,8 @@ Server::~Server() m_emerge->stopThreads(); // Delete things in the reverse order of creation - delete m_env; - - // N.B. the EmergeManager should be deleted after the Environment since Map - // depends on EmergeManager to write its current params to the map meta delete m_emerge; + delete m_env; delete m_rollback; delete m_banmanager; delete m_event; @@ -655,7 +652,7 @@ void Server::AsyncRunStep(bool initial_step) m_env->getGameTime(), m_lag, m_gamespec.id, - m_emerge->params.mg_name, + Mapgen::getMapgenName(m_emerge->mgparams->mgtype), m_mods); counter = 0.01; } diff --git a/src/subgame.cpp b/src/subgame.cpp index 7e9a0b368..55bbd3954 100644 --- a/src/subgame.cpp +++ b/src/subgame.cpp @@ -313,8 +313,8 @@ bool loadGameConfAndInitWorld(const std::string &path, const SubgameSpec &gamesp Settings conf; MapgenParams params; - params.load(*g_settings); - params.save(conf); + params.readParams(g_settings); + params.writeParams(&conf); conf.writeLines(oss); oss << "[end_of_params]\n"; diff --git a/src/unittest/CMakeLists.txt b/src/unittest/CMakeLists.txt index a07ed8ba5..34de99e12 100644 --- a/src/unittest/CMakeLists.txt +++ b/src/unittest/CMakeLists.txt @@ -6,6 +6,7 @@ set (UNITTEST_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/test_connection.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_filepath.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_inventory.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/test_map_settings_manager.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_mapnode.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_nodedef.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_noderesolver.cpp diff --git a/src/unittest/test_map_settings_manager.cpp b/src/unittest/test_map_settings_manager.cpp new file mode 100644 index 000000000..b2ad53192 --- /dev/null +++ b/src/unittest/test_map_settings_manager.cpp @@ -0,0 +1,255 @@ + /* +Minetest +Copyright (C) 2010-2014 kwolekr, Ryan Kwolek + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "test.h" + +#include "noise.h" +#include "settings.h" +#include "mapgen_v5.h" +#include "util/sha1.h" +#include "map_settings_manager.h" + +class TestMapSettingsManager : public TestBase { +public: + TestMapSettingsManager() { TestManager::registerTestModule(this); } + const char *getName() { return "TestMapSettingsManager"; } + + void makeUserConfig(Settings *conf); + std::string makeMetaFile(bool make_corrupt); + + void runTests(IGameDef *gamedef); + + void testMapSettingsManager(); + void testMapMetaSaveLoad(); + void testMapMetaFailures(); +}; + +static TestMapSettingsManager g_test_instance; + +void TestMapSettingsManager::runTests(IGameDef *gamedef) +{ + TEST(testMapSettingsManager); + TEST(testMapMetaSaveLoad); + TEST(testMapMetaFailures); +} + +//////////////////////////////////////////////////////////////////////////////// + + +void check_noise_params(const NoiseParams *np1, const NoiseParams *np2) +{ + UASSERTEQ(float, np1->offset, np2->offset); + UASSERTEQ(float, np1->scale, np2->scale); + UASSERT(np1->spread == np2->spread); + UASSERTEQ(s32, np1->seed, np2->seed); + UASSERTEQ(u16, np1->octaves, np2->octaves); + UASSERTEQ(float, np1->persist, np2->persist); + UASSERTEQ(float, np1->lacunarity, np2->lacunarity); + UASSERTEQ(u32, np1->flags, np2->flags); +} + + +std::string read_file_to_string(const std::string &filepath) +{ + std::string buf; + FILE *f = fopen(filepath.c_str(), "rb"); + if (!f) + return ""; + + fseek(f, 0, SEEK_END); + + long filesize = ftell(f); + if (filesize == -1) + return ""; + rewind(f); + + buf.resize(filesize); + + fread(&buf[0], 1, filesize, f); + + fclose(f); + return buf; +} + + +void TestMapSettingsManager::makeUserConfig(Settings *conf) +{ + conf->set("mg_name", "v7"); + conf->set("seed", "5678"); + conf->set("water_level", "20"); + conf->set("mgv5_np_factor", "0, 12, (500, 250, 500), 920382, 5, 0.45, 3.0"); + conf->set("mgv5_np_height", "0, 15, (500, 250, 500), 841746, 5, 0.5, 3.0"); + conf->set("mgv5_np_filler_depth", "20, 1, (150, 150, 150), 261, 4, 0.7, 1.0"); + conf->set("mgv5_np_ground", "-43, 40, (80, 80, 80), 983240, 4, 0.55, 2.0"); +} + + +std::string TestMapSettingsManager::makeMetaFile(bool make_corrupt) +{ + std::string metafile = getTestTempFile(); + + const char *metafile_contents = + "mg_name = v5\n" + "seed = 1234\n" + "mg_flags = light\n" + "mgv5_np_filler_depth = 20, 1, (150, 150, 150), 261, 4, 0.7, 1.0\n" + "mgv5_np_height = 20, 10, (250, 250, 250), 84174, 4, 0.5, 1.0\n"; + + FILE *f = fopen(metafile.c_str(), "wb"); + UASSERT(f != NULL); + + fputs(metafile_contents, f); + if (!make_corrupt) + fputs("[end_of_params]\n", f); + + fclose(f); + + return metafile; +} + + +void TestMapSettingsManager::testMapSettingsManager() +{ + Settings user_settings; + makeUserConfig(&user_settings); + + std::string test_mapmeta_path = makeMetaFile(false); + + MapSettingsManager mgr(&user_settings, test_mapmeta_path); + std::string value; + + UASSERT(mgr.getMapSetting("mg_name", &value)); + UASSERT(value == "v7"); + + // Pretend we're initializing the ServerMap + UASSERT(mgr.loadMapMeta()); + + // Pretend some scripts are requesting mapgen params + UASSERT(mgr.getMapSetting("mg_name", &value)); + UASSERT(value == "v5"); + UASSERT(mgr.getMapSetting("seed", &value)); + UASSERT(value == "1234"); + UASSERT(mgr.getMapSetting("water_level", &value)); + UASSERT(value == "20"); + + // Pretend we have some mapgen settings configured from the scripting + UASSERT(mgr.setMapSetting("water_level", "15")); + UASSERT(mgr.setMapSetting("seed", "02468")); + UASSERT(mgr.setMapSetting("mg_flags", "nolight", true)); + + NoiseParams script_np_filler_depth(0, 100, v3f(200, 100, 200), 261, 4, 0.7, 2.0); + NoiseParams script_np_factor(0, 100, v3f(50, 50, 50), 920381, 3, 0.45, 2.0); + NoiseParams script_np_height(0, 100, v3f(450, 450, 450), 84174, 4, 0.5, 2.0); + NoiseParams meta_np_height(20, 10, v3f(250, 250, 250), 84174, 4, 0.5, 1.0); + NoiseParams user_np_ground(-43, 40, v3f(80, 80, 80), 983240, 4, 0.55, 2.0, NOISE_FLAG_EASED); + + mgr.setMapSettingNoiseParams("mgv5_np_filler_depth", &script_np_filler_depth, true); + mgr.setMapSettingNoiseParams("mgv5_np_height", &script_np_height); + mgr.setMapSettingNoiseParams("mgv5_np_factor", &script_np_factor); + + // Now make our Params and see if the values are correctly sourced + MapgenParams *params = mgr.makeMapgenParams(); + UASSERT(params->mgtype == MAPGEN_V5); + UASSERT(params->chunksize == 5); + UASSERT(params->water_level == 15); + UASSERT(params->seed == 1234); + UASSERT((params->flags & MG_LIGHT) == 0); + + MapgenV5Params *v5params = (MapgenV5Params *)params; + + check_noise_params(&v5params->np_filler_depth, &script_np_filler_depth); + check_noise_params(&v5params->np_factor, &script_np_factor); + check_noise_params(&v5params->np_height, &meta_np_height); + check_noise_params(&v5params->np_ground, &user_np_ground); + + UASSERT(mgr.setMapSetting("foobar", "25") == false); + + // Pretend the ServerMap is shutting down + UASSERT(mgr.saveMapMeta()); + + // Make sure our interface expectations are met + UASSERT(mgr.mapgen_params == params); + UASSERT(mgr.makeMapgenParams() == params); + + // Load the resulting map_meta.txt and make sure it contains what we expect + unsigned char expected_contents_hash[20] = { + 0xf6, 0x44, 0x90, 0xb7, 0xab, 0xd8, 0x91, 0xf4, 0x08, 0x96, + 0xfc, 0x7e, 0xed, 0x01, 0xc5, 0x9a, 0xfd, 0x2f, 0x2d, 0x79 + }; + + SHA1 ctx; + std::string metafile_contents = read_file_to_string(test_mapmeta_path); + ctx.addBytes(&metafile_contents[0], metafile_contents.size()); + unsigned char *sha1_result = ctx.getDigest(); + int resultdiff = memcmp(sha1_result, expected_contents_hash, 20); + free(sha1_result); + + UASSERT(!resultdiff); +} + + +void TestMapSettingsManager::testMapMetaSaveLoad() +{ + Settings conf; + std::string path = getTestTempDirectory() + + DIR_DELIM + "foobar" + DIR_DELIM + "map_meta.txt"; + + // Create a set of mapgen params and save them to map meta + conf.set("seed", "12345"); + conf.set("water_level", "5"); + MapSettingsManager mgr1(&conf, path); + MapgenParams *params1 = mgr1.makeMapgenParams(); + UASSERT(params1); + UASSERT(mgr1.saveMapMeta()); + + // Now try loading the map meta to mapgen params + conf.set("seed", "67890"); + conf.set("water_level", "32"); + MapSettingsManager mgr2(&conf, path); + UASSERT(mgr2.loadMapMeta()); + MapgenParams *params2 = mgr2.makeMapgenParams(); + UASSERT(params2); + + // Check that both results are correct + UASSERTEQ(u64, params1->seed, 12345); + UASSERTEQ(s16, params1->water_level, 5); + UASSERTEQ(u64, params2->seed, 12345); + UASSERTEQ(s16, params2->water_level, 5); +} + + +void TestMapSettingsManager::testMapMetaFailures() +{ + std::string test_mapmeta_path; + Settings conf; + + // Check to see if it'll fail on a non-existent map meta file + test_mapmeta_path = "woobawooba/fgdfg/map_meta.txt"; + UASSERT(!fs::PathExists(test_mapmeta_path)); + + MapSettingsManager mgr1(&conf, test_mapmeta_path); + UASSERT(!mgr1.loadMapMeta()); + + // Check to see if it'll fail on a corrupt map meta file + test_mapmeta_path = makeMetaFile(true); + UASSERT(fs::PathExists(test_mapmeta_path)); + + MapSettingsManager mgr2(&conf, test_mapmeta_path); + UASSERT(!mgr2.loadMapMeta()); +} -- cgit v1.2.3 From e1aa98fe077faecb14a9820f3861bb1c82b5db6e Mon Sep 17 00:00:00 2001 From: est31 Date: Sun, 3 Jul 2016 19:36:51 +0200 Subject: Remove top left minetest watermark Move version information into the window caption. On popular player request. Fixes #4209. --- src/client/clientlauncher.cpp | 5 ++++- src/game.cpp | 11 ++++------- src/guiEngine.cpp | 16 +++------------- src/guiEngine.h | 4 ++-- 4 files changed, 13 insertions(+), 23 deletions(-) (limited to 'src') diff --git a/src/client/clientlauncher.cpp b/src/client/clientlauncher.cpp index ee8662ed6..a0781ef37 100644 --- a/src/client/clientlauncher.cpp +++ b/src/client/clientlauncher.cpp @@ -34,6 +34,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "fontengine.h" #include "joystick_controller.h" #include "clientlauncher.h" +#include "version.h" /* mainmenumanager.h */ @@ -185,7 +186,9 @@ bool ClientLauncher::run(GameParams &game_params, const Settings &cmd_args) { // Set the window caption const wchar_t *text = wgettext("Main Menu"); - device->setWindowCaption((utf8_to_wide(PROJECT_NAME_C) + L" [" + text + L"]").c_str()); + device->setWindowCaption((utf8_to_wide(PROJECT_NAME_C) + + L" " + utf8_to_wide(g_version_hash) + + L" [" + text + L"]").c_str()); delete[] text; try { // This is used for catching disconnects diff --git a/src/game.cpp b/src/game.cpp index ba77d299a..93d9e6d2c 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -1273,10 +1273,10 @@ static void updateChat(Client &client, f32 dtime, bool show_debug, setStaticText(guitext_chat, recent_chat); // Update gui element size and position - s32 chat_y = 5 + line_height; + s32 chat_y = 5; if (show_debug) - chat_y += line_height; + chat_y += 2 * line_height; // first pass to calculate height of text to be set s32 width = std::min(g_fontengine->getTextWidth(recent_chat.c_str()) + 10, @@ -2205,6 +2205,8 @@ bool Game::createClient(const std::string &playername, /* Set window caption */ std::wstring str = utf8_to_wide(PROJECT_NAME_C); + str += L" "; + str += utf8_to_wide(g_version_hash); str += L" ["; str += driver->getName(); str += L"]"; @@ -4347,11 +4349,6 @@ void Game::updateGui(float *statustext_time, const RunStats &stats, << ", RTT = " << client->getRTT(); setStaticText(guitext, utf8_to_wide(os.str()).c_str()); guitext->setVisible(true); - } else if (flags.show_hud || flags.show_chat) { - std::ostringstream os(std::ios_base::binary); - os << PROJECT_NAME_C " " << g_version_hash; - setStaticText(guitext, utf8_to_wide(os.str()).c_str()); - guitext->setVisible(true); } else { guitext->setVisible(false); } diff --git a/src/guiEngine.cpp b/src/guiEngine.cpp index b9d796ccb..e15533dcd 100644 --- a/src/guiEngine.cpp +++ b/src/guiEngine.cpp @@ -174,8 +174,7 @@ GUIEngine::GUIEngine( irr::IrrlichtDevice* dev, m_sound_manager = &dummySoundManager; //create topleft header - m_toplefttext = utf8_to_wide(std::string(PROJECT_NAME_C " ") + - g_version_hash); + m_toplefttext = L""; core::rect rect(0, 0, g_fontengine->getTextWidth(m_toplefttext.c_str()), g_fontengine->getTextHeight()); @@ -571,18 +570,9 @@ bool GUIEngine::downloadFile(std::string url, std::string target) } /******************************************************************************/ -void GUIEngine::setTopleftText(std::string append) +void GUIEngine::setTopleftText(const std::string &text) { - std::wstring toset = utf8_to_wide(std::string(PROJECT_NAME_C " ") + - g_version_hash); - - if (append != "") - { - toset += L" / "; - toset += utf8_to_wide(append); - } - - m_toplefttext = toset; + m_toplefttext = utf8_to_wide(text); updateTopLeftTextSize(); } diff --git a/src/guiEngine.h b/src/guiEngine.h index a59436953..897244808 100644 --- a/src/guiEngine.h +++ b/src/guiEngine.h @@ -270,10 +270,10 @@ private: void drawVersion(); /** - * specify text to be appended to version string + * specify text to appear as top left string * @param text to set */ - void setTopleftText(std::string append); + void setTopleftText(const std::string &text); /** pointer to gui element shown at topleft corner */ irr::gui::IGUIStaticText* m_irr_toplefttext; -- cgit v1.2.3 From f64914708092958f735337b3a14a33ca402b7893 Mon Sep 17 00:00:00 2001 From: est31 Date: Mon, 4 Jul 2016 20:49:13 +0200 Subject: Disable joysticks per default for now It seems that various different devices register as Joysticks on linux, and their presence has caused lots of confusion amongst minetest users. Therefore, disable the joystick feature. Closes #4261 Closes #4221 --- builtin/settingtypes.txt | 2 +- src/defaultsettings.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index c751f34f2..3f256ec43 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -105,7 +105,7 @@ random_input (Random input) bool false continuous_forward (Continuous forward) bool false # Enable Joysticks -enable_joysticks (Enable Joysticks) bool true +enable_joysticks (Enable Joysticks) bool false # The time in seconds it takes between repeated events # when holding down a joystick button combination. diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index 43a7a7c00..8ee26ecff 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -116,7 +116,7 @@ void set_default_settings(Settings *settings) settings->setDefault("free_move", "false"); settings->setDefault("noclip", "false"); settings->setDefault("continuous_forward", "false"); - settings->setDefault("enable_joysticks", "true"); + settings->setDefault("enable_joysticks", "false"); settings->setDefault("repeat_joystick_button_time", "0.17"); settings->setDefault("joystick_frustum_sensitivity", "170"); settings->setDefault("cinematic", "false"); -- cgit v1.2.3 From 1dfd977ec43370da6931b11a8d0469792c8ebc36 Mon Sep 17 00:00:00 2001 From: Rogier-5 Date: Mon, 4 Jul 2016 21:00:57 +0200 Subject: Fix & make linux conditionals uniform (#4278) The source used a hodge-podge of different combinations of different macros to check for linux: 'linux', '__linux', '__linux__'. As '__linux__' is standard (Posix), and the others are not, the source now uniformly uses __linux__. If either linux or __linux are defined, it is made sure that __linux__ is defined as well. --- src/guiChatConsole.cpp | 2 +- src/intlGUIEditBox.cpp | 2 +- src/porting.cpp | 4 ++-- src/porting.h | 8 ++++++-- src/threading/thread.cpp | 8 ++++---- 5 files changed, 14 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/guiChatConsole.cpp b/src/guiChatConsole.cpp index bb58d1305..8dd5ab032 100644 --- a/src/guiChatConsole.cpp +++ b/src/guiChatConsole.cpp @@ -630,7 +630,7 @@ bool GUIChatConsole::OnEvent(const SEvent& event) } else if(event.KeyInput.Char != 0 && !event.KeyInput.Control) { - #if (defined(linux) || defined(__linux)) + #if (defined(__linux__)) wchar_t wc = L'_'; mbtowc( &wc, (char *) &event.KeyInput.Char, sizeof(event.KeyInput.Char) ); prompt.input(wc); diff --git a/src/intlGUIEditBox.cpp b/src/intlGUIEditBox.cpp index 33bf8a13c..29f828076 100644 --- a/src/intlGUIEditBox.cpp +++ b/src/intlGUIEditBox.cpp @@ -271,7 +271,7 @@ bool intlGUIEditBox::OnEvent(const SEvent& event) break; case EET_KEY_INPUT_EVENT: { -#if (defined(linux) || defined(__linux) || defined(__FreeBSD__)) +#if (defined(__linux__) || defined(__FreeBSD__)) // ################################################################ // ValkaTR: // This part is the difference from the original intlGUIEditBox diff --git a/src/porting.cpp b/src/porting.cpp index 7ded58b3f..02ce6174b 100644 --- a/src/porting.cpp +++ b/src/porting.cpp @@ -258,7 +258,7 @@ bool getCurrentExecPath(char *buf, size_t len) //// Linux -#elif defined(linux) || defined(__linux) || defined(__linux__) +#elif defined(__linux__) bool getCurrentExecPath(char *buf, size_t len) { @@ -374,7 +374,7 @@ bool setSystemPaths() //// Linux -#elif defined(linux) || defined(__linux) +#elif defined(__linux__) bool setSystemPaths() { diff --git a/src/porting.h b/src/porting.h index 4d51c5058..d101a7324 100644 --- a/src/porting.h +++ b/src/porting.h @@ -60,7 +60,11 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include //for uintptr_t - #if (defined(linux) || defined(__linux) || defined(__GNU__)) && !defined(_GNU_SOURCE) + // Use standard Posix macro for Linux + #if (defined(linux) || defined(__linux)) && !defined(__linux__) + #define __linux__ + #endif + #if (defined(__linux__) || defined(__GNU__)) && !defined(_GNU_SOURCE) #define _GNU_SOURCE #endif @@ -321,7 +325,7 @@ inline const char *getPlatformName() return #if defined(ANDROID) "Android" -#elif defined(linux) || defined(__linux) || defined(__linux__) +#elif defined(__linux__) "Linux" #elif defined(_WIN32) || defined(_WIN64) "Windows" diff --git a/src/threading/thread.cpp b/src/threading/thread.cpp index 5161a6c01..0cd536795 100644 --- a/src/threading/thread.cpp +++ b/src/threading/thread.cpp @@ -54,7 +54,7 @@ DEALINGS IN THE SOFTWARE. // for setName -#if defined(linux) || defined(__linux) +#if defined(__linux__) #include #elif defined(__FreeBSD__) || defined(__OpenBSD__) #include @@ -70,7 +70,7 @@ DEALINGS IN THE SOFTWARE. // for bindToProcessor #if __FreeBSD_version >= 702106 typedef cpuset_t cpu_set_t; -#elif defined(__linux) || defined(linux) +#elif defined(__linux__) #include #elif defined(__sun) || defined(sun) #include @@ -261,7 +261,7 @@ DWORD WINAPI Thread::threadProc(LPVOID param) void Thread::setName(const std::string &name) { -#if defined(linux) || defined(__linux) +#if defined(__linux__) // It would be cleaner to do this with pthread_setname_np, // which was added to glibc in version 2.12, but some major @@ -363,7 +363,7 @@ bool Thread::bindToProcessor(unsigned int proc_number) return SetThreadAffinityMask(getThreadHandle(), 1 << proc_number); -#elif __FreeBSD_version >= 702106 || defined(__linux) || defined(linux) +#elif __FreeBSD_version >= 702106 || defined(__linux__) cpu_set_t cpuset; -- cgit v1.2.3 From c1bdb552bc3ec0fda1b82ab2c44a5c31ab53bd24 Mon Sep 17 00:00:00 2001 From: est31 Date: Mon, 4 Jul 2016 21:32:32 +0200 Subject: Temporarily disable "testStartStopWait" Threading unit test on mac The "testStartStopWait" unit test is unreliably failing on mac, for some time already. See bug #3786. Having the unittest fail unreliably doesn't help anybody but mostly inhibits the main feature of travis builds: to test PRs for regressions. Therefore, disable the specific unit test for until bug #3786 is fixed. --- src/unittest/test_threading.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/unittest/test_threading.cpp b/src/unittest/test_threading.cpp index f0df85b2d..224e123df 100644 --- a/src/unittest/test_threading.cpp +++ b/src/unittest/test_threading.cpp @@ -39,7 +39,9 @@ static TestThreading g_test_instance; void TestThreading::runTests(IGameDef *gamedef) { +#if !(defined(__MACH__) && defined(__APPLE__)) TEST(testStartStopWait); +#endif TEST(testThreadKill); TEST(testAtomicSemaphoreThread); } -- cgit v1.2.3 From 5d4d3f8366b74d9c3da892d94188defc49407ebf Mon Sep 17 00:00:00 2001 From: est31 Date: Mon, 4 Jul 2016 01:33:46 +0200 Subject: Finally set a window icon on X11 Since the creation of minetest, it had no window icon on X11. Now we have one. The misc/minetest-xorg-icon-128.png file is a rendering of the misc/minetest.svg file with inkscape, created with something like: inkscape -z -e misc/minetest-xorg-icon-128.png -w 128 -h 128 misc/minetest.svg --- misc/minetest-xorg-icon-128.png | Bin 0 -> 11241 bytes src/client/clientlauncher.cpp | 3 ++ src/porting.cpp | 87 ++++++++++++++++++++++++++++++++++++++++ src/porting.h | 3 ++ 4 files changed, 93 insertions(+) create mode 100644 misc/minetest-xorg-icon-128.png (limited to 'src') diff --git a/misc/minetest-xorg-icon-128.png b/misc/minetest-xorg-icon-128.png new file mode 100644 index 000000000..0241a911c Binary files /dev/null and b/misc/minetest-xorg-icon-128.png differ diff --git a/src/client/clientlauncher.cpp b/src/client/clientlauncher.cpp index a0781ef37..aa3c2d548 100644 --- a/src/client/clientlauncher.cpp +++ b/src/client/clientlauncher.cpp @@ -114,6 +114,9 @@ bool ClientLauncher::run(GameParams &game_params, const Settings &cmd_args) porting::setXorgClassHint(video_driver->getExposedVideoData(), PROJECT_NAME_C); + porting::setXorgWindowIcon(device, + porting::path_share + "/misc/minetest-xorg-icon-128.png"); + /* This changes the minimum allowed number of vertices in a VBO. Default is 500. diff --git a/src/porting.cpp b/src/porting.cpp index 02ce6174b..ddf8139ea 100644 --- a/src/porting.cpp +++ b/src/porting.cpp @@ -611,6 +611,93 @@ void setXorgClassHint(const video::SExposedVideoData &video_data, #endif } +bool setXorgWindowIcon(IrrlichtDevice *device, + const std::string &icon_file) +{ +#ifdef XORG_USED + + video::IVideoDriver *v_driver = device->getVideoDriver(); + + video::IImageLoader *image_loader = NULL; + for (u32 i = v_driver->getImageLoaderCount() - 1; i >= 0; i--) { + if (v_driver->getImageLoader(i)->isALoadableFileExtension(icon_file.c_str())) { + image_loader = v_driver->getImageLoader(i); + break; + } + } + + if (!image_loader) { + warningstream << "Could not find image loader for file '" + << icon_file << "'" << std::endl; + return false; + } + + io::IReadFile *icon_f = device->getFileSystem()->createAndOpenFile(icon_file.c_str()); + + if (!icon_f) { + warningstream << "Could not load icon file '" + << icon_file << "'" << std::endl; + return false; + } + + video::IImage *img = image_loader->loadImage(icon_f); + + if (!img) { + warningstream << "Could not load icon file '" + << icon_file << "'" << std::endl; + icon_f->drop(); + return false; + } + + u32 height = img->getDimension().Height; + u32 width = img->getDimension().Width; + + size_t icon_buffer_len = 2 + height * width; + long *icon_buffer = new long[icon_buffer_len]; + + icon_buffer[0] = width; + icon_buffer[1] = height; + + for (u32 x = 0; x < width; x++) { + for (u32 y = 0; y < height; y++) { + video::SColor col = img->getPixel(x, y); + long pixel_val = 0; + pixel_val |= (u8)col.getAlpha() << 24; + pixel_val |= (u8)col.getRed() << 16; + pixel_val |= (u8)col.getGreen() << 8; + pixel_val |= (u8)col.getBlue(); + icon_buffer[2 + x + y * width] = pixel_val; + } + } + + img->drop(); + icon_f->drop(); + + const video::SExposedVideoData &video_data = v_driver->getExposedVideoData(); + + Display *x11_dpl = (Display *)video_data.OpenGLLinux.X11Display; + + if (x11_dpl == NULL) { + warningstream << "Could not find x11 display for setting its icon." + << std::endl; + delete [] icon_buffer; + return false; + } + + Window x11_win = (Window)video_data.OpenGLLinux.X11Window; + + Atom net_wm_icon = XInternAtom(x11_dpl, "_NET_WM_ICON", False); + Atom cardinal = XInternAtom(x11_dpl, "CARDINAL", False); + XChangeProperty(x11_dpl, x11_win, + net_wm_icon, cardinal, 32, + PropModeReplace, (const unsigned char *)icon_buffer, + icon_buffer_len); + + delete [] icon_buffer; + + return true; +#endif +} //// //// Video/Display Information (Client-only) diff --git a/src/porting.h b/src/porting.h index d101a7324..40f6b4dc3 100644 --- a/src/porting.h +++ b/src/porting.h @@ -367,6 +367,9 @@ inline const char *getPlatformName() void setXorgClassHint(const video::SExposedVideoData &video_data, const std::string &name); +bool setXorgWindowIcon(IrrlichtDevice *device, + const std::string &icon_file); + // This only needs to be called at the start of execution, since all future // threads in the process inherit this exception handler void setWin32ExceptionHandler(); -- cgit v1.2.3 From 538af97d8d0d0a3c50a7092c029d8585079144c4 Mon Sep 17 00:00:00 2001 From: rubenwardy Date: Mon, 4 Jul 2016 18:01:58 +0100 Subject: Remove cinematic toggle on F8 Also update cinematic mode's description to include mouse --- builtin/settingtypes.txt | 4 ++-- minetest.conf.example | 5 ++--- src/defaultsettings.cpp | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index 3f256ec43..0b9d29a33 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -70,7 +70,7 @@ fast_move (Fast movement) bool false # This requires the "noclip" privilege on the server. noclip (Noclip) bool false -# Smooths camera when moving and looking around. +# Smooths camera when looking around. Also called look or mouse smoothing. # Useful for recording videos. cinematic (Cinematic mode) bool false @@ -178,7 +178,7 @@ keymap_noclip (Noclip key) key KEY_KEY_H # Key for toggling cinematic mode. # See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 -keymap_cinematic (Cinematic mode key) key KEY_F8 +keymap_cinematic (Cinematic mode key) key # Key for toggling display of minimap. # See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 diff --git a/minetest.conf.example b/minetest.conf.example index 35675dfe3..bc0d4c527 100644 --- a/minetest.conf.example +++ b/minetest.conf.example @@ -38,7 +38,7 @@ # type: bool # noclip = false -# Smooths camera when moving and looking around. +# Smooths camera when looking around. Also called look or mouse smoothing. # Useful for recording videos. # type: bool # cinematic = false @@ -162,7 +162,7 @@ # Key for toggling cinematic mode. # See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 # type: key -# keymap_cinematic = KEY_F8 +# keymap_cinematic = # Key for toggling display of minimap. # See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 @@ -1527,4 +1527,3 @@ # type: string # modstore_details_url = https://forum.minetest.net/mmdb/mod/*/ - diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index 8ee26ecff..3220b915a 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -50,7 +50,7 @@ void set_default_settings(Settings *settings) settings->setDefault("keymap_freemove", "KEY_KEY_K"); settings->setDefault("keymap_fastmove", "KEY_KEY_J"); settings->setDefault("keymap_noclip", "KEY_KEY_H"); - settings->setDefault("keymap_cinematic", "KEY_F8"); + settings->setDefault("keymap_cinematic", ""); settings->setDefault("keymap_screenshot", "KEY_F12"); settings->setDefault("keymap_toggle_hud", "KEY_F1"); settings->setDefault("keymap_toggle_chat", "KEY_F2"); -- cgit v1.2.3 From 6014e7257e1052b8a4b3e29793aaf296b94b80d7 Mon Sep 17 00:00:00 2001 From: Snipie Date: Tue, 5 Jul 2016 07:43:25 +0000 Subject: Fix for cropped text "Toggle Cinematic" --- src/guiKeyChangeMenu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/guiKeyChangeMenu.cpp b/src/guiKeyChangeMenu.cpp index b05818256..785e921f7 100644 --- a/src/guiKeyChangeMenu.cpp +++ b/src/guiKeyChangeMenu.cpp @@ -137,7 +137,7 @@ void GUIKeyChangeMenu::regenerateGui(v2u32 screensize) { key_setting *k = key_settings.at(i); { - core::rect < s32 > rect(0, 0, 110, 20); + core::rect < s32 > rect(0, 0, 150, 20); rect += topleft + v2s32(offset.X, offset.Y); Environment->addStaticText(k->button_name, rect, false, true, this, -1); } -- cgit v1.2.3 From 7a532056e29d00006382caad9ccde66e54ca8aa5 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Mon, 20 Jun 2016 18:11:05 +0200 Subject: Use mathematical function to determine yaw direction --- src/game.cpp | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/game.cpp b/src/game.cpp index 93d9e6d2c..9b9f3a75f 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -4298,23 +4298,12 @@ void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, inline static const char *yawToDirectionString(int yaw) { - // NOTE: TODO: This can be done mathematically without the else/else-if - // cascade. - - const char *player_direction; + static const char *direction[4] = {"North [+Z]", "West [-X]", "South [-Z]", "East [+X]"}; yaw = wrapDegrees_0_360(yaw); + yaw = (yaw + 45) % 360 / 90; - if (yaw >= 45 && yaw < 135) - player_direction = "West [-X]"; - else if (yaw >= 135 && yaw < 225) - player_direction = "South [-Z]"; - else if (yaw >= 225 && yaw < 315) - player_direction = "East [+X]"; - else - player_direction = "North [+Z]"; - - return player_direction; + return direction[yaw]; } -- cgit v1.2.3 From 281e9f39fdffd106e79122ae23e2b5443a5daea3 Mon Sep 17 00:00:00 2001 From: Foghrye4 Date: Mon, 25 Apr 2016 18:50:10 +0300 Subject: Adding minetest.clear_craft Modifications by est31: grammar fixes in doc + error messages and a little style fix, no functional change. --- doc/lua_api.txt | 8 ++++ src/craftdef.cpp | 90 ++++++++++++++++++++++++++++++++++++++++++ src/craftdef.h | 4 ++ src/script/lua_api/l_craft.cpp | 76 ++++++++++++++++++++++++++++++++++- src/script/lua_api/l_craft.h | 1 + 5 files changed, 178 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 297566068..a59a9c0f2 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -1847,6 +1847,14 @@ Call these functions only at load time! * `minetest.register_craftitem(name, item definition)` * `minetest.register_alias(name, convert_to)` * `minetest.register_craft(recipe)` + * Check recipe table syntax for different types below. +* `minetest.clear_craft(recipe)` + * Will erase existing craft based either on output item or on input recipe. + * Specify either output or input only. If you specify both, input will be ignored. For input use the same recipe table + syntax as for `minetest.register_craft(recipe)`. For output specify only the item, without a quantity. + * If no erase candidate could be found, Lua exception will be thrown. + * Warning! The type field ("shaped","cooking" or any other) will be ignored if the recipe + contains output. Erasing is then done independently from the crafting method. * `minetest.register_ore(ore definition)` * `minetest.register_decoration(decoration definition)` * `minetest.override_item(name, redefinition)` diff --git a/src/craftdef.cpp b/src/craftdef.cpp index d3f1edaf9..45d3018a7 100644 --- a/src/craftdef.cpp +++ b/src/craftdef.cpp @@ -960,6 +960,96 @@ public: return recipes; } + + virtual bool clearCraftRecipesByOutput(const CraftOutput &output, IGameDef *gamedef) + { + std::map >::iterator vec_iter = + m_output_craft_definitions.find(output.item); + + if (vec_iter == m_output_craft_definitions.end()) + return false; + + std::vector &vec = vec_iter->second; + for (std::vector::iterator i = vec.begin(); + i != vec.end(); ++i) { + CraftDefinition *def = *i; + // Recipes are not yet hashed at this point + std::vector &unhashed_inputs_vec = m_craft_defs[(int) CRAFT_HASH_TYPE_UNHASHED][0]; + std::vector new_vec_by_input; + /* We will preallocate necessary memory addresses, so we don't need to reallocate them later. + This would save us some performance. */ + new_vec_by_input.reserve(unhashed_inputs_vec.size()); + for (std::vector::iterator i2 = unhashed_inputs_vec.begin(); + i2 != unhashed_inputs_vec.end(); ++i2) { + if (def != *i2) { + new_vec_by_input.push_back(*i2); + } + } + m_craft_defs[(int) CRAFT_HASH_TYPE_UNHASHED][0].swap(new_vec_by_input); + } + m_output_craft_definitions.erase(output.item); + return true; + } + + virtual bool clearCraftRecipesByInput(CraftMethod craft_method, unsigned int craft_grid_width, + const std::vector &recipe, IGameDef *gamedef) + { + bool all_empty = true; + for (std::vector::size_type i = 0; + i < recipe.size(); i++) { + if (!recipe[i].empty()) { + all_empty = false; + break; + } + } + if (all_empty) + return false; + + CraftInput input(craft_method, craft_grid_width, craftGetItems(recipe, gamedef)); + // Recipes are not yet hashed at this point + std::vector &unhashed_inputs_vec = m_craft_defs[(int) CRAFT_HASH_TYPE_UNHASHED][0]; + std::vector new_vec_by_input; + bool got_hit = false; + for (std::vector::size_type + i = unhashed_inputs_vec.size(); i > 0; i--) { + CraftDefinition *def = unhashed_inputs_vec[i - 1]; + /* If the input doesn't match the recipe definition, this recipe definition later + will be added back in source map. */ + if (!def->check(input, gamedef)) { + new_vec_by_input.push_back(def); + continue; + } + CraftOutput output = def->getOutput(input, gamedef); + got_hit = true; + std::map >::iterator + vec_iter = m_output_craft_definitions.find(output.item); + if (vec_iter == m_output_craft_definitions.end()) + continue; + std::vector &vec = vec_iter->second; + std::vector new_vec_by_output; + /* We will preallocate necessary memory addresses, so we don't need + to reallocate them later. This would save us some performance. */ + new_vec_by_output.reserve(vec.size()); + for (std::vector::iterator i = vec.begin(); + i != vec.end(); ++i) { + /* If pointers from map by input and output are not same, + we will add 'CraftDefinition*' to a new vector. */ + if (def != *i) { + /* Adding dereferenced iterator value (which are + 'CraftDefinition' reference) to a new vector. */ + new_vec_by_output.push_back(*i); + } + } + // Swaps assigned to current key value with new vector for output map. + m_output_craft_definitions[output.item].swap(new_vec_by_output); + } + if (got_hit) + // Swaps value with new vector for input map. + m_craft_defs[(int) CRAFT_HASH_TYPE_UNHASHED][0].swap(new_vec_by_input); + + return got_hit; + } + virtual std::string dump() const { std::ostringstream os(std::ios::binary); diff --git a/src/craftdef.h b/src/craftdef.h index cebb2d7ae..695ee0c2c 100644 --- a/src/craftdef.h +++ b/src/craftdef.h @@ -426,6 +426,10 @@ public: virtual std::vector getCraftRecipes(CraftOutput &output, IGameDef *gamedef, unsigned limit=0) const=0; + virtual bool clearCraftRecipesByOutput(const CraftOutput &output, IGameDef *gamedef) = 0; + virtual bool clearCraftRecipesByInput(CraftMethod craft_method, + unsigned int craft_grid_width, const std::vector &recipe, IGameDef *gamedef) = 0; + // Print crafting recipes for debugging virtual std::string dump() const=0; diff --git a/src/script/lua_api/l_craft.cpp b/src/script/lua_api/l_craft.cpp index 391a0133d..d135c689f 100644 --- a/src/script/lua_api/l_craft.cpp +++ b/src/script/lua_api/l_craft.cpp @@ -34,7 +34,6 @@ struct EnumString ModApiCraft::es_CraftMethod[] = {0, NULL}, }; - // helper for register_craft bool ModApiCraft::readCraftRecipeShaped(lua_State *L, int index, int &width, std::vector &recipe) @@ -281,6 +280,80 @@ int ModApiCraft::l_register_craft(lua_State *L) return 0; /* number of results */ } +// clear_craft({[output=item], [recipe={{item00,item10},{item01,item11}}]) +int ModApiCraft::l_clear_craft(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + luaL_checktype(L, 1, LUA_TTABLE); + int table = 1; + + // Get the writable craft definition manager from the server + IWritableCraftDefManager *craftdef = + getServer(L)->getWritableCraftDefManager(); + + std::string output = getstringfield_default(L, table, "output", ""); + std::string type = getstringfield_default(L, table, "type", "shaped"); + CraftOutput c_output(output, 0); + if (output != "") { + if (craftdef->clearCraftRecipesByOutput(c_output, getServer(L))) + return 0; + else + throw LuaError("No craft recipe known for output" + " (output=\"" + output + "\")"); + } + std::vector recipe; + int width = 0; + CraftMethod method = CRAFT_METHOD_NORMAL; + /* + CraftDefinitionShaped + */ + if (type == "shaped") { + lua_getfield(L, table, "recipe"); + if (lua_isnil(L, -1)) + throw LuaError("Either output or recipe has to be defined"); + if (!readCraftRecipeShaped(L, -1, width, recipe)) + throw LuaError("Invalid crafting recipe"); + } + /* + CraftDefinitionShapeless + */ + else if (type == "shapeless") { + lua_getfield(L, table, "recipe"); + if (lua_isnil(L, -1)) + throw LuaError("Either output or recipe has to be defined"); + if (!readCraftRecipeShapeless(L, -1, recipe)) + throw LuaError("Invalid crafting recipe"); + } + /* + CraftDefinitionCooking + */ + else if (type == "cooking") { + method = CRAFT_METHOD_COOKING; + std::string rec = getstringfield_default(L, table, "recipe", ""); + if (rec == "") + throw LuaError("Crafting definition (cooking)" + " is missing a recipe"); + recipe.push_back(rec); + } + /* + CraftDefinitionFuel + */ + else if (type == "fuel") { + method = CRAFT_METHOD_FUEL; + std::string rec = getstringfield_default(L, table, "recipe", ""); + if (rec == "") + throw LuaError("Crafting definition (fuel)" + " is missing a recipe"); + recipe.push_back(rec); + } else { + throw LuaError("Unknown crafting definition type: \"" + type + "\""); + } + if (!craftdef->clearCraftRecipesByInput(method, width, recipe, getServer(L))) + throw LuaError("No crafting specified for input"); + lua_pop(L, 1); + return 0; +} + // get_craft_result(input) int ModApiCraft::l_get_craft_result(lua_State *L) { @@ -431,4 +504,5 @@ void ModApiCraft::Initialize(lua_State *L, int top) API_FCT(get_craft_recipe); API_FCT(get_craft_result); API_FCT(register_craft); + API_FCT(clear_craft); } diff --git a/src/script/lua_api/l_craft.h b/src/script/lua_api/l_craft.h index 548608776..eb2bce706 100644 --- a/src/script/lua_api/l_craft.h +++ b/src/script/lua_api/l_craft.h @@ -33,6 +33,7 @@ private: static int l_get_craft_recipe(lua_State *L); static int l_get_all_craft_recipes(lua_State *L); static int l_get_craft_result(lua_State *L); + static int l_clear_craft(lua_State *L); static bool readCraftReplacements(lua_State *L, int index, CraftReplacements &replacements); -- cgit v1.2.3 From 795f1c75cb6e337984cccd042371cb8b9a3ef6c1 Mon Sep 17 00:00:00 2001 From: Jay Arndt Date: Wed, 6 Jul 2016 20:15:36 -0500 Subject: Fix warning in porting::setXorgWindowIcon when building without X11 (#4288) --- src/porting.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/porting.cpp b/src/porting.cpp index ddf8139ea..15a18bdfb 100644 --- a/src/porting.cpp +++ b/src/porting.cpp @@ -695,8 +695,8 @@ bool setXorgWindowIcon(IrrlichtDevice *device, delete [] icon_buffer; - return true; #endif + return true; } //// -- cgit v1.2.3 From 483cca78a1312aa3b9c01049b5ebf6842929ec7a Mon Sep 17 00:00:00 2001 From: est31 Date: Fri, 8 Jul 2016 06:24:22 +0200 Subject: Fix -Wterminate warning on GCC 6 Fixes #4137 --- src/database-sqlite3.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/database-sqlite3.cpp b/src/database-sqlite3.cpp index 07df976d4..095d485c0 100644 --- a/src/database-sqlite3.cpp +++ b/src/database-sqlite3.cpp @@ -56,8 +56,14 @@ SQLite format specification: SQLOK(sqlite3_prepare_v2(m_database, query, -1, &m_stmt_##name, NULL),\ "Failed to prepare query '" query "'") -#define FINALIZE_STATEMENT(statement) \ - SQLOK(sqlite3_finalize(statement), "Failed to finalize " #statement) +#define SQLOK_ERRSTREAM(s, m) \ + if ((s) != SQLITE_OK) { \ + errorstream << (m) << ": " \ + << sqlite3_errmsg(m_database) << std::endl; \ + } + +#define FINALIZE_STATEMENT(statement) SQLOK_ERRSTREAM(sqlite3_finalize(statement), \ + "Failed to finalize " #statement) int Database_SQLite3::busyHandler(void *data, int count) { @@ -289,6 +295,6 @@ Database_SQLite3::~Database_SQLite3() FINALIZE_STATEMENT(m_stmt_end) FINALIZE_STATEMENT(m_stmt_delete) - SQLOK(sqlite3_close(m_database), "Failed to close database"); + SQLOK_ERRSTREAM(sqlite3_close(m_database), "Failed to close database"); } -- cgit v1.2.3 From 9db78dc4c4130c1ef08696e5d381a157cb66e656 Mon Sep 17 00:00:00 2001 From: est31 Date: Fri, 8 Jul 2016 07:26:00 +0200 Subject: Fix -Wterminate warnings in rollback.cpp as well Fix #4137 for rollback.cpp as well. --- src/rollback.cpp | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/rollback.cpp b/src/rollback.cpp index ac4324cab..4d34decf3 100644 --- a/src/rollback.cpp +++ b/src/rollback.cpp @@ -42,6 +42,14 @@ with this program; if not, write to the Free Software Foundation, Inc., } #define SQLOK(f) SQLRES(f, SQLITE_OK) +#define SQLOK_ERRSTREAM(s, m) \ + if ((s) != SQLITE_OK) { \ + errorstream << "RollbackManager: " << (m) << ": " \ + << sqlite3_errmsg(db) << std::endl; \ + } + +#define FINALIZE_STATEMENT(statement) \ + SQLOK_ERRSTREAM(sqlite3_finalize(statement), "Failed to finalize " #statement) class ItemStackRow : public ItemStack { public: @@ -109,17 +117,17 @@ RollbackManager::~RollbackManager() { flush(); - SQLOK(sqlite3_finalize(stmt_insert)); - SQLOK(sqlite3_finalize(stmt_replace)); - SQLOK(sqlite3_finalize(stmt_select)); - SQLOK(sqlite3_finalize(stmt_select_range)); - SQLOK(sqlite3_finalize(stmt_select_withActor)); - SQLOK(sqlite3_finalize(stmt_knownActor_select)); - SQLOK(sqlite3_finalize(stmt_knownActor_insert)); - SQLOK(sqlite3_finalize(stmt_knownNode_select)); - SQLOK(sqlite3_finalize(stmt_knownNode_insert)); - - SQLOK(sqlite3_close(db)); + FINALIZE_STATEMENT(stmt_insert); + FINALIZE_STATEMENT(stmt_replace); + FINALIZE_STATEMENT(stmt_select); + FINALIZE_STATEMENT(stmt_select_range); + FINALIZE_STATEMENT(stmt_select_withActor); + FINALIZE_STATEMENT(stmt_knownActor_select); + FINALIZE_STATEMENT(stmt_knownActor_insert); + FINALIZE_STATEMENT(stmt_knownNode_select); + FINALIZE_STATEMENT(stmt_knownNode_insert); + + SQLOK_ERRSTREAM(sqlite3_close(db), "Could not close db"); } -- cgit v1.2.3 From 4c530dea1a156c1ae307b5f304fb55d0619ee372 Mon Sep 17 00:00:00 2001 From: Snipie Date: Sat, 9 Jul 2016 14:00:14 +0000 Subject: Fix typos (#4298) --- src/guiEngine.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/guiEngine.cpp b/src/guiEngine.cpp index e15533dcd..a3c35f68d 100644 --- a/src/guiEngine.cpp +++ b/src/guiEngine.cpp @@ -433,7 +433,7 @@ void GUIEngine::drawOverlay(video::IVideoDriver* driver) video::ITexture* texture = m_textures[TEX_LAYER_OVERLAY].texture; - /* If no texture, draw background of solid color */ + /* If no texture, draw nothing */ if(!texture) return; @@ -462,7 +462,7 @@ void GUIEngine::drawHeader(video::IVideoDriver* driver) v2s32 splashsize(((f32)texture->getOriginalSize().Width) * mult, ((f32)texture->getOriginalSize().Height) * mult); - // Don't draw the header is there isn't enough room + // Don't draw the header if there isn't enough room s32 free_space = (((s32)screensize.Height)-320)/2; if (free_space > splashsize.Y) { -- cgit v1.2.3 From 24ca754bd5cc2a6340d8a393a37b71643c6fda60 Mon Sep 17 00:00:00 2001 From: est31 Date: Tue, 12 Jul 2016 20:49:33 +0200 Subject: Update minetest.conf.example, and settings_translation_file.cpp --- minetest.conf.example | 21 +++++++++++++++++++++ src/settings_translation_file.cpp | 20 ++++++++++++++------ 2 files changed, 35 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/minetest.conf.example b/minetest.conf.example index bc0d4c527..704501f36 100644 --- a/minetest.conf.example +++ b/minetest.conf.example @@ -83,6 +83,20 @@ # type: bool # continuous_forward = false +# Enable Joysticks +# type: bool +# enable_joysticks = false + +# The time in seconds it takes between repeated events +# when holding down a joystick button combination. +# type: float +# repeat_joystick_button_time = 0.17 + +# The sensitivity of the joystick axes for moving the +# ingame view frustum around. +# type: float +# joystick_frustum_sensitivity = 170 + # Key for moving the player forward. # See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 # type: key @@ -732,6 +746,12 @@ # type: string # serverlist_url = servers.minetest.net +# Disable escape sequences, e.g. chat coloring. +# Use this if you want to run a server with pre-0.4.14 clients and you want to disable +# the escape sequences generated by mods. +# type: bool +# disable_escape_sequences = false + ## Network # Network port to listen (UDP). @@ -1527,3 +1547,4 @@ # type: string # modstore_details_url = https://forum.minetest.net/mmdb/mod/*/ + diff --git a/src/settings_translation_file.cpp b/src/settings_translation_file.cpp index f7e14dd65..78d1c799f 100644 --- a/src/settings_translation_file.cpp +++ b/src/settings_translation_file.cpp @@ -14,7 +14,7 @@ fake_function() { gettext("Noclip"); gettext("If enabled together with fly mode, player is able to fly through solid nodes.\nThis requires the \"noclip\" privilege on the server."); gettext("Cinematic mode"); - gettext("Smooths camera when moving and looking around.\nUseful for recording videos."); + gettext("Smooths camera when looking around. Also called look or mouse smoothing.\nUseful for recording videos."); gettext("Camera smoothing"); gettext("Smooths rotation of camera. 0 to disable."); gettext("Camera smoothing in cinematic mode"); @@ -35,6 +35,12 @@ fake_function() { gettext("Enable random user input (only used for testing)."); gettext("Continuous forward"); gettext("Continuous forward movement (only used for testing)."); + gettext("Enable Joysticks"); + gettext("Enable Joysticks"); + gettext("Joystick button repetition invterval"); + gettext("The time in seconds it takes between repeated events\nwhen holding down a joystick button combination."); + gettext("Joystick frustum sensitivity"); + gettext("The sensitivity of the joystick axes for moving the\ningame view frustum around."); gettext("Forward key"); gettext("Key for moving the player forward.\nSee http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3"); gettext("Backward key"); @@ -306,6 +312,8 @@ fake_function() { gettext("Automaticaly report to the serverlist."); gettext("Serverlist URL"); gettext("Announce to this serverlist.\nIf you want to announce your ipv6 address, use serverlist_url = v6.servers.minetest.net."); + gettext("Disable escape sequences"); + gettext("Disable escape sequences, e.g. chat coloring.\nUse this if you want to run a server with pre-0.4.14 clients and you want to disable\nthe escape sequences generated by mods."); gettext("Network"); gettext("Server port"); gettext("Network port to listen (UDP).\nThis value will be overridden when starting from the main menu."); @@ -437,7 +445,7 @@ fake_function() { gettext("Map generation limit"); gettext("Where the map generator stops.\nPlease note:\n- Limited to 31000 (setting above has no effect)\n- The map generator works in groups of 80x80x80 nodes (5x5x5 MapBlocks).\n- Those groups have an offset of -32, -32 nodes from the origin.\n- Only groups which are within the map_generation_limit are generated"); gettext("Mapgen flags"); - gettext("Global map generation attributes.\nIn Mapgen v6 the 'decorations' flag controls all decorations except trees\nand junglegrass, in all other mapgens this flag controls all decorations.\nThe default flags set in the engine are: caves, light, decorations\nThe flags string modifies the engine defaults.\nFlags that are not specified in the flag string are not modified from the default.\nFlags starting with 'no' are used to explicitly disable them."); + gettext("Global map generation attributes.\nIn Mapgen v6 the 'decorations' flag controls all decorations except trees\nand junglegrass, in all other mapgens this flag controls all decorations.\nFlags that are not specified in the flag string are not modified from the default.\nFlags starting with 'no' are used to explicitly disable them."); gettext("Advanced"); gettext("Chunk size"); gettext("Size of chunks to be generated at once by mapgen, stated in mapblocks (16 nodes)."); @@ -466,7 +474,7 @@ fake_function() { gettext("Mapgen v5 cave2 noise parameters"); gettext("Mapgen v6"); gettext("Mapgen v6 flags"); - gettext("Map generation attributes specific to Mapgen v6.\nWhen snowbiomes are enabled jungles are automatically enabled, the 'jungles' flag is ignored.\nThe default flags set in the engine are: biomeblend, mudflow\nThe flags string modifies the engine defaults.\nFlags that are not specified in the flag string are not modified from the default.\nFlags starting with 'no' are used to explicitly disable them."); + gettext("Map generation attributes specific to Mapgen v6.\nWhen snowbiomes are enabled jungles are automatically enabled, the 'jungles' flag is ignored.\nFlags that are not specified in the flag string are not modified from the default.\nFlags starting with 'no' are used to explicitly disable them."); gettext("Mapgen v6 desert frequency"); gettext("Controls size of deserts and beaches in Mapgen v6.\nWhen snowbiomes are enabled 'mgv6_freq_desert' is ignored."); gettext("Mapgen v6 beach frequency"); @@ -483,7 +491,7 @@ fake_function() { gettext("Mapgen v6 apple trees noise parameters"); gettext("Mapgen v7"); gettext("Mapgen v7 flags"); - gettext("Map generation attributes specific to Mapgen v7.\nThe 'ridges' flag controls the rivers.\nThe default flags set in the engine are: mountains, ridges\nThe flags string modifies the engine defaults.\nFlags that are not specified in the flag string are not modified from the default.\nFlags starting with 'no' are used to explicitly disable them."); + gettext("Map generation attributes specific to Mapgen v7.\nThe 'ridges' flag controls the rivers.\nFlags that are not specified in the flag string are not modified from the default.\nFlags starting with 'no' are used to explicitly disable them."); gettext("Mapgen v7 cave width"); gettext("Controls width of tunnels, a smaller value creates wider tunnels."); gettext("Mapgen v7 terrain base noise parameters"); @@ -499,7 +507,7 @@ fake_function() { gettext("Mapgen v7 cave2 noise parameters"); gettext("Mapgen flat"); gettext("Mapgen flat flags"); - gettext("Map generation attributes specific to Mapgen flat.\nOccasional lakes and hills can be added to the flat world.\nThe default flags set in the engine are: none\nThe flags string modifies the engine defaults.\nFlags that are not specified in the flag string are not modified from the default.\nFlags starting with 'no' are used to explicitly disable them."); + gettext("Map generation attributes specific to Mapgen flat.\nOccasional lakes and hills can be added to the flat world.\nFlags that are not specified in the flag string are not modified from the default.\nFlags starting with 'no' are used to explicitly disable them."); gettext("Mapgen flat ground level"); gettext("Y of flat ground."); gettext("Mapgen flat large cave depth"); @@ -547,7 +555,7 @@ fake_function() { gettext("Mapgen Valleys"); gettext("General"); gettext("Valleys C Flags"); - gettext("Map generation attributes specific to Mapgen Valleys.\n'altitude_chill' makes higher elevations colder, which may cause biome issues.\n'humid_rivers' modifies the humidity around rivers and in areas where water would tend to pool,\nit may interfere with delicately adjusted biomes.\nThe default flags set in the engine are: altitude_chill, humid_rivers\nThe flags string modifies the engine defaults.\nFlags that are not specified in the flag string are not modified from the default.\nFlags starting with 'no' are used to explicitly disable them."); + gettext("Map generation attributes specific to Mapgen Valleys.\n'altitude_chill' makes higher elevations colder, which may cause biome issues.\n'humid_rivers' modifies the humidity around rivers and in areas where water would tend to pool,\nit may interfere with delicately adjusted biomes.\nFlags that are not specified in the flag string are not modified from the default.\nFlags starting with 'no' are used to explicitly disable them."); gettext("Altitude Chill"); gettext("The altitude at which temperature drops by 20C"); gettext("Large cave depth"); -- cgit v1.2.3 From 91f68e7d11e89dbc73ec259eb057de51099dfe8c Mon Sep 17 00:00:00 2001 From: paramat Date: Sun, 10 Jul 2016 16:03:53 +0100 Subject: Treegen: Improve use of signed vs. unsigned integers To fix GCC 6.1.1 compilation warning: 'assuming signed overflow does not occur when assuming that (X - c) > X is always false' --- src/treegen.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/treegen.cpp b/src/treegen.cpp index 36d387c57..f37bf0e86 100644 --- a/src/treegen.cpp +++ b/src/treegen.cpp @@ -770,9 +770,9 @@ void make_pine_tree(MMVManip &vmanip, v3s16 p0, INodeDefManager *ndef, s32 seed) MapNode snownode(c_snow); PseudoRandom pr(seed); - s16 trunk_h = pr.range(9, 13); + u16 trunk_h = pr.range(9, 13); v3s16 p1 = p0; - for (s16 ii = 0; ii < trunk_h; ii++) { + for (u16 ii = 0; ii < trunk_h; ii++) { if (vmanip.m_area.contains(p1)) { u32 vi = vmanip.m_area.index(p1); vmanip.m_data[vi] = treenode; @@ -790,7 +790,7 @@ void make_pine_tree(MMVManip &vmanip, v3s16 p0, INodeDefManager *ndef, s32 seed) leaves_d[i] = 0; // Upper branches - s16 dev = 3; + u16 dev = 3; for (s16 yy = -1; yy <= 1; yy++) { for (s16 zz = -dev; zz <= dev; zz++) { u32 i = leaves_a.index(v3s16(-dev, yy, zz)); -- cgit v1.2.3 From 2ba8ad1dfe2698cf9820a6dde3b40423cd719ddf Mon Sep 17 00:00:00 2001 From: paramat Date: Wed, 20 Jul 2016 00:23:08 +0100 Subject: Mgvalleys / cavegen: Place riverbed nodes under river water When a CavesNoiseIntersection tunnel intersects a river place biome 'riverbed' nodes in tunnel entrance instead of biome 'top' nodes. --- src/cavegen.cpp | 13 +++++++++++-- src/mapgen_valleys.cpp | 16 +++++++++------- 2 files changed, 20 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/cavegen.cpp b/src/cavegen.cpp index dc7355fe0..bb6aa25a6 100644 --- a/src/cavegen.cpp +++ b/src/cavegen.cpp @@ -79,6 +79,7 @@ void CavesNoiseIntersection::generateCaves(MMVManip *vm, for (s16 z = nmin.Z; z <= nmax.Z; z++) for (s16 x = nmin.X; x <= nmax.X; x++, index2d++) { bool column_is_open = false; // Is column open to overground + bool is_under_river = false; // Is column under river water bool is_tunnel = false; // Is tunnel or tunnel floor u32 vi = vm->m_area.index(x, nmax.Y, z); u32 index3d = (z - nmin.Z) * m_zstride_1d + m_csize.Y * m_ystride + @@ -99,6 +100,10 @@ void CavesNoiseIntersection::generateCaves(MMVManip *vm, c == biome->c_water) { column_is_open = true; continue; + } else if (c == biome->c_river_water) { + column_is_open = true; + is_under_river = true; + continue; } // Ground float d1 = contour(noise_cave1->result[index3d]); @@ -111,9 +116,13 @@ void CavesNoiseIntersection::generateCaves(MMVManip *vm, } else { // Not in tunnel or not ground content if (is_tunnel && column_is_open && - (c == biome->c_filler || c == biome->c_stone)) + (c == biome->c_filler || c == biome->c_stone)) { // Tunnel entrance floor - vm->m_data[vi] = MapNode(biome->c_top); + if (is_under_river) + vm->m_data[vi] = MapNode(biome->c_riverbed); + else + vm->m_data[vi] = MapNode(biome->c_top); + } column_is_open = false; is_tunnel = false; diff --git a/src/mapgen_valleys.cpp b/src/mapgen_valleys.cpp index a61f1b329..456e6aa1a 100644 --- a/src/mapgen_valleys.cpp +++ b/src/mapgen_valleys.cpp @@ -665,6 +665,7 @@ void MapgenValleys::generateCaves(s16 max_stone_y, s16 large_cave_depth) for (s16 x = node_min.X; x <= node_max.X; x++, index_2d++) { Biome *biome = (Biome *)m_bmgr->getRaw(biomemap[index_2d]); bool tunnel_air_above = false; + bool is_under_river = false; bool underground = false; u32 index_data = vm->m_area.index(x, node_max.Y, z); u32 index_3d = (z - node_min.Z) * zstride_1d + csize.Y * ystride + (x - node_min.X); @@ -696,14 +697,13 @@ void MapgenValleys::generateCaves(s16 max_stone_y, s16 large_cave_depth) } content_t c = vm->m_data[index_data].getContent(); + // Detect river water to place riverbed nodes in tunnels + if (c == biome->c_river_water) + is_under_river = true; + float d1 = contour(noise_cave1->result[index_3d]); float d2 = contour(noise_cave2->result[index_3d]); - // River water is not set as ground content - // in the default game. This can produce strange results - // when a tunnel undercuts a river. However, that's not for - // the mapgen to correct. Fix it in lua. - if (d1 * d2 > cave_width && ndef->get(c).is_ground_content) { // in a tunnel vm->m_data[index_data] = n_air; @@ -716,8 +716,10 @@ void MapgenValleys::generateCaves(s16 max_stone_y, s16 large_cave_depth) vm->m_area.add_y(em, j, 1); if (sr > terrain - y) { - // Put dirt in tunnels near the surface. - if (underground) + // Put biome nodes in tunnels near the surface + if (is_under_river) + vm->m_data[index_data] = MapNode(biome->c_riverbed); + else if (underground) vm->m_data[index_data] = MapNode(biome->c_filler); else vm->m_data[index_data] = MapNode(biome->c_top); -- cgit v1.2.3 From f833159a68f2ff605224ed7a5ac956e5a0ca42d8 Mon Sep 17 00:00:00 2001 From: est31 Date: Sun, 24 Jul 2016 17:41:36 +0200 Subject: Update minetest.conf.example and the settings translation file --- minetest.conf.example | 73 ++++++++++++++++++++++++++++++++------- src/settings_translation_file.cpp | 32 +++++++++++++---- 2 files changed, 87 insertions(+), 18 deletions(-) (limited to 'src') diff --git a/minetest.conf.example b/minetest.conf.example index 704501f36..139909d29 100644 --- a/minetest.conf.example +++ b/minetest.conf.example @@ -973,18 +973,6 @@ # type: enum values: legacy, log, error # deprecated_lua_api_handling = legacy -# Useful for mod developers. -# type: bool -# mod_profiling = false - -# Detailed mod profile data. Useful for mod developers. -# type: bool -# detailed_profiling = false - -# Profiler data print interval. 0 = disable. Useful for developers. -# type: int -# profiler_print_interval = 0 - # Number of extra blocks that can be loaded by /clearobjects at once. # This is a trade-off between sqlite transaction overhead and # memory consumption (4096=100MB, as a rule of thumb). @@ -1476,6 +1464,63 @@ # type: string # secure.http_mods = +## Advanced + +### Profiling + +# Load the game profiler to collect game profiling data. +# Provides a /profiler command to access the compiled profile. +# Useful for mod developers and server operators. +# type: bool +# profiler.load = false + +# The default format in which profiles are being saved, +# when calling `/profiler save [format]` without format. +# type: enum values: txt, csv, lua, json, json_pretty +# profiler.default_report_format = txt + +# The file path relative to your worldpath in which profiles will be saved to. +# +# type: string +# profiler.report_path = "" + +#### Instrumentation + +# Instrument the methods of entities on registration. +# type: bool +# instrument.entity = true + +# Instrument the action function of Active Block Modifiers on registration. +# type: bool +# instrument.abm = true + +# Instrument the action function of Loading Block Modifiers on registration. +# type: bool +# instrument.lbm = true + +# Instrument chatcommands on registration. +# type: bool +# instrument.chatcommand = true + +# Instrument global callback functions on registration. +# (anything you pass to a minetest.register_*() function) +# type: bool +# instrument.global_callback = true + +##### Advanced + +# Instrument builtin. +# This is usually only needed by core/builtin contributors +# type: bool +# instrument.builtin = false + +# Have the profiler instrument itself: +# * Instrument an empty function. +# This estimates the overhead, that instrumentation is adding (+1 function call). +# * Instrument the sampler being used to update the statistics. +# type: bool +# instrument.profiler = false + # # Client and Server # @@ -1548,3 +1593,7 @@ # type: string # modstore_details_url = https://forum.minetest.net/mmdb/mod/*/ +# Print the engine's profiling data in regular intervals (in seconds). 0 = disable. Useful for developers. +# type: int +# profiler_print_interval = 0 + diff --git a/src/settings_translation_file.cpp b/src/settings_translation_file.cpp index 78d1c799f..3e82279cd 100644 --- a/src/settings_translation_file.cpp +++ b/src/settings_translation_file.cpp @@ -405,12 +405,6 @@ fake_function() { gettext("Advanced"); gettext("Deprecated Lua API handling"); gettext("Handling for deprecated lua api calls:\n- legacy: (try to) mimic old behaviour (default for release).\n- log: mimic and log backtrace of deprecated call (default for debug).\n- error: abort on usage of deprecated call (suggested for mod developers)."); - gettext("Mod profiling"); - gettext("Useful for mod developers."); - gettext("Detailed mod profiling"); - gettext("Detailed mod profile data. Useful for mod developers."); - gettext("Profiling print interval"); - gettext("Profiler data print interval. 0 = disable. Useful for developers."); gettext("Max. clearobjects extra blocks"); gettext("Number of extra blocks that can be loaded by /clearobjects at once.\nThis is a trade-off between sqlite transaction overhead and\nmemory consumption (4096=100MB, as a rule of thumb)."); gettext("Unload unused server data"); @@ -600,6 +594,30 @@ fake_function() { gettext("Comma-separated list of trusted mods that are allowed to access insecure\nfunctions even when mod security is on (via request_insecure_environment())."); gettext("HTTP Mods"); gettext("Comma-seperated list of mods that are allowed to access HTTP APIs, which\nallow them to upload and download data to/from the internet."); + gettext("Advanced"); + gettext("Profiling"); + gettext("Load the game profiler"); + gettext("Load the game profiler to collect game profiling data.\nProvides a /profiler command to access the compiled profile.\nUseful for mod developers and server operators."); + gettext("Default report format"); + gettext("The default format in which profiles are being saved,\nwhen calling `/profiler save [format]` without format."); + gettext("Report path"); + gettext("The file path relative to your worldpath in which profiles will be saved to.\n"); + gettext("Instrumentation"); + gettext("Entity methods"); + gettext("Instrument the methods of entities on registration."); + gettext("Active Block Modifiers"); + gettext("Instrument the action function of Active Block Modifiers on registration."); + gettext("Loading Block Modifiers"); + gettext("Instrument the action function of Loading Block Modifiers on registration."); + gettext("Chatcommands"); + gettext("Instrument chatcommands on registration."); + gettext("Global callbacks"); + gettext("Instrument global callback functions on registration.\n(anything you pass to a minetest.register_*() function)"); + gettext("Advanced"); + gettext("Builtin"); + gettext("Instrument builtin.\nThis is usually only needed by core/builtin contributors"); + gettext("Profiler"); + gettext("Have the profiler instrument itself:\n* Instrument an empty function.\nThis estimates the overhead, that instrumentation is adding (+1 function call).\n* Instrument the sampler being used to update the statistics."); gettext("Client and Server"); gettext("Player name"); gettext("Name of the player.\nWhen running a server, clients connecting with this name are admins.\nWhen starting from the main menu, this is overridden."); @@ -625,4 +643,6 @@ fake_function() { gettext("Modstore download URL"); gettext("Modstore mods list URL"); gettext("Modstore details URL"); + gettext("Engine profiling data print interval"); + gettext("Print the engine's profiling data in regular intervals (in seconds). 0 = disable. Useful for developers."); } -- cgit v1.2.3 From 8a98cbda8417c713e03d1ac0be80e88cd2f08a0c Mon Sep 17 00:00:00 2001 From: SmallJoker Date: Tue, 26 Jul 2016 14:39:03 +0200 Subject: Upright sprite objects: Horizontally flip the front image For consistent horizontal sprite structure when seen from front and back Fix code style --- src/content_cao.cpp | 84 +++++++++++++++++++++++++---------------------------- 1 file changed, 40 insertions(+), 44 deletions(-) (limited to 'src') diff --git a/src/content_cao.cpp b/src/content_cao.cpp index eeb85c8a6..f414b2b9b 100644 --- a/src/content_cao.cpp +++ b/src/content_cao.cpp @@ -839,54 +839,50 @@ void GenericCAO::addToScene(scene::ISceneManager *smgr, setBillboardTextureMatrix(m_spritenode, txs, tys, 0, 0); } - } - else if(m_prop.visual == "upright_sprite") { + } else if (m_prop.visual == "upright_sprite") { scene::SMesh *mesh = new scene::SMesh(); - double dx = BS*m_prop.visual_size.X/2; - double dy = BS*m_prop.visual_size.Y/2; - { // Front - scene::IMeshBuffer *buf = new scene::SMeshBuffer(); + double dx = BS * m_prop.visual_size.X / 2; + double dy = BS * m_prop.visual_size.Y / 2; u8 li = m_last_light; - video::SColor c(255,li,li,li); - video::S3DVertex vertices[4] = - { - video::S3DVertex(-dx,-dy,0, 0,0,0, c, 0,1), - video::S3DVertex(dx,-dy,0, 0,0,0, c, 1,1), - video::S3DVertex(dx,dy,0, 0,0,0, c, 1,0), - video::S3DVertex(-dx,dy,0, 0,0,0, c, 0,0), - }; - u16 indices[] = {0,1,2,2,3,0}; - buf->append(vertices, 4, indices, 6); - // Set material - buf->getMaterial().setFlag(video::EMF_LIGHTING, false); - buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false); - buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true); - buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; - // Add to mesh - mesh->addMeshBuffer(buf); - buf->drop(); + video::SColor c(255, li, li, li); + + { // Front + scene::IMeshBuffer *buf = new scene::SMeshBuffer(); + video::S3DVertex vertices[4] = { + video::S3DVertex(-dx, -dy, 0, 0,0,0, c, 1,1), + video::S3DVertex( dx, -dy, 0, 0,0,0, c, 0,1), + video::S3DVertex( dx, dy, 0, 0,0,0, c, 0,0), + video::S3DVertex(-dx, dy, 0, 0,0,0, c, 1,0), + }; + u16 indices[] = {0,1,2,2,3,0}; + buf->append(vertices, 4, indices, 6); + // Set material + buf->getMaterial().setFlag(video::EMF_LIGHTING, false); + buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false); + buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true); + buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; + // Add to mesh + mesh->addMeshBuffer(buf); + buf->drop(); } { // Back - scene::IMeshBuffer *buf = new scene::SMeshBuffer(); - u8 li = m_last_light; - video::SColor c(255,li,li,li); - video::S3DVertex vertices[4] = - { - video::S3DVertex(dx,-dy,0, 0,0,0, c, 1,1), - video::S3DVertex(-dx,-dy,0, 0,0,0, c, 0,1), - video::S3DVertex(-dx,dy,0, 0,0,0, c, 0,0), - video::S3DVertex(dx,dy,0, 0,0,0, c, 1,0), - }; - u16 indices[] = {0,1,2,2,3,0}; - buf->append(vertices, 4, indices, 6); - // Set material - buf->getMaterial().setFlag(video::EMF_LIGHTING, false); - buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false); - buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true); - buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; - // Add to mesh - mesh->addMeshBuffer(buf); - buf->drop(); + scene::IMeshBuffer *buf = new scene::SMeshBuffer(); + video::S3DVertex vertices[4] = { + video::S3DVertex( dx,-dy, 0, 0,0,0, c, 1,1), + video::S3DVertex(-dx,-dy, 0, 0,0,0, c, 0,1), + video::S3DVertex(-dx, dy, 0, 0,0,0, c, 0,0), + video::S3DVertex( dx, dy, 0, 0,0,0, c, 1,0), + }; + u16 indices[] = {0,1,2,2,3,0}; + buf->append(vertices, 4, indices, 6); + // Set material + buf->getMaterial().setFlag(video::EMF_LIGHTING, false); + buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false); + buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true); + buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; + // Add to mesh + mesh->addMeshBuffer(buf); + buf->drop(); } m_meshnode = smgr->addMeshSceneNode(mesh, NULL); m_meshnode->grab(); -- cgit v1.2.3 From 9edc984b090ca585c0850fa05e0cecac86679638 Mon Sep 17 00:00:00 2001 From: est31 Date: Wed, 3 Aug 2016 23:20:36 +0200 Subject: Porting: Fix endless loop if image format is not recognized --- src/porting.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/porting.cpp b/src/porting.cpp index 15a18bdfb..acd047232 100644 --- a/src/porting.cpp +++ b/src/porting.cpp @@ -619,7 +619,8 @@ bool setXorgWindowIcon(IrrlichtDevice *device, video::IVideoDriver *v_driver = device->getVideoDriver(); video::IImageLoader *image_loader = NULL; - for (u32 i = v_driver->getImageLoaderCount() - 1; i >= 0; i--) { + u32 cnt = v_driver->getImageLoaderCount(); + for (u32 i = 0; i < cnt; i++) { if (v_driver->getImageLoader(i)->isALoadableFileExtension(icon_file.c_str())) { image_loader = v_driver->getImageLoader(i); break; -- cgit v1.2.3 From 4ec667190967591727294ebbca0a38259fc006c7 Mon Sep 17 00:00:00 2001 From: est31 Date: Thu, 4 Aug 2016 00:29:06 +0200 Subject: test_map_settings_manager: Fix Wunused-result warning --- src/unittest/test_map_settings_manager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/unittest/test_map_settings_manager.cpp b/src/unittest/test_map_settings_manager.cpp index b2ad53192..597ec9a31 100644 --- a/src/unittest/test_map_settings_manager.cpp +++ b/src/unittest/test_map_settings_manager.cpp @@ -81,7 +81,7 @@ std::string read_file_to_string(const std::string &filepath) buf.resize(filesize); - fread(&buf[0], 1, filesize, f); + UASSERTEQ(size_t, fread(&buf[0], 1, filesize, f), 1); fclose(f); return buf; -- cgit v1.2.3 From fca8e53842718f7f23e11587ae6235c072892b97 Mon Sep 17 00:00:00 2001 From: Dorian Wouters Date: Thu, 4 Aug 2016 00:41:54 +0200 Subject: Fix l_request_insecure_environment not ignoring all whitespace (#4395) l_request_insecure_environment didn't ignore all whitespace in the secure.trusted_mods config option. Replaces std::remove with std::remove_if and the isspace function. --- src/script/lua_api/l_util.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/script/lua_api/l_util.cpp b/src/script/lua_api/l_util.cpp index d090fc91c..95a5fc4d1 100644 --- a/src/script/lua_api/l_util.cpp +++ b/src/script/lua_api/l_util.cpp @@ -446,8 +446,9 @@ int ModApiUtil::l_request_insecure_environment(lua_State *L) // Check secure.trusted_mods const char *mod_name = lua_tostring(L, -1); std::string trusted_mods = g_settings->get("secure.trusted_mods"); - trusted_mods.erase(std::remove(trusted_mods.begin(), - trusted_mods.end(), ' '), trusted_mods.end()); + trusted_mods.erase(std::remove_if(trusted_mods.begin(), + trusted_mods.end(), static_cast(&std::isspace)), + trusted_mods.end()); std::vector mod_list = str_split(trusted_mods, ','); if (std::find(mod_list.begin(), mod_list.end(), mod_name) == mod_list.end()) { -- cgit v1.2.3 From ea12da939fdb0a8fd13de885d104af3031ffc3ac Mon Sep 17 00:00:00 2001 From: paramat Date: Fri, 29 Jul 2016 03:44:58 +0100 Subject: Mapgen: Remove unused 'flat' and 'trees' flags from mg_flags When the 'flat' and 'trees' flags were moved into mgv6_spflags they were left in mg_flags in an attempt to support old mgv6 worlds. However their appearence in mg_flags causes confusion, also, later, old-world support was found to be broken for mgv6 worlds with 'notrees'. This commit cleans up the mess and comes a month after a thread warning of the change, and explaining the required action, was posted in the news subforum. Only old mgv6 worlds with 'flat' or 'notrees' are affected, a small minority of worlds, the required action being correctly setting these flags in mgv6_spflags. Disable a section of the 'map settings manager' unit test which is to be changed as it is causing problems for pull requests. --- src/mapgen.cpp | 2 -- src/mapgen.h | 4 ++-- src/mapgen_v6.cpp | 10 +++++----- src/unittest/test_map_settings_manager.cpp | 8 ++++++-- 4 files changed, 13 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/mapgen.cpp b/src/mapgen.cpp index b6fda91ac..fd4f5858f 100644 --- a/src/mapgen.cpp +++ b/src/mapgen.cpp @@ -50,10 +50,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "dungeongen.h" FlagDesc flagdesc_mapgen[] = { - {"trees", MG_TREES}, {"caves", MG_CAVES}, {"dungeons", MG_DUNGEONS}, - {"flat", MG_FLAT}, {"light", MG_LIGHT}, {"decorations", MG_DECORATIONS}, {NULL, 0} diff --git a/src/mapgen.h b/src/mapgen.h index 5fcf2a365..403fb7470 100644 --- a/src/mapgen.h +++ b/src/mapgen.h @@ -30,10 +30,10 @@ with this program; if not, write to the Free Software Foundation, Inc., #define MAPGEN_DEFAULT_NAME "v6" /////////////////// Mapgen flags -#define MG_TREES 0x01 +#define MG_TREES 0x01 // Deprecated. Moved into mgv6 flags #define MG_CAVES 0x02 #define MG_DUNGEONS 0x04 -#define MG_FLAT 0x08 +#define MG_FLAT 0x08 // Deprecated. Moved into mgv6 flags #define MG_LIGHT 0x10 #define MG_DECORATIONS 0x20 diff --git a/src/mapgen_v6.cpp b/src/mapgen_v6.cpp index e4444963f..79617a830 100644 --- a/src/mapgen_v6.cpp +++ b/src/mapgen_v6.cpp @@ -268,7 +268,7 @@ float MapgenV6::baseTerrainLevel(float terrain_base, float terrain_higher, float MapgenV6::baseTerrainLevelFromNoise(v2s16 p) { - if ((spflags & MGV6_FLAT) || (flags & MG_FLAT)) + if (spflags & MGV6_FLAT) return water_level; float terrain_base = NoisePerlin2D_PO(&noise_terrain_base->np, @@ -294,7 +294,7 @@ float MapgenV6::baseTerrainLevelFromMap(v2s16 p) float MapgenV6::baseTerrainLevelFromMap(int index) { - if ((spflags & MGV6_FLAT) || (flags & MG_FLAT)) + if (spflags & MGV6_FLAT) return water_level; float terrain_base = noise_terrain_base->result[index]; @@ -402,7 +402,7 @@ bool MapgenV6::getHaveAppleTree(v2s16 p) float MapgenV6::getMudAmount(int index) { - if ((spflags & MGV6_FLAT) || (flags & MG_FLAT)) + if (spflags & MGV6_FLAT) return MGV6_AVERAGE_MUD_AMOUNT; /*return ((float)AVERAGE_MUD_AMOUNT + 2.0 * noise2d_perlin( @@ -599,7 +599,7 @@ void MapgenV6::makeChunk(BlockMakeData *data) growGrass(); // Generate some trees, and add grass, if a jungle - if ((spflags & MGV6_TREES) || (flags & MG_TREES)) + if (spflags & MGV6_TREES) placeTreesAndJungleGrass(); // Generate the registered decorations @@ -626,7 +626,7 @@ void MapgenV6::calculateNoise() int fx = full_node_min.X; int fz = full_node_min.Z; - if (!((spflags & MGV6_FLAT) || (flags & MG_FLAT))) { + if (!(spflags & MGV6_FLAT)) { noise_terrain_base->perlinMap2D_PO(x, 0.5, z, 0.5); noise_terrain_higher->perlinMap2D_PO(x, 0.5, z, 0.5); noise_steepness->perlinMap2D_PO(x, 0.5, z, 0.5); diff --git a/src/unittest/test_map_settings_manager.cpp b/src/unittest/test_map_settings_manager.cpp index 597ec9a31..9292bf87c 100644 --- a/src/unittest/test_map_settings_manager.cpp +++ b/src/unittest/test_map_settings_manager.cpp @@ -187,10 +187,13 @@ void TestMapSettingsManager::testMapSettingsManager() UASSERT(mgr.mapgen_params == params); UASSERT(mgr.makeMapgenParams() == params); +#if 0 + // TODO(paramat or hmmmm): change this to compare the result against a static file + // Load the resulting map_meta.txt and make sure it contains what we expect unsigned char expected_contents_hash[20] = { - 0xf6, 0x44, 0x90, 0xb7, 0xab, 0xd8, 0x91, 0xf4, 0x08, 0x96, - 0xfc, 0x7e, 0xed, 0x01, 0xc5, 0x9a, 0xfd, 0x2f, 0x2d, 0x79 + 0x48, 0x3f, 0x88, 0x5a, 0xc0, 0x7a, 0x14, 0x48, 0xa4, 0x71, + 0x78, 0x56, 0x95, 0x2d, 0xdc, 0x6a, 0xf7, 0x61, 0x36, 0x5f }; SHA1 ctx; @@ -201,6 +204,7 @@ void TestMapSettingsManager::testMapSettingsManager() free(sha1_result); UASSERT(!resultdiff); +#endif } -- cgit v1.2.3 From b6d845adb4a0f39a58610c827817b1f881bfcebc Mon Sep 17 00:00:00 2001 From: paramat Date: Wed, 3 Aug 2016 03:44:33 +0100 Subject: Decorations: Fix decoration height check errors Fix height check comparison from '>=' to '>'. Fix getHeight() for schematic decorations to account for 'deco place center y' flag and for how normal placement sinks schematic 1 node into the ground. Jungletrees were not being placed at y = 46, y = 47 despite having an acceptable 16 nodes of height above ground surface. --- src/mg_decoration.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/mg_decoration.cpp b/src/mg_decoration.cpp index 8b6abb5d5..70dd9817a 100644 --- a/src/mg_decoration.cpp +++ b/src/mg_decoration.cpp @@ -145,7 +145,7 @@ size_t Decoration::placeDeco(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax) y < y_min || y > y_max) continue; - if (y + getHeight() >= mg->vm->m_area.MaxEdge.Y) { + if (y + getHeight() > mg->vm->m_area.MaxEdge.Y) { continue; #if 0 printf("Decoration at (%d %d %d) cut off\n", x, y, z); @@ -370,5 +370,9 @@ size_t DecoSchematic::generate(MMVManip *vm, PcgRandom *pr, v3s16 p) int DecoSchematic::getHeight() { - return schematic->size.Y; + // Account for a schematic being sunk into the ground by flag. + // When placed normally account for how a schematic is placed + // sunk 1 node into the ground. + return (flags & DECO_PLACE_CENTER_Y) ? + (schematic->size.Y - 1) / 2 : schematic->size.Y - 1; } -- cgit v1.2.3 From 058a869b70072aba8baea47e359c45e82daaf152 Mon Sep 17 00:00:00 2001 From: Ner'zhul Date: Wed, 10 Aug 2016 12:08:05 +0200 Subject: Permit usage of std::unordered_map & std::unorderered_set on c++11 compilers (#4430) This fallback to std::map & std::set for older compilers Use UNORDERED_SET as an example in decoration and ore biome sets Use UNORDERED_MAP as an example in nameidmapping --- src/mg_decoration.cpp | 2 +- src/mg_decoration.h | 4 ++-- src/mg_ore.cpp | 10 +++++----- src/mg_ore.h | 3 ++- src/nameidmapping.cpp | 2 +- src/nameidmapping.h | 12 ++++++------ src/script/lua_api/l_mapgen.cpp | 4 ++-- src/util/cpp11_container.h | 35 +++++++++++++++++++++++++++++++++++ 8 files changed, 54 insertions(+), 18 deletions(-) create mode 100644 src/util/cpp11_container.h (limited to 'src') diff --git a/src/mg_decoration.cpp b/src/mg_decoration.cpp index 70dd9817a..127915b51 100644 --- a/src/mg_decoration.cpp +++ b/src/mg_decoration.cpp @@ -156,7 +156,7 @@ size_t Decoration::placeDeco(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax) } if (mg->biomemap) { - std::set::iterator iter; + UNORDERED_SET::iterator iter; if (!biomes.empty()) { iter = biomes.find(mg->biomemap[mapindex]); diff --git a/src/mg_decoration.h b/src/mg_decoration.h index ba3e9d3b2..da98fd482 100644 --- a/src/mg_decoration.h +++ b/src/mg_decoration.h @@ -20,7 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #ifndef MG_DECORATION_HEADER #define MG_DECORATION_HEADER -#include +#include "util/cpp11_container.h" #include "objdef.h" #include "noise.h" #include "nodedef.h" @@ -83,7 +83,7 @@ public: float fill_ratio; NoiseParams np; - std::set biomes; + UNORDERED_SET biomes; //std::list cutoffs; //Mutex cutoff_mutex; }; diff --git a/src/mg_ore.cpp b/src/mg_ore.cpp index 257901614..d840d745a 100644 --- a/src/mg_ore.cpp +++ b/src/mg_ore.cpp @@ -148,7 +148,7 @@ void OreScatter::generate(MMVManip *vm, int mapseed, u32 blockseed, if (biomemap && !biomes.empty()) { u32 index = sizex * (z0 - nmin.Z) + (x0 - nmin.X); - std::set::iterator it = biomes.find(biomemap[index]); + UNORDERED_SET::iterator it = biomes.find(biomemap[index]); if (it == biomes.end()) continue; } @@ -202,7 +202,7 @@ void OreSheet::generate(MMVManip *vm, int mapseed, u32 blockseed, continue; if (biomemap && !biomes.empty()) { - std::set::iterator it = biomes.find(biomemap[index]); + UNORDERED_SET::iterator it = biomes.find(biomemap[index]); if (it == biomes.end()) continue; } @@ -270,7 +270,7 @@ void OrePuff::generate(MMVManip *vm, int mapseed, u32 blockseed, continue; if (biomemap && !biomes.empty()) { - std::set::iterator it = biomes.find(biomemap[index]); + UNORDERED_SET::iterator it = biomes.find(biomemap[index]); if (it == biomes.end()) continue; } @@ -338,7 +338,7 @@ void OreBlob::generate(MMVManip *vm, int mapseed, u32 blockseed, if (biomemap && !biomes.empty()) { u32 bmapidx = sizex * (z0 - nmin.Z) + (x0 - nmin.X); - std::set::iterator it = biomes.find(biomemap[bmapidx]); + UNORDERED_SET::iterator it = biomes.find(biomemap[bmapidx]); if (it == biomes.end()) continue; } @@ -422,7 +422,7 @@ void OreVein::generate(MMVManip *vm, int mapseed, u32 blockseed, if (biomemap && !biomes.empty()) { u32 bmapidx = sizex * (z - nmin.Z) + (x - nmin.X); - std::set::iterator it = biomes.find(biomemap[bmapidx]); + UNORDERED_SET::iterator it = biomes.find(biomemap[bmapidx]); if (it == biomes.end()) continue; } diff --git a/src/mg_ore.h b/src/mg_ore.h index 2e065cee3..e95fdd330 100644 --- a/src/mg_ore.h +++ b/src/mg_ore.h @@ -20,6 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #ifndef MG_ORE_HEADER #define MG_ORE_HEADER +#include "util/cpp11_container.h" #include "objdef.h" #include "noise.h" #include "nodedef.h" @@ -64,7 +65,7 @@ public: float nthresh; // threshold for noise at which an ore is placed NoiseParams np; // noise for distribution of clusters (NULL for uniform scattering) Noise *noise; - std::set biomes; + UNORDERED_SET biomes; Ore(); virtual ~Ore(); diff --git a/src/nameidmapping.cpp b/src/nameidmapping.cpp index ed59ddd16..2af8befff 100644 --- a/src/nameidmapping.cpp +++ b/src/nameidmapping.cpp @@ -25,7 +25,7 @@ void NameIdMapping::serialize(std::ostream &os) const { writeU8(os, 0); // version writeU16(os, m_id_to_name.size()); - for(std::map::const_iterator + for(UNORDERED_MAP::const_iterator i = m_id_to_name.begin(); i != m_id_to_name.end(); ++i){ writeU16(os, i->first); diff --git a/src/nameidmapping.h b/src/nameidmapping.h index 417c441d2..23838c8ff 100644 --- a/src/nameidmapping.h +++ b/src/nameidmapping.h @@ -23,15 +23,15 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include #include -#include #include "irrlichttypes_bloated.h" +#include "util/cpp11_container.h" class NameIdMapping { public: void serialize(std::ostream &os) const; void deSerialize(std::istream &is); - + void clear(){ m_id_to_name.clear(); m_name_to_id.clear(); @@ -55,7 +55,7 @@ public: m_name_to_id.erase(name); } bool getName(u16 id, std::string &result) const{ - std::map::const_iterator i; + UNORDERED_MAP::const_iterator i; i = m_id_to_name.find(id); if(i == m_id_to_name.end()) return false; @@ -63,7 +63,7 @@ public: return true; } bool getId(const std::string &name, u16 &result) const{ - std::map::const_iterator i; + UNORDERED_MAP::const_iterator i; i = m_name_to_id.find(name); if(i == m_name_to_id.end()) return false; @@ -74,8 +74,8 @@ public: return m_id_to_name.size(); } private: - std::map m_id_to_name; - std::map m_name_to_id; + UNORDERED_MAP m_id_to_name; + UNORDERED_MAP m_name_to_id; }; #endif diff --git a/src/script/lua_api/l_mapgen.cpp b/src/script/lua_api/l_mapgen.cpp index 9f14838ce..9f28231eb 100644 --- a/src/script/lua_api/l_mapgen.cpp +++ b/src/script/lua_api/l_mapgen.cpp @@ -100,7 +100,7 @@ Biome *get_or_load_biome(lua_State *L, int index, BiomeManager *biomemgr); Biome *read_biome_def(lua_State *L, int index, INodeDefManager *ndef); size_t get_biome_list(lua_State *L, int index, - BiomeManager *biomemgr, std::set *biome_id_list); + BiomeManager *biomemgr, UNORDERED_SET *biome_id_list); Schematic *get_or_load_schematic(lua_State *L, int index, SchematicManager *schemmgr, StringMap *replace_names); @@ -401,7 +401,7 @@ Biome *read_biome_def(lua_State *L, int index, INodeDefManager *ndef) size_t get_biome_list(lua_State *L, int index, - BiomeManager *biomemgr, std::set *biome_id_list) + BiomeManager *biomemgr, UNORDERED_SET *biome_id_list) { if (index < 0) index = lua_gettop(L) + 1 + index; diff --git a/src/util/cpp11_container.h b/src/util/cpp11_container.h new file mode 100644 index 000000000..f839be19a --- /dev/null +++ b/src/util/cpp11_container.h @@ -0,0 +1,35 @@ +/* +Minetest +Copyright (C) 2016 nerzhul, Loic Blot + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef MT_CPP11CONTAINER_HEADER +#define MT_CPP11CONTAINER_HEADER + +#if __cplusplus >= 201103L + #include + #include + #define UNORDERED_MAP std::unordered_map + #define UNORDERED_SET std::unordered_set +#else + #include + #include + #define UNORDERED_MAP std::map + #define UNORDERED_SET std::set +#endif + +#endif -- cgit v1.2.3 From 4503b5097f99d2806763650f33d8ef3b49f77ce4 Mon Sep 17 00:00:00 2001 From: Rogier-5 Date: Wed, 10 Aug 2016 12:10:00 +0200 Subject: Fixes for compiling with a newer (system) jsoncpp (#4429) * Move included json code to jsoncpp subdirectory This is needed to avoid having to specify the minetest src directory as a system include when fixing the json includes. * Fix json includes They used "", so that the compiler searches the project's directory first. The result was that when compiling with a system jsoncpp, the project's own version of json.h was still included, instead of the system version. The includes now use <>, so a system location, or one specified with '-Ilocation' is searched only. * Fix for jsoncpp deprecated function warning When compiling with a newer version of jsoncpp (and ENABLE_SYSTEM_JSONCPP=true), jsoncpp emits a warning about a deprecated function that minetest uses. --- cmake/Modules/FindJson.cmake | 4 +- src/convert_json.h | 2 +- src/json/CMakeLists.txt | 7 - src/json/UPDATING | 16 - src/json/json.h | 1914 ----------------- src/json/jsoncpp.cpp | 4367 --------------------------------------- src/jsoncpp/json/CMakeLists.txt | 7 + src/jsoncpp/json/UPDATING | 16 + src/jsoncpp/json/json.h | 1914 +++++++++++++++++ src/jsoncpp/json/jsoncpp.cpp | 4367 +++++++++++++++++++++++++++++++++++++++ src/mods.h | 2 +- src/script/common/c_content.cpp | 7 +- src/script/lua_api/l_util.cpp | 2 +- src/serverlist.cpp | 2 +- src/serverlist.h | 2 +- 15 files changed, 6317 insertions(+), 6312 deletions(-) delete mode 100644 src/json/CMakeLists.txt delete mode 100644 src/json/UPDATING delete mode 100644 src/json/json.h delete mode 100644 src/json/jsoncpp.cpp create mode 100644 src/jsoncpp/json/CMakeLists.txt create mode 100644 src/jsoncpp/json/UPDATING create mode 100644 src/jsoncpp/json/json.h create mode 100644 src/jsoncpp/json/jsoncpp.cpp (limited to 'src') diff --git a/cmake/Modules/FindJson.cmake b/cmake/Modules/FindJson.cmake index 1558b0fcf..e69d6c4c0 100644 --- a/cmake/Modules/FindJson.cmake +++ b/cmake/Modules/FindJson.cmake @@ -20,8 +20,8 @@ endif() if(NOT JSONCPP_FOUND) message(STATUS "Using bundled JSONCPP library.") - set(JSON_INCLUDE_DIR ${PROJECT_SOURCE_DIR}/json) + set(JSON_INCLUDE_DIR ${PROJECT_SOURCE_DIR}/jsoncpp) set(JSON_LIBRARY jsoncpp) - add_subdirectory(json) + add_subdirectory(jsoncpp/json) endif() diff --git a/src/convert_json.h b/src/convert_json.h index 6732fcfa3..55321af5f 100644 --- a/src/convert_json.h +++ b/src/convert_json.h @@ -20,7 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #ifndef __CONVERT_JSON_H__ #define __CONVERT_JSON_H__ -#include "json/json.h" +#include struct ModStoreMod; struct ModStoreModDetails; diff --git a/src/json/CMakeLists.txt b/src/json/CMakeLists.txt deleted file mode 100644 index 9056e4b6d..000000000 --- a/src/json/CMakeLists.txt +++ /dev/null @@ -1,7 +0,0 @@ -if(MSVC) - set(CMAKE_CXX_FLAGS_RELEASE "/MT /O2 /Ob2 /D NDEBUG") -endif() - -add_library(jsoncpp jsoncpp.cpp) -target_link_libraries(jsoncpp) - diff --git a/src/json/UPDATING b/src/json/UPDATING deleted file mode 100644 index d00076601..000000000 --- a/src/json/UPDATING +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/sh -cd .. -svn co https://jsoncpp.svn.sourceforge.net/svnroot/jsoncpp/trunk/jsoncpp jsoncpp -svn up jsoncpp -cd jsoncpp -python amalgamate.py -cp -R dist/json .. -cp dist/jsoncpp.cpp ../json - -# maybe you need to patch: -# src/json/jsoncpp.cpp: -# -#include -# +#include "json/json.h" - -#svn export --force https://jsoncpp.svn.sourceforge.net/svnroot/jsoncpp/trunk/jsoncpp/src/lib_json json -#svn export --force https://jsoncpp.svn.sourceforge.net/svnroot/jsoncpp/trunk/jsoncpp/include/json json diff --git a/src/json/json.h b/src/json/json.h deleted file mode 100644 index 396aafa82..000000000 --- a/src/json/json.h +++ /dev/null @@ -1,1914 +0,0 @@ -/// Json-cpp amalgated header (http://jsoncpp.sourceforge.net/). -/// It is intented to be used with #include - -// ////////////////////////////////////////////////////////////////////// -// Beginning of content of file: LICENSE -// ////////////////////////////////////////////////////////////////////// - -/* -The JsonCpp library's source code, including accompanying documentation, -tests and demonstration applications, are licensed under the following -conditions... - -The author (Baptiste Lepilleur) explicitly disclaims copyright in all -jurisdictions which recognize such a disclaimer. In such jurisdictions, -this software is released into the Public Domain. - -In jurisdictions which do not recognize Public Domain property (e.g. Germany as of -2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur, and is -released under the terms of the MIT License (see below). - -In jurisdictions which recognize Public Domain property, the user of this -software may choose to accept it either as 1) Public Domain, 2) under the -conditions of the MIT License (see below), or 3) under the terms of dual -Public Domain/MIT License conditions described here, as they choose. - -The MIT License is about as close to Public Domain as a license can get, and is -described in clear, concise terms at: - - http://en.wikipedia.org/wiki/MIT_License - -The full text of the MIT License follows: - -======================================================================== -Copyright (c) 2007-2010 Baptiste Lepilleur - -Permission is hereby granted, free of charge, to any person -obtaining a copy of this software and associated documentation -files (the "Software"), to deal in the Software without -restriction, including without limitation the rights to use, copy, -modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -======================================================================== -(END LICENSE TEXT) - -The MIT license is compatible with both the GPL and commercial -software, affording one all of the rights of Public Domain with the -minor nuisance of being required to keep the above copyright notice -and license text in the source code. Note also that by accepting the -Public Domain "license" you can re-license your copy using whatever -license you like. - -*/ - -// ////////////////////////////////////////////////////////////////////// -// End of content of file: LICENSE -// ////////////////////////////////////////////////////////////////////// - - - - - -#ifndef JSON_AMALGATED_H_INCLUDED -# define JSON_AMALGATED_H_INCLUDED -/// If defined, indicates that the source file is amalgated -/// to prevent private header inclusion. -#define JSON_IS_AMALGAMATION - -// ////////////////////////////////////////////////////////////////////// -// Beginning of content of file: include/json/config.h -// ////////////////////////////////////////////////////////////////////// - -// Copyright 2007-2010 Baptiste Lepilleur -// Distributed under MIT license, or public domain if desired and -// recognized in your jurisdiction. -// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE - -#ifndef JSON_CONFIG_H_INCLUDED -# define JSON_CONFIG_H_INCLUDED - -/// If defined, indicates that json library is embedded in CppTL library. -//# define JSON_IN_CPPTL 1 - -/// If defined, indicates that json may leverage CppTL library -//# define JSON_USE_CPPTL 1 -/// If defined, indicates that cpptl vector based map should be used instead of std::map -/// as Value container. -//# define JSON_USE_CPPTL_SMALLMAP 1 -/// If defined, indicates that Json specific container should be used -/// (hash table & simple deque container with customizable allocator). -/// THIS FEATURE IS STILL EXPERIMENTAL! There is know bugs: See #3177332 -//# define JSON_VALUE_USE_INTERNAL_MAP 1 -/// Force usage of standard new/malloc based allocator instead of memory pool based allocator. -/// The memory pools allocator used optimization (initializing Value and ValueInternalLink -/// as if it was a POD) that may cause some validation tool to report errors. -/// Only has effects if JSON_VALUE_USE_INTERNAL_MAP is defined. -//# define JSON_USE_SIMPLE_INTERNAL_ALLOCATOR 1 - -// If non-zero, the library uses exceptions to report bad input instead of C -// assertion macros. The default is to use exceptions. -# ifndef JSON_USE_EXCEPTION -# define JSON_USE_EXCEPTION 1 -# endif - -/// If defined, indicates that the source file is amalgated -/// to prevent private header inclusion. -/// Remarks: it is automatically defined in the generated amalgated header. -// #define JSON_IS_AMALGAMATION - - -# ifdef JSON_IN_CPPTL -# include -# ifndef JSON_USE_CPPTL -# define JSON_USE_CPPTL 1 -# endif -# endif - -# ifdef JSON_IN_CPPTL -# define JSON_API CPPTL_API -# elif defined(JSON_DLL_BUILD) -# define JSON_API __declspec(dllexport) -# elif defined(JSON_DLL) -# define JSON_API __declspec(dllimport) -# else -# define JSON_API -# endif - -// If JSON_NO_INT64 is defined, then Json only support C++ "int" type for integer -// Storages, and 64 bits integer support is disabled. -// #define JSON_NO_INT64 1 - -#if defined(_MSC_VER) && _MSC_VER <= 1200 // MSVC 6 -// Microsoft Visual Studio 6 only support conversion from __int64 to double -// (no conversion from unsigned __int64). -#define JSON_USE_INT64_DOUBLE_CONVERSION 1 -#endif // if defined(_MSC_VER) && _MSC_VER < 1200 // MSVC 6 - -#if defined(_MSC_VER) && _MSC_VER >= 1500 // MSVC 2008 -/// Indicates that the following function is deprecated. -# define JSONCPP_DEPRECATED(message) __declspec(deprecated(message)) -#endif - -#if !defined(JSONCPP_DEPRECATED) -# define JSONCPP_DEPRECATED(message) -#endif // if !defined(JSONCPP_DEPRECATED) - -namespace Json { - typedef int Int; - typedef unsigned int UInt; -# if defined(JSON_NO_INT64) - typedef int LargestInt; - typedef unsigned int LargestUInt; -# undef JSON_HAS_INT64 -# else // if defined(JSON_NO_INT64) - // For Microsoft Visual use specific types as long long is not supported -# if defined(_MSC_VER) // Microsoft Visual Studio - typedef __int64 Int64; - typedef unsigned __int64 UInt64; -# else // if defined(_MSC_VER) // Other platforms, use long long - typedef long long int Int64; - typedef unsigned long long int UInt64; -# endif // if defined(_MSC_VER) - typedef Int64 LargestInt; - typedef UInt64 LargestUInt; -# define JSON_HAS_INT64 -# endif // if defined(JSON_NO_INT64) -} // end namespace Json - - -#endif // JSON_CONFIG_H_INCLUDED - -// ////////////////////////////////////////////////////////////////////// -// End of content of file: include/json/config.h -// ////////////////////////////////////////////////////////////////////// - - - - - - -// ////////////////////////////////////////////////////////////////////// -// Beginning of content of file: include/json/forwards.h -// ////////////////////////////////////////////////////////////////////// - -// Copyright 2007-2010 Baptiste Lepilleur -// Distributed under MIT license, or public domain if desired and -// recognized in your jurisdiction. -// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE - -#ifndef JSON_FORWARDS_H_INCLUDED -# define JSON_FORWARDS_H_INCLUDED - -#if !defined(JSON_IS_AMALGAMATION) -# include "config.h" -#endif // if !defined(JSON_IS_AMALGAMATION) - -namespace Json { - - // writer.h - class FastWriter; - class StyledWriter; - - // reader.h - class Reader; - - // features.h - class Features; - - // value.h - typedef unsigned int ArrayIndex; - class StaticString; - class Path; - class PathArgument; - class Value; - class ValueIteratorBase; - class ValueIterator; - class ValueConstIterator; -#ifdef JSON_VALUE_USE_INTERNAL_MAP - class ValueMapAllocator; - class ValueInternalLink; - class ValueInternalArray; - class ValueInternalMap; -#endif // #ifdef JSON_VALUE_USE_INTERNAL_MAP - -} // namespace Json - - -#endif // JSON_FORWARDS_H_INCLUDED - -// ////////////////////////////////////////////////////////////////////// -// End of content of file: include/json/forwards.h -// ////////////////////////////////////////////////////////////////////// - - - - - - -// ////////////////////////////////////////////////////////////////////// -// Beginning of content of file: include/json/features.h -// ////////////////////////////////////////////////////////////////////// - -// Copyright 2007-2010 Baptiste Lepilleur -// Distributed under MIT license, or public domain if desired and -// recognized in your jurisdiction. -// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE - -#ifndef CPPTL_JSON_FEATURES_H_INCLUDED -# define CPPTL_JSON_FEATURES_H_INCLUDED - -#if !defined(JSON_IS_AMALGAMATION) -# include "forwards.h" -#endif // if !defined(JSON_IS_AMALGAMATION) - -namespace Json { - - /** \brief Configuration passed to reader and writer. - * This configuration object can be used to force the Reader or Writer - * to behave in a standard conforming way. - */ - class JSON_API Features - { - public: - /** \brief A configuration that allows all features and assumes all strings are UTF-8. - * - C & C++ comments are allowed - * - Root object can be any JSON value - * - Assumes Value strings are encoded in UTF-8 - */ - static Features all(); - - /** \brief A configuration that is strictly compatible with the JSON specification. - * - Comments are forbidden. - * - Root object must be either an array or an object value. - * - Assumes Value strings are encoded in UTF-8 - */ - static Features strictMode(); - - /** \brief Initialize the configuration like JsonConfig::allFeatures; - */ - Features(); - - /// \c true if comments are allowed. Default: \c true. - bool allowComments_; - - /// \c true if root must be either an array or an object value. Default: \c false. - bool strictRoot_; - }; - -} // namespace Json - -#endif // CPPTL_JSON_FEATURES_H_INCLUDED - -// ////////////////////////////////////////////////////////////////////// -// End of content of file: include/json/features.h -// ////////////////////////////////////////////////////////////////////// - - - - - - -// ////////////////////////////////////////////////////////////////////// -// Beginning of content of file: include/json/value.h -// ////////////////////////////////////////////////////////////////////// - -// Copyright 2007-2010 Baptiste Lepilleur -// Distributed under MIT license, or public domain if desired and -// recognized in your jurisdiction. -// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE - -#ifndef CPPTL_JSON_H_INCLUDED -# define CPPTL_JSON_H_INCLUDED - -#if !defined(JSON_IS_AMALGAMATION) -# include "forwards.h" -#endif // if !defined(JSON_IS_AMALGAMATION) -# include -# include - -# ifndef JSON_USE_CPPTL_SMALLMAP -# include -# else -# include -# endif -# ifdef JSON_USE_CPPTL -# include -# endif - -/** \brief JSON (JavaScript Object Notation). - */ -namespace Json { - - /** \brief Type of the value held by a Value object. - */ - enum ValueType - { - nullValue = 0, ///< 'null' value - intValue, ///< signed integer value - uintValue, ///< unsigned integer value - realValue, ///< double value - stringValue, ///< UTF-8 string value - booleanValue, ///< bool value - arrayValue, ///< array value (ordered list) - objectValue ///< object value (collection of name/value pairs). - }; - - enum CommentPlacement - { - commentBefore = 0, ///< a comment placed on the line before a value - commentAfterOnSameLine, ///< a comment just after a value on the same line - commentAfter, ///< a comment on the line after a value (only make sense for root value) - numberOfCommentPlacement - }; - -//# ifdef JSON_USE_CPPTL -// typedef CppTL::AnyEnumerator EnumMemberNames; -// typedef CppTL::AnyEnumerator EnumValues; -//# endif - - /** \brief Lightweight wrapper to tag static string. - * - * Value constructor and objectValue member assignement takes advantage of the - * StaticString and avoid the cost of string duplication when storing the - * string or the member name. - * - * Example of usage: - * \code - * Json::Value aValue( StaticString("some text") ); - * Json::Value object; - * static const StaticString code("code"); - * object[code] = 1234; - * \endcode - */ - class JSON_API StaticString - { - public: - explicit StaticString( const char *czstring ) - : str_( czstring ) - { - } - - operator const char *() const - { - return str_; - } - - const char *c_str() const - { - return str_; - } - - private: - const char *str_; - }; - - /** \brief Represents a JSON value. - * - * This class is a discriminated union wrapper that can represents a: - * - signed integer [range: Value::minInt - Value::maxInt] - * - unsigned integer (range: 0 - Value::maxUInt) - * - double - * - UTF-8 string - * - boolean - * - 'null' - * - an ordered list of Value - * - collection of name/value pairs (javascript object) - * - * The type of the held value is represented by a #ValueType and - * can be obtained using type(). - * - * values of an #objectValue or #arrayValue can be accessed using operator[]() methods. - * Non const methods will automatically create the a #nullValue element - * if it does not exist. - * The sequence of an #arrayValue will be automatically resize and initialized - * with #nullValue. resize() can be used to enlarge or truncate an #arrayValue. - * - * The get() methods can be used to obtanis default value in the case the required element - * does not exist. - * - * It is possible to iterate over the list of a #objectValue values using - * the getMemberNames() method. - */ - class JSON_API Value - { - friend class ValueIteratorBase; -# ifdef JSON_VALUE_USE_INTERNAL_MAP - friend class ValueInternalLink; - friend class ValueInternalMap; -# endif - public: - typedef std::vector Members; - typedef ValueIterator iterator; - typedef ValueConstIterator const_iterator; - typedef Json::UInt UInt; - typedef Json::Int Int; -# if defined(JSON_HAS_INT64) - typedef Json::UInt64 UInt64; - typedef Json::Int64 Int64; -#endif // defined(JSON_HAS_INT64) - typedef Json::LargestInt LargestInt; - typedef Json::LargestUInt LargestUInt; - typedef Json::ArrayIndex ArrayIndex; - - static const Value null; - /// Minimum signed integer value that can be stored in a Json::Value. - static const LargestInt minLargestInt; - /// Maximum signed integer value that can be stored in a Json::Value. - static const LargestInt maxLargestInt; - /// Maximum unsigned integer value that can be stored in a Json::Value. - static const LargestUInt maxLargestUInt; - - /// Minimum signed int value that can be stored in a Json::Value. - static const Int minInt; - /// Maximum signed int value that can be stored in a Json::Value. - static const Int maxInt; - /// Maximum unsigned int value that can be stored in a Json::Value. - static const UInt maxUInt; - -# if defined(JSON_HAS_INT64) - /// Minimum signed 64 bits int value that can be stored in a Json::Value. - static const Int64 minInt64; - /// Maximum signed 64 bits int value that can be stored in a Json::Value. - static const Int64 maxInt64; - /// Maximum unsigned 64 bits int value that can be stored in a Json::Value. - static const UInt64 maxUInt64; -#endif // defined(JSON_HAS_INT64) - - private: -#ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION -# ifndef JSON_VALUE_USE_INTERNAL_MAP - class CZString - { - public: - enum DuplicationPolicy - { - noDuplication = 0, - duplicate, - duplicateOnCopy - }; - CZString( ArrayIndex index ); - CZString( const char *cstr, DuplicationPolicy allocate ); - CZString( const CZString &other ); - ~CZString(); - CZString &operator =( const CZString &other ); - bool operator<( const CZString &other ) const; - bool operator==( const CZString &other ) const; - ArrayIndex index() const; - const char *c_str() const; - bool isStaticString() const; - private: - void swap( CZString &other ); - const char *cstr_; - ArrayIndex index_; - }; - - public: -# ifndef JSON_USE_CPPTL_SMALLMAP - typedef std::map ObjectValues; -# else - typedef CppTL::SmallMap ObjectValues; -# endif // ifndef JSON_USE_CPPTL_SMALLMAP -# endif // ifndef JSON_VALUE_USE_INTERNAL_MAP -#endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION - - public: - /** \brief Create a default Value of the given type. - - This is a very useful constructor. - To create an empty array, pass arrayValue. - To create an empty object, pass objectValue. - Another Value can then be set to this one by assignment. - This is useful since clear() and resize() will not alter types. - - Examples: - \code - Json::Value null_value; // null - Json::Value arr_value(Json::arrayValue); // [] - Json::Value obj_value(Json::objectValue); // {} - \endcode - */ - Value( ValueType type = nullValue ); - Value( Int value ); - Value( UInt value ); -#if defined(JSON_HAS_INT64) - Value( Int64 value ); - Value( UInt64 value ); -#endif // if defined(JSON_HAS_INT64) - Value( double value ); - Value( const char *value ); - Value( const char *beginValue, const char *endValue ); - /** \brief Constructs a value from a static string. - - * Like other value string constructor but do not duplicate the string for - * internal storage. The given string must remain alive after the call to this - * constructor. - * Example of usage: - * \code - * Json::Value aValue( StaticString("some text") ); - * \endcode - */ - Value( const StaticString &value ); - Value( const std::string &value ); -# ifdef JSON_USE_CPPTL - Value( const CppTL::ConstString &value ); -# endif - Value( bool value ); - Value( const Value &other ); - ~Value(); - - Value &operator=( const Value &other ); - /// Swap values. - /// \note Currently, comments are intentionally not swapped, for - /// both logic and efficiency. - void swap( Value &other ); - - ValueType type() const; - - bool operator <( const Value &other ) const; - bool operator <=( const Value &other ) const; - bool operator >=( const Value &other ) const; - bool operator >( const Value &other ) const; - - bool operator ==( const Value &other ) const; - bool operator !=( const Value &other ) const; - - int compare( const Value &other ) const; - - const char *asCString() const; - std::string asString() const; -# ifdef JSON_USE_CPPTL - CppTL::ConstString asConstString() const; -# endif - Int asInt() const; - UInt asUInt() const; -#if defined(JSON_HAS_INT64) - Int64 asInt64() const; - UInt64 asUInt64() const; -#endif // if defined(JSON_HAS_INT64) - LargestInt asLargestInt() const; - LargestUInt asLargestUInt() const; - float asFloat() const; - double asDouble() const; - bool asBool() const; - - bool isNull() const; - bool isBool() const; - bool isInt() const; - bool isInt64() const; - bool isUInt() const; - bool isUInt64() const; - bool isIntegral() const; - bool isDouble() const; - bool isNumeric() const; - bool isString() const; - bool isArray() const; - bool isObject() const; - - bool isConvertibleTo( ValueType other ) const; - - /// Number of values in array or object - ArrayIndex size() const; - - /// \brief Return true if empty array, empty object, or null; - /// otherwise, false. - bool empty() const; - - /// Return isNull() - bool operator!() const; - - /// Remove all object members and array elements. - /// \pre type() is arrayValue, objectValue, or nullValue - /// \post type() is unchanged - void clear(); - - /// Resize the array to size elements. - /// New elements are initialized to null. - /// May only be called on nullValue or arrayValue. - /// \pre type() is arrayValue or nullValue - /// \post type() is arrayValue - void resize( ArrayIndex size ); - - /// Access an array element (zero based index ). - /// If the array contains less than index element, then null value are inserted - /// in the array so that its size is index+1. - /// (You may need to say 'value[0u]' to get your compiler to distinguish - /// this from the operator[] which takes a string.) - Value &operator[]( ArrayIndex index ); - - /// Access an array element (zero based index ). - /// If the array contains less than index element, then null value are inserted - /// in the array so that its size is index+1. - /// (You may need to say 'value[0u]' to get your compiler to distinguish - /// this from the operator[] which takes a string.) - Value &operator[]( int index ); - - /// Access an array element (zero based index ) - /// (You may need to say 'value[0u]' to get your compiler to distinguish - /// this from the operator[] which takes a string.) - const Value &operator[]( ArrayIndex index ) const; - - /// Access an array element (zero based index ) - /// (You may need to say 'value[0u]' to get your compiler to distinguish - /// this from the operator[] which takes a string.) - const Value &operator[]( int index ) const; - - /// If the array contains at least index+1 elements, returns the element value, - /// otherwise returns defaultValue. - Value get( ArrayIndex index, - const Value &defaultValue ) const; - /// Return true if index < size(). - bool isValidIndex( ArrayIndex index ) const; - /// \brief Append value to array at the end. - /// - /// Equivalent to jsonvalue[jsonvalue.size()] = value; - Value &append( const Value &value ); - - /// Access an object value by name, create a null member if it does not exist. - Value &operator[]( const char *key ); - /// Access an object value by name, returns null if there is no member with that name. - const Value &operator[]( const char *key ) const; - /// Access an object value by name, create a null member if it does not exist. - Value &operator[]( const std::string &key ); - /// Access an object value by name, returns null if there is no member with that name. - const Value &operator[]( const std::string &key ) const; - /** \brief Access an object value by name, create a null member if it does not exist. - - * If the object as no entry for that name, then the member name used to store - * the new entry is not duplicated. - * Example of use: - * \code - * Json::Value object; - * static const StaticString code("code"); - * object[code] = 1234; - * \endcode - */ - Value &operator[]( const StaticString &key ); -# ifdef JSON_USE_CPPTL - /// Access an object value by name, create a null member if it does not exist. - Value &operator[]( const CppTL::ConstString &key ); - /// Access an object value by name, returns null if there is no member with that name. - const Value &operator[]( const CppTL::ConstString &key ) const; -# endif - /// Return the member named key if it exist, defaultValue otherwise. - Value get( const char *key, - const Value &defaultValue ) const; - /// Return the member named key if it exist, defaultValue otherwise. - Value get( const std::string &key, - const Value &defaultValue ) const; -# ifdef JSON_USE_CPPTL - /// Return the member named key if it exist, defaultValue otherwise. - Value get( const CppTL::ConstString &key, - const Value &defaultValue ) const; -# endif - /// \brief Remove and return the named member. - /// - /// Do nothing if it did not exist. - /// \return the removed Value, or null. - /// \pre type() is objectValue or nullValue - /// \post type() is unchanged - Value removeMember( const char* key ); - /// Same as removeMember(const char*) - Value removeMember( const std::string &key ); - - /// Return true if the object has a member named key. - bool isMember( const char *key ) const; - /// Return true if the object has a member named key. - bool isMember( const std::string &key ) const; -# ifdef JSON_USE_CPPTL - /// Return true if the object has a member named key. - bool isMember( const CppTL::ConstString &key ) const; -# endif - - /// \brief Return a list of the member names. - /// - /// If null, return an empty list. - /// \pre type() is objectValue or nullValue - /// \post if type() was nullValue, it remains nullValue - Members getMemberNames() const; - -//# ifdef JSON_USE_CPPTL -// EnumMemberNames enumMemberNames() const; -// EnumValues enumValues() const; -//# endif - - /// Comments must be //... or /* ... */ - void setComment( const char *comment, - CommentPlacement placement ); - /// Comments must be //... or /* ... */ - void setComment( const std::string &comment, - CommentPlacement placement ); - bool hasComment( CommentPlacement placement ) const; - /// Include delimiters and embedded newlines. - std::string getComment( CommentPlacement placement ) const; - - std::string toStyledString() const; - - const_iterator begin() const; - const_iterator end() const; - - iterator begin(); - iterator end(); - - private: - Value &resolveReference( const char *key, - bool isStatic ); - -# ifdef JSON_VALUE_USE_INTERNAL_MAP - inline bool isItemAvailable() const - { - return itemIsUsed_ == 0; - } - - inline void setItemUsed( bool isUsed = true ) - { - itemIsUsed_ = isUsed ? 1 : 0; - } - - inline bool isMemberNameStatic() const - { - return memberNameIsStatic_ == 0; - } - - inline void setMemberNameIsStatic( bool isStatic ) - { - memberNameIsStatic_ = isStatic ? 1 : 0; - } -# endif // # ifdef JSON_VALUE_USE_INTERNAL_MAP - - private: - struct CommentInfo - { - CommentInfo(); - ~CommentInfo(); - - void setComment( const char *text ); - - char *comment_; - }; - - //struct MemberNamesTransform - //{ - // typedef const char *result_type; - // const char *operator()( const CZString &name ) const - // { - // return name.c_str(); - // } - //}; - - union ValueHolder - { - LargestInt int_; - LargestUInt uint_; - double real_; - bool bool_; - char *string_; -# ifdef JSON_VALUE_USE_INTERNAL_MAP - ValueInternalArray *array_; - ValueInternalMap *map_; -#else - ObjectValues *map_; -# endif - } value_; - ValueType type_ : 8; - int allocated_ : 1; // Notes: if declared as bool, bitfield is useless. -# ifdef JSON_VALUE_USE_INTERNAL_MAP - unsigned int itemIsUsed_ : 1; // used by the ValueInternalMap container. - int memberNameIsStatic_ : 1; // used by the ValueInternalMap container. -# endif - CommentInfo *comments_; - }; - - - /** \brief Experimental and untested: represents an element of the "path" to access a node. - */ - class PathArgument - { - public: - friend class Path; - - PathArgument(); - PathArgument( ArrayIndex index ); - PathArgument( const char *key ); - PathArgument( const std::string &key ); - - private: - enum Kind - { - kindNone = 0, - kindIndex, - kindKey - }; - std::string key_; - ArrayIndex index_; - Kind kind_; - }; - - /** \brief Experimental and untested: represents a "path" to access a node. - * - * Syntax: - * - "." => root node - * - ".[n]" => elements at index 'n' of root node (an array value) - * - ".name" => member named 'name' of root node (an object value) - * - ".name1.name2.name3" - * - ".[0][1][2].name1[3]" - * - ".%" => member name is provided as parameter - * - ".[%]" => index is provied as parameter - */ - class Path - { - public: - Path( const std::string &path, - const PathArgument &a1 = PathArgument(), - const PathArgument &a2 = PathArgument(), - const PathArgument &a3 = PathArgument(), - const PathArgument &a4 = PathArgument(), - const PathArgument &a5 = PathArgument() ); - - const Value &resolve( const Value &root ) const; - Value resolve( const Value &root, - const Value &defaultValue ) const; - /// Creates the "path" to access the specified node and returns a reference on the node. - Value &make( Value &root ) const; - - private: - typedef std::vector InArgs; - typedef std::vector Args; - - void makePath( const std::string &path, - const InArgs &in ); - void addPathInArg( const std::string &path, - const InArgs &in, - InArgs::const_iterator &itInArg, - PathArgument::Kind kind ); - void invalidPath( const std::string &path, - int location ); - - Args args_; - }; - - - -#ifdef JSON_VALUE_USE_INTERNAL_MAP - /** \brief Allocator to customize Value internal map. - * Below is an example of a simple implementation (default implementation actually - * use memory pool for speed). - * \code - class DefaultValueMapAllocator : public ValueMapAllocator - { - public: // overridden from ValueMapAllocator - virtual ValueInternalMap *newMap() - { - return new ValueInternalMap(); - } - - virtual ValueInternalMap *newMapCopy( const ValueInternalMap &other ) - { - return new ValueInternalMap( other ); - } - - virtual void destructMap( ValueInternalMap *map ) - { - delete map; - } - - virtual ValueInternalLink *allocateMapBuckets( unsigned int size ) - { - return new ValueInternalLink[size]; - } - - virtual void releaseMapBuckets( ValueInternalLink *links ) - { - delete [] links; - } - - virtual ValueInternalLink *allocateMapLink() - { - return new ValueInternalLink(); - } - - virtual void releaseMapLink( ValueInternalLink *link ) - { - delete link; - } - }; - * \endcode - */ - class JSON_API ValueMapAllocator - { - public: - virtual ~ValueMapAllocator(); - virtual ValueInternalMap *newMap() = 0; - virtual ValueInternalMap *newMapCopy( const ValueInternalMap &other ) = 0; - virtual void destructMap( ValueInternalMap *map ) = 0; - virtual ValueInternalLink *allocateMapBuckets( unsigned int size ) = 0; - virtual void releaseMapBuckets( ValueInternalLink *links ) = 0; - virtual ValueInternalLink *allocateMapLink() = 0; - virtual void releaseMapLink( ValueInternalLink *link ) = 0; - }; - - /** \brief ValueInternalMap hash-map bucket chain link (for internal use only). - * \internal previous_ & next_ allows for bidirectional traversal. - */ - class JSON_API ValueInternalLink - { - public: - enum { itemPerLink = 6 }; // sizeof(ValueInternalLink) = 128 on 32 bits architecture. - enum InternalFlags { - flagAvailable = 0, - flagUsed = 1 - }; - - ValueInternalLink(); - - ~ValueInternalLink(); - - Value items_[itemPerLink]; - char *keys_[itemPerLink]; - ValueInternalLink *previous_; - ValueInternalLink *next_; - }; - - - /** \brief A linked page based hash-table implementation used internally by Value. - * \internal ValueInternalMap is a tradional bucket based hash-table, with a linked - * list in each bucket to handle collision. There is an addional twist in that - * each node of the collision linked list is a page containing a fixed amount of - * value. This provides a better compromise between memory usage and speed. - * - * Each bucket is made up of a chained list of ValueInternalLink. The last - * link of a given bucket can be found in the 'previous_' field of the following bucket. - * The last link of the last bucket is stored in tailLink_ as it has no following bucket. - * Only the last link of a bucket may contains 'available' item. The last link always - * contains at least one element unless is it the bucket one very first link. - */ - class JSON_API ValueInternalMap - { - friend class ValueIteratorBase; - friend class Value; - public: - typedef unsigned int HashKey; - typedef unsigned int BucketIndex; - -# ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION - struct IteratorState - { - IteratorState() - : map_(0) - , link_(0) - , itemIndex_(0) - , bucketIndex_(0) - { - } - ValueInternalMap *map_; - ValueInternalLink *link_; - BucketIndex itemIndex_; - BucketIndex bucketIndex_; - }; -# endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION - - ValueInternalMap(); - ValueInternalMap( const ValueInternalMap &other ); - ValueInternalMap &operator =( const ValueInternalMap &other ); - ~ValueInternalMap(); - - void swap( ValueInternalMap &other ); - - BucketIndex size() const; - - void clear(); - - bool reserveDelta( BucketIndex growth ); - - bool reserve( BucketIndex newItemCount ); - - const Value *find( const char *key ) const; - - Value *find( const char *key ); - - Value &resolveReference( const char *key, - bool isStatic ); - - void remove( const char *key ); - - void doActualRemove( ValueInternalLink *link, - BucketIndex index, - BucketIndex bucketIndex ); - - ValueInternalLink *&getLastLinkInBucket( BucketIndex bucketIndex ); - - Value &setNewItem( const char *key, - bool isStatic, - ValueInternalLink *link, - BucketIndex index ); - - Value &unsafeAdd( const char *key, - bool isStatic, - HashKey hashedKey ); - - HashKey hash( const char *key ) const; - - int compare( const ValueInternalMap &other ) const; - - private: - void makeBeginIterator( IteratorState &it ) const; - void makeEndIterator( IteratorState &it ) const; - static bool equals( const IteratorState &x, const IteratorState &other ); - static void increment( IteratorState &iterator ); - static void incrementBucket( IteratorState &iterator ); - static void decrement( IteratorState &iterator ); - static const char *key( const IteratorState &iterator ); - static const char *key( const IteratorState &iterator, bool &isStatic ); - static Value &value( const IteratorState &iterator ); - static int distance( const IteratorState &x, const IteratorState &y ); - - private: - ValueInternalLink *buckets_; - ValueInternalLink *tailLink_; - BucketIndex bucketsSize_; - BucketIndex itemCount_; - }; - - /** \brief A simplified deque implementation used internally by Value. - * \internal - * It is based on a list of fixed "page", each page contains a fixed number of items. - * Instead of using a linked-list, a array of pointer is used for fast item look-up. - * Look-up for an element is as follow: - * - compute page index: pageIndex = itemIndex / itemsPerPage - * - look-up item in page: pages_[pageIndex][itemIndex % itemsPerPage] - * - * Insertion is amortized constant time (only the array containing the index of pointers - * need to be reallocated when items are appended). - */ - class JSON_API ValueInternalArray - { - friend class Value; - friend class ValueIteratorBase; - public: - enum { itemsPerPage = 8 }; // should be a power of 2 for fast divide and modulo. - typedef Value::ArrayIndex ArrayIndex; - typedef unsigned int PageIndex; - -# ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION - struct IteratorState // Must be a POD - { - IteratorState() - : array_(0) - , currentPageIndex_(0) - , currentItemIndex_(0) - { - } - ValueInternalArray *array_; - Value **currentPageIndex_; - unsigned int currentItemIndex_; - }; -# endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION - - ValueInternalArray(); - ValueInternalArray( const ValueInternalArray &other ); - ValueInternalArray &operator =( const ValueInternalArray &other ); - ~ValueInternalArray(); - void swap( ValueInternalArray &other ); - - void clear(); - void resize( ArrayIndex newSize ); - - Value &resolveReference( ArrayIndex index ); - - Value *find( ArrayIndex index ) const; - - ArrayIndex size() const; - - int compare( const ValueInternalArray &other ) const; - - private: - static bool equals( const IteratorState &x, const IteratorState &other ); - static void increment( IteratorState &iterator ); - static void decrement( IteratorState &iterator ); - static Value &dereference( const IteratorState &iterator ); - static Value &unsafeDereference( const IteratorState &iterator ); - static int distance( const IteratorState &x, const IteratorState &y ); - static ArrayIndex indexOf( const IteratorState &iterator ); - void makeBeginIterator( IteratorState &it ) const; - void makeEndIterator( IteratorState &it ) const; - void makeIterator( IteratorState &it, ArrayIndex index ) const; - - void makeIndexValid( ArrayIndex index ); - - Value **pages_; - ArrayIndex size_; - PageIndex pageCount_; - }; - - /** \brief Experimental: do not use. Allocator to customize Value internal array. - * Below is an example of a simple implementation (actual implementation use - * memory pool). - \code -class DefaultValueArrayAllocator : public ValueArrayAllocator -{ -public: // overridden from ValueArrayAllocator - virtual ~DefaultValueArrayAllocator() - { - } - - virtual ValueInternalArray *newArray() - { - return new ValueInternalArray(); - } - - virtual ValueInternalArray *newArrayCopy( const ValueInternalArray &other ) - { - return new ValueInternalArray( other ); - } - - virtual void destruct( ValueInternalArray *array ) - { - delete array; - } - - virtual void reallocateArrayPageIndex( Value **&indexes, - ValueInternalArray::PageIndex &indexCount, - ValueInternalArray::PageIndex minNewIndexCount ) - { - ValueInternalArray::PageIndex newIndexCount = (indexCount*3)/2 + 1; - if ( minNewIndexCount > newIndexCount ) - newIndexCount = minNewIndexCount; - void *newIndexes = realloc( indexes, sizeof(Value*) * newIndexCount ); - if ( !newIndexes ) - throw std::bad_alloc(); - indexCount = newIndexCount; - indexes = static_cast( newIndexes ); - } - virtual void releaseArrayPageIndex( Value **indexes, - ValueInternalArray::PageIndex indexCount ) - { - if ( indexes ) - free( indexes ); - } - - virtual Value *allocateArrayPage() - { - return static_cast( malloc( sizeof(Value) * ValueInternalArray::itemsPerPage ) ); - } - - virtual void releaseArrayPage( Value *value ) - { - if ( value ) - free( value ); - } -}; - \endcode - */ - class JSON_API ValueArrayAllocator - { - public: - virtual ~ValueArrayAllocator(); - virtual ValueInternalArray *newArray() = 0; - virtual ValueInternalArray *newArrayCopy( const ValueInternalArray &other ) = 0; - virtual void destructArray( ValueInternalArray *array ) = 0; - /** \brief Reallocate array page index. - * Reallocates an array of pointer on each page. - * \param indexes [input] pointer on the current index. May be \c NULL. - * [output] pointer on the new index of at least - * \a minNewIndexCount pages. - * \param indexCount [input] current number of pages in the index. - * [output] number of page the reallocated index can handle. - * \b MUST be >= \a minNewIndexCount. - * \param minNewIndexCount Minimum number of page the new index must be able to - * handle. - */ - virtual void reallocateArrayPageIndex( Value **&indexes, - ValueInternalArray::PageIndex &indexCount, - ValueInternalArray::PageIndex minNewIndexCount ) = 0; - virtual void releaseArrayPageIndex( Value **indexes, - ValueInternalArray::PageIndex indexCount ) = 0; - virtual Value *allocateArrayPage() = 0; - virtual void releaseArrayPage( Value *value ) = 0; - }; -#endif // #ifdef JSON_VALUE_USE_INTERNAL_MAP - - - /** \brief base class for Value iterators. - * - */ - class ValueIteratorBase - { - public: - typedef unsigned int size_t; - typedef int difference_type; - typedef ValueIteratorBase SelfType; - - ValueIteratorBase(); -#ifndef JSON_VALUE_USE_INTERNAL_MAP - explicit ValueIteratorBase( const Value::ObjectValues::iterator ¤t ); -#else - ValueIteratorBase( const ValueInternalArray::IteratorState &state ); - ValueIteratorBase( const ValueInternalMap::IteratorState &state ); -#endif - - bool operator ==( const SelfType &other ) const - { - return isEqual( other ); - } - - bool operator !=( const SelfType &other ) const - { - return !isEqual( other ); - } - - difference_type operator -( const SelfType &other ) const - { - return computeDistance( other ); - } - - /// Return either the index or the member name of the referenced value as a Value. - Value key() const; - - /// Return the index of the referenced Value. -1 if it is not an arrayValue. - UInt index() const; - - /// Return the member name of the referenced Value. "" if it is not an objectValue. - const char *memberName() const; - - protected: - Value &deref() const; - - void increment(); - - void decrement(); - - difference_type computeDistance( const SelfType &other ) const; - - bool isEqual( const SelfType &other ) const; - - void copy( const SelfType &other ); - - private: -#ifndef JSON_VALUE_USE_INTERNAL_MAP - Value::ObjectValues::iterator current_; - // Indicates that iterator is for a null value. - bool isNull_; -#else - union - { - ValueInternalArray::IteratorState array_; - ValueInternalMap::IteratorState map_; - } iterator_; - bool isArray_; -#endif - }; - - /** \brief const iterator for object and array value. - * - */ - class ValueConstIterator : public ValueIteratorBase - { - friend class Value; - public: - typedef unsigned int size_t; - typedef int difference_type; - typedef const Value &reference; - typedef const Value *pointer; - typedef ValueConstIterator SelfType; - - ValueConstIterator(); - private: - /*! \internal Use by Value to create an iterator. - */ -#ifndef JSON_VALUE_USE_INTERNAL_MAP - explicit ValueConstIterator( const Value::ObjectValues::iterator ¤t ); -#else - ValueConstIterator( const ValueInternalArray::IteratorState &state ); - ValueConstIterator( const ValueInternalMap::IteratorState &state ); -#endif - public: - SelfType &operator =( const ValueIteratorBase &other ); - - SelfType operator++( int ) - { - SelfType temp( *this ); - ++*this; - return temp; - } - - SelfType operator--( int ) - { - SelfType temp( *this ); - --*this; - return temp; - } - - SelfType &operator--() - { - decrement(); - return *this; - } - - SelfType &operator++() - { - increment(); - return *this; - } - - reference operator *() const - { - return deref(); - } - }; - - - /** \brief Iterator for object and array value. - */ - class ValueIterator : public ValueIteratorBase - { - friend class Value; - public: - typedef unsigned int size_t; - typedef int difference_type; - typedef Value &reference; - typedef Value *pointer; - typedef ValueIterator SelfType; - - ValueIterator(); - ValueIterator( const ValueConstIterator &other ); - ValueIterator( const ValueIterator &other ); - private: - /*! \internal Use by Value to create an iterator. - */ -#ifndef JSON_VALUE_USE_INTERNAL_MAP - explicit ValueIterator( const Value::ObjectValues::iterator ¤t ); -#else - ValueIterator( const ValueInternalArray::IteratorState &state ); - ValueIterator( const ValueInternalMap::IteratorState &state ); -#endif - public: - - SelfType &operator =( const SelfType &other ); - - SelfType operator++( int ) - { - SelfType temp( *this ); - ++*this; - return temp; - } - - SelfType operator--( int ) - { - SelfType temp( *this ); - --*this; - return temp; - } - - SelfType &operator--() - { - decrement(); - return *this; - } - - SelfType &operator++() - { - increment(); - return *this; - } - - reference operator *() const - { - return deref(); - } - }; - - -} // namespace Json - - -#endif // CPPTL_JSON_H_INCLUDED - -// ////////////////////////////////////////////////////////////////////// -// End of content of file: include/json/value.h -// ////////////////////////////////////////////////////////////////////// - - - - - - -// ////////////////////////////////////////////////////////////////////// -// Beginning of content of file: include/json/reader.h -// ////////////////////////////////////////////////////////////////////// - -// Copyright 2007-2010 Baptiste Lepilleur -// Distributed under MIT license, or public domain if desired and -// recognized in your jurisdiction. -// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE - -#ifndef CPPTL_JSON_READER_H_INCLUDED -# define CPPTL_JSON_READER_H_INCLUDED - -#if !defined(JSON_IS_AMALGAMATION) -# include "features.h" -# include "value.h" -#endif // if !defined(JSON_IS_AMALGAMATION) -# include -# include -# include - -namespace Json { - - /** \brief Unserialize a JSON document into a Value. - * - */ - class JSON_API Reader - { - public: - typedef char Char; - typedef const Char *Location; - - /** \brief Constructs a Reader allowing all features - * for parsing. - */ - Reader(); - - /** \brief Constructs a Reader allowing the specified feature set - * for parsing. - */ - Reader( const Features &features ); - - /** \brief Read a Value from a JSON document. - * \param document UTF-8 encoded string containing the document to read. - * \param root [out] Contains the root value of the document if it was - * successfully parsed. - * \param collectComments \c true to collect comment and allow writing them back during - * serialization, \c false to discard comments. - * This parameter is ignored if Features::allowComments_ - * is \c false. - * \return \c true if the document was successfully parsed, \c false if an error occurred. - */ - bool parse( const std::string &document, - Value &root, - bool collectComments = true ); - - /** \brief Read a Value from a JSON document. - * \param beginDoc Pointer on the beginning of the UTF-8 encoded string of the document to read. - * \param endDoc Pointer on the end of the UTF-8 encoded string of the document to read. - \ Must be >= beginDoc. - * \param root [out] Contains the root value of the document if it was - * successfully parsed. - * \param collectComments \c true to collect comment and allow writing them back during - * serialization, \c false to discard comments. - * This parameter is ignored if Features::allowComments_ - * is \c false. - * \return \c true if the document was successfully parsed, \c false if an error occurred. - */ - bool parse( const char *beginDoc, const char *endDoc, - Value &root, - bool collectComments = true ); - - /// \brief Parse from input stream. - /// \see Json::operator>>(std::istream&, Json::Value&). - bool parse( std::istream &is, - Value &root, - bool collectComments = true ); - - /** \brief Returns a user friendly string that list errors in the parsed document. - * \return Formatted error message with the list of errors with their location in - * the parsed document. An empty string is returned if no error occurred - * during parsing. - * \deprecated Use getFormattedErrorMessages() instead (typo fix). - */ - JSONCPP_DEPRECATED("Use getFormattedErrorMessages instead") - std::string getFormatedErrorMessages() const; - - /** \brief Returns a user friendly string that list errors in the parsed document. - * \return Formatted error message with the list of errors with their location in - * the parsed document. An empty string is returned if no error occurred - * during parsing. - */ - std::string getFormattedErrorMessages() const; - - private: - enum TokenType - { - tokenEndOfStream = 0, - tokenObjectBegin, - tokenObjectEnd, - tokenArrayBegin, - tokenArrayEnd, - tokenString, - tokenNumber, - tokenTrue, - tokenFalse, - tokenNull, - tokenArraySeparator, - tokenMemberSeparator, - tokenComment, - tokenError - }; - - class Token - { - public: - TokenType type_; - Location start_; - Location end_; - }; - - class ErrorInfo - { - public: - Token token_; - std::string message_; - Location extra_; - }; - - typedef std::deque Errors; - - bool expectToken( TokenType type, Token &token, const char *message ); - bool readToken( Token &token ); - void skipSpaces(); - bool match( Location pattern, - int patternLength ); - bool readComment(); - bool readCStyleComment(); - bool readCppStyleComment(); - bool readString(); - void readNumber(); - bool readValue(); - bool readObject( Token &token ); - bool readArray( Token &token ); - bool decodeNumber( Token &token ); - bool decodeString( Token &token ); - bool decodeString( Token &token, std::string &decoded ); - bool decodeDouble( Token &token ); - bool decodeUnicodeCodePoint( Token &token, - Location ¤t, - Location end, - unsigned int &unicode ); - bool decodeUnicodeEscapeSequence( Token &token, - Location ¤t, - Location end, - unsigned int &unicode ); - bool addError( const std::string &message, - Token &token, - Location extra = 0 ); - bool recoverFromError( TokenType skipUntilToken ); - bool addErrorAndRecover( const std::string &message, - Token &token, - TokenType skipUntilToken ); - void skipUntilSpace(); - Value ¤tValue(); - Char getNextChar(); - void getLocationLineAndColumn( Location location, - int &line, - int &column ) const; - std::string getLocationLineAndColumn( Location location ) const; - void addComment( Location begin, - Location end, - CommentPlacement placement ); - void skipCommentTokens( Token &token ); - - typedef std::stack Nodes; - Nodes nodes_; - Errors errors_; - std::string document_; - Location begin_; - Location end_; - Location current_; - Location lastValueEnd_; - Value *lastValue_; - std::string commentsBefore_; - Features features_; - bool collectComments_; - }; - - /** \brief Read from 'sin' into 'root'. - - Always keep comments from the input JSON. - - This can be used to read a file into a particular sub-object. - For example: - \code - Json::Value root; - cin >> root["dir"]["file"]; - cout << root; - \endcode - Result: - \verbatim - { - "dir": { - "file": { - // The input stream JSON would be nested here. - } - } - } - \endverbatim - \throw std::exception on parse error. - \see Json::operator<<() - */ - std::istream& operator>>( std::istream&, Value& ); - -} // namespace Json - -#endif // CPPTL_JSON_READER_H_INCLUDED - -// ////////////////////////////////////////////////////////////////////// -// End of content of file: include/json/reader.h -// ////////////////////////////////////////////////////////////////////// - - - - - - -// ////////////////////////////////////////////////////////////////////// -// Beginning of content of file: include/json/writer.h -// ////////////////////////////////////////////////////////////////////// - -// Copyright 2007-2010 Baptiste Lepilleur -// Distributed under MIT license, or public domain if desired and -// recognized in your jurisdiction. -// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE - -#ifndef JSON_WRITER_H_INCLUDED -# define JSON_WRITER_H_INCLUDED - -#if !defined(JSON_IS_AMALGAMATION) -# include "value.h" -#endif // if !defined(JSON_IS_AMALGAMATION) -# include -# include - -namespace Json { - - class Value; - - /** \brief Abstract class for writers. - */ - class JSON_API Writer - { - public: - virtual ~Writer(); - - virtual std::string write( const Value &root ) = 0; - }; - - /** \brief Outputs a Value in JSON format without formatting (not human friendly). - * - * The JSON document is written in a single line. It is not intended for 'human' consumption, - * but may be usefull to support feature such as RPC where bandwith is limited. - * \sa Reader, Value - */ - class JSON_API FastWriter : public Writer - { - public: - FastWriter(); - virtual ~FastWriter(){} - - void enableYAMLCompatibility(); - - /** \brief Drop the "null" string from the writer's output for nullValues. - * Strictly speaking, this is not valid JSON. But when the output is being - * fed to a browser's Javascript, it makes for smaller output and the - * browser can handle the output just fine. - */ - void dropNullPlaceholders(); - - public: // overridden from Writer - virtual std::string write( const Value &root ); - - private: - void writeValue( const Value &value ); - - std::string document_; - bool yamlCompatiblityEnabled_; - bool dropNullPlaceholders_; - }; - - /** \brief Writes a Value in JSON format in a human friendly way. - * - * The rules for line break and indent are as follow: - * - Object value: - * - if empty then print {} without indent and line break - * - if not empty the print '{', line break & indent, print one value per line - * and then unindent and line break and print '}'. - * - Array value: - * - if empty then print [] without indent and line break - * - if the array contains no object value, empty array or some other value types, - * and all the values fit on one lines, then print the array on a single line. - * - otherwise, it the values do not fit on one line, or the array contains - * object or non empty array, then print one value per line. - * - * If the Value have comments then they are outputed according to their #CommentPlacement. - * - * \sa Reader, Value, Value::setComment() - */ - class JSON_API StyledWriter: public Writer - { - public: - StyledWriter(); - virtual ~StyledWriter(){} - - public: // overridden from Writer - /** \brief Serialize a Value in JSON format. - * \param root Value to serialize. - * \return String containing the JSON document that represents the root value. - */ - virtual std::string write( const Value &root ); - - private: - void writeValue( const Value &value ); - void writeArrayValue( const Value &value ); - bool isMultineArray( const Value &value ); - void pushValue( const std::string &value ); - void writeIndent(); - void writeWithIndent( const std::string &value ); - void indent(); - void unindent(); - void writeCommentBeforeValue( const Value &root ); - void writeCommentAfterValueOnSameLine( const Value &root ); - bool hasCommentForValue( const Value &value ); - static std::string normalizeEOL( const std::string &text ); - - typedef std::vector ChildValues; - - ChildValues childValues_; - std::string document_; - std::string indentString_; - int rightMargin_; - int indentSize_; - bool addChildValues_; - }; - - /** \brief Writes a Value in JSON format in a human friendly way, - to a stream rather than to a string. - * - * The rules for line break and indent are as follow: - * - Object value: - * - if empty then print {} without indent and line break - * - if not empty the print '{', line break & indent, print one value per line - * and then unindent and line break and print '}'. - * - Array value: - * - if empty then print [] without indent and line break - * - if the array contains no object value, empty array or some other value types, - * and all the values fit on one lines, then print the array on a single line. - * - otherwise, it the values do not fit on one line, or the array contains - * object or non empty array, then print one value per line. - * - * If the Value have comments then they are outputed according to their #CommentPlacement. - * - * \param indentation Each level will be indented by this amount extra. - * \sa Reader, Value, Value::setComment() - */ - class JSON_API StyledStreamWriter - { - public: - StyledStreamWriter( std::string indentation="\t" ); - ~StyledStreamWriter(){} - - public: - /** \brief Serialize a Value in JSON format. - * \param out Stream to write to. (Can be ostringstream, e.g.) - * \param root Value to serialize. - * \note There is no point in deriving from Writer, since write() should not return a value. - */ - void write( std::ostream &out, const Value &root ); - - private: - void writeValue( const Value &value ); - void writeArrayValue( const Value &value ); - bool isMultineArray( const Value &value ); - void pushValue( const std::string &value ); - void writeIndent(); - void writeWithIndent( const std::string &value ); - void indent(); - void unindent(); - void writeCommentBeforeValue( const Value &root ); - void writeCommentAfterValueOnSameLine( const Value &root ); - bool hasCommentForValue( const Value &value ); - static std::string normalizeEOL( const std::string &text ); - - typedef std::vector ChildValues; - - ChildValues childValues_; - std::ostream* document_; - std::string indentString_; - int rightMargin_; - std::string indentation_; - bool addChildValues_; - }; - -# if defined(JSON_HAS_INT64) - std::string JSON_API valueToString( Int value ); - std::string JSON_API valueToString( UInt value ); -# endif // if defined(JSON_HAS_INT64) - std::string JSON_API valueToString( LargestInt value ); - std::string JSON_API valueToString( LargestUInt value ); - std::string JSON_API valueToString( double value ); - std::string JSON_API valueToString( bool value ); - std::string JSON_API valueToQuotedString( const char *value ); - - /// \brief Output using the StyledStreamWriter. - /// \see Json::operator>>() - std::ostream& operator<<( std::ostream&, const Value &root ); - -} // namespace Json - - - -#endif // JSON_WRITER_H_INCLUDED - -// ////////////////////////////////////////////////////////////////////// -// End of content of file: include/json/writer.h -// ////////////////////////////////////////////////////////////////////// - - - - - - -// ////////////////////////////////////////////////////////////////////// -// Beginning of content of file: include/json/assertions.h -// ////////////////////////////////////////////////////////////////////// - -// Copyright 2007-2010 Baptiste Lepilleur -// Distributed under MIT license, or public domain if desired and -// recognized in your jurisdiction. -// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE - -#ifndef CPPTL_JSON_ASSERTIONS_H_INCLUDED -# define CPPTL_JSON_ASSERTIONS_H_INCLUDED - -#include - -#if !defined(JSON_IS_AMALGAMATION) -# include -#endif // if !defined(JSON_IS_AMALGAMATION) - -#if JSON_USE_EXCEPTION -#define JSON_ASSERT( condition ) assert( condition ); // @todo <= change this into an exception throw -#define JSON_FAIL_MESSAGE( message ) throw std::runtime_error( message ); -#else // JSON_USE_EXCEPTION -#define JSON_ASSERT( condition ) assert( condition ); - -// The call to assert() will show the failure message in debug builds. In -// release bugs we write to invalid memory in order to crash hard, so that a -// debugger or crash reporter gets the chance to take over. We still call exit() -// afterward in order to tell the compiler that this macro doesn't return. -#define JSON_FAIL_MESSAGE( message ) { assert(false && message); strcpy(reinterpret_cast(666), message); exit(123); } - -#endif - -#define JSON_ASSERT_MESSAGE( condition, message ) if (!( condition )) { JSON_FAIL_MESSAGE( message ) } - -#endif // CPPTL_JSON_ASSERTIONS_H_INCLUDED - -// ////////////////////////////////////////////////////////////////////// -// End of content of file: include/json/assertions.h -// ////////////////////////////////////////////////////////////////////// - - - - - -#endif //ifndef JSON_AMALGATED_H_INCLUDED diff --git a/src/json/jsoncpp.cpp b/src/json/jsoncpp.cpp deleted file mode 100644 index 7a04736de..000000000 --- a/src/json/jsoncpp.cpp +++ /dev/null @@ -1,4367 +0,0 @@ -/// Json-cpp amalgated source (http://jsoncpp.sourceforge.net/). -/// It is intented to be used with #include - -// ////////////////////////////////////////////////////////////////////// -// Beginning of content of file: LICENSE -// ////////////////////////////////////////////////////////////////////// - -/* -The JsonCpp library's source code, including accompanying documentation, -tests and demonstration applications, are licensed under the following -conditions... - -The author (Baptiste Lepilleur) explicitly disclaims copyright in all -jurisdictions which recognize such a disclaimer. In such jurisdictions, -this software is released into the Public Domain. - -In jurisdictions which do not recognize Public Domain property (e.g. Germany as of -2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur, and is -released under the terms of the MIT License (see below). - -In jurisdictions which recognize Public Domain property, the user of this -software may choose to accept it either as 1) Public Domain, 2) under the -conditions of the MIT License (see below), or 3) under the terms of dual -Public Domain/MIT License conditions described here, as they choose. - -The MIT License is about as close to Public Domain as a license can get, and is -described in clear, concise terms at: - - http://en.wikipedia.org/wiki/MIT_License - -The full text of the MIT License follows: - -======================================================================== -Copyright (c) 2007-2010 Baptiste Lepilleur - -Permission is hereby granted, free of charge, to any person -obtaining a copy of this software and associated documentation -files (the "Software"), to deal in the Software without -restriction, including without limitation the rights to use, copy, -modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -======================================================================== -(END LICENSE TEXT) - -The MIT license is compatible with both the GPL and commercial -software, affording one all of the rights of Public Domain with the -minor nuisance of being required to keep the above copyright notice -and license text in the source code. Note also that by accepting the -Public Domain "license" you can re-license your copy using whatever -license you like. - -*/ - -// ////////////////////////////////////////////////////////////////////// -// End of content of file: LICENSE -// ////////////////////////////////////////////////////////////////////// - - - - - - -#include "json.h" - - -// ////////////////////////////////////////////////////////////////////// -// Beginning of content of file: src/lib_json/json_tool.h -// ////////////////////////////////////////////////////////////////////// - -// Copyright 2007-2010 Baptiste Lepilleur -// Distributed under MIT license, or public domain if desired and -// recognized in your jurisdiction. -// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE - -#ifndef LIB_JSONCPP_JSON_TOOL_H_INCLUDED -# define LIB_JSONCPP_JSON_TOOL_H_INCLUDED - -/* This header provides common string manipulation support, such as UTF-8, - * portable conversion from/to string... - * - * It is an internal header that must not be exposed. - */ - -namespace Json { - -/// Converts a unicode code-point to UTF-8. -static inline std::string -codePointToUTF8(unsigned int cp) -{ - std::string result; - - // based on description from http://en.wikipedia.org/wiki/UTF-8 - - if (cp <= 0x7f) - { - result.resize(1); - result[0] = static_cast(cp); - } - else if (cp <= 0x7FF) - { - result.resize(2); - result[1] = static_cast(0x80 | (0x3f & cp)); - result[0] = static_cast(0xC0 | (0x1f & (cp >> 6))); - } - else if (cp <= 0xFFFF) - { - result.resize(3); - result[2] = static_cast(0x80 | (0x3f & cp)); - result[1] = 0x80 | static_cast((0x3f & (cp >> 6))); - result[0] = 0xE0 | static_cast((0xf & (cp >> 12))); - } - else if (cp <= 0x10FFFF) - { - result.resize(4); - result[3] = static_cast(0x80 | (0x3f & cp)); - result[2] = static_cast(0x80 | (0x3f & (cp >> 6))); - result[1] = static_cast(0x80 | (0x3f & (cp >> 12))); - result[0] = static_cast(0xF0 | (0x7 & (cp >> 18))); - } - - return result; -} - - -/// Returns true if ch is a control character (in range [0,32[). -static inline bool -isControlCharacter(char ch) -{ - return ch > 0 && ch <= 0x1F; -} - - -enum { - /// Constant that specify the size of the buffer that must be passed to uintToString. - uintToStringBufferSize = 3*sizeof(LargestUInt)+1 -}; - -// Defines a char buffer for use with uintToString(). -typedef char UIntToStringBuffer[uintToStringBufferSize]; - - -/** Converts an unsigned integer to string. - * @param value Unsigned interger to convert to string - * @param current Input/Output string buffer. - * Must have at least uintToStringBufferSize chars free. - */ -static inline void -uintToString( LargestUInt value, - char *¤t ) -{ - *--current = 0; - do - { - *--current = char(value % 10) + '0'; - value /= 10; - } - while ( value != 0 ); -} - -} // namespace Json { - -#endif // LIB_JSONCPP_JSON_TOOL_H_INCLUDED - -// ////////////////////////////////////////////////////////////////////// -// End of content of file: src/lib_json/json_tool.h -// ////////////////////////////////////////////////////////////////////// - - - - - - -// ////////////////////////////////////////////////////////////////////// -// Beginning of content of file: src/lib_json/json_reader.cpp -// ////////////////////////////////////////////////////////////////////// - -// Copyright 2007-2011 Baptiste Lepilleur -// Distributed under MIT license, or public domain if desired and -// recognized in your jurisdiction. -// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE - -#if !defined(JSON_IS_AMALGAMATION) -# include -# include -# include -# include "json_tool.h" -#endif // if !defined(JSON_IS_AMALGAMATION) -#include -#include -#include -#include -#include - -#if defined(_MSC_VER) && _MSC_VER >= 1400 // VC++ 8.0 -#pragma warning( disable : 4996 ) // disable warning about strdup being deprecated. -#endif - -namespace Json { - -// Implementation of class Features -// //////////////////////////////// - -Features::Features() - : allowComments_( true ) - , strictRoot_( false ) -{ -} - - -Features -Features::all() -{ - return Features(); -} - - -Features -Features::strictMode() -{ - Features features; - features.allowComments_ = false; - features.strictRoot_ = true; - return features; -} - -// Implementation of class Reader -// //////////////////////////////// - - -static inline bool -in( Reader::Char c, Reader::Char c1, Reader::Char c2, Reader::Char c3, Reader::Char c4 ) -{ - return c == c1 || c == c2 || c == c3 || c == c4; -} - -static inline bool -in( Reader::Char c, Reader::Char c1, Reader::Char c2, Reader::Char c3, Reader::Char c4, Reader::Char c5 ) -{ - return c == c1 || c == c2 || c == c3 || c == c4 || c == c5; -} - - -static bool -containsNewLine( Reader::Location begin, - Reader::Location end ) -{ - for ( ;begin < end; ++begin ) - if ( *begin == '\n' || *begin == '\r' ) - return true; - return false; -} - - -// Class Reader -// ////////////////////////////////////////////////////////////////// - -Reader::Reader() - : errors_(), - document_(), - begin_(), - end_(), - current_(), - lastValueEnd_(), - lastValue_(), - commentsBefore_(), - features_( Features::all() ), - collectComments_() -{ -} - - -Reader::Reader( const Features &features ) - : errors_(), - document_(), - begin_(), - end_(), - current_(), - lastValueEnd_(), - lastValue_(), - commentsBefore_(), - features_( features ), - collectComments_() -{ -} - - -bool -Reader::parse( const std::string &document, - Value &root, - bool collectComments ) -{ - document_ = document; - const char *begin = document_.c_str(); - const char *end = begin + document_.length(); - return parse( begin, end, root, collectComments ); -} - - -bool -Reader::parse( std::istream& sin, - Value &root, - bool collectComments ) -{ - //std::istream_iterator begin(sin); - //std::istream_iterator end; - // Those would allow streamed input from a file, if parse() were a - // template function. - - // Since std::string is reference-counted, this at least does not - // create an extra copy. - std::string doc; - std::getline(sin, doc, (char)EOF); - return parse( doc, root, collectComments ); -} - -bool -Reader::parse( const char *beginDoc, const char *endDoc, - Value &root, - bool collectComments ) -{ - if ( !features_.allowComments_ ) - { - collectComments = false; - } - - begin_ = beginDoc; - end_ = endDoc; - collectComments_ = collectComments; - current_ = begin_; - lastValueEnd_ = 0; - lastValue_ = 0; - commentsBefore_ = ""; - errors_.clear(); - while ( !nodes_.empty() ) - nodes_.pop(); - nodes_.push( &root ); - - bool successful = readValue(); - Token token; - skipCommentTokens( token ); - if ( collectComments_ && !commentsBefore_.empty() ) - root.setComment( commentsBefore_, commentAfter ); - if ( features_.strictRoot_ ) - { - if ( !root.isArray() && !root.isObject() ) - { - // Set error location to start of doc, ideally should be first token found in doc - token.type_ = tokenError; - token.start_ = beginDoc; - token.end_ = endDoc; - addError( "A valid JSON document must be either an array or an object value.", - token ); - return false; - } - } - return successful; -} - - -bool -Reader::readValue() -{ - Token token; - skipCommentTokens( token ); - bool successful = true; - - if ( collectComments_ && !commentsBefore_.empty() ) - { - currentValue().setComment( commentsBefore_, commentBefore ); - commentsBefore_ = ""; - } - - - switch ( token.type_ ) - { - case tokenObjectBegin: - successful = readObject( token ); - break; - case tokenArrayBegin: - successful = readArray( token ); - break; - case tokenNumber: - successful = decodeNumber( token ); - break; - case tokenString: - successful = decodeString( token ); - break; - case tokenTrue: - currentValue() = true; - break; - case tokenFalse: - currentValue() = false; - break; - case tokenNull: - currentValue() = Value(); - break; - default: - return addError( "Syntax error: value, object or array expected.", token ); - } - - if ( collectComments_ ) - { - lastValueEnd_ = current_; - lastValue_ = ¤tValue(); - } - - return successful; -} - - -void -Reader::skipCommentTokens( Token &token ) -{ - if ( features_.allowComments_ ) - { - do - { - readToken( token ); - } - while ( token.type_ == tokenComment ); - } - else - { - readToken( token ); - } -} - - -bool -Reader::expectToken( TokenType type, Token &token, const char *message ) -{ - readToken( token ); - if ( token.type_ != type ) - return addError( message, token ); - return true; -} - - -bool -Reader::readToken( Token &token ) -{ - skipSpaces(); - token.start_ = current_; - Char c = getNextChar(); - bool ok = true; - switch ( c ) - { - case '{': - token.type_ = tokenObjectBegin; - break; - case '}': - token.type_ = tokenObjectEnd; - break; - case '[': - token.type_ = tokenArrayBegin; - break; - case ']': - token.type_ = tokenArrayEnd; - break; - case '"': - token.type_ = tokenString; - ok = readString(); - break; - case '/': - token.type_ = tokenComment; - ok = readComment(); - break; - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - case '-': - token.type_ = tokenNumber; - readNumber(); - break; - case 't': - token.type_ = tokenTrue; - ok = match( "rue", 3 ); - break; - case 'f': - token.type_ = tokenFalse; - ok = match( "alse", 4 ); - break; - case 'n': - token.type_ = tokenNull; - ok = match( "ull", 3 ); - break; - case ',': - token.type_ = tokenArraySeparator; - break; - case ':': - token.type_ = tokenMemberSeparator; - break; - case 0: - token.type_ = tokenEndOfStream; - break; - default: - ok = false; - break; - } - if ( !ok ) - token.type_ = tokenError; - token.end_ = current_; - return true; -} - - -void -Reader::skipSpaces() -{ - while ( current_ != end_ ) - { - Char c = *current_; - if ( c == ' ' || c == '\t' || c == '\r' || c == '\n' ) - ++current_; - else - break; - } -} - - -bool -Reader::match( Location pattern, - int patternLength ) -{ - if ( end_ - current_ < patternLength ) - return false; - int index = patternLength; - while ( index-- ) - if ( current_[index] != pattern[index] ) - return false; - current_ += patternLength; - return true; -} - - -bool -Reader::readComment() -{ - Location commentBegin = current_ - 1; - Char c = getNextChar(); - bool successful = false; - if ( c == '*' ) - successful = readCStyleComment(); - else if ( c == '/' ) - successful = readCppStyleComment(); - if ( !successful ) - return false; - - if ( collectComments_ ) - { - CommentPlacement placement = commentBefore; - if ( lastValueEnd_ && !containsNewLine( lastValueEnd_, commentBegin ) ) - { - if ( c != '*' || !containsNewLine( commentBegin, current_ ) ) - placement = commentAfterOnSameLine; - } - - addComment( commentBegin, current_, placement ); - } - return true; -} - - -void -Reader::addComment( Location begin, - Location end, - CommentPlacement placement ) -{ - assert( collectComments_ ); - if ( placement == commentAfterOnSameLine ) - { - assert( lastValue_ != 0 ); - lastValue_->setComment( std::string( begin, end ), placement ); - } - else - { - if ( !commentsBefore_.empty() ) - commentsBefore_ += "\n"; - commentsBefore_ += std::string( begin, end ); - } -} - - -bool -Reader::readCStyleComment() -{ - while ( current_ != end_ ) - { - Char c = getNextChar(); - if ( c == '*' && *current_ == '/' ) - break; - } - return getNextChar() == '/'; -} - - -bool -Reader::readCppStyleComment() -{ - while ( current_ != end_ ) - { - Char c = getNextChar(); - if ( c == '\r' || c == '\n' ) - break; - } - return true; -} - - -void -Reader::readNumber() -{ - while ( current_ != end_ ) - { - if ( !(*current_ >= '0' && *current_ <= '9') && - !in( *current_, '.', 'e', 'E', '+', '-' ) ) - break; - ++current_; - } -} - -bool -Reader::readString() -{ - Char c = 0; - while ( current_ != end_ ) - { - c = getNextChar(); - if ( c == '\\' ) - getNextChar(); - else if ( c == '"' ) - break; - } - return c == '"'; -} - - -bool -Reader::readObject( Token &/*tokenStart*/ ) -{ - Token tokenName; - std::string name; - currentValue() = Value( objectValue ); - while ( readToken( tokenName ) ) - { - bool initialTokenOk = true; - while ( tokenName.type_ == tokenComment && initialTokenOk ) - initialTokenOk = readToken( tokenName ); - if ( !initialTokenOk ) - break; - if ( tokenName.type_ == tokenObjectEnd && name.empty() ) // empty object - return true; - if ( tokenName.type_ != tokenString ) - break; - - name = ""; - if ( !decodeString( tokenName, name ) ) - return recoverFromError( tokenObjectEnd ); - - Token colon; - if ( !readToken( colon ) || colon.type_ != tokenMemberSeparator ) - { - return addErrorAndRecover( "Missing ':' after object member name", - colon, - tokenObjectEnd ); - } - Value &value = currentValue()[ name ]; - nodes_.push( &value ); - bool ok = readValue(); - nodes_.pop(); - if ( !ok ) // error already set - return recoverFromError( tokenObjectEnd ); - - Token comma; - if ( !readToken( comma ) - || ( comma.type_ != tokenObjectEnd && - comma.type_ != tokenArraySeparator && - comma.type_ != tokenComment ) ) - { - return addErrorAndRecover( "Missing ',' or '}' in object declaration", - comma, - tokenObjectEnd ); - } - bool finalizeTokenOk = true; - while ( comma.type_ == tokenComment && - finalizeTokenOk ) - finalizeTokenOk = readToken( comma ); - if ( comma.type_ == tokenObjectEnd ) - return true; - } - return addErrorAndRecover( "Missing '}' or object member name", - tokenName, - tokenObjectEnd ); -} - - -bool -Reader::readArray( Token &/*tokenStart*/ ) -{ - currentValue() = Value( arrayValue ); - skipSpaces(); - if ( *current_ == ']' ) // empty array - { - Token endArray; - readToken( endArray ); - return true; - } - int index = 0; - for (;;) - { - Value &value = currentValue()[ index++ ]; - nodes_.push( &value ); - bool ok = readValue(); - nodes_.pop(); - if ( !ok ) // error already set - return recoverFromError( tokenArrayEnd ); - - Token token; - // Accept Comment after last item in the array. - ok = readToken( token ); - while ( token.type_ == tokenComment && ok ) - { - ok = readToken( token ); - } - bool badTokenType = ( token.type_ != tokenArraySeparator && - token.type_ != tokenArrayEnd ); - if ( !ok || badTokenType ) - { - return addErrorAndRecover( "Missing ',' or ']' in array declaration", - token, - tokenArrayEnd ); - } - if ( token.type_ == tokenArrayEnd ) - break; - } - return true; -} - - -bool -Reader::decodeNumber( Token &token ) -{ - bool isDouble = false; - for ( Location inspect = token.start_; inspect != token.end_; ++inspect ) - { - isDouble = isDouble - || in( *inspect, '.', 'e', 'E', '+' ) - || ( *inspect == '-' && inspect != token.start_ ); - } - if ( isDouble ) - return decodeDouble( token ); - // Attempts to parse the number as an integer. If the number is - // larger than the maximum supported value of an integer then - // we decode the number as a double. - Location current = token.start_; - bool isNegative = *current == '-'; - if ( isNegative ) - ++current; - Value::LargestUInt maxIntegerValue = isNegative ? Value::LargestUInt(-Value::minLargestInt) - : Value::maxLargestUInt; - Value::LargestUInt threshold = maxIntegerValue / 10; - Value::LargestUInt value = 0; - while ( current < token.end_ ) - { - Char c = *current++; - if ( c < '0' || c > '9' ) - return addError( "'" + std::string( token.start_, token.end_ ) + "' is not a number.", token ); - Value::UInt digit(c - '0'); - if ( value >= threshold ) - { - // We've hit or exceeded the max value divided by 10 (rounded down). If - // a) we've only just touched the limit, b) this is the last digit, and - // c) it's small enough to fit in that rounding delta, we're okay. - // Otherwise treat this number as a double to avoid overflow. - if (value > threshold || - current != token.end_ || - digit > maxIntegerValue % 10) - { - return decodeDouble( token ); - } - } - value = value * 10 + digit; - } - if ( isNegative ) - currentValue() = -Value::LargestInt( value ); - else if ( value <= Value::LargestUInt(Value::maxInt) ) - currentValue() = Value::LargestInt( value ); - else - currentValue() = value; - return true; -} - - -bool -Reader::decodeDouble( Token &token ) -{ - double value = 0; - const int bufferSize = 32; - int count; - int length = int(token.end_ - token.start_); - - // Sanity check to avoid buffer overflow exploits. - if (length < 0) { - return addError( "Unable to parse token length", token ); - } - - // Avoid using a string constant for the format control string given to - // sscanf, as this can cause hard to debug crashes on OS X. See here for more - // info: - // - // http://developer.apple.com/library/mac/#DOCUMENTATION/DeveloperTools/gcc-4.0.1/gcc/Incompatibilities.html - char format[] = "%lf"; - - if ( length <= bufferSize ) - { - Char buffer[bufferSize+1]; - memcpy( buffer, token.start_, length ); - buffer[length] = 0; - count = sscanf( buffer, format, &value ); - } - else - { - std::string buffer( token.start_, token.end_ ); - count = sscanf( buffer.c_str(), format, &value ); - } - - if ( count != 1 ) - return addError( "'" + std::string( token.start_, token.end_ ) + "' is not a number.", token ); - currentValue() = value; - return true; -} - - -bool -Reader::decodeString( Token &token ) -{ - std::string decoded; - if ( !decodeString( token, decoded ) ) - return false; - currentValue() = decoded; - return true; -} - - -bool -Reader::decodeString( Token &token, std::string &decoded ) -{ - decoded.reserve( token.end_ - token.start_ - 2 ); - Location current = token.start_ + 1; // skip '"' - Location end = token.end_ - 1; // do not include '"' - while ( current != end ) - { - Char c = *current++; - if ( c == '"' ) - break; - else if ( c == '\\' ) - { - if ( current == end ) - return addError( "Empty escape sequence in string", token, current ); - Char escape = *current++; - switch ( escape ) - { - case '"': decoded += '"'; break; - case '/': decoded += '/'; break; - case '\\': decoded += '\\'; break; - case 'b': decoded += '\b'; break; - case 'f': decoded += '\f'; break; - case 'n': decoded += '\n'; break; - case 'r': decoded += '\r'; break; - case 't': decoded += '\t'; break; - case 'u': - { - unsigned int unicode; - if ( !decodeUnicodeCodePoint( token, current, end, unicode ) ) - return false; - decoded += codePointToUTF8(unicode); - } - break; - default: - return addError( "Bad escape sequence in string", token, current ); - } - } - else - { - decoded += c; - } - } - return true; -} - -bool -Reader::decodeUnicodeCodePoint( Token &token, - Location ¤t, - Location end, - unsigned int &unicode ) -{ - - if ( !decodeUnicodeEscapeSequence( token, current, end, unicode ) ) - return false; - if (unicode >= 0xD800 && unicode <= 0xDBFF) - { - // surrogate pairs - if (end - current < 6) - return addError( "additional six characters expected to parse unicode surrogate pair.", token, current ); - unsigned int surrogatePair; - if (*(current++) == '\\' && *(current++)== 'u') - { - if (decodeUnicodeEscapeSequence( token, current, end, surrogatePair )) - { - unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF); - } - else - return false; - } - else - return addError( "expecting another \\u token to begin the second half of a unicode surrogate pair", token, current ); - } - return true; -} - -bool -Reader::decodeUnicodeEscapeSequence( Token &token, - Location ¤t, - Location end, - unsigned int &unicode ) -{ - if ( end - current < 4 ) - return addError( "Bad unicode escape sequence in string: four digits expected.", token, current ); - unicode = 0; - for ( int index =0; index < 4; ++index ) - { - Char c = *current++; - unicode *= 16; - if ( c >= '0' && c <= '9' ) - unicode += c - '0'; - else if ( c >= 'a' && c <= 'f' ) - unicode += c - 'a' + 10; - else if ( c >= 'A' && c <= 'F' ) - unicode += c - 'A' + 10; - else - return addError( "Bad unicode escape sequence in string: hexadecimal digit expected.", token, current ); - } - return true; -} - - -bool -Reader::addError( const std::string &message, - Token &token, - Location extra ) -{ - ErrorInfo info; - info.token_ = token; - info.message_ = message; - info.extra_ = extra; - errors_.push_back( info ); - return false; -} - - -bool -Reader::recoverFromError( TokenType skipUntilToken ) -{ - int errorCount = int(errors_.size()); - Token skip; - for (;;) - { - if ( !readToken(skip) ) - errors_.resize( errorCount ); // discard errors caused by recovery - if ( skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream ) - break; - } - errors_.resize( errorCount ); - return false; -} - - -bool -Reader::addErrorAndRecover( const std::string &message, - Token &token, - TokenType skipUntilToken ) -{ - addError( message, token ); - return recoverFromError( skipUntilToken ); -} - - -Value & -Reader::currentValue() -{ - return *(nodes_.top()); -} - - -Reader::Char -Reader::getNextChar() -{ - if ( current_ == end_ ) - return 0; - return *current_++; -} - - -void -Reader::getLocationLineAndColumn( Location location, - int &line, - int &column ) const -{ - Location current = begin_; - Location lastLineStart = current; - line = 0; - while ( current < location && current != end_ ) - { - Char c = *current++; - if ( c == '\r' ) - { - if ( *current == '\n' ) - ++current; - lastLineStart = current; - ++line; - } - else if ( c == '\n' ) - { - lastLineStart = current; - ++line; - } - } - // column & line start at 1 - column = int(location - lastLineStart) + 1; - ++line; -} - - -std::string -Reader::getLocationLineAndColumn( Location location ) const -{ - int line, column; - getLocationLineAndColumn( location, line, column ); - char buffer[18+16+16+1]; - sprintf( buffer, "Line %d, Column %d", line, column ); - return buffer; -} - - -// Deprecated. Preserved for backward compatibility -std::string -Reader::getFormatedErrorMessages() const -{ - return getFormattedErrorMessages(); -} - - -std::string -Reader::getFormattedErrorMessages() const -{ - std::string formattedMessage; - for ( Errors::const_iterator itError = errors_.begin(); - itError != errors_.end(); - ++itError ) - { - const ErrorInfo &error = *itError; - formattedMessage += "* " + getLocationLineAndColumn( error.token_.start_ ) + "\n"; - formattedMessage += " " + error.message_ + "\n"; - if ( error.extra_ ) - formattedMessage += "See " + getLocationLineAndColumn( error.extra_ ) + " for detail.\n"; - } - return formattedMessage; -} - - -std::istream& operator>>( std::istream &sin, Value &root ) -{ - Json::Reader reader; - bool ok = reader.parse(sin, root, true); - if (!ok) { - fprintf( - stderr, - "Error from reader: %s", - reader.getFormattedErrorMessages().c_str()); - - JSON_FAIL_MESSAGE("reader error"); - } - return sin; -} - - -} // namespace Json - -// ////////////////////////////////////////////////////////////////////// -// End of content of file: src/lib_json/json_reader.cpp -// ////////////////////////////////////////////////////////////////////// - - - - - - -// ////////////////////////////////////////////////////////////////////// -// Beginning of content of file: src/lib_json/json_batchallocator.h -// ////////////////////////////////////////////////////////////////////// - -// Copyright 2007-2010 Baptiste Lepilleur -// Distributed under MIT license, or public domain if desired and -// recognized in your jurisdiction. -// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE - -#ifndef JSONCPP_BATCHALLOCATOR_H_INCLUDED -# define JSONCPP_BATCHALLOCATOR_H_INCLUDED - -# include -# include - -# ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION - -namespace Json { - -/* Fast memory allocator. - * - * This memory allocator allocates memory for a batch of object (specified by - * the page size, the number of object in each page). - * - * It does not allow the destruction of a single object. All the allocated objects - * can be destroyed at once. The memory can be either released or reused for future - * allocation. - * - * The in-place new operator must be used to construct the object using the pointer - * returned by allocate. - */ -template -class BatchAllocator -{ -public: - BatchAllocator( unsigned int objectsPerPage = 255 ) - : freeHead_( 0 ) - , objectsPerPage_( objectsPerPage ) - { -// printf( "Size: %d => %s\n", sizeof(AllocatedType), typeid(AllocatedType).name() ); - assert( sizeof(AllocatedType) * objectPerAllocation >= sizeof(AllocatedType *) ); // We must be able to store a slist in the object free space. - assert( objectsPerPage >= 16 ); - batches_ = allocateBatch( 0 ); // allocated a dummy page - currentBatch_ = batches_; - } - - ~BatchAllocator() - { - for ( BatchInfo *batch = batches_; batch; ) - { - BatchInfo *nextBatch = batch->next_; - free( batch ); - batch = nextBatch; - } - } - - /// allocate space for an array of objectPerAllocation object. - /// @warning it is the responsability of the caller to call objects constructors. - AllocatedType *allocate() - { - if ( freeHead_ ) // returns node from free list. - { - AllocatedType *object = freeHead_; - freeHead_ = *(AllocatedType **)object; - return object; - } - if ( currentBatch_->used_ == currentBatch_->end_ ) - { - currentBatch_ = currentBatch_->next_; - while ( currentBatch_ && currentBatch_->used_ == currentBatch_->end_ ) - currentBatch_ = currentBatch_->next_; - - if ( !currentBatch_ ) // no free batch found, allocate a new one - { - currentBatch_ = allocateBatch( objectsPerPage_ ); - currentBatch_->next_ = batches_; // insert at the head of the list - batches_ = currentBatch_; - } - } - AllocatedType *allocated = currentBatch_->used_; - currentBatch_->used_ += objectPerAllocation; - return allocated; - } - - /// Release the object. - /// @warning it is the responsability of the caller to actually destruct the object. - void release( AllocatedType *object ) - { - assert( object != 0 ); - *(AllocatedType **)object = freeHead_; - freeHead_ = object; - } - -private: - struct BatchInfo - { - BatchInfo *next_; - AllocatedType *used_; - AllocatedType *end_; - AllocatedType buffer_[objectPerAllocation]; - }; - - // disabled copy constructor and assignement operator. - BatchAllocator( const BatchAllocator & ); - void operator =( const BatchAllocator &); - - static BatchInfo *allocateBatch( unsigned int objectsPerPage ) - { - const unsigned int mallocSize = sizeof(BatchInfo) - sizeof(AllocatedType)* objectPerAllocation - + sizeof(AllocatedType) * objectPerAllocation * objectsPerPage; - BatchInfo *batch = static_cast( malloc( mallocSize ) ); - batch->next_ = 0; - batch->used_ = batch->buffer_; - batch->end_ = batch->buffer_ + objectsPerPage; - return batch; - } - - BatchInfo *batches_; - BatchInfo *currentBatch_; - /// Head of a single linked list within the allocated space of freeed object - AllocatedType *freeHead_; - unsigned int objectsPerPage_; -}; - - -} // namespace Json - -# endif // ifndef JSONCPP_DOC_INCLUDE_IMPLEMENTATION - -#endif // JSONCPP_BATCHALLOCATOR_H_INCLUDED - -// ////////////////////////////////////////////////////////////////////// -// End of content of file: src/lib_json/json_batchallocator.h -// ////////////////////////////////////////////////////////////////////// - - - - - - -// ////////////////////////////////////////////////////////////////////// -// Beginning of content of file: src/lib_json/json_valueiterator.inl -// ////////////////////////////////////////////////////////////////////// - -// Copyright 2007-2010 Baptiste Lepilleur -// Distributed under MIT license, or public domain if desired and -// recognized in your jurisdiction. -// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE - -// included by json_value.cpp - -namespace Json { - -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// class ValueIteratorBase -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// - -ValueIteratorBase::ValueIteratorBase() -#ifndef JSON_VALUE_USE_INTERNAL_MAP - : current_() - , isNull_( true ) -{ -} -#else - : isArray_( true ) - , isNull_( true ) -{ - iterator_.array_ = ValueInternalArray::IteratorState(); -} -#endif - - -#ifndef JSON_VALUE_USE_INTERNAL_MAP -ValueIteratorBase::ValueIteratorBase( const Value::ObjectValues::iterator ¤t ) - : current_( current ) - , isNull_( false ) -{ -} -#else -ValueIteratorBase::ValueIteratorBase( const ValueInternalArray::IteratorState &state ) - : isArray_( true ) -{ - iterator_.array_ = state; -} - - -ValueIteratorBase::ValueIteratorBase( const ValueInternalMap::IteratorState &state ) - : isArray_( false ) -{ - iterator_.map_ = state; -} -#endif - -Value & -ValueIteratorBase::deref() const -{ -#ifndef JSON_VALUE_USE_INTERNAL_MAP - return current_->second; -#else - if ( isArray_ ) - return ValueInternalArray::dereference( iterator_.array_ ); - return ValueInternalMap::value( iterator_.map_ ); -#endif -} - - -void -ValueIteratorBase::increment() -{ -#ifndef JSON_VALUE_USE_INTERNAL_MAP - ++current_; -#else - if ( isArray_ ) - ValueInternalArray::increment( iterator_.array_ ); - ValueInternalMap::increment( iterator_.map_ ); -#endif -} - - -void -ValueIteratorBase::decrement() -{ -#ifndef JSON_VALUE_USE_INTERNAL_MAP - --current_; -#else - if ( isArray_ ) - ValueInternalArray::decrement( iterator_.array_ ); - ValueInternalMap::decrement( iterator_.map_ ); -#endif -} - - -ValueIteratorBase::difference_type -ValueIteratorBase::computeDistance( const SelfType &other ) const -{ -#ifndef JSON_VALUE_USE_INTERNAL_MAP -# ifdef JSON_USE_CPPTL_SMALLMAP - return current_ - other.current_; -# else - // Iterator for null value are initialized using the default - // constructor, which initialize current_ to the default - // std::map::iterator. As begin() and end() are two instance - // of the default std::map::iterator, they can not be compared. - // To allow this, we handle this comparison specifically. - if ( isNull_ && other.isNull_ ) - { - return 0; - } - - - // Usage of std::distance is not portable (does not compile with Sun Studio 12 RogueWave STL, - // which is the one used by default). - // Using a portable hand-made version for non random iterator instead: - // return difference_type( std::distance( current_, other.current_ ) ); - difference_type myDistance = 0; - for ( Value::ObjectValues::iterator it = current_; it != other.current_; ++it ) - { - ++myDistance; - } - return myDistance; -# endif -#else - if ( isArray_ ) - return ValueInternalArray::distance( iterator_.array_, other.iterator_.array_ ); - return ValueInternalMap::distance( iterator_.map_, other.iterator_.map_ ); -#endif -} - - -bool -ValueIteratorBase::isEqual( const SelfType &other ) const -{ -#ifndef JSON_VALUE_USE_INTERNAL_MAP - if ( isNull_ ) - { - return other.isNull_; - } - return current_ == other.current_; -#else - if ( isArray_ ) - return ValueInternalArray::equals( iterator_.array_, other.iterator_.array_ ); - return ValueInternalMap::equals( iterator_.map_, other.iterator_.map_ ); -#endif -} - - -void -ValueIteratorBase::copy( const SelfType &other ) -{ -#ifndef JSON_VALUE_USE_INTERNAL_MAP - current_ = other.current_; -#else - if ( isArray_ ) - iterator_.array_ = other.iterator_.array_; - iterator_.map_ = other.iterator_.map_; -#endif -} - - -Value -ValueIteratorBase::key() const -{ -#ifndef JSON_VALUE_USE_INTERNAL_MAP - const Value::CZString czstring = (*current_).first; - if ( czstring.c_str() ) - { - if ( czstring.isStaticString() ) - return Value( StaticString( czstring.c_str() ) ); - return Value( czstring.c_str() ); - } - return Value( czstring.index() ); -#else - if ( isArray_ ) - return Value( ValueInternalArray::indexOf( iterator_.array_ ) ); - bool isStatic; - const char *memberName = ValueInternalMap::key( iterator_.map_, isStatic ); - if ( isStatic ) - return Value( StaticString( memberName ) ); - return Value( memberName ); -#endif -} - - -UInt -ValueIteratorBase::index() const -{ -#ifndef JSON_VALUE_USE_INTERNAL_MAP - const Value::CZString czstring = (*current_).first; - if ( !czstring.c_str() ) - return czstring.index(); - return Value::UInt( -1 ); -#else - if ( isArray_ ) - return Value::UInt( ValueInternalArray::indexOf( iterator_.array_ ) ); - return Value::UInt( -1 ); -#endif -} - - -const char * -ValueIteratorBase::memberName() const -{ -#ifndef JSON_VALUE_USE_INTERNAL_MAP - const char *name = (*current_).first.c_str(); - return name ? name : ""; -#else - if ( !isArray_ ) - return ValueInternalMap::key( iterator_.map_ ); - return ""; -#endif -} - - -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// class ValueConstIterator -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// - -ValueConstIterator::ValueConstIterator() -{ -} - - -#ifndef JSON_VALUE_USE_INTERNAL_MAP -ValueConstIterator::ValueConstIterator( const Value::ObjectValues::iterator ¤t ) - : ValueIteratorBase( current ) -{ -} -#else -ValueConstIterator::ValueConstIterator( const ValueInternalArray::IteratorState &state ) - : ValueIteratorBase( state ) -{ -} - -ValueConstIterator::ValueConstIterator( const ValueInternalMap::IteratorState &state ) - : ValueIteratorBase( state ) -{ -} -#endif - -ValueConstIterator & -ValueConstIterator::operator =( const ValueIteratorBase &other ) -{ - copy( other ); - return *this; -} - - -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// class ValueIterator -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// - -ValueIterator::ValueIterator() -{ -} - - -#ifndef JSON_VALUE_USE_INTERNAL_MAP -ValueIterator::ValueIterator( const Value::ObjectValues::iterator ¤t ) - : ValueIteratorBase( current ) -{ -} -#else -ValueIterator::ValueIterator( const ValueInternalArray::IteratorState &state ) - : ValueIteratorBase( state ) -{ -} - -ValueIterator::ValueIterator( const ValueInternalMap::IteratorState &state ) - : ValueIteratorBase( state ) -{ -} -#endif - -ValueIterator::ValueIterator( const ValueConstIterator &other ) - : ValueIteratorBase( other ) -{ -} - -ValueIterator::ValueIterator( const ValueIterator &other ) - : ValueIteratorBase( other ) -{ -} - -ValueIterator & -ValueIterator::operator =( const SelfType &other ) -{ - copy( other ); - return *this; -} - -} // namespace Json - -// ////////////////////////////////////////////////////////////////////// -// End of content of file: src/lib_json/json_valueiterator.inl -// ////////////////////////////////////////////////////////////////////// - - - - - - -// ////////////////////////////////////////////////////////////////////// -// Beginning of content of file: src/lib_json/json_value.cpp -// ////////////////////////////////////////////////////////////////////// - -// Copyright 2011 Baptiste Lepilleur -// Distributed under MIT license, or public domain if desired and -// recognized in your jurisdiction. -// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE - -#if !defined(JSON_IS_AMALGAMATION) -# include -# include -# include -# ifndef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR -# include "json_batchallocator.h" -# endif // #ifndef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR -#endif // if !defined(JSON_IS_AMALGAMATION) -#include -#include -#include -#include -#include -#include -#ifdef JSON_USE_CPPTL -# include -#endif -#include // size_t - -#define JSON_ASSERT_UNREACHABLE assert( false ) - -namespace Json { - -const Value Value::null; -const Int Value::minInt = Int( ~(UInt(-1)/2) ); -const Int Value::maxInt = Int( UInt(-1)/2 ); -const UInt Value::maxUInt = UInt(-1); -# if defined(JSON_HAS_INT64) -const Int64 Value::minInt64 = Int64( ~(UInt64(-1)/2) ); -const Int64 Value::maxInt64 = Int64( UInt64(-1)/2 ); -const UInt64 Value::maxUInt64 = UInt64(-1); -// The constant is hard-coded because some compiler have trouble -// converting Value::maxUInt64 to a double correctly (AIX/xlC). -// Assumes that UInt64 is a 64 bits integer. -static const double maxUInt64AsDouble = 18446744073709551615.0; -#endif // defined(JSON_HAS_INT64) -const LargestInt Value::minLargestInt = LargestInt( ~(LargestUInt(-1)/2) ); -const LargestInt Value::maxLargestInt = LargestInt( LargestUInt(-1)/2 ); -const LargestUInt Value::maxLargestUInt = LargestUInt(-1); - - -/// Unknown size marker -static const unsigned int unknown = (unsigned)-1; - -#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) -template -static inline bool InRange(double d, T min, U max) { - return d >= min && d <= max; -} -#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) -static inline double integerToDouble( Json::UInt64 value ) -{ - return static_cast( Int64(value/2) ) * 2.0 + Int64(value & 1); -} - -template -static inline double integerToDouble( T value ) -{ - return static_cast( value ); -} - -template -static inline bool InRange(double d, T min, U max) { - return d >= integerToDouble(min) && d <= integerToDouble(max); -} -#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) - - -/** Duplicates the specified string value. - * @param value Pointer to the string to duplicate. Must be zero-terminated if - * length is "unknown". - * @param length Length of the value. if equals to unknown, then it will be - * computed using strlen(value). - * @return Pointer on the duplicate instance of string. - */ -static inline char * -duplicateStringValue( const char *value, - unsigned int length = unknown ) -{ - if ( length == unknown ) - length = (unsigned int)strlen(value); - - // Avoid an integer overflow in the call to malloc below by limiting length - // to a sane value. - if (length >= (unsigned)Value::maxInt) - length = Value::maxInt - 1; - - char *newString = static_cast( malloc( length + 1 ) ); - JSON_ASSERT_MESSAGE( newString != 0, "Failed to allocate string value buffer" ); - memcpy( newString, value, length ); - newString[length] = 0; - return newString; -} - - -/** Free the string duplicated by duplicateStringValue(). - */ -static inline void -releaseStringValue( char *value ) -{ - if ( value ) - free( value ); -} - -} // namespace Json - - -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ValueInternals... -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -#if !defined(JSON_IS_AMALGAMATION) -# ifdef JSON_VALUE_USE_INTERNAL_MAP -# include "json_internalarray.inl" -# include "json_internalmap.inl" -# endif // JSON_VALUE_USE_INTERNAL_MAP - -# include "json_valueiterator.inl" -#endif // if !defined(JSON_IS_AMALGAMATION) - -namespace Json { - -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// class Value::CommentInfo -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// - - -Value::CommentInfo::CommentInfo() - : comment_( 0 ) -{ -} - -Value::CommentInfo::~CommentInfo() -{ - if ( comment_ ) - releaseStringValue( comment_ ); -} - - -void -Value::CommentInfo::setComment( const char *text ) -{ - if ( comment_ ) - releaseStringValue( comment_ ); - JSON_ASSERT( text != 0 ); - JSON_ASSERT_MESSAGE( text[0]=='\0' || text[0]=='/', "Comments must start with /"); - // It seems that /**/ style comments are acceptable as well. - comment_ = duplicateStringValue( text ); -} - - -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// class Value::CZString -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -# ifndef JSON_VALUE_USE_INTERNAL_MAP - -// Notes: index_ indicates if the string was allocated when -// a string is stored. - -Value::CZString::CZString( ArrayIndex index ) - : cstr_( 0 ) - , index_( index ) -{ -} - -Value::CZString::CZString( const char *cstr, DuplicationPolicy allocate ) - : cstr_( allocate == duplicate ? duplicateStringValue(cstr) - : cstr ) - , index_( allocate ) -{ -} - -Value::CZString::CZString( const CZString &other ) -: cstr_( other.index_ != noDuplication && other.cstr_ != 0 - ? duplicateStringValue( other.cstr_ ) - : other.cstr_ ) - , index_( other.cstr_ ? (other.index_ == noDuplication ? noDuplication : duplicate) - : other.index_ ) -{ -} - -Value::CZString::~CZString() -{ - if ( cstr_ && index_ == duplicate ) - releaseStringValue( const_cast( cstr_ ) ); -} - -void -Value::CZString::swap( CZString &other ) -{ - std::swap( cstr_, other.cstr_ ); - std::swap( index_, other.index_ ); -} - -Value::CZString & -Value::CZString::operator =( const CZString &other ) -{ - CZString temp( other ); - swap( temp ); - return *this; -} - -bool -Value::CZString::operator<( const CZString &other ) const -{ - if ( cstr_ ) - return strcmp( cstr_, other.cstr_ ) < 0; - return index_ < other.index_; -} - -bool -Value::CZString::operator==( const CZString &other ) const -{ - if ( cstr_ ) - return strcmp( cstr_, other.cstr_ ) == 0; - return index_ == other.index_; -} - - -ArrayIndex -Value::CZString::index() const -{ - return index_; -} - - -const char * -Value::CZString::c_str() const -{ - return cstr_; -} - -bool -Value::CZString::isStaticString() const -{ - return index_ == noDuplication; -} - -#endif // ifndef JSON_VALUE_USE_INTERNAL_MAP - - -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// class Value::Value -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// - -/*! \internal Default constructor initialization must be equivalent to: - * memset( this, 0, sizeof(Value) ) - * This optimization is used in ValueInternalMap fast allocator. - */ -Value::Value( ValueType type ) - : type_( type ) - , allocated_( false ) -# ifdef JSON_VALUE_USE_INTERNAL_MAP - , itemIsUsed_( 0 ) -#endif - , comments_( 0 ) -{ - switch ( type ) - { - case nullValue: - break; - case intValue: - case uintValue: - value_.int_ = 0; - break; - case realValue: - value_.real_ = 0.0; - break; - case stringValue: - value_.string_ = 0; - break; -#ifndef JSON_VALUE_USE_INTERNAL_MAP - case arrayValue: - case objectValue: - value_.map_ = new ObjectValues(); - break; -#else - case arrayValue: - value_.array_ = arrayAllocator()->newArray(); - break; - case objectValue: - value_.map_ = mapAllocator()->newMap(); - break; -#endif - case booleanValue: - value_.bool_ = false; - break; - default: - JSON_ASSERT_UNREACHABLE; - } -} - - -Value::Value( UInt value ) - : type_( uintValue ) - , allocated_( false ) -# ifdef JSON_VALUE_USE_INTERNAL_MAP - , itemIsUsed_( 0 ) -#endif - , comments_( 0 ) -{ - value_.uint_ = value; -} - -Value::Value( Int value ) - : type_( intValue ) - , allocated_( false ) -# ifdef JSON_VALUE_USE_INTERNAL_MAP - , itemIsUsed_( 0 ) -#endif - , comments_( 0 ) -{ - value_.int_ = value; -} - - -# if defined(JSON_HAS_INT64) -Value::Value( Int64 value ) - : type_( intValue ) - , allocated_( false ) -# ifdef JSON_VALUE_USE_INTERNAL_MAP - , itemIsUsed_( 0 ) -#endif - , comments_( 0 ) -{ - value_.int_ = value; -} - - -Value::Value( UInt64 value ) - : type_( uintValue ) - , allocated_( false ) -# ifdef JSON_VALUE_USE_INTERNAL_MAP - , itemIsUsed_( 0 ) -#endif - , comments_( 0 ) -{ - value_.uint_ = value; -} -#endif // defined(JSON_HAS_INT64) - -Value::Value( double value ) - : type_( realValue ) - , allocated_( false ) -# ifdef JSON_VALUE_USE_INTERNAL_MAP - , itemIsUsed_( 0 ) -#endif - , comments_( 0 ) -{ - value_.real_ = value; -} - -Value::Value( const char *value ) - : type_( stringValue ) - , allocated_( true ) -# ifdef JSON_VALUE_USE_INTERNAL_MAP - , itemIsUsed_( 0 ) -#endif - , comments_( 0 ) -{ - value_.string_ = duplicateStringValue( value ); -} - - -Value::Value( const char *beginValue, - const char *endValue ) - : type_( stringValue ) - , allocated_( true ) -# ifdef JSON_VALUE_USE_INTERNAL_MAP - , itemIsUsed_( 0 ) -#endif - , comments_( 0 ) -{ - value_.string_ = duplicateStringValue( beginValue, - (unsigned int)(endValue - beginValue) ); -} - - -Value::Value( const std::string &value ) - : type_( stringValue ) - , allocated_( true ) -# ifdef JSON_VALUE_USE_INTERNAL_MAP - , itemIsUsed_( 0 ) -#endif - , comments_( 0 ) -{ - value_.string_ = duplicateStringValue( value.c_str(), - (unsigned int)value.length() ); - -} - -Value::Value( const StaticString &value ) - : type_( stringValue ) - , allocated_( false ) -# ifdef JSON_VALUE_USE_INTERNAL_MAP - , itemIsUsed_( 0 ) -#endif - , comments_( 0 ) -{ - value_.string_ = const_cast( value.c_str() ); -} - - -# ifdef JSON_USE_CPPTL -Value::Value( const CppTL::ConstString &value ) - : type_( stringValue ) - , allocated_( true ) -# ifdef JSON_VALUE_USE_INTERNAL_MAP - , itemIsUsed_( 0 ) -#endif - , comments_( 0 ) -{ - value_.string_ = duplicateStringValue( value, value.length() ); -} -# endif - -Value::Value( bool value ) - : type_( booleanValue ) - , allocated_( false ) -# ifdef JSON_VALUE_USE_INTERNAL_MAP - , itemIsUsed_( 0 ) -#endif - , comments_( 0 ) -{ - value_.bool_ = value; -} - - -Value::Value( const Value &other ) - : type_( other.type_ ) - , allocated_( false ) -# ifdef JSON_VALUE_USE_INTERNAL_MAP - , itemIsUsed_( 0 ) -#endif - , comments_( 0 ) -{ - switch ( type_ ) - { - case nullValue: - case intValue: - case uintValue: - case realValue: - case booleanValue: - value_ = other.value_; - break; - case stringValue: - if ( other.value_.string_ ) - { - value_.string_ = duplicateStringValue( other.value_.string_ ); - allocated_ = true; - } - else - value_.string_ = 0; - break; -#ifndef JSON_VALUE_USE_INTERNAL_MAP - case arrayValue: - case objectValue: - value_.map_ = new ObjectValues( *other.value_.map_ ); - break; -#else - case arrayValue: - value_.array_ = arrayAllocator()->newArrayCopy( *other.value_.array_ ); - break; - case objectValue: - value_.map_ = mapAllocator()->newMapCopy( *other.value_.map_ ); - break; -#endif - default: - JSON_ASSERT_UNREACHABLE; - } - if ( other.comments_ ) - { - comments_ = new CommentInfo[numberOfCommentPlacement]; - for ( int comment =0; comment < numberOfCommentPlacement; ++comment ) - { - const CommentInfo &otherComment = other.comments_[comment]; - if ( otherComment.comment_ ) - comments_[comment].setComment( otherComment.comment_ ); - } - } -} - - -Value::~Value() -{ - switch ( type_ ) - { - case nullValue: - case intValue: - case uintValue: - case realValue: - case booleanValue: - break; - case stringValue: - if ( allocated_ ) - releaseStringValue( value_.string_ ); - break; -#ifndef JSON_VALUE_USE_INTERNAL_MAP - case arrayValue: - case objectValue: - delete value_.map_; - break; -#else - case arrayValue: - arrayAllocator()->destructArray( value_.array_ ); - break; - case objectValue: - mapAllocator()->destructMap( value_.map_ ); - break; -#endif - default: - JSON_ASSERT_UNREACHABLE; - } - - if ( comments_ ) - delete[] comments_; -} - -Value & -Value::operator=( const Value &other ) -{ - Value temp( other ); - swap( temp ); - return *this; -} - -void -Value::swap( Value &other ) -{ - ValueType temp = type_; - type_ = other.type_; - other.type_ = temp; - std::swap( value_, other.value_ ); - int temp2 = allocated_; - allocated_ = other.allocated_; - other.allocated_ = temp2; -} - -ValueType -Value::type() const -{ - return type_; -} - - -int -Value::compare( const Value &other ) const -{ - if ( *this < other ) - return -1; - if ( *this > other ) - return 1; - return 0; -} - - -bool -Value::operator <( const Value &other ) const -{ - int typeDelta = type_ - other.type_; - if ( typeDelta ) - return typeDelta < 0 ? true : false; - switch ( type_ ) - { - case nullValue: - return false; - case intValue: - return value_.int_ < other.value_.int_; - case uintValue: - return value_.uint_ < other.value_.uint_; - case realValue: - return value_.real_ < other.value_.real_; - case booleanValue: - return value_.bool_ < other.value_.bool_; - case stringValue: - return ( value_.string_ == 0 && other.value_.string_ ) - || ( other.value_.string_ - && value_.string_ - && strcmp( value_.string_, other.value_.string_ ) < 0 ); -#ifndef JSON_VALUE_USE_INTERNAL_MAP - case arrayValue: - case objectValue: - { - int delta = int( value_.map_->size() - other.value_.map_->size() ); - if ( delta ) - return delta < 0; - return (*value_.map_) < (*other.value_.map_); - } -#else - case arrayValue: - return value_.array_->compare( *(other.value_.array_) ) < 0; - case objectValue: - return value_.map_->compare( *(other.value_.map_) ) < 0; -#endif - default: - JSON_ASSERT_UNREACHABLE; - } - return false; // unreachable -} - -bool -Value::operator <=( const Value &other ) const -{ - return !(other < *this); -} - -bool -Value::operator >=( const Value &other ) const -{ - return !(*this < other); -} - -bool -Value::operator >( const Value &other ) const -{ - return other < *this; -} - -bool -Value::operator ==( const Value &other ) const -{ - //if ( type_ != other.type_ ) - // GCC 2.95.3 says: - // attempt to take address of bit-field structure member `Json::Value::type_' - // Beats me, but a temp solves the problem. - int temp = other.type_; - if ( type_ != temp ) - return false; - switch ( type_ ) - { - case nullValue: - return true; - case intValue: - return value_.int_ == other.value_.int_; - case uintValue: - return value_.uint_ == other.value_.uint_; - case realValue: - return value_.real_ == other.value_.real_; - case booleanValue: - return value_.bool_ == other.value_.bool_; - case stringValue: - return ( value_.string_ == other.value_.string_ ) - || ( other.value_.string_ - && value_.string_ - && strcmp( value_.string_, other.value_.string_ ) == 0 ); -#ifndef JSON_VALUE_USE_INTERNAL_MAP - case arrayValue: - case objectValue: - return value_.map_->size() == other.value_.map_->size() - && (*value_.map_) == (*other.value_.map_); -#else - case arrayValue: - return value_.array_->compare( *(other.value_.array_) ) == 0; - case objectValue: - return value_.map_->compare( *(other.value_.map_) ) == 0; -#endif - default: - JSON_ASSERT_UNREACHABLE; - } - return false; // unreachable -} - -bool -Value::operator !=( const Value &other ) const -{ - return !( *this == other ); -} - -const char * -Value::asCString() const -{ - JSON_ASSERT( type_ == stringValue ); - return value_.string_; -} - - -std::string -Value::asString() const -{ - switch ( type_ ) - { - case nullValue: - return ""; - case stringValue: - return value_.string_ ? value_.string_ : ""; - case booleanValue: - return value_.bool_ ? "true" : "false"; - case intValue: - return valueToString( value_.int_ ); - case uintValue: - return valueToString( value_.uint_ ); - case realValue: - return valueToString( value_.real_ ); - default: - JSON_FAIL_MESSAGE( "Type is not convertible to string" ); - } -} - -# ifdef JSON_USE_CPPTL -CppTL::ConstString -Value::asConstString() const -{ - return CppTL::ConstString( asString().c_str() ); -} -# endif - - -Value::Int -Value::asInt() const -{ - switch ( type_ ) - { - case intValue: - JSON_ASSERT_MESSAGE(isInt(), "LargestInt out of Int range"); - return Int(value_.int_); - case uintValue: - JSON_ASSERT_MESSAGE(isInt(), "LargestUInt out of Int range"); - return Int(value_.uint_); - case realValue: - JSON_ASSERT_MESSAGE(InRange(value_.real_, minInt, maxInt), "double out of Int range"); - return Int(value_.real_); - case nullValue: - return 0; - case booleanValue: - return value_.bool_ ? 1 : 0; - default: - break; - } - JSON_FAIL_MESSAGE("Value is not convertible to Int."); -} - - -Value::UInt -Value::asUInt() const -{ - switch ( type_ ) - { - case intValue: - JSON_ASSERT_MESSAGE(isUInt(), "LargestInt out of UInt range"); - return UInt(value_.int_); - case uintValue: - JSON_ASSERT_MESSAGE(isUInt(), "LargestUInt out of UInt range"); - return UInt(value_.uint_); - case realValue: - JSON_ASSERT_MESSAGE(InRange(value_.real_, 0, maxUInt), "double out of UInt range"); - return UInt( value_.real_ ); - case nullValue: - return 0; - case booleanValue: - return value_.bool_ ? 1 : 0; - default: - break; - } - JSON_FAIL_MESSAGE("Value is not convertible to UInt."); -} - - -# if defined(JSON_HAS_INT64) - -Value::Int64 -Value::asInt64() const -{ - switch ( type_ ) - { - case intValue: - return Int64(value_.int_); - case uintValue: - JSON_ASSERT_MESSAGE(isInt64(), "LargestUInt out of Int64 range"); - return Int64(value_.uint_); - case realValue: - JSON_ASSERT_MESSAGE(InRange(value_.real_, minInt64, maxInt64), "double out of Int64 range"); - return Int64(value_.real_); - case nullValue: - return 0; - case booleanValue: - return value_.bool_ ? 1 : 0; - default: - break; - } - JSON_FAIL_MESSAGE("Value is not convertible to Int64."); -} - - -Value::UInt64 -Value::asUInt64() const -{ - switch ( type_ ) - { - case intValue: - JSON_ASSERT_MESSAGE(isUInt64(), "LargestInt out of UInt64 range"); - return UInt64(value_.int_); - case uintValue: - return UInt64(value_.uint_); - case realValue: - JSON_ASSERT_MESSAGE(InRange(value_.real_, 0, maxUInt64), "double out of UInt64 range"); - return UInt64( value_.real_ ); - case nullValue: - return 0; - case booleanValue: - return value_.bool_ ? 1 : 0; - default: - break; - } - JSON_FAIL_MESSAGE("Value is not convertible to UInt64."); -} -# endif // if defined(JSON_HAS_INT64) - - -LargestInt -Value::asLargestInt() const -{ -#if defined(JSON_NO_INT64) - return asInt(); -#else - return asInt64(); -#endif -} - - -LargestUInt -Value::asLargestUInt() const -{ -#if defined(JSON_NO_INT64) - return asUInt(); -#else - return asUInt64(); -#endif -} - - -double -Value::asDouble() const -{ - switch ( type_ ) - { - case intValue: - return static_cast( value_.int_ ); - case uintValue: -#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) - return static_cast( value_.uint_ ); -#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) - return integerToDouble( value_.uint_ ); -#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) - case realValue: - return value_.real_; - case nullValue: - return 0.0; - case booleanValue: - return value_.bool_ ? 1.0 : 0.0; - default: - break; - } - JSON_FAIL_MESSAGE("Value is not convertible to double."); -} - -float -Value::asFloat() const -{ - switch ( type_ ) - { - case intValue: - return static_cast( value_.int_ ); - case uintValue: -#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) - return static_cast( value_.uint_ ); -#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) - return integerToDouble( value_.uint_ ); -#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) - case realValue: - return static_cast( value_.real_ ); - case nullValue: - return 0.0; - case booleanValue: - return value_.bool_ ? 1.0f : 0.0f; - default: - break; - } - JSON_FAIL_MESSAGE("Value is not convertible to float."); -} - -bool -Value::asBool() const -{ - switch ( type_ ) - { - case booleanValue: - return value_.bool_; - case nullValue: - return false; - case intValue: - return value_.int_ ? true : false; - case uintValue: - return value_.uint_ ? true : false; - case realValue: - return value_.real_ ? true : false; - default: - break; - } - JSON_FAIL_MESSAGE("Value is not convertible to bool."); -} - - -bool -Value::isConvertibleTo( ValueType other ) const -{ - switch ( other ) - { - case nullValue: - return ( isNumeric() && asDouble() == 0.0 ) - || ( type_ == booleanValue && value_.bool_ == false ) - || ( type_ == stringValue && asString() == "" ) - || ( type_ == arrayValue && value_.map_->size() == 0 ) - || ( type_ == objectValue && value_.map_->size() == 0 ) - || type_ == nullValue; - case intValue: - return isInt() - || (type_ == realValue && InRange(value_.real_, minInt, maxInt)) - || type_ == booleanValue - || type_ == nullValue; - case uintValue: - return isUInt() - || (type_ == realValue && InRange(value_.real_, 0, maxUInt)) - || type_ == booleanValue - || type_ == nullValue; - case realValue: - return isNumeric() - || type_ == booleanValue - || type_ == nullValue; - case booleanValue: - return isNumeric() - || type_ == booleanValue - || type_ == nullValue; - case stringValue: - return isNumeric() - || type_ == booleanValue - || type_ == stringValue - || type_ == nullValue; - case arrayValue: - return type_ == arrayValue - || type_ == nullValue; - case objectValue: - return type_ == objectValue - || type_ == nullValue; - } - JSON_ASSERT_UNREACHABLE; - return false; -} - - -/// Number of values in array or object -ArrayIndex -Value::size() const -{ - switch ( type_ ) - { - case nullValue: - case intValue: - case uintValue: - case realValue: - case booleanValue: - case stringValue: - return 0; -#ifndef JSON_VALUE_USE_INTERNAL_MAP - case arrayValue: // size of the array is highest index + 1 - if ( !value_.map_->empty() ) - { - ObjectValues::const_iterator itLast = value_.map_->end(); - --itLast; - return (*itLast).first.index()+1; - } - return 0; - case objectValue: - return ArrayIndex( value_.map_->size() ); -#else - case arrayValue: - return Int( value_.array_->size() ); - case objectValue: - return Int( value_.map_->size() ); -#endif - } - JSON_ASSERT_UNREACHABLE; - return 0; // unreachable; -} - - -bool -Value::empty() const -{ - if ( isNull() || isArray() || isObject() ) - return size() == 0u; - else - return false; -} - - -bool -Value::operator!() const -{ - return isNull(); -} - - -void -Value::clear() -{ - JSON_ASSERT( type_ == nullValue || type_ == arrayValue || type_ == objectValue ); - - switch ( type_ ) - { -#ifndef JSON_VALUE_USE_INTERNAL_MAP - case arrayValue: - case objectValue: - value_.map_->clear(); - break; -#else - case arrayValue: - value_.array_->clear(); - break; - case objectValue: - value_.map_->clear(); - break; -#endif - default: - break; - } -} - -void -Value::resize( ArrayIndex newSize ) -{ - JSON_ASSERT( type_ == nullValue || type_ == arrayValue ); - if ( type_ == nullValue ) - *this = Value( arrayValue ); -#ifndef JSON_VALUE_USE_INTERNAL_MAP - ArrayIndex oldSize = size(); - if ( newSize == 0 ) - clear(); - else if ( newSize > oldSize ) - (*this)[ newSize - 1 ]; - else - { - for ( ArrayIndex index = newSize; index < oldSize; ++index ) - { - value_.map_->erase( index ); - } - assert( size() == newSize ); - } -#else - value_.array_->resize( newSize ); -#endif -} - - -Value & -Value::operator[]( ArrayIndex index ) -{ - JSON_ASSERT( type_ == nullValue || type_ == arrayValue ); - if ( type_ == nullValue ) - *this = Value( arrayValue ); -#ifndef JSON_VALUE_USE_INTERNAL_MAP - CZString key( index ); - ObjectValues::iterator it = value_.map_->lower_bound( key ); - if ( it != value_.map_->end() && (*it).first == key ) - return (*it).second; - - ObjectValues::value_type defaultValue( key, null ); - it = value_.map_->insert( it, defaultValue ); - return (*it).second; -#else - return value_.array_->resolveReference( index ); -#endif -} - - -Value & -Value::operator[]( int index ) -{ - JSON_ASSERT( index >= 0 ); - return (*this)[ ArrayIndex(index) ]; -} - - -const Value & -Value::operator[]( ArrayIndex index ) const -{ - JSON_ASSERT( type_ == nullValue || type_ == arrayValue ); - if ( type_ == nullValue ) - return null; -#ifndef JSON_VALUE_USE_INTERNAL_MAP - CZString key( index ); - ObjectValues::const_iterator it = value_.map_->find( key ); - if ( it == value_.map_->end() ) - return null; - return (*it).second; -#else - Value *value = value_.array_->find( index ); - return value ? *value : null; -#endif -} - - -const Value & -Value::operator[]( int index ) const -{ - JSON_ASSERT( index >= 0 ); - return (*this)[ ArrayIndex(index) ]; -} - - -Value & -Value::operator[]( const char *key ) -{ - return resolveReference( key, false ); -} - - -Value & -Value::resolveReference( const char *key, - bool isStatic ) -{ - JSON_ASSERT( type_ == nullValue || type_ == objectValue ); - if ( type_ == nullValue ) - *this = Value( objectValue ); -#ifndef JSON_VALUE_USE_INTERNAL_MAP - CZString actualKey( key, isStatic ? CZString::noDuplication - : CZString::duplicateOnCopy ); - ObjectValues::iterator it = value_.map_->lower_bound( actualKey ); - if ( it != value_.map_->end() && (*it).first == actualKey ) - return (*it).second; - - ObjectValues::value_type defaultValue( actualKey, null ); - it = value_.map_->insert( it, defaultValue ); - Value &value = (*it).second; - return value; -#else - return value_.map_->resolveReference( key, isStatic ); -#endif -} - - -Value -Value::get( ArrayIndex index, - const Value &defaultValue ) const -{ - const Value *value = &((*this)[index]); - return value == &null ? defaultValue : *value; -} - - -bool -Value::isValidIndex( ArrayIndex index ) const -{ - return index < size(); -} - - - -const Value & -Value::operator[]( const char *key ) const -{ - JSON_ASSERT( type_ == nullValue || type_ == objectValue ); - if ( type_ == nullValue ) - return null; -#ifndef JSON_VALUE_USE_INTERNAL_MAP - CZString actualKey( key, CZString::noDuplication ); - ObjectValues::const_iterator it = value_.map_->find( actualKey ); - if ( it == value_.map_->end() ) - return null; - return (*it).second; -#else - const Value *value = value_.map_->find( key ); - return value ? *value : null; -#endif -} - - -Value & -Value::operator[]( const std::string &key ) -{ - return (*this)[ key.c_str() ]; -} - - -const Value & -Value::operator[]( const std::string &key ) const -{ - return (*this)[ key.c_str() ]; -} - -Value & -Value::operator[]( const StaticString &key ) -{ - return resolveReference( key, true ); -} - - -# ifdef JSON_USE_CPPTL -Value & -Value::operator[]( const CppTL::ConstString &key ) -{ - return (*this)[ key.c_str() ]; -} - - -const Value & -Value::operator[]( const CppTL::ConstString &key ) const -{ - return (*this)[ key.c_str() ]; -} -# endif - - -Value & -Value::append( const Value &value ) -{ - return (*this)[size()] = value; -} - - -Value -Value::get( const char *key, - const Value &defaultValue ) const -{ - const Value *value = &((*this)[key]); - return value == &null ? defaultValue : *value; -} - - -Value -Value::get( const std::string &key, - const Value &defaultValue ) const -{ - return get( key.c_str(), defaultValue ); -} - -Value -Value::removeMember( const char* key ) -{ - JSON_ASSERT( type_ == nullValue || type_ == objectValue ); - if ( type_ == nullValue ) - return null; -#ifndef JSON_VALUE_USE_INTERNAL_MAP - CZString actualKey( key, CZString::noDuplication ); - ObjectValues::iterator it = value_.map_->find( actualKey ); - if ( it == value_.map_->end() ) - return null; - Value old(it->second); - value_.map_->erase(it); - return old; -#else - Value *value = value_.map_->find( key ); - if (value){ - Value old(*value); - value_.map_.remove( key ); - return old; - } else { - return null; - } -#endif -} - -Value -Value::removeMember( const std::string &key ) -{ - return removeMember( key.c_str() ); -} - -# ifdef JSON_USE_CPPTL -Value -Value::get( const CppTL::ConstString &key, - const Value &defaultValue ) const -{ - return get( key.c_str(), defaultValue ); -} -# endif - -bool -Value::isMember( const char *key ) const -{ - const Value *value = &((*this)[key]); - return value != &null; -} - - -bool -Value::isMember( const std::string &key ) const -{ - return isMember( key.c_str() ); -} - - -# ifdef JSON_USE_CPPTL -bool -Value::isMember( const CppTL::ConstString &key ) const -{ - return isMember( key.c_str() ); -} -#endif - -Value::Members -Value::getMemberNames() const -{ - JSON_ASSERT( type_ == nullValue || type_ == objectValue ); - if ( type_ == nullValue ) - return Value::Members(); - Members members; - members.reserve( value_.map_->size() ); -#ifndef JSON_VALUE_USE_INTERNAL_MAP - ObjectValues::const_iterator it = value_.map_->begin(); - ObjectValues::const_iterator itEnd = value_.map_->end(); - for ( ; it != itEnd; ++it ) - members.push_back( std::string( (*it).first.c_str() ) ); -#else - ValueInternalMap::IteratorState it; - ValueInternalMap::IteratorState itEnd; - value_.map_->makeBeginIterator( it ); - value_.map_->makeEndIterator( itEnd ); - for ( ; !ValueInternalMap::equals( it, itEnd ); ValueInternalMap::increment(it) ) - members.push_back( std::string( ValueInternalMap::key( it ) ) ); -#endif - return members; -} -// -//# ifdef JSON_USE_CPPTL -//EnumMemberNames -//Value::enumMemberNames() const -//{ -// if ( type_ == objectValue ) -// { -// return CppTL::Enum::any( CppTL::Enum::transform( -// CppTL::Enum::keys( *(value_.map_), CppTL::Type() ), -// MemberNamesTransform() ) ); -// } -// return EnumMemberNames(); -//} -// -// -//EnumValues -//Value::enumValues() const -//{ -// if ( type_ == objectValue || type_ == arrayValue ) -// return CppTL::Enum::anyValues( *(value_.map_), -// CppTL::Type() ); -// return EnumValues(); -//} -// -//# endif - -static bool IsIntegral(double d) { - double integral_part; - return modf(d, &integral_part) == 0.0; -} - - -bool -Value::isNull() const -{ - return type_ == nullValue; -} - - -bool -Value::isBool() const -{ - return type_ == booleanValue; -} - - -bool -Value::isInt() const -{ - switch ( type_ ) - { - case intValue: - return value_.int_ >= minInt && value_.int_ <= maxInt; - case uintValue: - return value_.uint_ <= UInt(maxInt); - case realValue: - return value_.real_ >= minInt && - value_.real_ <= maxInt && - IsIntegral(value_.real_); - default: - break; - } - return false; -} - - -bool -Value::isUInt() const -{ - switch ( type_ ) - { - case intValue: - return value_.int_ >= 0 && LargestUInt(value_.int_) <= LargestUInt(maxUInt); - case uintValue: - return value_.uint_ <= maxUInt; - case realValue: - return value_.real_ >= 0 && - value_.real_ <= maxUInt && - IsIntegral(value_.real_); - default: - break; - } - return false; -} - -bool -Value::isInt64() const -{ -# if defined(JSON_HAS_INT64) - switch ( type_ ) - { - case intValue: - return true; - case uintValue: - return value_.uint_ <= UInt64(maxInt64); - case realValue: - // Note that maxInt64 (= 2^63 - 1) is not exactly representable as a - // double, so double(maxInt64) will be rounded up to 2^63. Therefore we - // require the value to be strictly less than the limit. - return value_.real_ >= double(minInt64) && - value_.real_ < double(maxInt64) && - IsIntegral(value_.real_); - default: - break; - } -# endif // JSON_HAS_INT64 - return false; -} - -bool -Value::isUInt64() const -{ -# if defined(JSON_HAS_INT64) - switch ( type_ ) - { - case intValue: - return value_.int_ >= 0; - case uintValue: - return true; - case realValue: - // Note that maxUInt64 (= 2^64 - 1) is not exactly representable as a - // double, so double(maxUInt64) will be rounded up to 2^64. Therefore we - // require the value to be strictly less than the limit. - return value_.real_ >= 0 && - value_.real_ < maxUInt64AsDouble && - IsIntegral(value_.real_); - default: - break; - } -# endif // JSON_HAS_INT64 - return false; -} - - -bool -Value::isIntegral() const -{ -#if defined(JSON_HAS_INT64) - return isInt64() || isUInt64(); -#else - return isInt() || isUInt(); -#endif -} - - -bool -Value::isDouble() const -{ - return type_ == realValue || isIntegral(); -} - - -bool -Value::isNumeric() const -{ - return isIntegral() || isDouble(); -} - - -bool -Value::isString() const -{ - return type_ == stringValue; -} - - -bool -Value::isArray() const -{ - return type_ == arrayValue; -} - - -bool -Value::isObject() const -{ - return type_ == objectValue; -} - - -void -Value::setComment( const char *comment, - CommentPlacement placement ) -{ - if ( !comments_ ) - comments_ = new CommentInfo[numberOfCommentPlacement]; - comments_[placement].setComment( comment ); -} - - -void -Value::setComment( const std::string &comment, - CommentPlacement placement ) -{ - setComment( comment.c_str(), placement ); -} - - -bool -Value::hasComment( CommentPlacement placement ) const -{ - return comments_ != 0 && comments_[placement].comment_ != 0; -} - -std::string -Value::getComment( CommentPlacement placement ) const -{ - if ( hasComment(placement) ) - return comments_[placement].comment_; - return ""; -} - - -std::string -Value::toStyledString() const -{ - StyledWriter writer; - return writer.write( *this ); -} - - -Value::const_iterator -Value::begin() const -{ - switch ( type_ ) - { -#ifdef JSON_VALUE_USE_INTERNAL_MAP - case arrayValue: - if ( value_.array_ ) - { - ValueInternalArray::IteratorState it; - value_.array_->makeBeginIterator( it ); - return const_iterator( it ); - } - break; - case objectValue: - if ( value_.map_ ) - { - ValueInternalMap::IteratorState it; - value_.map_->makeBeginIterator( it ); - return const_iterator( it ); - } - break; -#else - case arrayValue: - case objectValue: - if ( value_.map_ ) - return const_iterator( value_.map_->begin() ); - break; -#endif - default: - break; - } - return const_iterator(); -} - -Value::const_iterator -Value::end() const -{ - switch ( type_ ) - { -#ifdef JSON_VALUE_USE_INTERNAL_MAP - case arrayValue: - if ( value_.array_ ) - { - ValueInternalArray::IteratorState it; - value_.array_->makeEndIterator( it ); - return const_iterator( it ); - } - break; - case objectValue: - if ( value_.map_ ) - { - ValueInternalMap::IteratorState it; - value_.map_->makeEndIterator( it ); - return const_iterator( it ); - } - break; -#else - case arrayValue: - case objectValue: - if ( value_.map_ ) - return const_iterator( value_.map_->end() ); - break; -#endif - default: - break; - } - return const_iterator(); -} - - -Value::iterator -Value::begin() -{ - switch ( type_ ) - { -#ifdef JSON_VALUE_USE_INTERNAL_MAP - case arrayValue: - if ( value_.array_ ) - { - ValueInternalArray::IteratorState it; - value_.array_->makeBeginIterator( it ); - return iterator( it ); - } - break; - case objectValue: - if ( value_.map_ ) - { - ValueInternalMap::IteratorState it; - value_.map_->makeBeginIterator( it ); - return iterator( it ); - } - break; -#else - case arrayValue: - case objectValue: - if ( value_.map_ ) - return iterator( value_.map_->begin() ); - break; -#endif - default: - break; - } - return iterator(); -} - -Value::iterator -Value::end() -{ - switch ( type_ ) - { -#ifdef JSON_VALUE_USE_INTERNAL_MAP - case arrayValue: - if ( value_.array_ ) - { - ValueInternalArray::IteratorState it; - value_.array_->makeEndIterator( it ); - return iterator( it ); - } - break; - case objectValue: - if ( value_.map_ ) - { - ValueInternalMap::IteratorState it; - value_.map_->makeEndIterator( it ); - return iterator( it ); - } - break; -#else - case arrayValue: - case objectValue: - if ( value_.map_ ) - return iterator( value_.map_->end() ); - break; -#endif - default: - break; - } - return iterator(); -} - - -// class PathArgument -// ////////////////////////////////////////////////////////////////// - -PathArgument::PathArgument() - : key_() - , index_() - , kind_( kindNone ) -{ -} - - -PathArgument::PathArgument( ArrayIndex index ) - : key_() - , index_( index ) - , kind_( kindIndex ) -{ -} - - -PathArgument::PathArgument( const char *key ) - : key_( key ) - , index_() - , kind_( kindKey ) -{ -} - - -PathArgument::PathArgument( const std::string &key ) - : key_( key.c_str() ) - , index_() - , kind_( kindKey ) -{ -} - -// class Path -// ////////////////////////////////////////////////////////////////// - -Path::Path( const std::string &path, - const PathArgument &a1, - const PathArgument &a2, - const PathArgument &a3, - const PathArgument &a4, - const PathArgument &a5 ) -{ - InArgs in; - in.push_back( &a1 ); - in.push_back( &a2 ); - in.push_back( &a3 ); - in.push_back( &a4 ); - in.push_back( &a5 ); - makePath( path, in ); -} - - -void -Path::makePath( const std::string &path, - const InArgs &in ) -{ - const char *current = path.c_str(); - const char *end = current + path.length(); - InArgs::const_iterator itInArg = in.begin(); - while ( current != end ) - { - if ( *current == '[' ) - { - ++current; - if ( *current == '%' ) - addPathInArg( path, in, itInArg, PathArgument::kindIndex ); - else - { - ArrayIndex index = 0; - for ( ; current != end && *current >= '0' && *current <= '9'; ++current ) - index = index * 10 + ArrayIndex(*current - '0'); - args_.push_back( index ); - } - if ( current == end || *current++ != ']' ) - invalidPath( path, int(current - path.c_str()) ); - } - else if ( *current == '%' ) - { - addPathInArg( path, in, itInArg, PathArgument::kindKey ); - ++current; - } - else if ( *current == '.' ) - { - ++current; - } - else - { - const char *beginName = current; - while ( current != end && !strchr( "[.", *current ) ) - ++current; - args_.push_back( std::string( beginName, current ) ); - } - } -} - - -void -Path::addPathInArg( const std::string &path, - const InArgs &in, - InArgs::const_iterator &itInArg, - PathArgument::Kind kind ) -{ - if ( itInArg == in.end() ) - { - // Error: missing argument %d - } - else if ( (*itInArg)->kind_ != kind ) - { - // Error: bad argument type - } - else - { - args_.push_back( **itInArg ); - } -} - - -void -Path::invalidPath( const std::string &path, - int location ) -{ - // Error: invalid path. -} - - -const Value & -Path::resolve( const Value &root ) const -{ - const Value *node = &root; - for ( Args::const_iterator it = args_.begin(); it != args_.end(); ++it ) - { - const PathArgument &arg = *it; - if ( arg.kind_ == PathArgument::kindIndex ) - { - if ( !node->isArray() || !node->isValidIndex( arg.index_ ) ) - { - // Error: unable to resolve path (array value expected at position... - } - node = &((*node)[arg.index_]); - } - else if ( arg.kind_ == PathArgument::kindKey ) - { - if ( !node->isObject() ) - { - // Error: unable to resolve path (object value expected at position...) - } - node = &((*node)[arg.key_]); - if ( node == &Value::null ) - { - // Error: unable to resolve path (object has no member named '' at position...) - } - } - } - return *node; -} - - -Value -Path::resolve( const Value &root, - const Value &defaultValue ) const -{ - const Value *node = &root; - for ( Args::const_iterator it = args_.begin(); it != args_.end(); ++it ) - { - const PathArgument &arg = *it; - if ( arg.kind_ == PathArgument::kindIndex ) - { - if ( !node->isArray() || !node->isValidIndex( arg.index_ ) ) - return defaultValue; - node = &((*node)[arg.index_]); - } - else if ( arg.kind_ == PathArgument::kindKey ) - { - if ( !node->isObject() ) - return defaultValue; - node = &((*node)[arg.key_]); - if ( node == &Value::null ) - return defaultValue; - } - } - return *node; -} - - -Value & -Path::make( Value &root ) const -{ - Value *node = &root; - for ( Args::const_iterator it = args_.begin(); it != args_.end(); ++it ) - { - const PathArgument &arg = *it; - if ( arg.kind_ == PathArgument::kindIndex ) - { - if ( !node->isArray() ) - { - // Error: node is not an array at position ... - } - node = &((*node)[arg.index_]); - } - else if ( arg.kind_ == PathArgument::kindKey ) - { - if ( !node->isObject() ) - { - // Error: node is not an object at position... - } - node = &((*node)[arg.key_]); - } - } - return *node; -} - - -} // namespace Json - -// ////////////////////////////////////////////////////////////////////// -// End of content of file: src/lib_json/json_value.cpp -// ////////////////////////////////////////////////////////////////////// - - - - - - -// ////////////////////////////////////////////////////////////////////// -// Beginning of content of file: src/lib_json/json_writer.cpp -// ////////////////////////////////////////////////////////////////////// - -// Copyright 2011 Baptiste Lepilleur -// Distributed under MIT license, or public domain if desired and -// recognized in your jurisdiction. -// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE - -#if !defined(JSON_IS_AMALGAMATION) -# include -# include "json_tool.h" -#endif // if !defined(JSON_IS_AMALGAMATION) -#include -#include -#include -#include -#include -#include - -#if defined(_MSC_VER) && _MSC_VER >= 1400 // VC++ 8.0 -#pragma warning( disable : 4996 ) // disable warning about strdup being deprecated. -#endif - -namespace Json { - -static bool containsControlCharacter( const char* str ) -{ - while ( *str ) - { - if ( isControlCharacter( *(str++) ) ) - return true; - } - return false; -} - - -std::string valueToString( LargestInt value ) -{ - UIntToStringBuffer buffer; - char *current = buffer + sizeof(buffer); - bool isNegative = value < 0; - if ( isNegative ) - value = -value; - uintToString( LargestUInt(value), current ); - if ( isNegative ) - *--current = '-'; - assert( current >= buffer ); - return current; -} - - -std::string valueToString( LargestUInt value ) -{ - UIntToStringBuffer buffer; - char *current = buffer + sizeof(buffer); - uintToString( value, current ); - assert( current >= buffer ); - return current; -} - -#if defined(JSON_HAS_INT64) - -std::string valueToString( Int value ) -{ - return valueToString( LargestInt(value) ); -} - - -std::string valueToString( UInt value ) -{ - return valueToString( LargestUInt(value) ); -} - -#endif // # if defined(JSON_HAS_INT64) - - -std::string valueToString( double value ) -{ - char buffer[32]; -#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) // Use secure version with visual studio 2005 to avoid warning. - sprintf_s(buffer, sizeof(buffer), "%#.16g", value); -#else - sprintf(buffer, "%#.16g", value); -#endif - char* ch = buffer + strlen(buffer) - 1; - if (*ch != '0') return buffer; // nothing to truncate, so save time - while(ch > buffer && *ch == '0'){ - --ch; - } - char* last_nonzero = ch; - while(ch >= buffer){ - switch(*ch){ - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - --ch; - continue; - case '.': - // Truncate zeroes to save bytes in output, but keep one. - *(last_nonzero+2) = '\0'; - return buffer; - default: - return buffer; - } - } - return buffer; -} - - -std::string valueToString( bool value ) -{ - return value ? "true" : "false"; -} - -std::string valueToQuotedString( const char *value ) -{ - if (value == NULL) - return ""; - // Not sure how to handle unicode... - if (strpbrk(value, "\"\\\b\f\n\r\t") == NULL && !containsControlCharacter( value )) - return std::string("\"") + value + "\""; - // We have to walk value and escape any special characters. - // Appending to std::string is not efficient, but this should be rare. - // (Note: forward slashes are *not* rare, but I am not escaping them.) - std::string::size_type maxsize = strlen(value)*2 + 3; // allescaped+quotes+NULL - std::string result; - result.reserve(maxsize); // to avoid lots of mallocs - result += "\""; - for (const char* c=value; *c != 0; ++c) - { - switch(*c) - { - case '\"': - result += "\\\""; - break; - case '\\': - result += "\\\\"; - break; - case '\b': - result += "\\b"; - break; - case '\f': - result += "\\f"; - break; - case '\n': - result += "\\n"; - break; - case '\r': - result += "\\r"; - break; - case '\t': - result += "\\t"; - break; - //case '/': - // Even though \/ is considered a legal escape in JSON, a bare - // slash is also legal, so I see no reason to escape it. - // (I hope I am not misunderstanding something. - // blep notes: actually escaping \/ may be useful in javascript to avoid (*c); - result += oss.str(); - } - else - { - result += *c; - } - break; - } - } - result += "\""; - return result; -} - -// Class Writer -// ////////////////////////////////////////////////////////////////// -Writer::~Writer() -{ -} - - -// Class FastWriter -// ////////////////////////////////////////////////////////////////// - -FastWriter::FastWriter() - : yamlCompatiblityEnabled_( false ), - dropNullPlaceholders_( false ) -{ -} - - -void -FastWriter::enableYAMLCompatibility() -{ - yamlCompatiblityEnabled_ = true; -} - - -void -FastWriter::dropNullPlaceholders() -{ - dropNullPlaceholders_ = true; -} - - -std::string -FastWriter::write( const Value &root ) -{ - document_ = ""; - writeValue( root ); - document_ += "\n"; - return document_; -} - - -void -FastWriter::writeValue( const Value &value ) -{ - switch ( value.type() ) - { - case nullValue: - if (!dropNullPlaceholders_) document_ += "null"; - break; - case intValue: - document_ += valueToString( value.asLargestInt() ); - break; - case uintValue: - document_ += valueToString( value.asLargestUInt() ); - break; - case realValue: - document_ += valueToString( value.asDouble() ); - break; - case stringValue: - document_ += valueToQuotedString( value.asCString() ); - break; - case booleanValue: - document_ += valueToString( value.asBool() ); - break; - case arrayValue: - { - document_ += "["; - int size = value.size(); - for ( int index =0; index < size; ++index ) - { - if ( index > 0 ) - document_ += ","; - writeValue( value[index] ); - } - document_ += "]"; - } - break; - case objectValue: - { - Value::Members members( value.getMemberNames() ); - document_ += "{"; - for ( Value::Members::iterator it = members.begin(); - it != members.end(); - ++it ) - { - const std::string &name = *it; - if ( it != members.begin() ) - document_ += ","; - document_ += valueToQuotedString( name.c_str() ); - document_ += yamlCompatiblityEnabled_ ? ": " - : ":"; - writeValue( value[name] ); - } - document_ += "}"; - } - break; - } -} - - -// Class StyledWriter -// ////////////////////////////////////////////////////////////////// - -StyledWriter::StyledWriter() - : rightMargin_( 74 ) - , indentSize_( 3 ) - , addChildValues_() -{ -} - - -std::string -StyledWriter::write( const Value &root ) -{ - document_ = ""; - addChildValues_ = false; - indentString_ = ""; - writeCommentBeforeValue( root ); - writeValue( root ); - writeCommentAfterValueOnSameLine( root ); - document_ += "\n"; - return document_; -} - - -void -StyledWriter::writeValue( const Value &value ) -{ - switch ( value.type() ) - { - case nullValue: - pushValue( "null" ); - break; - case intValue: - pushValue( valueToString( value.asLargestInt() ) ); - break; - case uintValue: - pushValue( valueToString( value.asLargestUInt() ) ); - break; - case realValue: - pushValue( valueToString( value.asDouble() ) ); - break; - case stringValue: - pushValue( valueToQuotedString( value.asCString() ) ); - break; - case booleanValue: - pushValue( valueToString( value.asBool() ) ); - break; - case arrayValue: - writeArrayValue( value); - break; - case objectValue: - { - Value::Members members( value.getMemberNames() ); - if ( members.empty() ) - pushValue( "{}" ); - else - { - writeWithIndent( "{" ); - indent(); - Value::Members::iterator it = members.begin(); - for (;;) - { - const std::string &name = *it; - const Value &childValue = value[name]; - writeCommentBeforeValue( childValue ); - writeWithIndent( valueToQuotedString( name.c_str() ) ); - document_ += " : "; - writeValue( childValue ); - if ( ++it == members.end() ) - { - writeCommentAfterValueOnSameLine( childValue ); - break; - } - document_ += ","; - writeCommentAfterValueOnSameLine( childValue ); - } - unindent(); - writeWithIndent( "}" ); - } - } - break; - } -} - - -void -StyledWriter::writeArrayValue( const Value &value ) -{ - unsigned size = value.size(); - if ( size == 0 ) - pushValue( "[]" ); - else - { - bool isArrayMultiLine = isMultineArray( value ); - if ( isArrayMultiLine ) - { - writeWithIndent( "[" ); - indent(); - bool hasChildValue = !childValues_.empty(); - unsigned index =0; - for (;;) - { - const Value &childValue = value[index]; - writeCommentBeforeValue( childValue ); - if ( hasChildValue ) - writeWithIndent( childValues_[index] ); - else - { - writeIndent(); - writeValue( childValue ); - } - if ( ++index == size ) - { - writeCommentAfterValueOnSameLine( childValue ); - break; - } - document_ += ","; - writeCommentAfterValueOnSameLine( childValue ); - } - unindent(); - writeWithIndent( "]" ); - } - else // output on a single line - { - assert( childValues_.size() == size ); - document_ += "[ "; - for ( unsigned index =0; index < size; ++index ) - { - if ( index > 0 ) - document_ += ", "; - document_ += childValues_[index]; - } - document_ += " ]"; - } - } -} - - -bool -StyledWriter::isMultineArray( const Value &value ) -{ - int size = value.size(); - bool isMultiLine = size*3 >= rightMargin_ ; - childValues_.clear(); - for ( int index =0; index < size && !isMultiLine; ++index ) - { - const Value &childValue = value[index]; - isMultiLine = isMultiLine || - ( (childValue.isArray() || childValue.isObject()) && - childValue.size() > 0 ); - } - if ( !isMultiLine ) // check if line length > max line length - { - childValues_.reserve( size ); - addChildValues_ = true; - int lineLength = 4 + (size-1)*2; // '[ ' + ', '*n + ' ]' - for ( int index =0; index < size && !isMultiLine; ++index ) - { - writeValue( value[index] ); - lineLength += int( childValues_[index].length() ); - isMultiLine = isMultiLine && hasCommentForValue( value[index] ); - } - addChildValues_ = false; - isMultiLine = isMultiLine || lineLength >= rightMargin_; - } - return isMultiLine; -} - - -void -StyledWriter::pushValue( const std::string &value ) -{ - if ( addChildValues_ ) - childValues_.push_back( value ); - else - document_ += value; -} - - -void -StyledWriter::writeIndent() -{ - if ( !document_.empty() ) - { - char last = document_[document_.length()-1]; - if ( last == ' ' ) // already indented - return; - if ( last != '\n' ) // Comments may add new-line - document_ += '\n'; - } - document_ += indentString_; -} - - -void -StyledWriter::writeWithIndent( const std::string &value ) -{ - writeIndent(); - document_ += value; -} - - -void -StyledWriter::indent() -{ - indentString_ += std::string( indentSize_, ' ' ); -} - - -void -StyledWriter::unindent() -{ - assert( int(indentString_.size()) >= indentSize_ ); - indentString_.resize( indentString_.size() - indentSize_ ); -} - - -void -StyledWriter::writeCommentBeforeValue( const Value &root ) -{ - if ( !root.hasComment( commentBefore ) ) - return; - document_ += normalizeEOL( root.getComment( commentBefore ) ); - document_ += "\n"; -} - - -void -StyledWriter::writeCommentAfterValueOnSameLine( const Value &root ) -{ - if ( root.hasComment( commentAfterOnSameLine ) ) - document_ += " " + normalizeEOL( root.getComment( commentAfterOnSameLine ) ); - - if ( root.hasComment( commentAfter ) ) - { - document_ += "\n"; - document_ += normalizeEOL( root.getComment( commentAfter ) ); - document_ += "\n"; - } -} - - -bool -StyledWriter::hasCommentForValue( const Value &value ) -{ - return value.hasComment( commentBefore ) - || value.hasComment( commentAfterOnSameLine ) - || value.hasComment( commentAfter ); -} - - -std::string -StyledWriter::normalizeEOL( const std::string &text ) -{ - std::string normalized; - normalized.reserve( text.length() ); - const char *begin = text.c_str(); - const char *end = begin + text.length(); - const char *current = begin; - while ( current != end ) - { - char c = *current++; - if ( c == '\r' ) // mac or dos EOL - { - if ( *current == '\n' ) // convert dos EOL - ++current; - normalized += '\n'; - } - else // handle unix EOL & other char - normalized += c; - } - return normalized; -} - - -// Class StyledStreamWriter -// ////////////////////////////////////////////////////////////////// - -StyledStreamWriter::StyledStreamWriter( std::string indentation ) - : document_(NULL) - , rightMargin_( 74 ) - , indentation_( indentation ) - , addChildValues_() -{ -} - - -void -StyledStreamWriter::write( std::ostream &out, const Value &root ) -{ - document_ = &out; - addChildValues_ = false; - indentString_ = ""; - writeCommentBeforeValue( root ); - writeValue( root ); - writeCommentAfterValueOnSameLine( root ); - *document_ << "\n"; - document_ = NULL; // Forget the stream, for safety. -} - - -void -StyledStreamWriter::writeValue( const Value &value ) -{ - switch ( value.type() ) - { - case nullValue: - pushValue( "null" ); - break; - case intValue: - pushValue( valueToString( value.asLargestInt() ) ); - break; - case uintValue: - pushValue( valueToString( value.asLargestUInt() ) ); - break; - case realValue: - pushValue( valueToString( value.asDouble() ) ); - break; - case stringValue: - pushValue( valueToQuotedString( value.asCString() ) ); - break; - case booleanValue: - pushValue( valueToString( value.asBool() ) ); - break; - case arrayValue: - writeArrayValue( value); - break; - case objectValue: - { - Value::Members members( value.getMemberNames() ); - if ( members.empty() ) - pushValue( "{}" ); - else - { - writeWithIndent( "{" ); - indent(); - Value::Members::iterator it = members.begin(); - for (;;) - { - const std::string &name = *it; - const Value &childValue = value[name]; - writeCommentBeforeValue( childValue ); - writeWithIndent( valueToQuotedString( name.c_str() ) ); - *document_ << " : "; - writeValue( childValue ); - if ( ++it == members.end() ) - { - writeCommentAfterValueOnSameLine( childValue ); - break; - } - *document_ << ","; - writeCommentAfterValueOnSameLine( childValue ); - } - unindent(); - writeWithIndent( "}" ); - } - } - break; - } -} - - -void -StyledStreamWriter::writeArrayValue( const Value &value ) -{ - unsigned size = value.size(); - if ( size == 0 ) - pushValue( "[]" ); - else - { - bool isArrayMultiLine = isMultineArray( value ); - if ( isArrayMultiLine ) - { - writeWithIndent( "[" ); - indent(); - bool hasChildValue = !childValues_.empty(); - unsigned index =0; - for (;;) - { - const Value &childValue = value[index]; - writeCommentBeforeValue( childValue ); - if ( hasChildValue ) - writeWithIndent( childValues_[index] ); - else - { - writeIndent(); - writeValue( childValue ); - } - if ( ++index == size ) - { - writeCommentAfterValueOnSameLine( childValue ); - break; - } - *document_ << ","; - writeCommentAfterValueOnSameLine( childValue ); - } - unindent(); - writeWithIndent( "]" ); - } - else // output on a single line - { - assert( childValues_.size() == size ); - *document_ << "[ "; - for ( unsigned index =0; index < size; ++index ) - { - if ( index > 0 ) - *document_ << ", "; - *document_ << childValues_[index]; - } - *document_ << " ]"; - } - } -} - - -bool -StyledStreamWriter::isMultineArray( const Value &value ) -{ - int size = value.size(); - bool isMultiLine = size*3 >= rightMargin_ ; - childValues_.clear(); - for ( int index =0; index < size && !isMultiLine; ++index ) - { - const Value &childValue = value[index]; - isMultiLine = isMultiLine || - ( (childValue.isArray() || childValue.isObject()) && - childValue.size() > 0 ); - } - if ( !isMultiLine ) // check if line length > max line length - { - childValues_.reserve( size ); - addChildValues_ = true; - int lineLength = 4 + (size-1)*2; // '[ ' + ', '*n + ' ]' - for ( int index =0; index < size && !isMultiLine; ++index ) - { - writeValue( value[index] ); - lineLength += int( childValues_[index].length() ); - isMultiLine = isMultiLine && hasCommentForValue( value[index] ); - } - addChildValues_ = false; - isMultiLine = isMultiLine || lineLength >= rightMargin_; - } - return isMultiLine; -} - - -void -StyledStreamWriter::pushValue( const std::string &value ) -{ - if ( addChildValues_ ) - childValues_.push_back( value ); - else - *document_ << value; -} - - -void -StyledStreamWriter::writeIndent() -{ - /* - Some comments in this method would have been nice. ;-) - - if ( !document_.empty() ) - { - char last = document_[document_.length()-1]; - if ( last == ' ' ) // already indented - return; - if ( last != '\n' ) // Comments may add new-line - *document_ << '\n'; - } - */ - *document_ << '\n' << indentString_; -} - - -void -StyledStreamWriter::writeWithIndent( const std::string &value ) -{ - writeIndent(); - *document_ << value; -} - - -void -StyledStreamWriter::indent() -{ - indentString_ += indentation_; -} - - -void -StyledStreamWriter::unindent() -{ - assert( indentString_.size() >= indentation_.size() ); - indentString_.resize( indentString_.size() - indentation_.size() ); -} - - -void -StyledStreamWriter::writeCommentBeforeValue( const Value &root ) -{ - if ( !root.hasComment( commentBefore ) ) - return; - *document_ << normalizeEOL( root.getComment( commentBefore ) ); - *document_ << "\n"; -} - - -void -StyledStreamWriter::writeCommentAfterValueOnSameLine( const Value &root ) -{ - if ( root.hasComment( commentAfterOnSameLine ) ) - *document_ << " " + normalizeEOL( root.getComment( commentAfterOnSameLine ) ); - - if ( root.hasComment( commentAfter ) ) - { - *document_ << "\n"; - *document_ << normalizeEOL( root.getComment( commentAfter ) ); - *document_ << "\n"; - } -} - - -bool -StyledStreamWriter::hasCommentForValue( const Value &value ) -{ - return value.hasComment( commentBefore ) - || value.hasComment( commentAfterOnSameLine ) - || value.hasComment( commentAfter ); -} - - -std::string -StyledStreamWriter::normalizeEOL( const std::string &text ) -{ - std::string normalized; - normalized.reserve( text.length() ); - const char *begin = text.c_str(); - const char *end = begin + text.length(); - const char *current = begin; - while ( current != end ) - { - char c = *current++; - if ( c == '\r' ) // mac or dos EOL - { - if ( *current == '\n' ) // convert dos EOL - ++current; - normalized += '\n'; - } - else // handle unix EOL & other char - normalized += c; - } - return normalized; -} - - -std::ostream& operator<<( std::ostream &sout, const Value &root ) -{ - Json::StyledStreamWriter writer; - writer.write(sout, root); - return sout; -} - - -} // namespace Json - -// ////////////////////////////////////////////////////////////////////// -// End of content of file: src/lib_json/json_writer.cpp -// ////////////////////////////////////////////////////////////////////// - - - - - diff --git a/src/jsoncpp/json/CMakeLists.txt b/src/jsoncpp/json/CMakeLists.txt new file mode 100644 index 000000000..9056e4b6d --- /dev/null +++ b/src/jsoncpp/json/CMakeLists.txt @@ -0,0 +1,7 @@ +if(MSVC) + set(CMAKE_CXX_FLAGS_RELEASE "/MT /O2 /Ob2 /D NDEBUG") +endif() + +add_library(jsoncpp jsoncpp.cpp) +target_link_libraries(jsoncpp) + diff --git a/src/jsoncpp/json/UPDATING b/src/jsoncpp/json/UPDATING new file mode 100644 index 000000000..d00076601 --- /dev/null +++ b/src/jsoncpp/json/UPDATING @@ -0,0 +1,16 @@ +#!/bin/sh +cd .. +svn co https://jsoncpp.svn.sourceforge.net/svnroot/jsoncpp/trunk/jsoncpp jsoncpp +svn up jsoncpp +cd jsoncpp +python amalgamate.py +cp -R dist/json .. +cp dist/jsoncpp.cpp ../json + +# maybe you need to patch: +# src/json/jsoncpp.cpp: +# -#include +# +#include "json/json.h" + +#svn export --force https://jsoncpp.svn.sourceforge.net/svnroot/jsoncpp/trunk/jsoncpp/src/lib_json json +#svn export --force https://jsoncpp.svn.sourceforge.net/svnroot/jsoncpp/trunk/jsoncpp/include/json json diff --git a/src/jsoncpp/json/json.h b/src/jsoncpp/json/json.h new file mode 100644 index 000000000..396aafa82 --- /dev/null +++ b/src/jsoncpp/json/json.h @@ -0,0 +1,1914 @@ +/// Json-cpp amalgated header (http://jsoncpp.sourceforge.net/). +/// It is intented to be used with #include + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: LICENSE +// ////////////////////////////////////////////////////////////////////// + +/* +The JsonCpp library's source code, including accompanying documentation, +tests and demonstration applications, are licensed under the following +conditions... + +The author (Baptiste Lepilleur) explicitly disclaims copyright in all +jurisdictions which recognize such a disclaimer. In such jurisdictions, +this software is released into the Public Domain. + +In jurisdictions which do not recognize Public Domain property (e.g. Germany as of +2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur, and is +released under the terms of the MIT License (see below). + +In jurisdictions which recognize Public Domain property, the user of this +software may choose to accept it either as 1) Public Domain, 2) under the +conditions of the MIT License (see below), or 3) under the terms of dual +Public Domain/MIT License conditions described here, as they choose. + +The MIT License is about as close to Public Domain as a license can get, and is +described in clear, concise terms at: + + http://en.wikipedia.org/wiki/MIT_License + +The full text of the MIT License follows: + +======================================================================== +Copyright (c) 2007-2010 Baptiste Lepilleur + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, copy, +modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +======================================================================== +(END LICENSE TEXT) + +The MIT license is compatible with both the GPL and commercial +software, affording one all of the rights of Public Domain with the +minor nuisance of being required to keep the above copyright notice +and license text in the source code. Note also that by accepting the +Public Domain "license" you can re-license your copy using whatever +license you like. + +*/ + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: LICENSE +// ////////////////////////////////////////////////////////////////////// + + + + + +#ifndef JSON_AMALGATED_H_INCLUDED +# define JSON_AMALGATED_H_INCLUDED +/// If defined, indicates that the source file is amalgated +/// to prevent private header inclusion. +#define JSON_IS_AMALGAMATION + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/config.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSON_CONFIG_H_INCLUDED +# define JSON_CONFIG_H_INCLUDED + +/// If defined, indicates that json library is embedded in CppTL library. +//# define JSON_IN_CPPTL 1 + +/// If defined, indicates that json may leverage CppTL library +//# define JSON_USE_CPPTL 1 +/// If defined, indicates that cpptl vector based map should be used instead of std::map +/// as Value container. +//# define JSON_USE_CPPTL_SMALLMAP 1 +/// If defined, indicates that Json specific container should be used +/// (hash table & simple deque container with customizable allocator). +/// THIS FEATURE IS STILL EXPERIMENTAL! There is know bugs: See #3177332 +//# define JSON_VALUE_USE_INTERNAL_MAP 1 +/// Force usage of standard new/malloc based allocator instead of memory pool based allocator. +/// The memory pools allocator used optimization (initializing Value and ValueInternalLink +/// as if it was a POD) that may cause some validation tool to report errors. +/// Only has effects if JSON_VALUE_USE_INTERNAL_MAP is defined. +//# define JSON_USE_SIMPLE_INTERNAL_ALLOCATOR 1 + +// If non-zero, the library uses exceptions to report bad input instead of C +// assertion macros. The default is to use exceptions. +# ifndef JSON_USE_EXCEPTION +# define JSON_USE_EXCEPTION 1 +# endif + +/// If defined, indicates that the source file is amalgated +/// to prevent private header inclusion. +/// Remarks: it is automatically defined in the generated amalgated header. +// #define JSON_IS_AMALGAMATION + + +# ifdef JSON_IN_CPPTL +# include +# ifndef JSON_USE_CPPTL +# define JSON_USE_CPPTL 1 +# endif +# endif + +# ifdef JSON_IN_CPPTL +# define JSON_API CPPTL_API +# elif defined(JSON_DLL_BUILD) +# define JSON_API __declspec(dllexport) +# elif defined(JSON_DLL) +# define JSON_API __declspec(dllimport) +# else +# define JSON_API +# endif + +// If JSON_NO_INT64 is defined, then Json only support C++ "int" type for integer +// Storages, and 64 bits integer support is disabled. +// #define JSON_NO_INT64 1 + +#if defined(_MSC_VER) && _MSC_VER <= 1200 // MSVC 6 +// Microsoft Visual Studio 6 only support conversion from __int64 to double +// (no conversion from unsigned __int64). +#define JSON_USE_INT64_DOUBLE_CONVERSION 1 +#endif // if defined(_MSC_VER) && _MSC_VER < 1200 // MSVC 6 + +#if defined(_MSC_VER) && _MSC_VER >= 1500 // MSVC 2008 +/// Indicates that the following function is deprecated. +# define JSONCPP_DEPRECATED(message) __declspec(deprecated(message)) +#endif + +#if !defined(JSONCPP_DEPRECATED) +# define JSONCPP_DEPRECATED(message) +#endif // if !defined(JSONCPP_DEPRECATED) + +namespace Json { + typedef int Int; + typedef unsigned int UInt; +# if defined(JSON_NO_INT64) + typedef int LargestInt; + typedef unsigned int LargestUInt; +# undef JSON_HAS_INT64 +# else // if defined(JSON_NO_INT64) + // For Microsoft Visual use specific types as long long is not supported +# if defined(_MSC_VER) // Microsoft Visual Studio + typedef __int64 Int64; + typedef unsigned __int64 UInt64; +# else // if defined(_MSC_VER) // Other platforms, use long long + typedef long long int Int64; + typedef unsigned long long int UInt64; +# endif // if defined(_MSC_VER) + typedef Int64 LargestInt; + typedef UInt64 LargestUInt; +# define JSON_HAS_INT64 +# endif // if defined(JSON_NO_INT64) +} // end namespace Json + + +#endif // JSON_CONFIG_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/config.h +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/forwards.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSON_FORWARDS_H_INCLUDED +# define JSON_FORWARDS_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +# include "config.h" +#endif // if !defined(JSON_IS_AMALGAMATION) + +namespace Json { + + // writer.h + class FastWriter; + class StyledWriter; + + // reader.h + class Reader; + + // features.h + class Features; + + // value.h + typedef unsigned int ArrayIndex; + class StaticString; + class Path; + class PathArgument; + class Value; + class ValueIteratorBase; + class ValueIterator; + class ValueConstIterator; +#ifdef JSON_VALUE_USE_INTERNAL_MAP + class ValueMapAllocator; + class ValueInternalLink; + class ValueInternalArray; + class ValueInternalMap; +#endif // #ifdef JSON_VALUE_USE_INTERNAL_MAP + +} // namespace Json + + +#endif // JSON_FORWARDS_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/forwards.h +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/features.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef CPPTL_JSON_FEATURES_H_INCLUDED +# define CPPTL_JSON_FEATURES_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +# include "forwards.h" +#endif // if !defined(JSON_IS_AMALGAMATION) + +namespace Json { + + /** \brief Configuration passed to reader and writer. + * This configuration object can be used to force the Reader or Writer + * to behave in a standard conforming way. + */ + class JSON_API Features + { + public: + /** \brief A configuration that allows all features and assumes all strings are UTF-8. + * - C & C++ comments are allowed + * - Root object can be any JSON value + * - Assumes Value strings are encoded in UTF-8 + */ + static Features all(); + + /** \brief A configuration that is strictly compatible with the JSON specification. + * - Comments are forbidden. + * - Root object must be either an array or an object value. + * - Assumes Value strings are encoded in UTF-8 + */ + static Features strictMode(); + + /** \brief Initialize the configuration like JsonConfig::allFeatures; + */ + Features(); + + /// \c true if comments are allowed. Default: \c true. + bool allowComments_; + + /// \c true if root must be either an array or an object value. Default: \c false. + bool strictRoot_; + }; + +} // namespace Json + +#endif // CPPTL_JSON_FEATURES_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/features.h +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/value.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef CPPTL_JSON_H_INCLUDED +# define CPPTL_JSON_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +# include "forwards.h" +#endif // if !defined(JSON_IS_AMALGAMATION) +# include +# include + +# ifndef JSON_USE_CPPTL_SMALLMAP +# include +# else +# include +# endif +# ifdef JSON_USE_CPPTL +# include +# endif + +/** \brief JSON (JavaScript Object Notation). + */ +namespace Json { + + /** \brief Type of the value held by a Value object. + */ + enum ValueType + { + nullValue = 0, ///< 'null' value + intValue, ///< signed integer value + uintValue, ///< unsigned integer value + realValue, ///< double value + stringValue, ///< UTF-8 string value + booleanValue, ///< bool value + arrayValue, ///< array value (ordered list) + objectValue ///< object value (collection of name/value pairs). + }; + + enum CommentPlacement + { + commentBefore = 0, ///< a comment placed on the line before a value + commentAfterOnSameLine, ///< a comment just after a value on the same line + commentAfter, ///< a comment on the line after a value (only make sense for root value) + numberOfCommentPlacement + }; + +//# ifdef JSON_USE_CPPTL +// typedef CppTL::AnyEnumerator EnumMemberNames; +// typedef CppTL::AnyEnumerator EnumValues; +//# endif + + /** \brief Lightweight wrapper to tag static string. + * + * Value constructor and objectValue member assignement takes advantage of the + * StaticString and avoid the cost of string duplication when storing the + * string or the member name. + * + * Example of usage: + * \code + * Json::Value aValue( StaticString("some text") ); + * Json::Value object; + * static const StaticString code("code"); + * object[code] = 1234; + * \endcode + */ + class JSON_API StaticString + { + public: + explicit StaticString( const char *czstring ) + : str_( czstring ) + { + } + + operator const char *() const + { + return str_; + } + + const char *c_str() const + { + return str_; + } + + private: + const char *str_; + }; + + /** \brief Represents a JSON value. + * + * This class is a discriminated union wrapper that can represents a: + * - signed integer [range: Value::minInt - Value::maxInt] + * - unsigned integer (range: 0 - Value::maxUInt) + * - double + * - UTF-8 string + * - boolean + * - 'null' + * - an ordered list of Value + * - collection of name/value pairs (javascript object) + * + * The type of the held value is represented by a #ValueType and + * can be obtained using type(). + * + * values of an #objectValue or #arrayValue can be accessed using operator[]() methods. + * Non const methods will automatically create the a #nullValue element + * if it does not exist. + * The sequence of an #arrayValue will be automatically resize and initialized + * with #nullValue. resize() can be used to enlarge or truncate an #arrayValue. + * + * The get() methods can be used to obtanis default value in the case the required element + * does not exist. + * + * It is possible to iterate over the list of a #objectValue values using + * the getMemberNames() method. + */ + class JSON_API Value + { + friend class ValueIteratorBase; +# ifdef JSON_VALUE_USE_INTERNAL_MAP + friend class ValueInternalLink; + friend class ValueInternalMap; +# endif + public: + typedef std::vector Members; + typedef ValueIterator iterator; + typedef ValueConstIterator const_iterator; + typedef Json::UInt UInt; + typedef Json::Int Int; +# if defined(JSON_HAS_INT64) + typedef Json::UInt64 UInt64; + typedef Json::Int64 Int64; +#endif // defined(JSON_HAS_INT64) + typedef Json::LargestInt LargestInt; + typedef Json::LargestUInt LargestUInt; + typedef Json::ArrayIndex ArrayIndex; + + static const Value null; + /// Minimum signed integer value that can be stored in a Json::Value. + static const LargestInt minLargestInt; + /// Maximum signed integer value that can be stored in a Json::Value. + static const LargestInt maxLargestInt; + /// Maximum unsigned integer value that can be stored in a Json::Value. + static const LargestUInt maxLargestUInt; + + /// Minimum signed int value that can be stored in a Json::Value. + static const Int minInt; + /// Maximum signed int value that can be stored in a Json::Value. + static const Int maxInt; + /// Maximum unsigned int value that can be stored in a Json::Value. + static const UInt maxUInt; + +# if defined(JSON_HAS_INT64) + /// Minimum signed 64 bits int value that can be stored in a Json::Value. + static const Int64 minInt64; + /// Maximum signed 64 bits int value that can be stored in a Json::Value. + static const Int64 maxInt64; + /// Maximum unsigned 64 bits int value that can be stored in a Json::Value. + static const UInt64 maxUInt64; +#endif // defined(JSON_HAS_INT64) + + private: +#ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION +# ifndef JSON_VALUE_USE_INTERNAL_MAP + class CZString + { + public: + enum DuplicationPolicy + { + noDuplication = 0, + duplicate, + duplicateOnCopy + }; + CZString( ArrayIndex index ); + CZString( const char *cstr, DuplicationPolicy allocate ); + CZString( const CZString &other ); + ~CZString(); + CZString &operator =( const CZString &other ); + bool operator<( const CZString &other ) const; + bool operator==( const CZString &other ) const; + ArrayIndex index() const; + const char *c_str() const; + bool isStaticString() const; + private: + void swap( CZString &other ); + const char *cstr_; + ArrayIndex index_; + }; + + public: +# ifndef JSON_USE_CPPTL_SMALLMAP + typedef std::map ObjectValues; +# else + typedef CppTL::SmallMap ObjectValues; +# endif // ifndef JSON_USE_CPPTL_SMALLMAP +# endif // ifndef JSON_VALUE_USE_INTERNAL_MAP +#endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION + + public: + /** \brief Create a default Value of the given type. + + This is a very useful constructor. + To create an empty array, pass arrayValue. + To create an empty object, pass objectValue. + Another Value can then be set to this one by assignment. + This is useful since clear() and resize() will not alter types. + + Examples: + \code + Json::Value null_value; // null + Json::Value arr_value(Json::arrayValue); // [] + Json::Value obj_value(Json::objectValue); // {} + \endcode + */ + Value( ValueType type = nullValue ); + Value( Int value ); + Value( UInt value ); +#if defined(JSON_HAS_INT64) + Value( Int64 value ); + Value( UInt64 value ); +#endif // if defined(JSON_HAS_INT64) + Value( double value ); + Value( const char *value ); + Value( const char *beginValue, const char *endValue ); + /** \brief Constructs a value from a static string. + + * Like other value string constructor but do not duplicate the string for + * internal storage. The given string must remain alive after the call to this + * constructor. + * Example of usage: + * \code + * Json::Value aValue( StaticString("some text") ); + * \endcode + */ + Value( const StaticString &value ); + Value( const std::string &value ); +# ifdef JSON_USE_CPPTL + Value( const CppTL::ConstString &value ); +# endif + Value( bool value ); + Value( const Value &other ); + ~Value(); + + Value &operator=( const Value &other ); + /// Swap values. + /// \note Currently, comments are intentionally not swapped, for + /// both logic and efficiency. + void swap( Value &other ); + + ValueType type() const; + + bool operator <( const Value &other ) const; + bool operator <=( const Value &other ) const; + bool operator >=( const Value &other ) const; + bool operator >( const Value &other ) const; + + bool operator ==( const Value &other ) const; + bool operator !=( const Value &other ) const; + + int compare( const Value &other ) const; + + const char *asCString() const; + std::string asString() const; +# ifdef JSON_USE_CPPTL + CppTL::ConstString asConstString() const; +# endif + Int asInt() const; + UInt asUInt() const; +#if defined(JSON_HAS_INT64) + Int64 asInt64() const; + UInt64 asUInt64() const; +#endif // if defined(JSON_HAS_INT64) + LargestInt asLargestInt() const; + LargestUInt asLargestUInt() const; + float asFloat() const; + double asDouble() const; + bool asBool() const; + + bool isNull() const; + bool isBool() const; + bool isInt() const; + bool isInt64() const; + bool isUInt() const; + bool isUInt64() const; + bool isIntegral() const; + bool isDouble() const; + bool isNumeric() const; + bool isString() const; + bool isArray() const; + bool isObject() const; + + bool isConvertibleTo( ValueType other ) const; + + /// Number of values in array or object + ArrayIndex size() const; + + /// \brief Return true if empty array, empty object, or null; + /// otherwise, false. + bool empty() const; + + /// Return isNull() + bool operator!() const; + + /// Remove all object members and array elements. + /// \pre type() is arrayValue, objectValue, or nullValue + /// \post type() is unchanged + void clear(); + + /// Resize the array to size elements. + /// New elements are initialized to null. + /// May only be called on nullValue or arrayValue. + /// \pre type() is arrayValue or nullValue + /// \post type() is arrayValue + void resize( ArrayIndex size ); + + /// Access an array element (zero based index ). + /// If the array contains less than index element, then null value are inserted + /// in the array so that its size is index+1. + /// (You may need to say 'value[0u]' to get your compiler to distinguish + /// this from the operator[] which takes a string.) + Value &operator[]( ArrayIndex index ); + + /// Access an array element (zero based index ). + /// If the array contains less than index element, then null value are inserted + /// in the array so that its size is index+1. + /// (You may need to say 'value[0u]' to get your compiler to distinguish + /// this from the operator[] which takes a string.) + Value &operator[]( int index ); + + /// Access an array element (zero based index ) + /// (You may need to say 'value[0u]' to get your compiler to distinguish + /// this from the operator[] which takes a string.) + const Value &operator[]( ArrayIndex index ) const; + + /// Access an array element (zero based index ) + /// (You may need to say 'value[0u]' to get your compiler to distinguish + /// this from the operator[] which takes a string.) + const Value &operator[]( int index ) const; + + /// If the array contains at least index+1 elements, returns the element value, + /// otherwise returns defaultValue. + Value get( ArrayIndex index, + const Value &defaultValue ) const; + /// Return true if index < size(). + bool isValidIndex( ArrayIndex index ) const; + /// \brief Append value to array at the end. + /// + /// Equivalent to jsonvalue[jsonvalue.size()] = value; + Value &append( const Value &value ); + + /// Access an object value by name, create a null member if it does not exist. + Value &operator[]( const char *key ); + /// Access an object value by name, returns null if there is no member with that name. + const Value &operator[]( const char *key ) const; + /// Access an object value by name, create a null member if it does not exist. + Value &operator[]( const std::string &key ); + /// Access an object value by name, returns null if there is no member with that name. + const Value &operator[]( const std::string &key ) const; + /** \brief Access an object value by name, create a null member if it does not exist. + + * If the object as no entry for that name, then the member name used to store + * the new entry is not duplicated. + * Example of use: + * \code + * Json::Value object; + * static const StaticString code("code"); + * object[code] = 1234; + * \endcode + */ + Value &operator[]( const StaticString &key ); +# ifdef JSON_USE_CPPTL + /// Access an object value by name, create a null member if it does not exist. + Value &operator[]( const CppTL::ConstString &key ); + /// Access an object value by name, returns null if there is no member with that name. + const Value &operator[]( const CppTL::ConstString &key ) const; +# endif + /// Return the member named key if it exist, defaultValue otherwise. + Value get( const char *key, + const Value &defaultValue ) const; + /// Return the member named key if it exist, defaultValue otherwise. + Value get( const std::string &key, + const Value &defaultValue ) const; +# ifdef JSON_USE_CPPTL + /// Return the member named key if it exist, defaultValue otherwise. + Value get( const CppTL::ConstString &key, + const Value &defaultValue ) const; +# endif + /// \brief Remove and return the named member. + /// + /// Do nothing if it did not exist. + /// \return the removed Value, or null. + /// \pre type() is objectValue or nullValue + /// \post type() is unchanged + Value removeMember( const char* key ); + /// Same as removeMember(const char*) + Value removeMember( const std::string &key ); + + /// Return true if the object has a member named key. + bool isMember( const char *key ) const; + /// Return true if the object has a member named key. + bool isMember( const std::string &key ) const; +# ifdef JSON_USE_CPPTL + /// Return true if the object has a member named key. + bool isMember( const CppTL::ConstString &key ) const; +# endif + + /// \brief Return a list of the member names. + /// + /// If null, return an empty list. + /// \pre type() is objectValue or nullValue + /// \post if type() was nullValue, it remains nullValue + Members getMemberNames() const; + +//# ifdef JSON_USE_CPPTL +// EnumMemberNames enumMemberNames() const; +// EnumValues enumValues() const; +//# endif + + /// Comments must be //... or /* ... */ + void setComment( const char *comment, + CommentPlacement placement ); + /// Comments must be //... or /* ... */ + void setComment( const std::string &comment, + CommentPlacement placement ); + bool hasComment( CommentPlacement placement ) const; + /// Include delimiters and embedded newlines. + std::string getComment( CommentPlacement placement ) const; + + std::string toStyledString() const; + + const_iterator begin() const; + const_iterator end() const; + + iterator begin(); + iterator end(); + + private: + Value &resolveReference( const char *key, + bool isStatic ); + +# ifdef JSON_VALUE_USE_INTERNAL_MAP + inline bool isItemAvailable() const + { + return itemIsUsed_ == 0; + } + + inline void setItemUsed( bool isUsed = true ) + { + itemIsUsed_ = isUsed ? 1 : 0; + } + + inline bool isMemberNameStatic() const + { + return memberNameIsStatic_ == 0; + } + + inline void setMemberNameIsStatic( bool isStatic ) + { + memberNameIsStatic_ = isStatic ? 1 : 0; + } +# endif // # ifdef JSON_VALUE_USE_INTERNAL_MAP + + private: + struct CommentInfo + { + CommentInfo(); + ~CommentInfo(); + + void setComment( const char *text ); + + char *comment_; + }; + + //struct MemberNamesTransform + //{ + // typedef const char *result_type; + // const char *operator()( const CZString &name ) const + // { + // return name.c_str(); + // } + //}; + + union ValueHolder + { + LargestInt int_; + LargestUInt uint_; + double real_; + bool bool_; + char *string_; +# ifdef JSON_VALUE_USE_INTERNAL_MAP + ValueInternalArray *array_; + ValueInternalMap *map_; +#else + ObjectValues *map_; +# endif + } value_; + ValueType type_ : 8; + int allocated_ : 1; // Notes: if declared as bool, bitfield is useless. +# ifdef JSON_VALUE_USE_INTERNAL_MAP + unsigned int itemIsUsed_ : 1; // used by the ValueInternalMap container. + int memberNameIsStatic_ : 1; // used by the ValueInternalMap container. +# endif + CommentInfo *comments_; + }; + + + /** \brief Experimental and untested: represents an element of the "path" to access a node. + */ + class PathArgument + { + public: + friend class Path; + + PathArgument(); + PathArgument( ArrayIndex index ); + PathArgument( const char *key ); + PathArgument( const std::string &key ); + + private: + enum Kind + { + kindNone = 0, + kindIndex, + kindKey + }; + std::string key_; + ArrayIndex index_; + Kind kind_; + }; + + /** \brief Experimental and untested: represents a "path" to access a node. + * + * Syntax: + * - "." => root node + * - ".[n]" => elements at index 'n' of root node (an array value) + * - ".name" => member named 'name' of root node (an object value) + * - ".name1.name2.name3" + * - ".[0][1][2].name1[3]" + * - ".%" => member name is provided as parameter + * - ".[%]" => index is provied as parameter + */ + class Path + { + public: + Path( const std::string &path, + const PathArgument &a1 = PathArgument(), + const PathArgument &a2 = PathArgument(), + const PathArgument &a3 = PathArgument(), + const PathArgument &a4 = PathArgument(), + const PathArgument &a5 = PathArgument() ); + + const Value &resolve( const Value &root ) const; + Value resolve( const Value &root, + const Value &defaultValue ) const; + /// Creates the "path" to access the specified node and returns a reference on the node. + Value &make( Value &root ) const; + + private: + typedef std::vector InArgs; + typedef std::vector Args; + + void makePath( const std::string &path, + const InArgs &in ); + void addPathInArg( const std::string &path, + const InArgs &in, + InArgs::const_iterator &itInArg, + PathArgument::Kind kind ); + void invalidPath( const std::string &path, + int location ); + + Args args_; + }; + + + +#ifdef JSON_VALUE_USE_INTERNAL_MAP + /** \brief Allocator to customize Value internal map. + * Below is an example of a simple implementation (default implementation actually + * use memory pool for speed). + * \code + class DefaultValueMapAllocator : public ValueMapAllocator + { + public: // overridden from ValueMapAllocator + virtual ValueInternalMap *newMap() + { + return new ValueInternalMap(); + } + + virtual ValueInternalMap *newMapCopy( const ValueInternalMap &other ) + { + return new ValueInternalMap( other ); + } + + virtual void destructMap( ValueInternalMap *map ) + { + delete map; + } + + virtual ValueInternalLink *allocateMapBuckets( unsigned int size ) + { + return new ValueInternalLink[size]; + } + + virtual void releaseMapBuckets( ValueInternalLink *links ) + { + delete [] links; + } + + virtual ValueInternalLink *allocateMapLink() + { + return new ValueInternalLink(); + } + + virtual void releaseMapLink( ValueInternalLink *link ) + { + delete link; + } + }; + * \endcode + */ + class JSON_API ValueMapAllocator + { + public: + virtual ~ValueMapAllocator(); + virtual ValueInternalMap *newMap() = 0; + virtual ValueInternalMap *newMapCopy( const ValueInternalMap &other ) = 0; + virtual void destructMap( ValueInternalMap *map ) = 0; + virtual ValueInternalLink *allocateMapBuckets( unsigned int size ) = 0; + virtual void releaseMapBuckets( ValueInternalLink *links ) = 0; + virtual ValueInternalLink *allocateMapLink() = 0; + virtual void releaseMapLink( ValueInternalLink *link ) = 0; + }; + + /** \brief ValueInternalMap hash-map bucket chain link (for internal use only). + * \internal previous_ & next_ allows for bidirectional traversal. + */ + class JSON_API ValueInternalLink + { + public: + enum { itemPerLink = 6 }; // sizeof(ValueInternalLink) = 128 on 32 bits architecture. + enum InternalFlags { + flagAvailable = 0, + flagUsed = 1 + }; + + ValueInternalLink(); + + ~ValueInternalLink(); + + Value items_[itemPerLink]; + char *keys_[itemPerLink]; + ValueInternalLink *previous_; + ValueInternalLink *next_; + }; + + + /** \brief A linked page based hash-table implementation used internally by Value. + * \internal ValueInternalMap is a tradional bucket based hash-table, with a linked + * list in each bucket to handle collision. There is an addional twist in that + * each node of the collision linked list is a page containing a fixed amount of + * value. This provides a better compromise between memory usage and speed. + * + * Each bucket is made up of a chained list of ValueInternalLink. The last + * link of a given bucket can be found in the 'previous_' field of the following bucket. + * The last link of the last bucket is stored in tailLink_ as it has no following bucket. + * Only the last link of a bucket may contains 'available' item. The last link always + * contains at least one element unless is it the bucket one very first link. + */ + class JSON_API ValueInternalMap + { + friend class ValueIteratorBase; + friend class Value; + public: + typedef unsigned int HashKey; + typedef unsigned int BucketIndex; + +# ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION + struct IteratorState + { + IteratorState() + : map_(0) + , link_(0) + , itemIndex_(0) + , bucketIndex_(0) + { + } + ValueInternalMap *map_; + ValueInternalLink *link_; + BucketIndex itemIndex_; + BucketIndex bucketIndex_; + }; +# endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION + + ValueInternalMap(); + ValueInternalMap( const ValueInternalMap &other ); + ValueInternalMap &operator =( const ValueInternalMap &other ); + ~ValueInternalMap(); + + void swap( ValueInternalMap &other ); + + BucketIndex size() const; + + void clear(); + + bool reserveDelta( BucketIndex growth ); + + bool reserve( BucketIndex newItemCount ); + + const Value *find( const char *key ) const; + + Value *find( const char *key ); + + Value &resolveReference( const char *key, + bool isStatic ); + + void remove( const char *key ); + + void doActualRemove( ValueInternalLink *link, + BucketIndex index, + BucketIndex bucketIndex ); + + ValueInternalLink *&getLastLinkInBucket( BucketIndex bucketIndex ); + + Value &setNewItem( const char *key, + bool isStatic, + ValueInternalLink *link, + BucketIndex index ); + + Value &unsafeAdd( const char *key, + bool isStatic, + HashKey hashedKey ); + + HashKey hash( const char *key ) const; + + int compare( const ValueInternalMap &other ) const; + + private: + void makeBeginIterator( IteratorState &it ) const; + void makeEndIterator( IteratorState &it ) const; + static bool equals( const IteratorState &x, const IteratorState &other ); + static void increment( IteratorState &iterator ); + static void incrementBucket( IteratorState &iterator ); + static void decrement( IteratorState &iterator ); + static const char *key( const IteratorState &iterator ); + static const char *key( const IteratorState &iterator, bool &isStatic ); + static Value &value( const IteratorState &iterator ); + static int distance( const IteratorState &x, const IteratorState &y ); + + private: + ValueInternalLink *buckets_; + ValueInternalLink *tailLink_; + BucketIndex bucketsSize_; + BucketIndex itemCount_; + }; + + /** \brief A simplified deque implementation used internally by Value. + * \internal + * It is based on a list of fixed "page", each page contains a fixed number of items. + * Instead of using a linked-list, a array of pointer is used for fast item look-up. + * Look-up for an element is as follow: + * - compute page index: pageIndex = itemIndex / itemsPerPage + * - look-up item in page: pages_[pageIndex][itemIndex % itemsPerPage] + * + * Insertion is amortized constant time (only the array containing the index of pointers + * need to be reallocated when items are appended). + */ + class JSON_API ValueInternalArray + { + friend class Value; + friend class ValueIteratorBase; + public: + enum { itemsPerPage = 8 }; // should be a power of 2 for fast divide and modulo. + typedef Value::ArrayIndex ArrayIndex; + typedef unsigned int PageIndex; + +# ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION + struct IteratorState // Must be a POD + { + IteratorState() + : array_(0) + , currentPageIndex_(0) + , currentItemIndex_(0) + { + } + ValueInternalArray *array_; + Value **currentPageIndex_; + unsigned int currentItemIndex_; + }; +# endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION + + ValueInternalArray(); + ValueInternalArray( const ValueInternalArray &other ); + ValueInternalArray &operator =( const ValueInternalArray &other ); + ~ValueInternalArray(); + void swap( ValueInternalArray &other ); + + void clear(); + void resize( ArrayIndex newSize ); + + Value &resolveReference( ArrayIndex index ); + + Value *find( ArrayIndex index ) const; + + ArrayIndex size() const; + + int compare( const ValueInternalArray &other ) const; + + private: + static bool equals( const IteratorState &x, const IteratorState &other ); + static void increment( IteratorState &iterator ); + static void decrement( IteratorState &iterator ); + static Value &dereference( const IteratorState &iterator ); + static Value &unsafeDereference( const IteratorState &iterator ); + static int distance( const IteratorState &x, const IteratorState &y ); + static ArrayIndex indexOf( const IteratorState &iterator ); + void makeBeginIterator( IteratorState &it ) const; + void makeEndIterator( IteratorState &it ) const; + void makeIterator( IteratorState &it, ArrayIndex index ) const; + + void makeIndexValid( ArrayIndex index ); + + Value **pages_; + ArrayIndex size_; + PageIndex pageCount_; + }; + + /** \brief Experimental: do not use. Allocator to customize Value internal array. + * Below is an example of a simple implementation (actual implementation use + * memory pool). + \code +class DefaultValueArrayAllocator : public ValueArrayAllocator +{ +public: // overridden from ValueArrayAllocator + virtual ~DefaultValueArrayAllocator() + { + } + + virtual ValueInternalArray *newArray() + { + return new ValueInternalArray(); + } + + virtual ValueInternalArray *newArrayCopy( const ValueInternalArray &other ) + { + return new ValueInternalArray( other ); + } + + virtual void destruct( ValueInternalArray *array ) + { + delete array; + } + + virtual void reallocateArrayPageIndex( Value **&indexes, + ValueInternalArray::PageIndex &indexCount, + ValueInternalArray::PageIndex minNewIndexCount ) + { + ValueInternalArray::PageIndex newIndexCount = (indexCount*3)/2 + 1; + if ( minNewIndexCount > newIndexCount ) + newIndexCount = minNewIndexCount; + void *newIndexes = realloc( indexes, sizeof(Value*) * newIndexCount ); + if ( !newIndexes ) + throw std::bad_alloc(); + indexCount = newIndexCount; + indexes = static_cast( newIndexes ); + } + virtual void releaseArrayPageIndex( Value **indexes, + ValueInternalArray::PageIndex indexCount ) + { + if ( indexes ) + free( indexes ); + } + + virtual Value *allocateArrayPage() + { + return static_cast( malloc( sizeof(Value) * ValueInternalArray::itemsPerPage ) ); + } + + virtual void releaseArrayPage( Value *value ) + { + if ( value ) + free( value ); + } +}; + \endcode + */ + class JSON_API ValueArrayAllocator + { + public: + virtual ~ValueArrayAllocator(); + virtual ValueInternalArray *newArray() = 0; + virtual ValueInternalArray *newArrayCopy( const ValueInternalArray &other ) = 0; + virtual void destructArray( ValueInternalArray *array ) = 0; + /** \brief Reallocate array page index. + * Reallocates an array of pointer on each page. + * \param indexes [input] pointer on the current index. May be \c NULL. + * [output] pointer on the new index of at least + * \a minNewIndexCount pages. + * \param indexCount [input] current number of pages in the index. + * [output] number of page the reallocated index can handle. + * \b MUST be >= \a minNewIndexCount. + * \param minNewIndexCount Minimum number of page the new index must be able to + * handle. + */ + virtual void reallocateArrayPageIndex( Value **&indexes, + ValueInternalArray::PageIndex &indexCount, + ValueInternalArray::PageIndex minNewIndexCount ) = 0; + virtual void releaseArrayPageIndex( Value **indexes, + ValueInternalArray::PageIndex indexCount ) = 0; + virtual Value *allocateArrayPage() = 0; + virtual void releaseArrayPage( Value *value ) = 0; + }; +#endif // #ifdef JSON_VALUE_USE_INTERNAL_MAP + + + /** \brief base class for Value iterators. + * + */ + class ValueIteratorBase + { + public: + typedef unsigned int size_t; + typedef int difference_type; + typedef ValueIteratorBase SelfType; + + ValueIteratorBase(); +#ifndef JSON_VALUE_USE_INTERNAL_MAP + explicit ValueIteratorBase( const Value::ObjectValues::iterator ¤t ); +#else + ValueIteratorBase( const ValueInternalArray::IteratorState &state ); + ValueIteratorBase( const ValueInternalMap::IteratorState &state ); +#endif + + bool operator ==( const SelfType &other ) const + { + return isEqual( other ); + } + + bool operator !=( const SelfType &other ) const + { + return !isEqual( other ); + } + + difference_type operator -( const SelfType &other ) const + { + return computeDistance( other ); + } + + /// Return either the index or the member name of the referenced value as a Value. + Value key() const; + + /// Return the index of the referenced Value. -1 if it is not an arrayValue. + UInt index() const; + + /// Return the member name of the referenced Value. "" if it is not an objectValue. + const char *memberName() const; + + protected: + Value &deref() const; + + void increment(); + + void decrement(); + + difference_type computeDistance( const SelfType &other ) const; + + bool isEqual( const SelfType &other ) const; + + void copy( const SelfType &other ); + + private: +#ifndef JSON_VALUE_USE_INTERNAL_MAP + Value::ObjectValues::iterator current_; + // Indicates that iterator is for a null value. + bool isNull_; +#else + union + { + ValueInternalArray::IteratorState array_; + ValueInternalMap::IteratorState map_; + } iterator_; + bool isArray_; +#endif + }; + + /** \brief const iterator for object and array value. + * + */ + class ValueConstIterator : public ValueIteratorBase + { + friend class Value; + public: + typedef unsigned int size_t; + typedef int difference_type; + typedef const Value &reference; + typedef const Value *pointer; + typedef ValueConstIterator SelfType; + + ValueConstIterator(); + private: + /*! \internal Use by Value to create an iterator. + */ +#ifndef JSON_VALUE_USE_INTERNAL_MAP + explicit ValueConstIterator( const Value::ObjectValues::iterator ¤t ); +#else + ValueConstIterator( const ValueInternalArray::IteratorState &state ); + ValueConstIterator( const ValueInternalMap::IteratorState &state ); +#endif + public: + SelfType &operator =( const ValueIteratorBase &other ); + + SelfType operator++( int ) + { + SelfType temp( *this ); + ++*this; + return temp; + } + + SelfType operator--( int ) + { + SelfType temp( *this ); + --*this; + return temp; + } + + SelfType &operator--() + { + decrement(); + return *this; + } + + SelfType &operator++() + { + increment(); + return *this; + } + + reference operator *() const + { + return deref(); + } + }; + + + /** \brief Iterator for object and array value. + */ + class ValueIterator : public ValueIteratorBase + { + friend class Value; + public: + typedef unsigned int size_t; + typedef int difference_type; + typedef Value &reference; + typedef Value *pointer; + typedef ValueIterator SelfType; + + ValueIterator(); + ValueIterator( const ValueConstIterator &other ); + ValueIterator( const ValueIterator &other ); + private: + /*! \internal Use by Value to create an iterator. + */ +#ifndef JSON_VALUE_USE_INTERNAL_MAP + explicit ValueIterator( const Value::ObjectValues::iterator ¤t ); +#else + ValueIterator( const ValueInternalArray::IteratorState &state ); + ValueIterator( const ValueInternalMap::IteratorState &state ); +#endif + public: + + SelfType &operator =( const SelfType &other ); + + SelfType operator++( int ) + { + SelfType temp( *this ); + ++*this; + return temp; + } + + SelfType operator--( int ) + { + SelfType temp( *this ); + --*this; + return temp; + } + + SelfType &operator--() + { + decrement(); + return *this; + } + + SelfType &operator++() + { + increment(); + return *this; + } + + reference operator *() const + { + return deref(); + } + }; + + +} // namespace Json + + +#endif // CPPTL_JSON_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/value.h +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/reader.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef CPPTL_JSON_READER_H_INCLUDED +# define CPPTL_JSON_READER_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +# include "features.h" +# include "value.h" +#endif // if !defined(JSON_IS_AMALGAMATION) +# include +# include +# include + +namespace Json { + + /** \brief Unserialize a JSON document into a Value. + * + */ + class JSON_API Reader + { + public: + typedef char Char; + typedef const Char *Location; + + /** \brief Constructs a Reader allowing all features + * for parsing. + */ + Reader(); + + /** \brief Constructs a Reader allowing the specified feature set + * for parsing. + */ + Reader( const Features &features ); + + /** \brief Read a Value from a JSON document. + * \param document UTF-8 encoded string containing the document to read. + * \param root [out] Contains the root value of the document if it was + * successfully parsed. + * \param collectComments \c true to collect comment and allow writing them back during + * serialization, \c false to discard comments. + * This parameter is ignored if Features::allowComments_ + * is \c false. + * \return \c true if the document was successfully parsed, \c false if an error occurred. + */ + bool parse( const std::string &document, + Value &root, + bool collectComments = true ); + + /** \brief Read a Value from a JSON document. + * \param beginDoc Pointer on the beginning of the UTF-8 encoded string of the document to read. + * \param endDoc Pointer on the end of the UTF-8 encoded string of the document to read. + \ Must be >= beginDoc. + * \param root [out] Contains the root value of the document if it was + * successfully parsed. + * \param collectComments \c true to collect comment and allow writing them back during + * serialization, \c false to discard comments. + * This parameter is ignored if Features::allowComments_ + * is \c false. + * \return \c true if the document was successfully parsed, \c false if an error occurred. + */ + bool parse( const char *beginDoc, const char *endDoc, + Value &root, + bool collectComments = true ); + + /// \brief Parse from input stream. + /// \see Json::operator>>(std::istream&, Json::Value&). + bool parse( std::istream &is, + Value &root, + bool collectComments = true ); + + /** \brief Returns a user friendly string that list errors in the parsed document. + * \return Formatted error message with the list of errors with their location in + * the parsed document. An empty string is returned if no error occurred + * during parsing. + * \deprecated Use getFormattedErrorMessages() instead (typo fix). + */ + JSONCPP_DEPRECATED("Use getFormattedErrorMessages instead") + std::string getFormatedErrorMessages() const; + + /** \brief Returns a user friendly string that list errors in the parsed document. + * \return Formatted error message with the list of errors with their location in + * the parsed document. An empty string is returned if no error occurred + * during parsing. + */ + std::string getFormattedErrorMessages() const; + + private: + enum TokenType + { + tokenEndOfStream = 0, + tokenObjectBegin, + tokenObjectEnd, + tokenArrayBegin, + tokenArrayEnd, + tokenString, + tokenNumber, + tokenTrue, + tokenFalse, + tokenNull, + tokenArraySeparator, + tokenMemberSeparator, + tokenComment, + tokenError + }; + + class Token + { + public: + TokenType type_; + Location start_; + Location end_; + }; + + class ErrorInfo + { + public: + Token token_; + std::string message_; + Location extra_; + }; + + typedef std::deque Errors; + + bool expectToken( TokenType type, Token &token, const char *message ); + bool readToken( Token &token ); + void skipSpaces(); + bool match( Location pattern, + int patternLength ); + bool readComment(); + bool readCStyleComment(); + bool readCppStyleComment(); + bool readString(); + void readNumber(); + bool readValue(); + bool readObject( Token &token ); + bool readArray( Token &token ); + bool decodeNumber( Token &token ); + bool decodeString( Token &token ); + bool decodeString( Token &token, std::string &decoded ); + bool decodeDouble( Token &token ); + bool decodeUnicodeCodePoint( Token &token, + Location ¤t, + Location end, + unsigned int &unicode ); + bool decodeUnicodeEscapeSequence( Token &token, + Location ¤t, + Location end, + unsigned int &unicode ); + bool addError( const std::string &message, + Token &token, + Location extra = 0 ); + bool recoverFromError( TokenType skipUntilToken ); + bool addErrorAndRecover( const std::string &message, + Token &token, + TokenType skipUntilToken ); + void skipUntilSpace(); + Value ¤tValue(); + Char getNextChar(); + void getLocationLineAndColumn( Location location, + int &line, + int &column ) const; + std::string getLocationLineAndColumn( Location location ) const; + void addComment( Location begin, + Location end, + CommentPlacement placement ); + void skipCommentTokens( Token &token ); + + typedef std::stack Nodes; + Nodes nodes_; + Errors errors_; + std::string document_; + Location begin_; + Location end_; + Location current_; + Location lastValueEnd_; + Value *lastValue_; + std::string commentsBefore_; + Features features_; + bool collectComments_; + }; + + /** \brief Read from 'sin' into 'root'. + + Always keep comments from the input JSON. + + This can be used to read a file into a particular sub-object. + For example: + \code + Json::Value root; + cin >> root["dir"]["file"]; + cout << root; + \endcode + Result: + \verbatim + { + "dir": { + "file": { + // The input stream JSON would be nested here. + } + } + } + \endverbatim + \throw std::exception on parse error. + \see Json::operator<<() + */ + std::istream& operator>>( std::istream&, Value& ); + +} // namespace Json + +#endif // CPPTL_JSON_READER_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/reader.h +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/writer.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSON_WRITER_H_INCLUDED +# define JSON_WRITER_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +# include "value.h" +#endif // if !defined(JSON_IS_AMALGAMATION) +# include +# include + +namespace Json { + + class Value; + + /** \brief Abstract class for writers. + */ + class JSON_API Writer + { + public: + virtual ~Writer(); + + virtual std::string write( const Value &root ) = 0; + }; + + /** \brief Outputs a Value in JSON format without formatting (not human friendly). + * + * The JSON document is written in a single line. It is not intended for 'human' consumption, + * but may be usefull to support feature such as RPC where bandwith is limited. + * \sa Reader, Value + */ + class JSON_API FastWriter : public Writer + { + public: + FastWriter(); + virtual ~FastWriter(){} + + void enableYAMLCompatibility(); + + /** \brief Drop the "null" string from the writer's output for nullValues. + * Strictly speaking, this is not valid JSON. But when the output is being + * fed to a browser's Javascript, it makes for smaller output and the + * browser can handle the output just fine. + */ + void dropNullPlaceholders(); + + public: // overridden from Writer + virtual std::string write( const Value &root ); + + private: + void writeValue( const Value &value ); + + std::string document_; + bool yamlCompatiblityEnabled_; + bool dropNullPlaceholders_; + }; + + /** \brief Writes a Value in JSON format in a human friendly way. + * + * The rules for line break and indent are as follow: + * - Object value: + * - if empty then print {} without indent and line break + * - if not empty the print '{', line break & indent, print one value per line + * and then unindent and line break and print '}'. + * - Array value: + * - if empty then print [] without indent and line break + * - if the array contains no object value, empty array or some other value types, + * and all the values fit on one lines, then print the array on a single line. + * - otherwise, it the values do not fit on one line, or the array contains + * object or non empty array, then print one value per line. + * + * If the Value have comments then they are outputed according to their #CommentPlacement. + * + * \sa Reader, Value, Value::setComment() + */ + class JSON_API StyledWriter: public Writer + { + public: + StyledWriter(); + virtual ~StyledWriter(){} + + public: // overridden from Writer + /** \brief Serialize a Value in JSON format. + * \param root Value to serialize. + * \return String containing the JSON document that represents the root value. + */ + virtual std::string write( const Value &root ); + + private: + void writeValue( const Value &value ); + void writeArrayValue( const Value &value ); + bool isMultineArray( const Value &value ); + void pushValue( const std::string &value ); + void writeIndent(); + void writeWithIndent( const std::string &value ); + void indent(); + void unindent(); + void writeCommentBeforeValue( const Value &root ); + void writeCommentAfterValueOnSameLine( const Value &root ); + bool hasCommentForValue( const Value &value ); + static std::string normalizeEOL( const std::string &text ); + + typedef std::vector ChildValues; + + ChildValues childValues_; + std::string document_; + std::string indentString_; + int rightMargin_; + int indentSize_; + bool addChildValues_; + }; + + /** \brief Writes a Value in JSON format in a human friendly way, + to a stream rather than to a string. + * + * The rules for line break and indent are as follow: + * - Object value: + * - if empty then print {} without indent and line break + * - if not empty the print '{', line break & indent, print one value per line + * and then unindent and line break and print '}'. + * - Array value: + * - if empty then print [] without indent and line break + * - if the array contains no object value, empty array or some other value types, + * and all the values fit on one lines, then print the array on a single line. + * - otherwise, it the values do not fit on one line, or the array contains + * object or non empty array, then print one value per line. + * + * If the Value have comments then they are outputed according to their #CommentPlacement. + * + * \param indentation Each level will be indented by this amount extra. + * \sa Reader, Value, Value::setComment() + */ + class JSON_API StyledStreamWriter + { + public: + StyledStreamWriter( std::string indentation="\t" ); + ~StyledStreamWriter(){} + + public: + /** \brief Serialize a Value in JSON format. + * \param out Stream to write to. (Can be ostringstream, e.g.) + * \param root Value to serialize. + * \note There is no point in deriving from Writer, since write() should not return a value. + */ + void write( std::ostream &out, const Value &root ); + + private: + void writeValue( const Value &value ); + void writeArrayValue( const Value &value ); + bool isMultineArray( const Value &value ); + void pushValue( const std::string &value ); + void writeIndent(); + void writeWithIndent( const std::string &value ); + void indent(); + void unindent(); + void writeCommentBeforeValue( const Value &root ); + void writeCommentAfterValueOnSameLine( const Value &root ); + bool hasCommentForValue( const Value &value ); + static std::string normalizeEOL( const std::string &text ); + + typedef std::vector ChildValues; + + ChildValues childValues_; + std::ostream* document_; + std::string indentString_; + int rightMargin_; + std::string indentation_; + bool addChildValues_; + }; + +# if defined(JSON_HAS_INT64) + std::string JSON_API valueToString( Int value ); + std::string JSON_API valueToString( UInt value ); +# endif // if defined(JSON_HAS_INT64) + std::string JSON_API valueToString( LargestInt value ); + std::string JSON_API valueToString( LargestUInt value ); + std::string JSON_API valueToString( double value ); + std::string JSON_API valueToString( bool value ); + std::string JSON_API valueToQuotedString( const char *value ); + + /// \brief Output using the StyledStreamWriter. + /// \see Json::operator>>() + std::ostream& operator<<( std::ostream&, const Value &root ); + +} // namespace Json + + + +#endif // JSON_WRITER_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/writer.h +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: include/json/assertions.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef CPPTL_JSON_ASSERTIONS_H_INCLUDED +# define CPPTL_JSON_ASSERTIONS_H_INCLUDED + +#include + +#if !defined(JSON_IS_AMALGAMATION) +# include +#endif // if !defined(JSON_IS_AMALGAMATION) + +#if JSON_USE_EXCEPTION +#define JSON_ASSERT( condition ) assert( condition ); // @todo <= change this into an exception throw +#define JSON_FAIL_MESSAGE( message ) throw std::runtime_error( message ); +#else // JSON_USE_EXCEPTION +#define JSON_ASSERT( condition ) assert( condition ); + +// The call to assert() will show the failure message in debug builds. In +// release bugs we write to invalid memory in order to crash hard, so that a +// debugger or crash reporter gets the chance to take over. We still call exit() +// afterward in order to tell the compiler that this macro doesn't return. +#define JSON_FAIL_MESSAGE( message ) { assert(false && message); strcpy(reinterpret_cast(666), message); exit(123); } + +#endif + +#define JSON_ASSERT_MESSAGE( condition, message ) if (!( condition )) { JSON_FAIL_MESSAGE( message ) } + +#endif // CPPTL_JSON_ASSERTIONS_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: include/json/assertions.h +// ////////////////////////////////////////////////////////////////////// + + + + + +#endif //ifndef JSON_AMALGATED_H_INCLUDED diff --git a/src/jsoncpp/json/jsoncpp.cpp b/src/jsoncpp/json/jsoncpp.cpp new file mode 100644 index 000000000..7a04736de --- /dev/null +++ b/src/jsoncpp/json/jsoncpp.cpp @@ -0,0 +1,4367 @@ +/// Json-cpp amalgated source (http://jsoncpp.sourceforge.net/). +/// It is intented to be used with #include + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: LICENSE +// ////////////////////////////////////////////////////////////////////// + +/* +The JsonCpp library's source code, including accompanying documentation, +tests and demonstration applications, are licensed under the following +conditions... + +The author (Baptiste Lepilleur) explicitly disclaims copyright in all +jurisdictions which recognize such a disclaimer. In such jurisdictions, +this software is released into the Public Domain. + +In jurisdictions which do not recognize Public Domain property (e.g. Germany as of +2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur, and is +released under the terms of the MIT License (see below). + +In jurisdictions which recognize Public Domain property, the user of this +software may choose to accept it either as 1) Public Domain, 2) under the +conditions of the MIT License (see below), or 3) under the terms of dual +Public Domain/MIT License conditions described here, as they choose. + +The MIT License is about as close to Public Domain as a license can get, and is +described in clear, concise terms at: + + http://en.wikipedia.org/wiki/MIT_License + +The full text of the MIT License follows: + +======================================================================== +Copyright (c) 2007-2010 Baptiste Lepilleur + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, copy, +modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +======================================================================== +(END LICENSE TEXT) + +The MIT license is compatible with both the GPL and commercial +software, affording one all of the rights of Public Domain with the +minor nuisance of being required to keep the above copyright notice +and license text in the source code. Note also that by accepting the +Public Domain "license" you can re-license your copy using whatever +license you like. + +*/ + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: LICENSE +// ////////////////////////////////////////////////////////////////////// + + + + + + +#include "json.h" + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: src/lib_json/json_tool.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef LIB_JSONCPP_JSON_TOOL_H_INCLUDED +# define LIB_JSONCPP_JSON_TOOL_H_INCLUDED + +/* This header provides common string manipulation support, such as UTF-8, + * portable conversion from/to string... + * + * It is an internal header that must not be exposed. + */ + +namespace Json { + +/// Converts a unicode code-point to UTF-8. +static inline std::string +codePointToUTF8(unsigned int cp) +{ + std::string result; + + // based on description from http://en.wikipedia.org/wiki/UTF-8 + + if (cp <= 0x7f) + { + result.resize(1); + result[0] = static_cast(cp); + } + else if (cp <= 0x7FF) + { + result.resize(2); + result[1] = static_cast(0x80 | (0x3f & cp)); + result[0] = static_cast(0xC0 | (0x1f & (cp >> 6))); + } + else if (cp <= 0xFFFF) + { + result.resize(3); + result[2] = static_cast(0x80 | (0x3f & cp)); + result[1] = 0x80 | static_cast((0x3f & (cp >> 6))); + result[0] = 0xE0 | static_cast((0xf & (cp >> 12))); + } + else if (cp <= 0x10FFFF) + { + result.resize(4); + result[3] = static_cast(0x80 | (0x3f & cp)); + result[2] = static_cast(0x80 | (0x3f & (cp >> 6))); + result[1] = static_cast(0x80 | (0x3f & (cp >> 12))); + result[0] = static_cast(0xF0 | (0x7 & (cp >> 18))); + } + + return result; +} + + +/// Returns true if ch is a control character (in range [0,32[). +static inline bool +isControlCharacter(char ch) +{ + return ch > 0 && ch <= 0x1F; +} + + +enum { + /// Constant that specify the size of the buffer that must be passed to uintToString. + uintToStringBufferSize = 3*sizeof(LargestUInt)+1 +}; + +// Defines a char buffer for use with uintToString(). +typedef char UIntToStringBuffer[uintToStringBufferSize]; + + +/** Converts an unsigned integer to string. + * @param value Unsigned interger to convert to string + * @param current Input/Output string buffer. + * Must have at least uintToStringBufferSize chars free. + */ +static inline void +uintToString( LargestUInt value, + char *¤t ) +{ + *--current = 0; + do + { + *--current = char(value % 10) + '0'; + value /= 10; + } + while ( value != 0 ); +} + +} // namespace Json { + +#endif // LIB_JSONCPP_JSON_TOOL_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: src/lib_json/json_tool.h +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: src/lib_json/json_reader.cpp +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2011 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#if !defined(JSON_IS_AMALGAMATION) +# include +# include +# include +# include "json_tool.h" +#endif // if !defined(JSON_IS_AMALGAMATION) +#include +#include +#include +#include +#include + +#if defined(_MSC_VER) && _MSC_VER >= 1400 // VC++ 8.0 +#pragma warning( disable : 4996 ) // disable warning about strdup being deprecated. +#endif + +namespace Json { + +// Implementation of class Features +// //////////////////////////////// + +Features::Features() + : allowComments_( true ) + , strictRoot_( false ) +{ +} + + +Features +Features::all() +{ + return Features(); +} + + +Features +Features::strictMode() +{ + Features features; + features.allowComments_ = false; + features.strictRoot_ = true; + return features; +} + +// Implementation of class Reader +// //////////////////////////////// + + +static inline bool +in( Reader::Char c, Reader::Char c1, Reader::Char c2, Reader::Char c3, Reader::Char c4 ) +{ + return c == c1 || c == c2 || c == c3 || c == c4; +} + +static inline bool +in( Reader::Char c, Reader::Char c1, Reader::Char c2, Reader::Char c3, Reader::Char c4, Reader::Char c5 ) +{ + return c == c1 || c == c2 || c == c3 || c == c4 || c == c5; +} + + +static bool +containsNewLine( Reader::Location begin, + Reader::Location end ) +{ + for ( ;begin < end; ++begin ) + if ( *begin == '\n' || *begin == '\r' ) + return true; + return false; +} + + +// Class Reader +// ////////////////////////////////////////////////////////////////// + +Reader::Reader() + : errors_(), + document_(), + begin_(), + end_(), + current_(), + lastValueEnd_(), + lastValue_(), + commentsBefore_(), + features_( Features::all() ), + collectComments_() +{ +} + + +Reader::Reader( const Features &features ) + : errors_(), + document_(), + begin_(), + end_(), + current_(), + lastValueEnd_(), + lastValue_(), + commentsBefore_(), + features_( features ), + collectComments_() +{ +} + + +bool +Reader::parse( const std::string &document, + Value &root, + bool collectComments ) +{ + document_ = document; + const char *begin = document_.c_str(); + const char *end = begin + document_.length(); + return parse( begin, end, root, collectComments ); +} + + +bool +Reader::parse( std::istream& sin, + Value &root, + bool collectComments ) +{ + //std::istream_iterator begin(sin); + //std::istream_iterator end; + // Those would allow streamed input from a file, if parse() were a + // template function. + + // Since std::string is reference-counted, this at least does not + // create an extra copy. + std::string doc; + std::getline(sin, doc, (char)EOF); + return parse( doc, root, collectComments ); +} + +bool +Reader::parse( const char *beginDoc, const char *endDoc, + Value &root, + bool collectComments ) +{ + if ( !features_.allowComments_ ) + { + collectComments = false; + } + + begin_ = beginDoc; + end_ = endDoc; + collectComments_ = collectComments; + current_ = begin_; + lastValueEnd_ = 0; + lastValue_ = 0; + commentsBefore_ = ""; + errors_.clear(); + while ( !nodes_.empty() ) + nodes_.pop(); + nodes_.push( &root ); + + bool successful = readValue(); + Token token; + skipCommentTokens( token ); + if ( collectComments_ && !commentsBefore_.empty() ) + root.setComment( commentsBefore_, commentAfter ); + if ( features_.strictRoot_ ) + { + if ( !root.isArray() && !root.isObject() ) + { + // Set error location to start of doc, ideally should be first token found in doc + token.type_ = tokenError; + token.start_ = beginDoc; + token.end_ = endDoc; + addError( "A valid JSON document must be either an array or an object value.", + token ); + return false; + } + } + return successful; +} + + +bool +Reader::readValue() +{ + Token token; + skipCommentTokens( token ); + bool successful = true; + + if ( collectComments_ && !commentsBefore_.empty() ) + { + currentValue().setComment( commentsBefore_, commentBefore ); + commentsBefore_ = ""; + } + + + switch ( token.type_ ) + { + case tokenObjectBegin: + successful = readObject( token ); + break; + case tokenArrayBegin: + successful = readArray( token ); + break; + case tokenNumber: + successful = decodeNumber( token ); + break; + case tokenString: + successful = decodeString( token ); + break; + case tokenTrue: + currentValue() = true; + break; + case tokenFalse: + currentValue() = false; + break; + case tokenNull: + currentValue() = Value(); + break; + default: + return addError( "Syntax error: value, object or array expected.", token ); + } + + if ( collectComments_ ) + { + lastValueEnd_ = current_; + lastValue_ = ¤tValue(); + } + + return successful; +} + + +void +Reader::skipCommentTokens( Token &token ) +{ + if ( features_.allowComments_ ) + { + do + { + readToken( token ); + } + while ( token.type_ == tokenComment ); + } + else + { + readToken( token ); + } +} + + +bool +Reader::expectToken( TokenType type, Token &token, const char *message ) +{ + readToken( token ); + if ( token.type_ != type ) + return addError( message, token ); + return true; +} + + +bool +Reader::readToken( Token &token ) +{ + skipSpaces(); + token.start_ = current_; + Char c = getNextChar(); + bool ok = true; + switch ( c ) + { + case '{': + token.type_ = tokenObjectBegin; + break; + case '}': + token.type_ = tokenObjectEnd; + break; + case '[': + token.type_ = tokenArrayBegin; + break; + case ']': + token.type_ = tokenArrayEnd; + break; + case '"': + token.type_ = tokenString; + ok = readString(); + break; + case '/': + token.type_ = tokenComment; + ok = readComment(); + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '-': + token.type_ = tokenNumber; + readNumber(); + break; + case 't': + token.type_ = tokenTrue; + ok = match( "rue", 3 ); + break; + case 'f': + token.type_ = tokenFalse; + ok = match( "alse", 4 ); + break; + case 'n': + token.type_ = tokenNull; + ok = match( "ull", 3 ); + break; + case ',': + token.type_ = tokenArraySeparator; + break; + case ':': + token.type_ = tokenMemberSeparator; + break; + case 0: + token.type_ = tokenEndOfStream; + break; + default: + ok = false; + break; + } + if ( !ok ) + token.type_ = tokenError; + token.end_ = current_; + return true; +} + + +void +Reader::skipSpaces() +{ + while ( current_ != end_ ) + { + Char c = *current_; + if ( c == ' ' || c == '\t' || c == '\r' || c == '\n' ) + ++current_; + else + break; + } +} + + +bool +Reader::match( Location pattern, + int patternLength ) +{ + if ( end_ - current_ < patternLength ) + return false; + int index = patternLength; + while ( index-- ) + if ( current_[index] != pattern[index] ) + return false; + current_ += patternLength; + return true; +} + + +bool +Reader::readComment() +{ + Location commentBegin = current_ - 1; + Char c = getNextChar(); + bool successful = false; + if ( c == '*' ) + successful = readCStyleComment(); + else if ( c == '/' ) + successful = readCppStyleComment(); + if ( !successful ) + return false; + + if ( collectComments_ ) + { + CommentPlacement placement = commentBefore; + if ( lastValueEnd_ && !containsNewLine( lastValueEnd_, commentBegin ) ) + { + if ( c != '*' || !containsNewLine( commentBegin, current_ ) ) + placement = commentAfterOnSameLine; + } + + addComment( commentBegin, current_, placement ); + } + return true; +} + + +void +Reader::addComment( Location begin, + Location end, + CommentPlacement placement ) +{ + assert( collectComments_ ); + if ( placement == commentAfterOnSameLine ) + { + assert( lastValue_ != 0 ); + lastValue_->setComment( std::string( begin, end ), placement ); + } + else + { + if ( !commentsBefore_.empty() ) + commentsBefore_ += "\n"; + commentsBefore_ += std::string( begin, end ); + } +} + + +bool +Reader::readCStyleComment() +{ + while ( current_ != end_ ) + { + Char c = getNextChar(); + if ( c == '*' && *current_ == '/' ) + break; + } + return getNextChar() == '/'; +} + + +bool +Reader::readCppStyleComment() +{ + while ( current_ != end_ ) + { + Char c = getNextChar(); + if ( c == '\r' || c == '\n' ) + break; + } + return true; +} + + +void +Reader::readNumber() +{ + while ( current_ != end_ ) + { + if ( !(*current_ >= '0' && *current_ <= '9') && + !in( *current_, '.', 'e', 'E', '+', '-' ) ) + break; + ++current_; + } +} + +bool +Reader::readString() +{ + Char c = 0; + while ( current_ != end_ ) + { + c = getNextChar(); + if ( c == '\\' ) + getNextChar(); + else if ( c == '"' ) + break; + } + return c == '"'; +} + + +bool +Reader::readObject( Token &/*tokenStart*/ ) +{ + Token tokenName; + std::string name; + currentValue() = Value( objectValue ); + while ( readToken( tokenName ) ) + { + bool initialTokenOk = true; + while ( tokenName.type_ == tokenComment && initialTokenOk ) + initialTokenOk = readToken( tokenName ); + if ( !initialTokenOk ) + break; + if ( tokenName.type_ == tokenObjectEnd && name.empty() ) // empty object + return true; + if ( tokenName.type_ != tokenString ) + break; + + name = ""; + if ( !decodeString( tokenName, name ) ) + return recoverFromError( tokenObjectEnd ); + + Token colon; + if ( !readToken( colon ) || colon.type_ != tokenMemberSeparator ) + { + return addErrorAndRecover( "Missing ':' after object member name", + colon, + tokenObjectEnd ); + } + Value &value = currentValue()[ name ]; + nodes_.push( &value ); + bool ok = readValue(); + nodes_.pop(); + if ( !ok ) // error already set + return recoverFromError( tokenObjectEnd ); + + Token comma; + if ( !readToken( comma ) + || ( comma.type_ != tokenObjectEnd && + comma.type_ != tokenArraySeparator && + comma.type_ != tokenComment ) ) + { + return addErrorAndRecover( "Missing ',' or '}' in object declaration", + comma, + tokenObjectEnd ); + } + bool finalizeTokenOk = true; + while ( comma.type_ == tokenComment && + finalizeTokenOk ) + finalizeTokenOk = readToken( comma ); + if ( comma.type_ == tokenObjectEnd ) + return true; + } + return addErrorAndRecover( "Missing '}' or object member name", + tokenName, + tokenObjectEnd ); +} + + +bool +Reader::readArray( Token &/*tokenStart*/ ) +{ + currentValue() = Value( arrayValue ); + skipSpaces(); + if ( *current_ == ']' ) // empty array + { + Token endArray; + readToken( endArray ); + return true; + } + int index = 0; + for (;;) + { + Value &value = currentValue()[ index++ ]; + nodes_.push( &value ); + bool ok = readValue(); + nodes_.pop(); + if ( !ok ) // error already set + return recoverFromError( tokenArrayEnd ); + + Token token; + // Accept Comment after last item in the array. + ok = readToken( token ); + while ( token.type_ == tokenComment && ok ) + { + ok = readToken( token ); + } + bool badTokenType = ( token.type_ != tokenArraySeparator && + token.type_ != tokenArrayEnd ); + if ( !ok || badTokenType ) + { + return addErrorAndRecover( "Missing ',' or ']' in array declaration", + token, + tokenArrayEnd ); + } + if ( token.type_ == tokenArrayEnd ) + break; + } + return true; +} + + +bool +Reader::decodeNumber( Token &token ) +{ + bool isDouble = false; + for ( Location inspect = token.start_; inspect != token.end_; ++inspect ) + { + isDouble = isDouble + || in( *inspect, '.', 'e', 'E', '+' ) + || ( *inspect == '-' && inspect != token.start_ ); + } + if ( isDouble ) + return decodeDouble( token ); + // Attempts to parse the number as an integer. If the number is + // larger than the maximum supported value of an integer then + // we decode the number as a double. + Location current = token.start_; + bool isNegative = *current == '-'; + if ( isNegative ) + ++current; + Value::LargestUInt maxIntegerValue = isNegative ? Value::LargestUInt(-Value::minLargestInt) + : Value::maxLargestUInt; + Value::LargestUInt threshold = maxIntegerValue / 10; + Value::LargestUInt value = 0; + while ( current < token.end_ ) + { + Char c = *current++; + if ( c < '0' || c > '9' ) + return addError( "'" + std::string( token.start_, token.end_ ) + "' is not a number.", token ); + Value::UInt digit(c - '0'); + if ( value >= threshold ) + { + // We've hit or exceeded the max value divided by 10 (rounded down). If + // a) we've only just touched the limit, b) this is the last digit, and + // c) it's small enough to fit in that rounding delta, we're okay. + // Otherwise treat this number as a double to avoid overflow. + if (value > threshold || + current != token.end_ || + digit > maxIntegerValue % 10) + { + return decodeDouble( token ); + } + } + value = value * 10 + digit; + } + if ( isNegative ) + currentValue() = -Value::LargestInt( value ); + else if ( value <= Value::LargestUInt(Value::maxInt) ) + currentValue() = Value::LargestInt( value ); + else + currentValue() = value; + return true; +} + + +bool +Reader::decodeDouble( Token &token ) +{ + double value = 0; + const int bufferSize = 32; + int count; + int length = int(token.end_ - token.start_); + + // Sanity check to avoid buffer overflow exploits. + if (length < 0) { + return addError( "Unable to parse token length", token ); + } + + // Avoid using a string constant for the format control string given to + // sscanf, as this can cause hard to debug crashes on OS X. See here for more + // info: + // + // http://developer.apple.com/library/mac/#DOCUMENTATION/DeveloperTools/gcc-4.0.1/gcc/Incompatibilities.html + char format[] = "%lf"; + + if ( length <= bufferSize ) + { + Char buffer[bufferSize+1]; + memcpy( buffer, token.start_, length ); + buffer[length] = 0; + count = sscanf( buffer, format, &value ); + } + else + { + std::string buffer( token.start_, token.end_ ); + count = sscanf( buffer.c_str(), format, &value ); + } + + if ( count != 1 ) + return addError( "'" + std::string( token.start_, token.end_ ) + "' is not a number.", token ); + currentValue() = value; + return true; +} + + +bool +Reader::decodeString( Token &token ) +{ + std::string decoded; + if ( !decodeString( token, decoded ) ) + return false; + currentValue() = decoded; + return true; +} + + +bool +Reader::decodeString( Token &token, std::string &decoded ) +{ + decoded.reserve( token.end_ - token.start_ - 2 ); + Location current = token.start_ + 1; // skip '"' + Location end = token.end_ - 1; // do not include '"' + while ( current != end ) + { + Char c = *current++; + if ( c == '"' ) + break; + else if ( c == '\\' ) + { + if ( current == end ) + return addError( "Empty escape sequence in string", token, current ); + Char escape = *current++; + switch ( escape ) + { + case '"': decoded += '"'; break; + case '/': decoded += '/'; break; + case '\\': decoded += '\\'; break; + case 'b': decoded += '\b'; break; + case 'f': decoded += '\f'; break; + case 'n': decoded += '\n'; break; + case 'r': decoded += '\r'; break; + case 't': decoded += '\t'; break; + case 'u': + { + unsigned int unicode; + if ( !decodeUnicodeCodePoint( token, current, end, unicode ) ) + return false; + decoded += codePointToUTF8(unicode); + } + break; + default: + return addError( "Bad escape sequence in string", token, current ); + } + } + else + { + decoded += c; + } + } + return true; +} + +bool +Reader::decodeUnicodeCodePoint( Token &token, + Location ¤t, + Location end, + unsigned int &unicode ) +{ + + if ( !decodeUnicodeEscapeSequence( token, current, end, unicode ) ) + return false; + if (unicode >= 0xD800 && unicode <= 0xDBFF) + { + // surrogate pairs + if (end - current < 6) + return addError( "additional six characters expected to parse unicode surrogate pair.", token, current ); + unsigned int surrogatePair; + if (*(current++) == '\\' && *(current++)== 'u') + { + if (decodeUnicodeEscapeSequence( token, current, end, surrogatePair )) + { + unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF); + } + else + return false; + } + else + return addError( "expecting another \\u token to begin the second half of a unicode surrogate pair", token, current ); + } + return true; +} + +bool +Reader::decodeUnicodeEscapeSequence( Token &token, + Location ¤t, + Location end, + unsigned int &unicode ) +{ + if ( end - current < 4 ) + return addError( "Bad unicode escape sequence in string: four digits expected.", token, current ); + unicode = 0; + for ( int index =0; index < 4; ++index ) + { + Char c = *current++; + unicode *= 16; + if ( c >= '0' && c <= '9' ) + unicode += c - '0'; + else if ( c >= 'a' && c <= 'f' ) + unicode += c - 'a' + 10; + else if ( c >= 'A' && c <= 'F' ) + unicode += c - 'A' + 10; + else + return addError( "Bad unicode escape sequence in string: hexadecimal digit expected.", token, current ); + } + return true; +} + + +bool +Reader::addError( const std::string &message, + Token &token, + Location extra ) +{ + ErrorInfo info; + info.token_ = token; + info.message_ = message; + info.extra_ = extra; + errors_.push_back( info ); + return false; +} + + +bool +Reader::recoverFromError( TokenType skipUntilToken ) +{ + int errorCount = int(errors_.size()); + Token skip; + for (;;) + { + if ( !readToken(skip) ) + errors_.resize( errorCount ); // discard errors caused by recovery + if ( skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream ) + break; + } + errors_.resize( errorCount ); + return false; +} + + +bool +Reader::addErrorAndRecover( const std::string &message, + Token &token, + TokenType skipUntilToken ) +{ + addError( message, token ); + return recoverFromError( skipUntilToken ); +} + + +Value & +Reader::currentValue() +{ + return *(nodes_.top()); +} + + +Reader::Char +Reader::getNextChar() +{ + if ( current_ == end_ ) + return 0; + return *current_++; +} + + +void +Reader::getLocationLineAndColumn( Location location, + int &line, + int &column ) const +{ + Location current = begin_; + Location lastLineStart = current; + line = 0; + while ( current < location && current != end_ ) + { + Char c = *current++; + if ( c == '\r' ) + { + if ( *current == '\n' ) + ++current; + lastLineStart = current; + ++line; + } + else if ( c == '\n' ) + { + lastLineStart = current; + ++line; + } + } + // column & line start at 1 + column = int(location - lastLineStart) + 1; + ++line; +} + + +std::string +Reader::getLocationLineAndColumn( Location location ) const +{ + int line, column; + getLocationLineAndColumn( location, line, column ); + char buffer[18+16+16+1]; + sprintf( buffer, "Line %d, Column %d", line, column ); + return buffer; +} + + +// Deprecated. Preserved for backward compatibility +std::string +Reader::getFormatedErrorMessages() const +{ + return getFormattedErrorMessages(); +} + + +std::string +Reader::getFormattedErrorMessages() const +{ + std::string formattedMessage; + for ( Errors::const_iterator itError = errors_.begin(); + itError != errors_.end(); + ++itError ) + { + const ErrorInfo &error = *itError; + formattedMessage += "* " + getLocationLineAndColumn( error.token_.start_ ) + "\n"; + formattedMessage += " " + error.message_ + "\n"; + if ( error.extra_ ) + formattedMessage += "See " + getLocationLineAndColumn( error.extra_ ) + " for detail.\n"; + } + return formattedMessage; +} + + +std::istream& operator>>( std::istream &sin, Value &root ) +{ + Json::Reader reader; + bool ok = reader.parse(sin, root, true); + if (!ok) { + fprintf( + stderr, + "Error from reader: %s", + reader.getFormattedErrorMessages().c_str()); + + JSON_FAIL_MESSAGE("reader error"); + } + return sin; +} + + +} // namespace Json + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: src/lib_json/json_reader.cpp +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: src/lib_json/json_batchallocator.h +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSONCPP_BATCHALLOCATOR_H_INCLUDED +# define JSONCPP_BATCHALLOCATOR_H_INCLUDED + +# include +# include + +# ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION + +namespace Json { + +/* Fast memory allocator. + * + * This memory allocator allocates memory for a batch of object (specified by + * the page size, the number of object in each page). + * + * It does not allow the destruction of a single object. All the allocated objects + * can be destroyed at once. The memory can be either released or reused for future + * allocation. + * + * The in-place new operator must be used to construct the object using the pointer + * returned by allocate. + */ +template +class BatchAllocator +{ +public: + BatchAllocator( unsigned int objectsPerPage = 255 ) + : freeHead_( 0 ) + , objectsPerPage_( objectsPerPage ) + { +// printf( "Size: %d => %s\n", sizeof(AllocatedType), typeid(AllocatedType).name() ); + assert( sizeof(AllocatedType) * objectPerAllocation >= sizeof(AllocatedType *) ); // We must be able to store a slist in the object free space. + assert( objectsPerPage >= 16 ); + batches_ = allocateBatch( 0 ); // allocated a dummy page + currentBatch_ = batches_; + } + + ~BatchAllocator() + { + for ( BatchInfo *batch = batches_; batch; ) + { + BatchInfo *nextBatch = batch->next_; + free( batch ); + batch = nextBatch; + } + } + + /// allocate space for an array of objectPerAllocation object. + /// @warning it is the responsability of the caller to call objects constructors. + AllocatedType *allocate() + { + if ( freeHead_ ) // returns node from free list. + { + AllocatedType *object = freeHead_; + freeHead_ = *(AllocatedType **)object; + return object; + } + if ( currentBatch_->used_ == currentBatch_->end_ ) + { + currentBatch_ = currentBatch_->next_; + while ( currentBatch_ && currentBatch_->used_ == currentBatch_->end_ ) + currentBatch_ = currentBatch_->next_; + + if ( !currentBatch_ ) // no free batch found, allocate a new one + { + currentBatch_ = allocateBatch( objectsPerPage_ ); + currentBatch_->next_ = batches_; // insert at the head of the list + batches_ = currentBatch_; + } + } + AllocatedType *allocated = currentBatch_->used_; + currentBatch_->used_ += objectPerAllocation; + return allocated; + } + + /// Release the object. + /// @warning it is the responsability of the caller to actually destruct the object. + void release( AllocatedType *object ) + { + assert( object != 0 ); + *(AllocatedType **)object = freeHead_; + freeHead_ = object; + } + +private: + struct BatchInfo + { + BatchInfo *next_; + AllocatedType *used_; + AllocatedType *end_; + AllocatedType buffer_[objectPerAllocation]; + }; + + // disabled copy constructor and assignement operator. + BatchAllocator( const BatchAllocator & ); + void operator =( const BatchAllocator &); + + static BatchInfo *allocateBatch( unsigned int objectsPerPage ) + { + const unsigned int mallocSize = sizeof(BatchInfo) - sizeof(AllocatedType)* objectPerAllocation + + sizeof(AllocatedType) * objectPerAllocation * objectsPerPage; + BatchInfo *batch = static_cast( malloc( mallocSize ) ); + batch->next_ = 0; + batch->used_ = batch->buffer_; + batch->end_ = batch->buffer_ + objectsPerPage; + return batch; + } + + BatchInfo *batches_; + BatchInfo *currentBatch_; + /// Head of a single linked list within the allocated space of freeed object + AllocatedType *freeHead_; + unsigned int objectsPerPage_; +}; + + +} // namespace Json + +# endif // ifndef JSONCPP_DOC_INCLUDE_IMPLEMENTATION + +#endif // JSONCPP_BATCHALLOCATOR_H_INCLUDED + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: src/lib_json/json_batchallocator.h +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: src/lib_json/json_valueiterator.inl +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +// included by json_value.cpp + +namespace Json { + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class ValueIteratorBase +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +ValueIteratorBase::ValueIteratorBase() +#ifndef JSON_VALUE_USE_INTERNAL_MAP + : current_() + , isNull_( true ) +{ +} +#else + : isArray_( true ) + , isNull_( true ) +{ + iterator_.array_ = ValueInternalArray::IteratorState(); +} +#endif + + +#ifndef JSON_VALUE_USE_INTERNAL_MAP +ValueIteratorBase::ValueIteratorBase( const Value::ObjectValues::iterator ¤t ) + : current_( current ) + , isNull_( false ) +{ +} +#else +ValueIteratorBase::ValueIteratorBase( const ValueInternalArray::IteratorState &state ) + : isArray_( true ) +{ + iterator_.array_ = state; +} + + +ValueIteratorBase::ValueIteratorBase( const ValueInternalMap::IteratorState &state ) + : isArray_( false ) +{ + iterator_.map_ = state; +} +#endif + +Value & +ValueIteratorBase::deref() const +{ +#ifndef JSON_VALUE_USE_INTERNAL_MAP + return current_->second; +#else + if ( isArray_ ) + return ValueInternalArray::dereference( iterator_.array_ ); + return ValueInternalMap::value( iterator_.map_ ); +#endif +} + + +void +ValueIteratorBase::increment() +{ +#ifndef JSON_VALUE_USE_INTERNAL_MAP + ++current_; +#else + if ( isArray_ ) + ValueInternalArray::increment( iterator_.array_ ); + ValueInternalMap::increment( iterator_.map_ ); +#endif +} + + +void +ValueIteratorBase::decrement() +{ +#ifndef JSON_VALUE_USE_INTERNAL_MAP + --current_; +#else + if ( isArray_ ) + ValueInternalArray::decrement( iterator_.array_ ); + ValueInternalMap::decrement( iterator_.map_ ); +#endif +} + + +ValueIteratorBase::difference_type +ValueIteratorBase::computeDistance( const SelfType &other ) const +{ +#ifndef JSON_VALUE_USE_INTERNAL_MAP +# ifdef JSON_USE_CPPTL_SMALLMAP + return current_ - other.current_; +# else + // Iterator for null value are initialized using the default + // constructor, which initialize current_ to the default + // std::map::iterator. As begin() and end() are two instance + // of the default std::map::iterator, they can not be compared. + // To allow this, we handle this comparison specifically. + if ( isNull_ && other.isNull_ ) + { + return 0; + } + + + // Usage of std::distance is not portable (does not compile with Sun Studio 12 RogueWave STL, + // which is the one used by default). + // Using a portable hand-made version for non random iterator instead: + // return difference_type( std::distance( current_, other.current_ ) ); + difference_type myDistance = 0; + for ( Value::ObjectValues::iterator it = current_; it != other.current_; ++it ) + { + ++myDistance; + } + return myDistance; +# endif +#else + if ( isArray_ ) + return ValueInternalArray::distance( iterator_.array_, other.iterator_.array_ ); + return ValueInternalMap::distance( iterator_.map_, other.iterator_.map_ ); +#endif +} + + +bool +ValueIteratorBase::isEqual( const SelfType &other ) const +{ +#ifndef JSON_VALUE_USE_INTERNAL_MAP + if ( isNull_ ) + { + return other.isNull_; + } + return current_ == other.current_; +#else + if ( isArray_ ) + return ValueInternalArray::equals( iterator_.array_, other.iterator_.array_ ); + return ValueInternalMap::equals( iterator_.map_, other.iterator_.map_ ); +#endif +} + + +void +ValueIteratorBase::copy( const SelfType &other ) +{ +#ifndef JSON_VALUE_USE_INTERNAL_MAP + current_ = other.current_; +#else + if ( isArray_ ) + iterator_.array_ = other.iterator_.array_; + iterator_.map_ = other.iterator_.map_; +#endif +} + + +Value +ValueIteratorBase::key() const +{ +#ifndef JSON_VALUE_USE_INTERNAL_MAP + const Value::CZString czstring = (*current_).first; + if ( czstring.c_str() ) + { + if ( czstring.isStaticString() ) + return Value( StaticString( czstring.c_str() ) ); + return Value( czstring.c_str() ); + } + return Value( czstring.index() ); +#else + if ( isArray_ ) + return Value( ValueInternalArray::indexOf( iterator_.array_ ) ); + bool isStatic; + const char *memberName = ValueInternalMap::key( iterator_.map_, isStatic ); + if ( isStatic ) + return Value( StaticString( memberName ) ); + return Value( memberName ); +#endif +} + + +UInt +ValueIteratorBase::index() const +{ +#ifndef JSON_VALUE_USE_INTERNAL_MAP + const Value::CZString czstring = (*current_).first; + if ( !czstring.c_str() ) + return czstring.index(); + return Value::UInt( -1 ); +#else + if ( isArray_ ) + return Value::UInt( ValueInternalArray::indexOf( iterator_.array_ ) ); + return Value::UInt( -1 ); +#endif +} + + +const char * +ValueIteratorBase::memberName() const +{ +#ifndef JSON_VALUE_USE_INTERNAL_MAP + const char *name = (*current_).first.c_str(); + return name ? name : ""; +#else + if ( !isArray_ ) + return ValueInternalMap::key( iterator_.map_ ); + return ""; +#endif +} + + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class ValueConstIterator +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +ValueConstIterator::ValueConstIterator() +{ +} + + +#ifndef JSON_VALUE_USE_INTERNAL_MAP +ValueConstIterator::ValueConstIterator( const Value::ObjectValues::iterator ¤t ) + : ValueIteratorBase( current ) +{ +} +#else +ValueConstIterator::ValueConstIterator( const ValueInternalArray::IteratorState &state ) + : ValueIteratorBase( state ) +{ +} + +ValueConstIterator::ValueConstIterator( const ValueInternalMap::IteratorState &state ) + : ValueIteratorBase( state ) +{ +} +#endif + +ValueConstIterator & +ValueConstIterator::operator =( const ValueIteratorBase &other ) +{ + copy( other ); + return *this; +} + + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class ValueIterator +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +ValueIterator::ValueIterator() +{ +} + + +#ifndef JSON_VALUE_USE_INTERNAL_MAP +ValueIterator::ValueIterator( const Value::ObjectValues::iterator ¤t ) + : ValueIteratorBase( current ) +{ +} +#else +ValueIterator::ValueIterator( const ValueInternalArray::IteratorState &state ) + : ValueIteratorBase( state ) +{ +} + +ValueIterator::ValueIterator( const ValueInternalMap::IteratorState &state ) + : ValueIteratorBase( state ) +{ +} +#endif + +ValueIterator::ValueIterator( const ValueConstIterator &other ) + : ValueIteratorBase( other ) +{ +} + +ValueIterator::ValueIterator( const ValueIterator &other ) + : ValueIteratorBase( other ) +{ +} + +ValueIterator & +ValueIterator::operator =( const SelfType &other ) +{ + copy( other ); + return *this; +} + +} // namespace Json + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: src/lib_json/json_valueiterator.inl +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: src/lib_json/json_value.cpp +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2011 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#if !defined(JSON_IS_AMALGAMATION) +# include +# include +# include +# ifndef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR +# include "json_batchallocator.h" +# endif // #ifndef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR +#endif // if !defined(JSON_IS_AMALGAMATION) +#include +#include +#include +#include +#include +#include +#ifdef JSON_USE_CPPTL +# include +#endif +#include // size_t + +#define JSON_ASSERT_UNREACHABLE assert( false ) + +namespace Json { + +const Value Value::null; +const Int Value::minInt = Int( ~(UInt(-1)/2) ); +const Int Value::maxInt = Int( UInt(-1)/2 ); +const UInt Value::maxUInt = UInt(-1); +# if defined(JSON_HAS_INT64) +const Int64 Value::minInt64 = Int64( ~(UInt64(-1)/2) ); +const Int64 Value::maxInt64 = Int64( UInt64(-1)/2 ); +const UInt64 Value::maxUInt64 = UInt64(-1); +// The constant is hard-coded because some compiler have trouble +// converting Value::maxUInt64 to a double correctly (AIX/xlC). +// Assumes that UInt64 is a 64 bits integer. +static const double maxUInt64AsDouble = 18446744073709551615.0; +#endif // defined(JSON_HAS_INT64) +const LargestInt Value::minLargestInt = LargestInt( ~(LargestUInt(-1)/2) ); +const LargestInt Value::maxLargestInt = LargestInt( LargestUInt(-1)/2 ); +const LargestUInt Value::maxLargestUInt = LargestUInt(-1); + + +/// Unknown size marker +static const unsigned int unknown = (unsigned)-1; + +#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) +template +static inline bool InRange(double d, T min, U max) { + return d >= min && d <= max; +} +#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) +static inline double integerToDouble( Json::UInt64 value ) +{ + return static_cast( Int64(value/2) ) * 2.0 + Int64(value & 1); +} + +template +static inline double integerToDouble( T value ) +{ + return static_cast( value ); +} + +template +static inline bool InRange(double d, T min, U max) { + return d >= integerToDouble(min) && d <= integerToDouble(max); +} +#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + + +/** Duplicates the specified string value. + * @param value Pointer to the string to duplicate. Must be zero-terminated if + * length is "unknown". + * @param length Length of the value. if equals to unknown, then it will be + * computed using strlen(value). + * @return Pointer on the duplicate instance of string. + */ +static inline char * +duplicateStringValue( const char *value, + unsigned int length = unknown ) +{ + if ( length == unknown ) + length = (unsigned int)strlen(value); + + // Avoid an integer overflow in the call to malloc below by limiting length + // to a sane value. + if (length >= (unsigned)Value::maxInt) + length = Value::maxInt - 1; + + char *newString = static_cast( malloc( length + 1 ) ); + JSON_ASSERT_MESSAGE( newString != 0, "Failed to allocate string value buffer" ); + memcpy( newString, value, length ); + newString[length] = 0; + return newString; +} + + +/** Free the string duplicated by duplicateStringValue(). + */ +static inline void +releaseStringValue( char *value ) +{ + if ( value ) + free( value ); +} + +} // namespace Json + + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ValueInternals... +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +#if !defined(JSON_IS_AMALGAMATION) +# ifdef JSON_VALUE_USE_INTERNAL_MAP +# include "json_internalarray.inl" +# include "json_internalmap.inl" +# endif // JSON_VALUE_USE_INTERNAL_MAP + +# include "json_valueiterator.inl" +#endif // if !defined(JSON_IS_AMALGAMATION) + +namespace Json { + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class Value::CommentInfo +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + + +Value::CommentInfo::CommentInfo() + : comment_( 0 ) +{ +} + +Value::CommentInfo::~CommentInfo() +{ + if ( comment_ ) + releaseStringValue( comment_ ); +} + + +void +Value::CommentInfo::setComment( const char *text ) +{ + if ( comment_ ) + releaseStringValue( comment_ ); + JSON_ASSERT( text != 0 ); + JSON_ASSERT_MESSAGE( text[0]=='\0' || text[0]=='/', "Comments must start with /"); + // It seems that /**/ style comments are acceptable as well. + comment_ = duplicateStringValue( text ); +} + + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class Value::CZString +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +# ifndef JSON_VALUE_USE_INTERNAL_MAP + +// Notes: index_ indicates if the string was allocated when +// a string is stored. + +Value::CZString::CZString( ArrayIndex index ) + : cstr_( 0 ) + , index_( index ) +{ +} + +Value::CZString::CZString( const char *cstr, DuplicationPolicy allocate ) + : cstr_( allocate == duplicate ? duplicateStringValue(cstr) + : cstr ) + , index_( allocate ) +{ +} + +Value::CZString::CZString( const CZString &other ) +: cstr_( other.index_ != noDuplication && other.cstr_ != 0 + ? duplicateStringValue( other.cstr_ ) + : other.cstr_ ) + , index_( other.cstr_ ? (other.index_ == noDuplication ? noDuplication : duplicate) + : other.index_ ) +{ +} + +Value::CZString::~CZString() +{ + if ( cstr_ && index_ == duplicate ) + releaseStringValue( const_cast( cstr_ ) ); +} + +void +Value::CZString::swap( CZString &other ) +{ + std::swap( cstr_, other.cstr_ ); + std::swap( index_, other.index_ ); +} + +Value::CZString & +Value::CZString::operator =( const CZString &other ) +{ + CZString temp( other ); + swap( temp ); + return *this; +} + +bool +Value::CZString::operator<( const CZString &other ) const +{ + if ( cstr_ ) + return strcmp( cstr_, other.cstr_ ) < 0; + return index_ < other.index_; +} + +bool +Value::CZString::operator==( const CZString &other ) const +{ + if ( cstr_ ) + return strcmp( cstr_, other.cstr_ ) == 0; + return index_ == other.index_; +} + + +ArrayIndex +Value::CZString::index() const +{ + return index_; +} + + +const char * +Value::CZString::c_str() const +{ + return cstr_; +} + +bool +Value::CZString::isStaticString() const +{ + return index_ == noDuplication; +} + +#endif // ifndef JSON_VALUE_USE_INTERNAL_MAP + + +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// class Value::Value +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////// + +/*! \internal Default constructor initialization must be equivalent to: + * memset( this, 0, sizeof(Value) ) + * This optimization is used in ValueInternalMap fast allocator. + */ +Value::Value( ValueType type ) + : type_( type ) + , allocated_( false ) +# ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_( 0 ) +#endif + , comments_( 0 ) +{ + switch ( type ) + { + case nullValue: + break; + case intValue: + case uintValue: + value_.int_ = 0; + break; + case realValue: + value_.real_ = 0.0; + break; + case stringValue: + value_.string_ = 0; + break; +#ifndef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + case objectValue: + value_.map_ = new ObjectValues(); + break; +#else + case arrayValue: + value_.array_ = arrayAllocator()->newArray(); + break; + case objectValue: + value_.map_ = mapAllocator()->newMap(); + break; +#endif + case booleanValue: + value_.bool_ = false; + break; + default: + JSON_ASSERT_UNREACHABLE; + } +} + + +Value::Value( UInt value ) + : type_( uintValue ) + , allocated_( false ) +# ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_( 0 ) +#endif + , comments_( 0 ) +{ + value_.uint_ = value; +} + +Value::Value( Int value ) + : type_( intValue ) + , allocated_( false ) +# ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_( 0 ) +#endif + , comments_( 0 ) +{ + value_.int_ = value; +} + + +# if defined(JSON_HAS_INT64) +Value::Value( Int64 value ) + : type_( intValue ) + , allocated_( false ) +# ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_( 0 ) +#endif + , comments_( 0 ) +{ + value_.int_ = value; +} + + +Value::Value( UInt64 value ) + : type_( uintValue ) + , allocated_( false ) +# ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_( 0 ) +#endif + , comments_( 0 ) +{ + value_.uint_ = value; +} +#endif // defined(JSON_HAS_INT64) + +Value::Value( double value ) + : type_( realValue ) + , allocated_( false ) +# ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_( 0 ) +#endif + , comments_( 0 ) +{ + value_.real_ = value; +} + +Value::Value( const char *value ) + : type_( stringValue ) + , allocated_( true ) +# ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_( 0 ) +#endif + , comments_( 0 ) +{ + value_.string_ = duplicateStringValue( value ); +} + + +Value::Value( const char *beginValue, + const char *endValue ) + : type_( stringValue ) + , allocated_( true ) +# ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_( 0 ) +#endif + , comments_( 0 ) +{ + value_.string_ = duplicateStringValue( beginValue, + (unsigned int)(endValue - beginValue) ); +} + + +Value::Value( const std::string &value ) + : type_( stringValue ) + , allocated_( true ) +# ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_( 0 ) +#endif + , comments_( 0 ) +{ + value_.string_ = duplicateStringValue( value.c_str(), + (unsigned int)value.length() ); + +} + +Value::Value( const StaticString &value ) + : type_( stringValue ) + , allocated_( false ) +# ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_( 0 ) +#endif + , comments_( 0 ) +{ + value_.string_ = const_cast( value.c_str() ); +} + + +# ifdef JSON_USE_CPPTL +Value::Value( const CppTL::ConstString &value ) + : type_( stringValue ) + , allocated_( true ) +# ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_( 0 ) +#endif + , comments_( 0 ) +{ + value_.string_ = duplicateStringValue( value, value.length() ); +} +# endif + +Value::Value( bool value ) + : type_( booleanValue ) + , allocated_( false ) +# ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_( 0 ) +#endif + , comments_( 0 ) +{ + value_.bool_ = value; +} + + +Value::Value( const Value &other ) + : type_( other.type_ ) + , allocated_( false ) +# ifdef JSON_VALUE_USE_INTERNAL_MAP + , itemIsUsed_( 0 ) +#endif + , comments_( 0 ) +{ + switch ( type_ ) + { + case nullValue: + case intValue: + case uintValue: + case realValue: + case booleanValue: + value_ = other.value_; + break; + case stringValue: + if ( other.value_.string_ ) + { + value_.string_ = duplicateStringValue( other.value_.string_ ); + allocated_ = true; + } + else + value_.string_ = 0; + break; +#ifndef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + case objectValue: + value_.map_ = new ObjectValues( *other.value_.map_ ); + break; +#else + case arrayValue: + value_.array_ = arrayAllocator()->newArrayCopy( *other.value_.array_ ); + break; + case objectValue: + value_.map_ = mapAllocator()->newMapCopy( *other.value_.map_ ); + break; +#endif + default: + JSON_ASSERT_UNREACHABLE; + } + if ( other.comments_ ) + { + comments_ = new CommentInfo[numberOfCommentPlacement]; + for ( int comment =0; comment < numberOfCommentPlacement; ++comment ) + { + const CommentInfo &otherComment = other.comments_[comment]; + if ( otherComment.comment_ ) + comments_[comment].setComment( otherComment.comment_ ); + } + } +} + + +Value::~Value() +{ + switch ( type_ ) + { + case nullValue: + case intValue: + case uintValue: + case realValue: + case booleanValue: + break; + case stringValue: + if ( allocated_ ) + releaseStringValue( value_.string_ ); + break; +#ifndef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + case objectValue: + delete value_.map_; + break; +#else + case arrayValue: + arrayAllocator()->destructArray( value_.array_ ); + break; + case objectValue: + mapAllocator()->destructMap( value_.map_ ); + break; +#endif + default: + JSON_ASSERT_UNREACHABLE; + } + + if ( comments_ ) + delete[] comments_; +} + +Value & +Value::operator=( const Value &other ) +{ + Value temp( other ); + swap( temp ); + return *this; +} + +void +Value::swap( Value &other ) +{ + ValueType temp = type_; + type_ = other.type_; + other.type_ = temp; + std::swap( value_, other.value_ ); + int temp2 = allocated_; + allocated_ = other.allocated_; + other.allocated_ = temp2; +} + +ValueType +Value::type() const +{ + return type_; +} + + +int +Value::compare( const Value &other ) const +{ + if ( *this < other ) + return -1; + if ( *this > other ) + return 1; + return 0; +} + + +bool +Value::operator <( const Value &other ) const +{ + int typeDelta = type_ - other.type_; + if ( typeDelta ) + return typeDelta < 0 ? true : false; + switch ( type_ ) + { + case nullValue: + return false; + case intValue: + return value_.int_ < other.value_.int_; + case uintValue: + return value_.uint_ < other.value_.uint_; + case realValue: + return value_.real_ < other.value_.real_; + case booleanValue: + return value_.bool_ < other.value_.bool_; + case stringValue: + return ( value_.string_ == 0 && other.value_.string_ ) + || ( other.value_.string_ + && value_.string_ + && strcmp( value_.string_, other.value_.string_ ) < 0 ); +#ifndef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + case objectValue: + { + int delta = int( value_.map_->size() - other.value_.map_->size() ); + if ( delta ) + return delta < 0; + return (*value_.map_) < (*other.value_.map_); + } +#else + case arrayValue: + return value_.array_->compare( *(other.value_.array_) ) < 0; + case objectValue: + return value_.map_->compare( *(other.value_.map_) ) < 0; +#endif + default: + JSON_ASSERT_UNREACHABLE; + } + return false; // unreachable +} + +bool +Value::operator <=( const Value &other ) const +{ + return !(other < *this); +} + +bool +Value::operator >=( const Value &other ) const +{ + return !(*this < other); +} + +bool +Value::operator >( const Value &other ) const +{ + return other < *this; +} + +bool +Value::operator ==( const Value &other ) const +{ + //if ( type_ != other.type_ ) + // GCC 2.95.3 says: + // attempt to take address of bit-field structure member `Json::Value::type_' + // Beats me, but a temp solves the problem. + int temp = other.type_; + if ( type_ != temp ) + return false; + switch ( type_ ) + { + case nullValue: + return true; + case intValue: + return value_.int_ == other.value_.int_; + case uintValue: + return value_.uint_ == other.value_.uint_; + case realValue: + return value_.real_ == other.value_.real_; + case booleanValue: + return value_.bool_ == other.value_.bool_; + case stringValue: + return ( value_.string_ == other.value_.string_ ) + || ( other.value_.string_ + && value_.string_ + && strcmp( value_.string_, other.value_.string_ ) == 0 ); +#ifndef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + case objectValue: + return value_.map_->size() == other.value_.map_->size() + && (*value_.map_) == (*other.value_.map_); +#else + case arrayValue: + return value_.array_->compare( *(other.value_.array_) ) == 0; + case objectValue: + return value_.map_->compare( *(other.value_.map_) ) == 0; +#endif + default: + JSON_ASSERT_UNREACHABLE; + } + return false; // unreachable +} + +bool +Value::operator !=( const Value &other ) const +{ + return !( *this == other ); +} + +const char * +Value::asCString() const +{ + JSON_ASSERT( type_ == stringValue ); + return value_.string_; +} + + +std::string +Value::asString() const +{ + switch ( type_ ) + { + case nullValue: + return ""; + case stringValue: + return value_.string_ ? value_.string_ : ""; + case booleanValue: + return value_.bool_ ? "true" : "false"; + case intValue: + return valueToString( value_.int_ ); + case uintValue: + return valueToString( value_.uint_ ); + case realValue: + return valueToString( value_.real_ ); + default: + JSON_FAIL_MESSAGE( "Type is not convertible to string" ); + } +} + +# ifdef JSON_USE_CPPTL +CppTL::ConstString +Value::asConstString() const +{ + return CppTL::ConstString( asString().c_str() ); +} +# endif + + +Value::Int +Value::asInt() const +{ + switch ( type_ ) + { + case intValue: + JSON_ASSERT_MESSAGE(isInt(), "LargestInt out of Int range"); + return Int(value_.int_); + case uintValue: + JSON_ASSERT_MESSAGE(isInt(), "LargestUInt out of Int range"); + return Int(value_.uint_); + case realValue: + JSON_ASSERT_MESSAGE(InRange(value_.real_, minInt, maxInt), "double out of Int range"); + return Int(value_.real_); + case nullValue: + return 0; + case booleanValue: + return value_.bool_ ? 1 : 0; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to Int."); +} + + +Value::UInt +Value::asUInt() const +{ + switch ( type_ ) + { + case intValue: + JSON_ASSERT_MESSAGE(isUInt(), "LargestInt out of UInt range"); + return UInt(value_.int_); + case uintValue: + JSON_ASSERT_MESSAGE(isUInt(), "LargestUInt out of UInt range"); + return UInt(value_.uint_); + case realValue: + JSON_ASSERT_MESSAGE(InRange(value_.real_, 0, maxUInt), "double out of UInt range"); + return UInt( value_.real_ ); + case nullValue: + return 0; + case booleanValue: + return value_.bool_ ? 1 : 0; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to UInt."); +} + + +# if defined(JSON_HAS_INT64) + +Value::Int64 +Value::asInt64() const +{ + switch ( type_ ) + { + case intValue: + return Int64(value_.int_); + case uintValue: + JSON_ASSERT_MESSAGE(isInt64(), "LargestUInt out of Int64 range"); + return Int64(value_.uint_); + case realValue: + JSON_ASSERT_MESSAGE(InRange(value_.real_, minInt64, maxInt64), "double out of Int64 range"); + return Int64(value_.real_); + case nullValue: + return 0; + case booleanValue: + return value_.bool_ ? 1 : 0; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to Int64."); +} + + +Value::UInt64 +Value::asUInt64() const +{ + switch ( type_ ) + { + case intValue: + JSON_ASSERT_MESSAGE(isUInt64(), "LargestInt out of UInt64 range"); + return UInt64(value_.int_); + case uintValue: + return UInt64(value_.uint_); + case realValue: + JSON_ASSERT_MESSAGE(InRange(value_.real_, 0, maxUInt64), "double out of UInt64 range"); + return UInt64( value_.real_ ); + case nullValue: + return 0; + case booleanValue: + return value_.bool_ ? 1 : 0; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to UInt64."); +} +# endif // if defined(JSON_HAS_INT64) + + +LargestInt +Value::asLargestInt() const +{ +#if defined(JSON_NO_INT64) + return asInt(); +#else + return asInt64(); +#endif +} + + +LargestUInt +Value::asLargestUInt() const +{ +#if defined(JSON_NO_INT64) + return asUInt(); +#else + return asUInt64(); +#endif +} + + +double +Value::asDouble() const +{ + switch ( type_ ) + { + case intValue: + return static_cast( value_.int_ ); + case uintValue: +#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + return static_cast( value_.uint_ ); +#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + return integerToDouble( value_.uint_ ); +#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + case realValue: + return value_.real_; + case nullValue: + return 0.0; + case booleanValue: + return value_.bool_ ? 1.0 : 0.0; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to double."); +} + +float +Value::asFloat() const +{ + switch ( type_ ) + { + case intValue: + return static_cast( value_.int_ ); + case uintValue: +#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + return static_cast( value_.uint_ ); +#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + return integerToDouble( value_.uint_ ); +#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) + case realValue: + return static_cast( value_.real_ ); + case nullValue: + return 0.0; + case booleanValue: + return value_.bool_ ? 1.0f : 0.0f; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to float."); +} + +bool +Value::asBool() const +{ + switch ( type_ ) + { + case booleanValue: + return value_.bool_; + case nullValue: + return false; + case intValue: + return value_.int_ ? true : false; + case uintValue: + return value_.uint_ ? true : false; + case realValue: + return value_.real_ ? true : false; + default: + break; + } + JSON_FAIL_MESSAGE("Value is not convertible to bool."); +} + + +bool +Value::isConvertibleTo( ValueType other ) const +{ + switch ( other ) + { + case nullValue: + return ( isNumeric() && asDouble() == 0.0 ) + || ( type_ == booleanValue && value_.bool_ == false ) + || ( type_ == stringValue && asString() == "" ) + || ( type_ == arrayValue && value_.map_->size() == 0 ) + || ( type_ == objectValue && value_.map_->size() == 0 ) + || type_ == nullValue; + case intValue: + return isInt() + || (type_ == realValue && InRange(value_.real_, minInt, maxInt)) + || type_ == booleanValue + || type_ == nullValue; + case uintValue: + return isUInt() + || (type_ == realValue && InRange(value_.real_, 0, maxUInt)) + || type_ == booleanValue + || type_ == nullValue; + case realValue: + return isNumeric() + || type_ == booleanValue + || type_ == nullValue; + case booleanValue: + return isNumeric() + || type_ == booleanValue + || type_ == nullValue; + case stringValue: + return isNumeric() + || type_ == booleanValue + || type_ == stringValue + || type_ == nullValue; + case arrayValue: + return type_ == arrayValue + || type_ == nullValue; + case objectValue: + return type_ == objectValue + || type_ == nullValue; + } + JSON_ASSERT_UNREACHABLE; + return false; +} + + +/// Number of values in array or object +ArrayIndex +Value::size() const +{ + switch ( type_ ) + { + case nullValue: + case intValue: + case uintValue: + case realValue: + case booleanValue: + case stringValue: + return 0; +#ifndef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: // size of the array is highest index + 1 + if ( !value_.map_->empty() ) + { + ObjectValues::const_iterator itLast = value_.map_->end(); + --itLast; + return (*itLast).first.index()+1; + } + return 0; + case objectValue: + return ArrayIndex( value_.map_->size() ); +#else + case arrayValue: + return Int( value_.array_->size() ); + case objectValue: + return Int( value_.map_->size() ); +#endif + } + JSON_ASSERT_UNREACHABLE; + return 0; // unreachable; +} + + +bool +Value::empty() const +{ + if ( isNull() || isArray() || isObject() ) + return size() == 0u; + else + return false; +} + + +bool +Value::operator!() const +{ + return isNull(); +} + + +void +Value::clear() +{ + JSON_ASSERT( type_ == nullValue || type_ == arrayValue || type_ == objectValue ); + + switch ( type_ ) + { +#ifndef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + case objectValue: + value_.map_->clear(); + break; +#else + case arrayValue: + value_.array_->clear(); + break; + case objectValue: + value_.map_->clear(); + break; +#endif + default: + break; + } +} + +void +Value::resize( ArrayIndex newSize ) +{ + JSON_ASSERT( type_ == nullValue || type_ == arrayValue ); + if ( type_ == nullValue ) + *this = Value( arrayValue ); +#ifndef JSON_VALUE_USE_INTERNAL_MAP + ArrayIndex oldSize = size(); + if ( newSize == 0 ) + clear(); + else if ( newSize > oldSize ) + (*this)[ newSize - 1 ]; + else + { + for ( ArrayIndex index = newSize; index < oldSize; ++index ) + { + value_.map_->erase( index ); + } + assert( size() == newSize ); + } +#else + value_.array_->resize( newSize ); +#endif +} + + +Value & +Value::operator[]( ArrayIndex index ) +{ + JSON_ASSERT( type_ == nullValue || type_ == arrayValue ); + if ( type_ == nullValue ) + *this = Value( arrayValue ); +#ifndef JSON_VALUE_USE_INTERNAL_MAP + CZString key( index ); + ObjectValues::iterator it = value_.map_->lower_bound( key ); + if ( it != value_.map_->end() && (*it).first == key ) + return (*it).second; + + ObjectValues::value_type defaultValue( key, null ); + it = value_.map_->insert( it, defaultValue ); + return (*it).second; +#else + return value_.array_->resolveReference( index ); +#endif +} + + +Value & +Value::operator[]( int index ) +{ + JSON_ASSERT( index >= 0 ); + return (*this)[ ArrayIndex(index) ]; +} + + +const Value & +Value::operator[]( ArrayIndex index ) const +{ + JSON_ASSERT( type_ == nullValue || type_ == arrayValue ); + if ( type_ == nullValue ) + return null; +#ifndef JSON_VALUE_USE_INTERNAL_MAP + CZString key( index ); + ObjectValues::const_iterator it = value_.map_->find( key ); + if ( it == value_.map_->end() ) + return null; + return (*it).second; +#else + Value *value = value_.array_->find( index ); + return value ? *value : null; +#endif +} + + +const Value & +Value::operator[]( int index ) const +{ + JSON_ASSERT( index >= 0 ); + return (*this)[ ArrayIndex(index) ]; +} + + +Value & +Value::operator[]( const char *key ) +{ + return resolveReference( key, false ); +} + + +Value & +Value::resolveReference( const char *key, + bool isStatic ) +{ + JSON_ASSERT( type_ == nullValue || type_ == objectValue ); + if ( type_ == nullValue ) + *this = Value( objectValue ); +#ifndef JSON_VALUE_USE_INTERNAL_MAP + CZString actualKey( key, isStatic ? CZString::noDuplication + : CZString::duplicateOnCopy ); + ObjectValues::iterator it = value_.map_->lower_bound( actualKey ); + if ( it != value_.map_->end() && (*it).first == actualKey ) + return (*it).second; + + ObjectValues::value_type defaultValue( actualKey, null ); + it = value_.map_->insert( it, defaultValue ); + Value &value = (*it).second; + return value; +#else + return value_.map_->resolveReference( key, isStatic ); +#endif +} + + +Value +Value::get( ArrayIndex index, + const Value &defaultValue ) const +{ + const Value *value = &((*this)[index]); + return value == &null ? defaultValue : *value; +} + + +bool +Value::isValidIndex( ArrayIndex index ) const +{ + return index < size(); +} + + + +const Value & +Value::operator[]( const char *key ) const +{ + JSON_ASSERT( type_ == nullValue || type_ == objectValue ); + if ( type_ == nullValue ) + return null; +#ifndef JSON_VALUE_USE_INTERNAL_MAP + CZString actualKey( key, CZString::noDuplication ); + ObjectValues::const_iterator it = value_.map_->find( actualKey ); + if ( it == value_.map_->end() ) + return null; + return (*it).second; +#else + const Value *value = value_.map_->find( key ); + return value ? *value : null; +#endif +} + + +Value & +Value::operator[]( const std::string &key ) +{ + return (*this)[ key.c_str() ]; +} + + +const Value & +Value::operator[]( const std::string &key ) const +{ + return (*this)[ key.c_str() ]; +} + +Value & +Value::operator[]( const StaticString &key ) +{ + return resolveReference( key, true ); +} + + +# ifdef JSON_USE_CPPTL +Value & +Value::operator[]( const CppTL::ConstString &key ) +{ + return (*this)[ key.c_str() ]; +} + + +const Value & +Value::operator[]( const CppTL::ConstString &key ) const +{ + return (*this)[ key.c_str() ]; +} +# endif + + +Value & +Value::append( const Value &value ) +{ + return (*this)[size()] = value; +} + + +Value +Value::get( const char *key, + const Value &defaultValue ) const +{ + const Value *value = &((*this)[key]); + return value == &null ? defaultValue : *value; +} + + +Value +Value::get( const std::string &key, + const Value &defaultValue ) const +{ + return get( key.c_str(), defaultValue ); +} + +Value +Value::removeMember( const char* key ) +{ + JSON_ASSERT( type_ == nullValue || type_ == objectValue ); + if ( type_ == nullValue ) + return null; +#ifndef JSON_VALUE_USE_INTERNAL_MAP + CZString actualKey( key, CZString::noDuplication ); + ObjectValues::iterator it = value_.map_->find( actualKey ); + if ( it == value_.map_->end() ) + return null; + Value old(it->second); + value_.map_->erase(it); + return old; +#else + Value *value = value_.map_->find( key ); + if (value){ + Value old(*value); + value_.map_.remove( key ); + return old; + } else { + return null; + } +#endif +} + +Value +Value::removeMember( const std::string &key ) +{ + return removeMember( key.c_str() ); +} + +# ifdef JSON_USE_CPPTL +Value +Value::get( const CppTL::ConstString &key, + const Value &defaultValue ) const +{ + return get( key.c_str(), defaultValue ); +} +# endif + +bool +Value::isMember( const char *key ) const +{ + const Value *value = &((*this)[key]); + return value != &null; +} + + +bool +Value::isMember( const std::string &key ) const +{ + return isMember( key.c_str() ); +} + + +# ifdef JSON_USE_CPPTL +bool +Value::isMember( const CppTL::ConstString &key ) const +{ + return isMember( key.c_str() ); +} +#endif + +Value::Members +Value::getMemberNames() const +{ + JSON_ASSERT( type_ == nullValue || type_ == objectValue ); + if ( type_ == nullValue ) + return Value::Members(); + Members members; + members.reserve( value_.map_->size() ); +#ifndef JSON_VALUE_USE_INTERNAL_MAP + ObjectValues::const_iterator it = value_.map_->begin(); + ObjectValues::const_iterator itEnd = value_.map_->end(); + for ( ; it != itEnd; ++it ) + members.push_back( std::string( (*it).first.c_str() ) ); +#else + ValueInternalMap::IteratorState it; + ValueInternalMap::IteratorState itEnd; + value_.map_->makeBeginIterator( it ); + value_.map_->makeEndIterator( itEnd ); + for ( ; !ValueInternalMap::equals( it, itEnd ); ValueInternalMap::increment(it) ) + members.push_back( std::string( ValueInternalMap::key( it ) ) ); +#endif + return members; +} +// +//# ifdef JSON_USE_CPPTL +//EnumMemberNames +//Value::enumMemberNames() const +//{ +// if ( type_ == objectValue ) +// { +// return CppTL::Enum::any( CppTL::Enum::transform( +// CppTL::Enum::keys( *(value_.map_), CppTL::Type() ), +// MemberNamesTransform() ) ); +// } +// return EnumMemberNames(); +//} +// +// +//EnumValues +//Value::enumValues() const +//{ +// if ( type_ == objectValue || type_ == arrayValue ) +// return CppTL::Enum::anyValues( *(value_.map_), +// CppTL::Type() ); +// return EnumValues(); +//} +// +//# endif + +static bool IsIntegral(double d) { + double integral_part; + return modf(d, &integral_part) == 0.0; +} + + +bool +Value::isNull() const +{ + return type_ == nullValue; +} + + +bool +Value::isBool() const +{ + return type_ == booleanValue; +} + + +bool +Value::isInt() const +{ + switch ( type_ ) + { + case intValue: + return value_.int_ >= minInt && value_.int_ <= maxInt; + case uintValue: + return value_.uint_ <= UInt(maxInt); + case realValue: + return value_.real_ >= minInt && + value_.real_ <= maxInt && + IsIntegral(value_.real_); + default: + break; + } + return false; +} + + +bool +Value::isUInt() const +{ + switch ( type_ ) + { + case intValue: + return value_.int_ >= 0 && LargestUInt(value_.int_) <= LargestUInt(maxUInt); + case uintValue: + return value_.uint_ <= maxUInt; + case realValue: + return value_.real_ >= 0 && + value_.real_ <= maxUInt && + IsIntegral(value_.real_); + default: + break; + } + return false; +} + +bool +Value::isInt64() const +{ +# if defined(JSON_HAS_INT64) + switch ( type_ ) + { + case intValue: + return true; + case uintValue: + return value_.uint_ <= UInt64(maxInt64); + case realValue: + // Note that maxInt64 (= 2^63 - 1) is not exactly representable as a + // double, so double(maxInt64) will be rounded up to 2^63. Therefore we + // require the value to be strictly less than the limit. + return value_.real_ >= double(minInt64) && + value_.real_ < double(maxInt64) && + IsIntegral(value_.real_); + default: + break; + } +# endif // JSON_HAS_INT64 + return false; +} + +bool +Value::isUInt64() const +{ +# if defined(JSON_HAS_INT64) + switch ( type_ ) + { + case intValue: + return value_.int_ >= 0; + case uintValue: + return true; + case realValue: + // Note that maxUInt64 (= 2^64 - 1) is not exactly representable as a + // double, so double(maxUInt64) will be rounded up to 2^64. Therefore we + // require the value to be strictly less than the limit. + return value_.real_ >= 0 && + value_.real_ < maxUInt64AsDouble && + IsIntegral(value_.real_); + default: + break; + } +# endif // JSON_HAS_INT64 + return false; +} + + +bool +Value::isIntegral() const +{ +#if defined(JSON_HAS_INT64) + return isInt64() || isUInt64(); +#else + return isInt() || isUInt(); +#endif +} + + +bool +Value::isDouble() const +{ + return type_ == realValue || isIntegral(); +} + + +bool +Value::isNumeric() const +{ + return isIntegral() || isDouble(); +} + + +bool +Value::isString() const +{ + return type_ == stringValue; +} + + +bool +Value::isArray() const +{ + return type_ == arrayValue; +} + + +bool +Value::isObject() const +{ + return type_ == objectValue; +} + + +void +Value::setComment( const char *comment, + CommentPlacement placement ) +{ + if ( !comments_ ) + comments_ = new CommentInfo[numberOfCommentPlacement]; + comments_[placement].setComment( comment ); +} + + +void +Value::setComment( const std::string &comment, + CommentPlacement placement ) +{ + setComment( comment.c_str(), placement ); +} + + +bool +Value::hasComment( CommentPlacement placement ) const +{ + return comments_ != 0 && comments_[placement].comment_ != 0; +} + +std::string +Value::getComment( CommentPlacement placement ) const +{ + if ( hasComment(placement) ) + return comments_[placement].comment_; + return ""; +} + + +std::string +Value::toStyledString() const +{ + StyledWriter writer; + return writer.write( *this ); +} + + +Value::const_iterator +Value::begin() const +{ + switch ( type_ ) + { +#ifdef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + if ( value_.array_ ) + { + ValueInternalArray::IteratorState it; + value_.array_->makeBeginIterator( it ); + return const_iterator( it ); + } + break; + case objectValue: + if ( value_.map_ ) + { + ValueInternalMap::IteratorState it; + value_.map_->makeBeginIterator( it ); + return const_iterator( it ); + } + break; +#else + case arrayValue: + case objectValue: + if ( value_.map_ ) + return const_iterator( value_.map_->begin() ); + break; +#endif + default: + break; + } + return const_iterator(); +} + +Value::const_iterator +Value::end() const +{ + switch ( type_ ) + { +#ifdef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + if ( value_.array_ ) + { + ValueInternalArray::IteratorState it; + value_.array_->makeEndIterator( it ); + return const_iterator( it ); + } + break; + case objectValue: + if ( value_.map_ ) + { + ValueInternalMap::IteratorState it; + value_.map_->makeEndIterator( it ); + return const_iterator( it ); + } + break; +#else + case arrayValue: + case objectValue: + if ( value_.map_ ) + return const_iterator( value_.map_->end() ); + break; +#endif + default: + break; + } + return const_iterator(); +} + + +Value::iterator +Value::begin() +{ + switch ( type_ ) + { +#ifdef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + if ( value_.array_ ) + { + ValueInternalArray::IteratorState it; + value_.array_->makeBeginIterator( it ); + return iterator( it ); + } + break; + case objectValue: + if ( value_.map_ ) + { + ValueInternalMap::IteratorState it; + value_.map_->makeBeginIterator( it ); + return iterator( it ); + } + break; +#else + case arrayValue: + case objectValue: + if ( value_.map_ ) + return iterator( value_.map_->begin() ); + break; +#endif + default: + break; + } + return iterator(); +} + +Value::iterator +Value::end() +{ + switch ( type_ ) + { +#ifdef JSON_VALUE_USE_INTERNAL_MAP + case arrayValue: + if ( value_.array_ ) + { + ValueInternalArray::IteratorState it; + value_.array_->makeEndIterator( it ); + return iterator( it ); + } + break; + case objectValue: + if ( value_.map_ ) + { + ValueInternalMap::IteratorState it; + value_.map_->makeEndIterator( it ); + return iterator( it ); + } + break; +#else + case arrayValue: + case objectValue: + if ( value_.map_ ) + return iterator( value_.map_->end() ); + break; +#endif + default: + break; + } + return iterator(); +} + + +// class PathArgument +// ////////////////////////////////////////////////////////////////// + +PathArgument::PathArgument() + : key_() + , index_() + , kind_( kindNone ) +{ +} + + +PathArgument::PathArgument( ArrayIndex index ) + : key_() + , index_( index ) + , kind_( kindIndex ) +{ +} + + +PathArgument::PathArgument( const char *key ) + : key_( key ) + , index_() + , kind_( kindKey ) +{ +} + + +PathArgument::PathArgument( const std::string &key ) + : key_( key.c_str() ) + , index_() + , kind_( kindKey ) +{ +} + +// class Path +// ////////////////////////////////////////////////////////////////// + +Path::Path( const std::string &path, + const PathArgument &a1, + const PathArgument &a2, + const PathArgument &a3, + const PathArgument &a4, + const PathArgument &a5 ) +{ + InArgs in; + in.push_back( &a1 ); + in.push_back( &a2 ); + in.push_back( &a3 ); + in.push_back( &a4 ); + in.push_back( &a5 ); + makePath( path, in ); +} + + +void +Path::makePath( const std::string &path, + const InArgs &in ) +{ + const char *current = path.c_str(); + const char *end = current + path.length(); + InArgs::const_iterator itInArg = in.begin(); + while ( current != end ) + { + if ( *current == '[' ) + { + ++current; + if ( *current == '%' ) + addPathInArg( path, in, itInArg, PathArgument::kindIndex ); + else + { + ArrayIndex index = 0; + for ( ; current != end && *current >= '0' && *current <= '9'; ++current ) + index = index * 10 + ArrayIndex(*current - '0'); + args_.push_back( index ); + } + if ( current == end || *current++ != ']' ) + invalidPath( path, int(current - path.c_str()) ); + } + else if ( *current == '%' ) + { + addPathInArg( path, in, itInArg, PathArgument::kindKey ); + ++current; + } + else if ( *current == '.' ) + { + ++current; + } + else + { + const char *beginName = current; + while ( current != end && !strchr( "[.", *current ) ) + ++current; + args_.push_back( std::string( beginName, current ) ); + } + } +} + + +void +Path::addPathInArg( const std::string &path, + const InArgs &in, + InArgs::const_iterator &itInArg, + PathArgument::Kind kind ) +{ + if ( itInArg == in.end() ) + { + // Error: missing argument %d + } + else if ( (*itInArg)->kind_ != kind ) + { + // Error: bad argument type + } + else + { + args_.push_back( **itInArg ); + } +} + + +void +Path::invalidPath( const std::string &path, + int location ) +{ + // Error: invalid path. +} + + +const Value & +Path::resolve( const Value &root ) const +{ + const Value *node = &root; + for ( Args::const_iterator it = args_.begin(); it != args_.end(); ++it ) + { + const PathArgument &arg = *it; + if ( arg.kind_ == PathArgument::kindIndex ) + { + if ( !node->isArray() || !node->isValidIndex( arg.index_ ) ) + { + // Error: unable to resolve path (array value expected at position... + } + node = &((*node)[arg.index_]); + } + else if ( arg.kind_ == PathArgument::kindKey ) + { + if ( !node->isObject() ) + { + // Error: unable to resolve path (object value expected at position...) + } + node = &((*node)[arg.key_]); + if ( node == &Value::null ) + { + // Error: unable to resolve path (object has no member named '' at position...) + } + } + } + return *node; +} + + +Value +Path::resolve( const Value &root, + const Value &defaultValue ) const +{ + const Value *node = &root; + for ( Args::const_iterator it = args_.begin(); it != args_.end(); ++it ) + { + const PathArgument &arg = *it; + if ( arg.kind_ == PathArgument::kindIndex ) + { + if ( !node->isArray() || !node->isValidIndex( arg.index_ ) ) + return defaultValue; + node = &((*node)[arg.index_]); + } + else if ( arg.kind_ == PathArgument::kindKey ) + { + if ( !node->isObject() ) + return defaultValue; + node = &((*node)[arg.key_]); + if ( node == &Value::null ) + return defaultValue; + } + } + return *node; +} + + +Value & +Path::make( Value &root ) const +{ + Value *node = &root; + for ( Args::const_iterator it = args_.begin(); it != args_.end(); ++it ) + { + const PathArgument &arg = *it; + if ( arg.kind_ == PathArgument::kindIndex ) + { + if ( !node->isArray() ) + { + // Error: node is not an array at position ... + } + node = &((*node)[arg.index_]); + } + else if ( arg.kind_ == PathArgument::kindKey ) + { + if ( !node->isObject() ) + { + // Error: node is not an object at position... + } + node = &((*node)[arg.key_]); + } + } + return *node; +} + + +} // namespace Json + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: src/lib_json/json_value.cpp +// ////////////////////////////////////////////////////////////////////// + + + + + + +// ////////////////////////////////////////////////////////////////////// +// Beginning of content of file: src/lib_json/json_writer.cpp +// ////////////////////////////////////////////////////////////////////// + +// Copyright 2011 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#if !defined(JSON_IS_AMALGAMATION) +# include +# include "json_tool.h" +#endif // if !defined(JSON_IS_AMALGAMATION) +#include +#include +#include +#include +#include +#include + +#if defined(_MSC_VER) && _MSC_VER >= 1400 // VC++ 8.0 +#pragma warning( disable : 4996 ) // disable warning about strdup being deprecated. +#endif + +namespace Json { + +static bool containsControlCharacter( const char* str ) +{ + while ( *str ) + { + if ( isControlCharacter( *(str++) ) ) + return true; + } + return false; +} + + +std::string valueToString( LargestInt value ) +{ + UIntToStringBuffer buffer; + char *current = buffer + sizeof(buffer); + bool isNegative = value < 0; + if ( isNegative ) + value = -value; + uintToString( LargestUInt(value), current ); + if ( isNegative ) + *--current = '-'; + assert( current >= buffer ); + return current; +} + + +std::string valueToString( LargestUInt value ) +{ + UIntToStringBuffer buffer; + char *current = buffer + sizeof(buffer); + uintToString( value, current ); + assert( current >= buffer ); + return current; +} + +#if defined(JSON_HAS_INT64) + +std::string valueToString( Int value ) +{ + return valueToString( LargestInt(value) ); +} + + +std::string valueToString( UInt value ) +{ + return valueToString( LargestUInt(value) ); +} + +#endif // # if defined(JSON_HAS_INT64) + + +std::string valueToString( double value ) +{ + char buffer[32]; +#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) // Use secure version with visual studio 2005 to avoid warning. + sprintf_s(buffer, sizeof(buffer), "%#.16g", value); +#else + sprintf(buffer, "%#.16g", value); +#endif + char* ch = buffer + strlen(buffer) - 1; + if (*ch != '0') return buffer; // nothing to truncate, so save time + while(ch > buffer && *ch == '0'){ + --ch; + } + char* last_nonzero = ch; + while(ch >= buffer){ + switch(*ch){ + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + --ch; + continue; + case '.': + // Truncate zeroes to save bytes in output, but keep one. + *(last_nonzero+2) = '\0'; + return buffer; + default: + return buffer; + } + } + return buffer; +} + + +std::string valueToString( bool value ) +{ + return value ? "true" : "false"; +} + +std::string valueToQuotedString( const char *value ) +{ + if (value == NULL) + return ""; + // Not sure how to handle unicode... + if (strpbrk(value, "\"\\\b\f\n\r\t") == NULL && !containsControlCharacter( value )) + return std::string("\"") + value + "\""; + // We have to walk value and escape any special characters. + // Appending to std::string is not efficient, but this should be rare. + // (Note: forward slashes are *not* rare, but I am not escaping them.) + std::string::size_type maxsize = strlen(value)*2 + 3; // allescaped+quotes+NULL + std::string result; + result.reserve(maxsize); // to avoid lots of mallocs + result += "\""; + for (const char* c=value; *c != 0; ++c) + { + switch(*c) + { + case '\"': + result += "\\\""; + break; + case '\\': + result += "\\\\"; + break; + case '\b': + result += "\\b"; + break; + case '\f': + result += "\\f"; + break; + case '\n': + result += "\\n"; + break; + case '\r': + result += "\\r"; + break; + case '\t': + result += "\\t"; + break; + //case '/': + // Even though \/ is considered a legal escape in JSON, a bare + // slash is also legal, so I see no reason to escape it. + // (I hope I am not misunderstanding something. + // blep notes: actually escaping \/ may be useful in javascript to avoid (*c); + result += oss.str(); + } + else + { + result += *c; + } + break; + } + } + result += "\""; + return result; +} + +// Class Writer +// ////////////////////////////////////////////////////////////////// +Writer::~Writer() +{ +} + + +// Class FastWriter +// ////////////////////////////////////////////////////////////////// + +FastWriter::FastWriter() + : yamlCompatiblityEnabled_( false ), + dropNullPlaceholders_( false ) +{ +} + + +void +FastWriter::enableYAMLCompatibility() +{ + yamlCompatiblityEnabled_ = true; +} + + +void +FastWriter::dropNullPlaceholders() +{ + dropNullPlaceholders_ = true; +} + + +std::string +FastWriter::write( const Value &root ) +{ + document_ = ""; + writeValue( root ); + document_ += "\n"; + return document_; +} + + +void +FastWriter::writeValue( const Value &value ) +{ + switch ( value.type() ) + { + case nullValue: + if (!dropNullPlaceholders_) document_ += "null"; + break; + case intValue: + document_ += valueToString( value.asLargestInt() ); + break; + case uintValue: + document_ += valueToString( value.asLargestUInt() ); + break; + case realValue: + document_ += valueToString( value.asDouble() ); + break; + case stringValue: + document_ += valueToQuotedString( value.asCString() ); + break; + case booleanValue: + document_ += valueToString( value.asBool() ); + break; + case arrayValue: + { + document_ += "["; + int size = value.size(); + for ( int index =0; index < size; ++index ) + { + if ( index > 0 ) + document_ += ","; + writeValue( value[index] ); + } + document_ += "]"; + } + break; + case objectValue: + { + Value::Members members( value.getMemberNames() ); + document_ += "{"; + for ( Value::Members::iterator it = members.begin(); + it != members.end(); + ++it ) + { + const std::string &name = *it; + if ( it != members.begin() ) + document_ += ","; + document_ += valueToQuotedString( name.c_str() ); + document_ += yamlCompatiblityEnabled_ ? ": " + : ":"; + writeValue( value[name] ); + } + document_ += "}"; + } + break; + } +} + + +// Class StyledWriter +// ////////////////////////////////////////////////////////////////// + +StyledWriter::StyledWriter() + : rightMargin_( 74 ) + , indentSize_( 3 ) + , addChildValues_() +{ +} + + +std::string +StyledWriter::write( const Value &root ) +{ + document_ = ""; + addChildValues_ = false; + indentString_ = ""; + writeCommentBeforeValue( root ); + writeValue( root ); + writeCommentAfterValueOnSameLine( root ); + document_ += "\n"; + return document_; +} + + +void +StyledWriter::writeValue( const Value &value ) +{ + switch ( value.type() ) + { + case nullValue: + pushValue( "null" ); + break; + case intValue: + pushValue( valueToString( value.asLargestInt() ) ); + break; + case uintValue: + pushValue( valueToString( value.asLargestUInt() ) ); + break; + case realValue: + pushValue( valueToString( value.asDouble() ) ); + break; + case stringValue: + pushValue( valueToQuotedString( value.asCString() ) ); + break; + case booleanValue: + pushValue( valueToString( value.asBool() ) ); + break; + case arrayValue: + writeArrayValue( value); + break; + case objectValue: + { + Value::Members members( value.getMemberNames() ); + if ( members.empty() ) + pushValue( "{}" ); + else + { + writeWithIndent( "{" ); + indent(); + Value::Members::iterator it = members.begin(); + for (;;) + { + const std::string &name = *it; + const Value &childValue = value[name]; + writeCommentBeforeValue( childValue ); + writeWithIndent( valueToQuotedString( name.c_str() ) ); + document_ += " : "; + writeValue( childValue ); + if ( ++it == members.end() ) + { + writeCommentAfterValueOnSameLine( childValue ); + break; + } + document_ += ","; + writeCommentAfterValueOnSameLine( childValue ); + } + unindent(); + writeWithIndent( "}" ); + } + } + break; + } +} + + +void +StyledWriter::writeArrayValue( const Value &value ) +{ + unsigned size = value.size(); + if ( size == 0 ) + pushValue( "[]" ); + else + { + bool isArrayMultiLine = isMultineArray( value ); + if ( isArrayMultiLine ) + { + writeWithIndent( "[" ); + indent(); + bool hasChildValue = !childValues_.empty(); + unsigned index =0; + for (;;) + { + const Value &childValue = value[index]; + writeCommentBeforeValue( childValue ); + if ( hasChildValue ) + writeWithIndent( childValues_[index] ); + else + { + writeIndent(); + writeValue( childValue ); + } + if ( ++index == size ) + { + writeCommentAfterValueOnSameLine( childValue ); + break; + } + document_ += ","; + writeCommentAfterValueOnSameLine( childValue ); + } + unindent(); + writeWithIndent( "]" ); + } + else // output on a single line + { + assert( childValues_.size() == size ); + document_ += "[ "; + for ( unsigned index =0; index < size; ++index ) + { + if ( index > 0 ) + document_ += ", "; + document_ += childValues_[index]; + } + document_ += " ]"; + } + } +} + + +bool +StyledWriter::isMultineArray( const Value &value ) +{ + int size = value.size(); + bool isMultiLine = size*3 >= rightMargin_ ; + childValues_.clear(); + for ( int index =0; index < size && !isMultiLine; ++index ) + { + const Value &childValue = value[index]; + isMultiLine = isMultiLine || + ( (childValue.isArray() || childValue.isObject()) && + childValue.size() > 0 ); + } + if ( !isMultiLine ) // check if line length > max line length + { + childValues_.reserve( size ); + addChildValues_ = true; + int lineLength = 4 + (size-1)*2; // '[ ' + ', '*n + ' ]' + for ( int index =0; index < size && !isMultiLine; ++index ) + { + writeValue( value[index] ); + lineLength += int( childValues_[index].length() ); + isMultiLine = isMultiLine && hasCommentForValue( value[index] ); + } + addChildValues_ = false; + isMultiLine = isMultiLine || lineLength >= rightMargin_; + } + return isMultiLine; +} + + +void +StyledWriter::pushValue( const std::string &value ) +{ + if ( addChildValues_ ) + childValues_.push_back( value ); + else + document_ += value; +} + + +void +StyledWriter::writeIndent() +{ + if ( !document_.empty() ) + { + char last = document_[document_.length()-1]; + if ( last == ' ' ) // already indented + return; + if ( last != '\n' ) // Comments may add new-line + document_ += '\n'; + } + document_ += indentString_; +} + + +void +StyledWriter::writeWithIndent( const std::string &value ) +{ + writeIndent(); + document_ += value; +} + + +void +StyledWriter::indent() +{ + indentString_ += std::string( indentSize_, ' ' ); +} + + +void +StyledWriter::unindent() +{ + assert( int(indentString_.size()) >= indentSize_ ); + indentString_.resize( indentString_.size() - indentSize_ ); +} + + +void +StyledWriter::writeCommentBeforeValue( const Value &root ) +{ + if ( !root.hasComment( commentBefore ) ) + return; + document_ += normalizeEOL( root.getComment( commentBefore ) ); + document_ += "\n"; +} + + +void +StyledWriter::writeCommentAfterValueOnSameLine( const Value &root ) +{ + if ( root.hasComment( commentAfterOnSameLine ) ) + document_ += " " + normalizeEOL( root.getComment( commentAfterOnSameLine ) ); + + if ( root.hasComment( commentAfter ) ) + { + document_ += "\n"; + document_ += normalizeEOL( root.getComment( commentAfter ) ); + document_ += "\n"; + } +} + + +bool +StyledWriter::hasCommentForValue( const Value &value ) +{ + return value.hasComment( commentBefore ) + || value.hasComment( commentAfterOnSameLine ) + || value.hasComment( commentAfter ); +} + + +std::string +StyledWriter::normalizeEOL( const std::string &text ) +{ + std::string normalized; + normalized.reserve( text.length() ); + const char *begin = text.c_str(); + const char *end = begin + text.length(); + const char *current = begin; + while ( current != end ) + { + char c = *current++; + if ( c == '\r' ) // mac or dos EOL + { + if ( *current == '\n' ) // convert dos EOL + ++current; + normalized += '\n'; + } + else // handle unix EOL & other char + normalized += c; + } + return normalized; +} + + +// Class StyledStreamWriter +// ////////////////////////////////////////////////////////////////// + +StyledStreamWriter::StyledStreamWriter( std::string indentation ) + : document_(NULL) + , rightMargin_( 74 ) + , indentation_( indentation ) + , addChildValues_() +{ +} + + +void +StyledStreamWriter::write( std::ostream &out, const Value &root ) +{ + document_ = &out; + addChildValues_ = false; + indentString_ = ""; + writeCommentBeforeValue( root ); + writeValue( root ); + writeCommentAfterValueOnSameLine( root ); + *document_ << "\n"; + document_ = NULL; // Forget the stream, for safety. +} + + +void +StyledStreamWriter::writeValue( const Value &value ) +{ + switch ( value.type() ) + { + case nullValue: + pushValue( "null" ); + break; + case intValue: + pushValue( valueToString( value.asLargestInt() ) ); + break; + case uintValue: + pushValue( valueToString( value.asLargestUInt() ) ); + break; + case realValue: + pushValue( valueToString( value.asDouble() ) ); + break; + case stringValue: + pushValue( valueToQuotedString( value.asCString() ) ); + break; + case booleanValue: + pushValue( valueToString( value.asBool() ) ); + break; + case arrayValue: + writeArrayValue( value); + break; + case objectValue: + { + Value::Members members( value.getMemberNames() ); + if ( members.empty() ) + pushValue( "{}" ); + else + { + writeWithIndent( "{" ); + indent(); + Value::Members::iterator it = members.begin(); + for (;;) + { + const std::string &name = *it; + const Value &childValue = value[name]; + writeCommentBeforeValue( childValue ); + writeWithIndent( valueToQuotedString( name.c_str() ) ); + *document_ << " : "; + writeValue( childValue ); + if ( ++it == members.end() ) + { + writeCommentAfterValueOnSameLine( childValue ); + break; + } + *document_ << ","; + writeCommentAfterValueOnSameLine( childValue ); + } + unindent(); + writeWithIndent( "}" ); + } + } + break; + } +} + + +void +StyledStreamWriter::writeArrayValue( const Value &value ) +{ + unsigned size = value.size(); + if ( size == 0 ) + pushValue( "[]" ); + else + { + bool isArrayMultiLine = isMultineArray( value ); + if ( isArrayMultiLine ) + { + writeWithIndent( "[" ); + indent(); + bool hasChildValue = !childValues_.empty(); + unsigned index =0; + for (;;) + { + const Value &childValue = value[index]; + writeCommentBeforeValue( childValue ); + if ( hasChildValue ) + writeWithIndent( childValues_[index] ); + else + { + writeIndent(); + writeValue( childValue ); + } + if ( ++index == size ) + { + writeCommentAfterValueOnSameLine( childValue ); + break; + } + *document_ << ","; + writeCommentAfterValueOnSameLine( childValue ); + } + unindent(); + writeWithIndent( "]" ); + } + else // output on a single line + { + assert( childValues_.size() == size ); + *document_ << "[ "; + for ( unsigned index =0; index < size; ++index ) + { + if ( index > 0 ) + *document_ << ", "; + *document_ << childValues_[index]; + } + *document_ << " ]"; + } + } +} + + +bool +StyledStreamWriter::isMultineArray( const Value &value ) +{ + int size = value.size(); + bool isMultiLine = size*3 >= rightMargin_ ; + childValues_.clear(); + for ( int index =0; index < size && !isMultiLine; ++index ) + { + const Value &childValue = value[index]; + isMultiLine = isMultiLine || + ( (childValue.isArray() || childValue.isObject()) && + childValue.size() > 0 ); + } + if ( !isMultiLine ) // check if line length > max line length + { + childValues_.reserve( size ); + addChildValues_ = true; + int lineLength = 4 + (size-1)*2; // '[ ' + ', '*n + ' ]' + for ( int index =0; index < size && !isMultiLine; ++index ) + { + writeValue( value[index] ); + lineLength += int( childValues_[index].length() ); + isMultiLine = isMultiLine && hasCommentForValue( value[index] ); + } + addChildValues_ = false; + isMultiLine = isMultiLine || lineLength >= rightMargin_; + } + return isMultiLine; +} + + +void +StyledStreamWriter::pushValue( const std::string &value ) +{ + if ( addChildValues_ ) + childValues_.push_back( value ); + else + *document_ << value; +} + + +void +StyledStreamWriter::writeIndent() +{ + /* + Some comments in this method would have been nice. ;-) + + if ( !document_.empty() ) + { + char last = document_[document_.length()-1]; + if ( last == ' ' ) // already indented + return; + if ( last != '\n' ) // Comments may add new-line + *document_ << '\n'; + } + */ + *document_ << '\n' << indentString_; +} + + +void +StyledStreamWriter::writeWithIndent( const std::string &value ) +{ + writeIndent(); + *document_ << value; +} + + +void +StyledStreamWriter::indent() +{ + indentString_ += indentation_; +} + + +void +StyledStreamWriter::unindent() +{ + assert( indentString_.size() >= indentation_.size() ); + indentString_.resize( indentString_.size() - indentation_.size() ); +} + + +void +StyledStreamWriter::writeCommentBeforeValue( const Value &root ) +{ + if ( !root.hasComment( commentBefore ) ) + return; + *document_ << normalizeEOL( root.getComment( commentBefore ) ); + *document_ << "\n"; +} + + +void +StyledStreamWriter::writeCommentAfterValueOnSameLine( const Value &root ) +{ + if ( root.hasComment( commentAfterOnSameLine ) ) + *document_ << " " + normalizeEOL( root.getComment( commentAfterOnSameLine ) ); + + if ( root.hasComment( commentAfter ) ) + { + *document_ << "\n"; + *document_ << normalizeEOL( root.getComment( commentAfter ) ); + *document_ << "\n"; + } +} + + +bool +StyledStreamWriter::hasCommentForValue( const Value &value ) +{ + return value.hasComment( commentBefore ) + || value.hasComment( commentAfterOnSameLine ) + || value.hasComment( commentAfter ); +} + + +std::string +StyledStreamWriter::normalizeEOL( const std::string &text ) +{ + std::string normalized; + normalized.reserve( text.length() ); + const char *begin = text.c_str(); + const char *end = begin + text.length(); + const char *current = begin; + while ( current != end ) + { + char c = *current++; + if ( c == '\r' ) // mac or dos EOL + { + if ( *current == '\n' ) // convert dos EOL + ++current; + normalized += '\n'; + } + else // handle unix EOL & other char + normalized += c; + } + return normalized; +} + + +std::ostream& operator<<( std::ostream &sout, const Value &root ) +{ + Json::StyledStreamWriter writer; + writer.write(sout, root); + return sout; +} + + +} // namespace Json + +// ////////////////////////////////////////////////////////////////////// +// End of content of file: src/lib_json/json_writer.cpp +// ////////////////////////////////////////////////////////////////////// + + + + + diff --git a/src/mods.h b/src/mods.h index 12576516d..af7777d18 100644 --- a/src/mods.h +++ b/src/mods.h @@ -26,7 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include #include -#include "json/json.h" +#include #include "config.h" #define MODNAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyz0123456789_" diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index 06e20c2a0..c664101ea 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -33,7 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "porting.h" #include "mg_schematic.h" #include "noise.h" -#include "json/json.h" +#include struct EnumString es_TileAnimationType[] = { @@ -1250,8 +1250,13 @@ static bool push_json_value_helper(lua_State *L, const Json::Value &value, lua_newtable(L); for (Json::Value::const_iterator it = value.begin(); it != value.end(); ++it) { +#ifndef JSONCPP_STRING const char *str = it.memberName(); lua_pushstring(L, str ? str : ""); +#else + std::string str = it.name(); + lua_pushstring(L, str.c_str()); +#endif push_json_value_helper(L, *it, nullindex); lua_rawset(L, -3); } diff --git a/src/script/lua_api/l_util.cpp b/src/script/lua_api/l_util.cpp index 95a5fc4d1..13c0d702f 100644 --- a/src/script/lua_api/l_util.cpp +++ b/src/script/lua_api/l_util.cpp @@ -23,7 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "common/c_content.h" #include "cpp_api/s_async.h" #include "serialization.h" -#include "json/json.h" +#include #include "cpp_api/s_security.h" #include "porting.h" #include "debug.h" diff --git a/src/serverlist.cpp b/src/serverlist.cpp index de7962a68..87ca5dc04 100644 --- a/src/serverlist.cpp +++ b/src/serverlist.cpp @@ -29,7 +29,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "porting.h" #include "log.h" #include "network/networkprotocol.h" -#include "json/json.h" +#include #include "convert_json.h" #include "httpfetch.h" #include "util/string.h" diff --git a/src/serverlist.h b/src/serverlist.h index 8ffea44cc..0747c3920 100644 --- a/src/serverlist.h +++ b/src/serverlist.h @@ -20,7 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include "config.h" #include "mods.h" -#include "json/json.h" +#include #ifndef SERVERLIST_HEADER #define SERVERLIST_HEADER -- cgit v1.2.3 From c013c73f338b1c2227662458ebc650883f83271c Mon Sep 17 00:00:00 2001 From: Tomas Date: Wed, 10 Aug 2016 12:17:48 +0200 Subject: Lua->C getintfield() use lua_tointeger (#4408) previously function used tonumber which returned float this caused errors in large numbers and resulted in obj-def-handlers being invalid when retrived from lua tables in c --- src/script/common/c_converter.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/script/common/c_converter.cpp b/src/script/common/c_converter.cpp index 55c4a5f5a..857300fa5 100644 --- a/src/script/common/c_converter.cpp +++ b/src/script/common/c_converter.cpp @@ -391,7 +391,7 @@ bool getintfield(lua_State *L, int table, lua_getfield(L, table, fieldname); bool got = false; if(lua_isnumber(L, -1)){ - result = lua_tonumber(L, -1); + result = lua_tointeger(L, -1); got = true; } lua_pop(L, 1); @@ -404,7 +404,7 @@ bool getintfield(lua_State *L, int table, lua_getfield(L, table, fieldname); bool got = false; if(lua_isnumber(L, -1)){ - result = lua_tonumber(L, -1); + result = lua_tointeger(L, -1); got = true; } lua_pop(L, 1); @@ -417,7 +417,7 @@ bool getintfield(lua_State *L, int table, lua_getfield(L, table, fieldname); bool got = false; if(lua_isnumber(L, -1)){ - result = lua_tonumber(L, -1); + result = lua_tointeger(L, -1); got = true; } lua_pop(L, 1); @@ -430,7 +430,7 @@ bool getintfield(lua_State *L, int table, lua_getfield(L, table, fieldname); bool got = false; if(lua_isnumber(L, -1)){ - result = lua_tonumber(L, -1); + result = lua_tointeger(L, -1); got = true; } lua_pop(L, 1); -- cgit v1.2.3 From 2c31b79235dd83de753fce5890c5797e149048b8 Mon Sep 17 00:00:00 2001 From: "Esteban I. Ruiz Moreno" Date: Sun, 31 Mar 2013 00:30:32 -0300 Subject: Add zoom, tweakable with zoom_fov, default key: Z (like optifine) --- src/camera.cpp | 10 ++++++++-- src/camera.h | 1 + src/client/keys.h | 1 + src/defaultsettings.cpp | 2 ++ src/game.cpp | 2 ++ src/guiKeyChangeMenu.cpp | 2 ++ src/player.h | 4 ++++ 7 files changed, 20 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/camera.cpp b/src/camera.cpp index 6893b8cbf..e1d6dd910 100644 --- a/src/camera.cpp +++ b/src/camera.cpp @@ -103,6 +103,7 @@ Camera::Camera(scene::ISceneManager* smgr, MapDrawControl& draw_control, m_cache_fall_bobbing_amount = g_settings->getFloat("fall_bobbing_amount"); m_cache_view_bobbing_amount = g_settings->getFloat("view_bobbing_amount"); m_cache_fov = g_settings->getFloat("fov"); + m_cache_zoom_fov = g_settings->getFloat("zoom_fov"); m_cache_view_bobbing = g_settings->getBool("view_bobbing"); m_nametags.clear(); } @@ -387,8 +388,13 @@ void Camera::update(LocalPlayer* player, f32 frametime, f32 busytime, if (m_camera_mode == CAMERA_MODE_THIRD_FRONT) m_camera_position = my_cp; - // Get FOV setting - f32 fov_degrees = m_cache_fov; + // Get FOV + f32 fov_degrees; + if (player->getPlayerControl().zoom) { + fov_degrees = m_cache_zoom_fov; + } else { + fov_degrees = m_cache_fov; + } fov_degrees = MYMAX(fov_degrees, 10.0); fov_degrees = MYMIN(fov_degrees, 170.0); diff --git a/src/camera.h b/src/camera.h index ce46c3190..cb0e9686d 100644 --- a/src/camera.h +++ b/src/camera.h @@ -231,6 +231,7 @@ private: f32 m_cache_fall_bobbing_amount; f32 m_cache_view_bobbing_amount; f32 m_cache_fov; + f32 m_cache_zoom_fov; bool m_cache_view_bobbing; std::list m_nametags; diff --git a/src/client/keys.h b/src/client/keys.h index 0921bc166..6467c443e 100644 --- a/src/client/keys.h +++ b/src/client/keys.h @@ -59,6 +59,7 @@ public: INCREASE_VIEWING_RANGE, DECREASE_VIEWING_RANGE, RANGESELECT, + ZOOM, QUICKTUNE_NEXT, QUICKTUNE_PREV, diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index 3220b915a..42b232afc 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -40,6 +40,7 @@ void set_default_settings(Settings *settings) settings->setDefault("keymap_jump", "KEY_SPACE"); settings->setDefault("keymap_sneak", "KEY_LSHIFT"); settings->setDefault("keymap_drop", "KEY_KEY_Q"); + settings->setDefault("keymap_zoom", "KEY_KEY_Z"); settings->setDefault("keymap_inventory", "KEY_KEY_I"); settings->setDefault("keymap_special1", "KEY_KEY_E"); settings->setDefault("keymap_chat", "KEY_KEY_T"); @@ -74,6 +75,7 @@ void set_default_settings(Settings *settings) settings->setDefault("always_fly_fast", "true"); settings->setDefault("directional_colored_fog", "true"); settings->setDefault("tooltip_show_delay", "400"); + settings->setDefault("zoom_fov", "15"); // Some (temporary) keys for debugging settings->setDefault("keymap_print_debug_stacks", "KEY_KEY_P"); diff --git a/src/game.cpp b/src/game.cpp index 9b9f3a75f..1a036d03a 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -1371,6 +1371,7 @@ void KeyCache::populate() = getKeySetting("keymap_decrease_viewing_range_min"); key[KeyType::RANGESELECT] = getKeySetting("keymap_rangeselect"); + key[KeyType::ZOOM] = getKeySetting("keymap_zoom"); key[KeyType::QUICKTUNE_NEXT] = getKeySetting("keymap_quicktune_next"); key[KeyType::QUICKTUNE_PREV] = getKeySetting("keymap_quicktune_prev"); @@ -3270,6 +3271,7 @@ void Game::updatePlayerControl(const CameraOrientation &cam) isKeyDown(KeyType::JUMP), isKeyDown(KeyType::SPECIAL1), isKeyDown(KeyType::SNEAK), + isKeyDown(KeyType::ZOOM), isLeftPressed(), isRightPressed(), cam.camera_pitch, diff --git a/src/guiKeyChangeMenu.cpp b/src/guiKeyChangeMenu.cpp index 785e921f7..07137d1bc 100644 --- a/src/guiKeyChangeMenu.cpp +++ b/src/guiKeyChangeMenu.cpp @@ -59,6 +59,7 @@ enum GUI_ID_KEY_INVENTORY_BUTTON, GUI_ID_KEY_DUMP_BUTTON, GUI_ID_KEY_RANGE_BUTTON, + GUI_ID_KEY_ZOOM_BUTTON, // other GUI_ID_CB_AUX1_DESCENDS, GUI_ID_CB_DOUBLETAP_JUMP, @@ -414,5 +415,6 @@ void GUIKeyChangeMenu::init_keys() this->add_key(GUI_ID_KEY_NOCLIP_BUTTON, wgettext("Toggle noclip"), "keymap_noclip"); this->add_key(GUI_ID_KEY_RANGE_BUTTON, wgettext("Range select"), "keymap_rangeselect"); this->add_key(GUI_ID_KEY_DUMP_BUTTON, wgettext("Print stacks"), "keymap_print_debug_stacks"); + this->add_key(GUI_ID_KEY_ZOOM_BUTTON, wgettext("Zoom"), "keymap_zoom"); } diff --git a/src/player.h b/src/player.h index e6fcf388a..eab00bb04 100644 --- a/src/player.h +++ b/src/player.h @@ -49,6 +49,7 @@ struct PlayerControl sidew_move_joystick_axis = .0f; forw_move_joystick_axis = .0f; } + PlayerControl( bool a_up, bool a_down, @@ -57,6 +58,7 @@ struct PlayerControl bool a_jump, bool a_aux1, bool a_sneak, + bool a_zoom, bool a_LMB, bool a_RMB, float a_pitch, @@ -72,6 +74,7 @@ struct PlayerControl jump = a_jump; aux1 = a_aux1; sneak = a_sneak; + zoom = a_zoom; LMB = a_LMB; RMB = a_RMB; pitch = a_pitch; @@ -86,6 +89,7 @@ struct PlayerControl bool jump; bool aux1; bool sneak; + bool zoom; bool LMB; bool RMB; float pitch; -- cgit v1.2.3 From 82494b8cc199fb92248c6c30ce7ef18b7e7133ae Mon Sep 17 00:00:00 2001 From: Ben Deutsch Date: Tue, 2 Aug 2016 21:36:34 +0200 Subject: Zooming the camera requires the 'zoom' privilege --- builtin/game/privileges.lua | 4 ++++ src/camera.cpp | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/builtin/game/privileges.lua b/builtin/game/privileges.lua index 5838b9374..05c79440c 100644 --- a/builtin/game/privileges.lua +++ b/builtin/game/privileges.lua @@ -54,4 +54,8 @@ core.register_privilege("noclip", { give_to_singleplayer = false, }) core.register_privilege("rollback", "Can use the rollback functionality") +core.register_privilege("zoom", { + description = "Can zoom the camera", + give_to_singleplayer = false, +}) diff --git a/src/camera.cpp b/src/camera.cpp index e1d6dd910..e1fec4a3e 100644 --- a/src/camera.cpp +++ b/src/camera.cpp @@ -390,7 +390,7 @@ void Camera::update(LocalPlayer* player, f32 frametime, f32 busytime, // Get FOV f32 fov_degrees; - if (player->getPlayerControl().zoom) { + if (player->getPlayerControl().zoom && m_gamedef->checkLocalPrivilege("zoom")) { fov_degrees = m_cache_zoom_fov; } else { fov_degrees = m_cache_fov; -- cgit v1.2.3 From 48b3bb980d4a026d32739acc1982f16e3c303c5b Mon Sep 17 00:00:00 2001 From: David Carlier Date: Thu, 28 Jul 2016 08:56:22 +0100 Subject: couple of memory leaks fixes. --- src/script/cpp_api/s_security.cpp | 18 +++++++++++++++++- src/sound_openal.cpp | 1 + src/unittest/test_map_settings_manager.cpp | 4 +++- src/util/areastore.cpp | 1 + src/util/srp.cpp | 2 +- 5 files changed, 23 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/script/cpp_api/s_security.cpp b/src/script/cpp_api/s_security.cpp index e117a4811..0e64c4c61 100644 --- a/src/script/cpp_api/s_security.cpp +++ b/src/script/cpp_api/s_security.cpp @@ -285,6 +285,10 @@ bool ScriptApiSecurity::safeLoadFile(lua_State *L, const char *path) if (c == LUA_SIGNATURE[0]) { lua_pushliteral(L, "Bytecode prohibited when mod security is enabled."); + std::fclose(fp); + if (path) { + delete [] chunk_name; + } return false; } @@ -295,7 +299,15 @@ bool ScriptApiSecurity::safeLoadFile(lua_State *L, const char *path) size_t size = std::ftell(fp) - start; char *code = new char[size]; ret = std::fseek(fp, start, SEEK_SET); - CHECK_FILE_ERR(ret, fp); + if (ret) { + lua_pushfstring(L, "%s: %s", path, strerror(errno)); + std::fclose(fp); + delete [] code; + if (path) { + delete [] chunk_name; + } + return false; + } size_t num_read = std::fread(code, 1, size, fp); if (path) { @@ -303,6 +315,10 @@ bool ScriptApiSecurity::safeLoadFile(lua_State *L, const char *path) } if (num_read != size) { lua_pushliteral(L, "Error reading file to load."); + delete [] code; + if (path) { + delete [] chunk_name; + } return false; } diff --git a/src/sound_openal.cpp b/src/sound_openal.cpp index e2b6d937a..1832a0c77 100644 --- a/src/sound_openal.cpp +++ b/src/sound_openal.cpp @@ -144,6 +144,7 @@ SoundBuffer *load_opened_ogg_file(OggVorbis_File *oggFile, ov_clear(oggFile); infostream << "Audio: Error decoding " << filename_for_logging << std::endl; + delete snd; return NULL; } diff --git a/src/unittest/test_map_settings_manager.cpp b/src/unittest/test_map_settings_manager.cpp index 9292bf87c..4f5ac80f2 100644 --- a/src/unittest/test_map_settings_manager.cpp +++ b/src/unittest/test_map_settings_manager.cpp @@ -75,8 +75,10 @@ std::string read_file_to_string(const std::string &filepath) fseek(f, 0, SEEK_END); long filesize = ftell(f); - if (filesize == -1) + if (filesize == -1) { + fclose(f); return ""; + } rewind(f); buf.resize(filesize); diff --git a/src/util/areastore.cpp b/src/util/areastore.cpp index 58f08a8c2..cef67da2c 100644 --- a/src/util/areastore.cpp +++ b/src/util/areastore.cpp @@ -95,6 +95,7 @@ void AreaStore::deserialize(std::istream &is) is.read(data, data_len); a.data = std::string(data, data_len); insertArea(&a); + delete [] data; } } diff --git a/src/util/srp.cpp b/src/util/srp.cpp index 0d3c938a3..77c1816e8 100644 --- a/src/util/srp.cpp +++ b/src/util/srp.cpp @@ -542,7 +542,7 @@ static SRP_Result fill_buff() if (!fp) return SRP_ERR; - if (fread(g_rand_buff, sizeof(g_rand_buff), 1, fp) != 1) return SRP_ERR; + if (fread(g_rand_buff, sizeof(g_rand_buff), 1, fp) != 1) { fclose(fp); return SRP_ERR; } if (fclose(fp)) return SRP_ERR; #endif return SRP_OK; -- cgit v1.2.3 From 35f47e5461042b3d190f8ea583aec2aa26f1c484 Mon Sep 17 00:00:00 2001 From: Rogier-5 Date: Thu, 11 Aug 2016 00:22:32 +0200 Subject: Add infotext containing entity type (e.g. mobs:cow) to unknown entities (#4431) If a mod is disabled, or upgraded without sufficient backward compatibility, then entities it has put into the world become unknown, and continue moving around, but are completely unrecognisable. This change allows the player to see their type, and therefore which mod is or was responsible. --- src/content_sao.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/content_sao.cpp b/src/content_sao.cpp index 53bf3154f..937363b76 100644 --- a/src/content_sao.cpp +++ b/src/content_sao.cpp @@ -175,6 +175,8 @@ void LuaEntitySAO::addedToEnvironment(u32 dtime_s) // Activate entity, supplying serialized state m_env->getScriptIface()-> luaentity_Activate(m_id, m_init_state.c_str(), dtime_s); + } else { + m_prop.infotext = m_init_name; } } -- cgit v1.2.3 From b11720af459d44a553cb5d23ef776a632fb30a65 Mon Sep 17 00:00:00 2001 From: Rogier-5 Date: Thu, 11 Aug 2016 19:22:40 +0200 Subject: Use the standard to_string() functions for C++11 (#4279) If compiling according to a C++ version before C++11, then define std::to_string ourselves. Add a to_wstring version as well As std::to_string() for floating point types uses %.6f as floating point format converter, instead of %G, it needs special care. To preserve ftos() behavior (which is expected to use the %G format converter), it no longer uses to_string(). --- src/client.cpp | 2 +- src/util/string.h | 47 +++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 42 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/client.cpp b/src/client.cpp index 4ffcec6ba..483b22caa 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -820,7 +820,7 @@ void Client::initLocalMapSaving(const Address &address, const std::string world_path = porting::path_user + DIR_DELIM + "worlds" + DIR_DELIM + "server_" - + hostname + "_" + to_string(address.getPort()); + + hostname + "_" + std::to_string(address.getPort()); fs::CreateAllDirs(world_path); diff --git a/src/util/string.h b/src/util/string.h index 8f4ef4711..724543a36 100644 --- a/src/util/string.h +++ b/src/util/string.h @@ -27,6 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include #include +#include #include #define STRINGIFY(x) #x @@ -350,23 +351,57 @@ inline T from_string(const std::string &str) /// Returns a 64-bit signed value represented by the string \p str (decimal). inline s64 stoi64(const std::string &str) { return from_string(str); } -// TODO: Replace with C++11 std::to_string. +#if __cplusplus < 201103L +namespace std { /// Returns a string representing the value \p val. template -inline std::string to_string(T val) +inline string to_string(T val) { - std::ostringstream oss; + ostringstream oss; oss << val; return oss.str(); } +#define DEFINE_STD_TOSTRING_FLOATINGPOINT(T) \ + template <> \ + inline string to_string(T val) \ + { \ + ostringstream oss; \ + oss << std::fixed \ + << std::setprecision(6) \ + << val; \ + return oss.str(); \ + } +DEFINE_STD_TOSTRING_FLOATINGPOINT(float) +DEFINE_STD_TOSTRING_FLOATINGPOINT(double) +DEFINE_STD_TOSTRING_FLOATINGPOINT(long double) + +#undef DEFINE_STD_TOSTRING_FLOATINGPOINT + +/// Returns a wide string representing the value \p val +template +inline wstring to_wstring(T val) +{ + return utf8_to_wide(to_string(val)); +} +} +#endif /// Returns a string representing the decimal value of the 32-bit value \p i. -inline std::string itos(s32 i) { return to_string(i); } +inline std::string itos(s32 i) { return std::to_string(i); } /// Returns a string representing the decimal value of the 64-bit value \p i. -inline std::string i64tos(s64 i) { return to_string(i); } +inline std::string i64tos(s64 i) { return std::to_string(i); } + +// std::to_string uses the '%.6f' conversion, which is inconsistent with +// std::ostream::operator<<() and impractical too. ftos() uses the +// more generic and std::ostream::operator<<()-compatible '%G' format. /// Returns a string representing the decimal value of the float value \p f. -inline std::string ftos(float f) { return to_string(f); } +inline std::string ftos(float f) +{ + std::ostringstream oss; + oss << f; + return oss.str(); +} /** -- cgit v1.2.3 From f21dae63390aa872062bcfc96fc6817b0fcfe601 Mon Sep 17 00:00:00 2001 From: Thomas--S Date: Sat, 2 Jul 2016 17:58:08 +0200 Subject: Add an [opacity: texture modifier. Makes the base image transparent according to the given ratio. r must be between 0 and 255. 0 means totally transparent. 255 means totally opaque. Useful for texture overlaying. --- doc/lua_api.txt | 10 ++++++++++ src/client/tile.cpp | 30 ++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) (limited to 'src') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index ce40e082c..182f5c821 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -292,6 +292,16 @@ Example: default_sandstone.png^[resize:16x16 +#### `[opacity:` + Makes the base image transparent according to the given ratio. + r must be between 0 and 255. + 0 means totally transparent. + 255 means totally opaque. + +Example: + + default_sandstone.png^[opacity:127 + #### `[brighten` Brightens the texture. diff --git a/src/client/tile.cpp b/src/client/tile.cpp index ec8c95f02..3b5d2a3ae 100644 --- a/src/client/tile.cpp +++ b/src/client/tile.cpp @@ -1735,6 +1735,36 @@ bool TextureSource::generateImagePart(std::string part_of_name, baseimg->drop(); baseimg = image; } + /* + [opacity:R + Makes the base image transparent according to the given ratio. + R must be between 0 and 255. + 0 means totally transparent. + 255 means totally opaque. + */ + else if (str_starts_with(part_of_name, "[opacity:")) { + if (baseimg == NULL) { + errorstream << "generateImagePart(): baseimg == NULL " + << "for part_of_name=\"" << part_of_name + << "\", cancelling." << std::endl; + return false; + } + + Strfnd sf(part_of_name); + sf.next(":"); + + u32 ratio = mystoi(sf.next(""), 0, 255); + + core::dimension2d dim = baseimg->getDimension(); + + for (u32 y = 0; y < dim.Height; y++) + for (u32 x = 0; x < dim.Width; x++) + { + video::SColor c = baseimg->getPixel(x,y); + c.setAlpha(floor((c.getAlpha() * ratio) / 255 + 0.5)); + baseimg->setPixel(x,y,c); + } + } else { errorstream << "generateImagePart(): Invalid " -- cgit v1.2.3 From 5c0e659516857733d968707490b16bcf62f33c92 Mon Sep 17 00:00:00 2001 From: SmallJoker Date: Sat, 30 Jul 2016 12:50:22 +0200 Subject: Script API: Make the craft recipe field 'method' consistent --- src/script/lua_api/l_craft.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/script/lua_api/l_craft.cpp b/src/script/lua_api/l_craft.cpp index d135c689f..2236566de 100644 --- a/src/script/lua_api/l_craft.cpp +++ b/src/script/lua_api/l_craft.cpp @@ -422,20 +422,28 @@ static void push_craft_recipe(lua_State *L, IGameDef *gdef, } lua_setfield(L, -2, "items"); setintfield(L, -1, "width", input.width); + + std::string method_s; switch (input.method) { case CRAFT_METHOD_NORMAL: - lua_pushstring(L, "normal"); + method_s = "normal"; break; case CRAFT_METHOD_COOKING: - lua_pushstring(L, "cooking"); + method_s = "cooking"; break; case CRAFT_METHOD_FUEL: - lua_pushstring(L, "fuel"); + method_s = "fuel"; break; default: - lua_pushstring(L, "unknown"); + method_s = "unknown"; } + lua_pushstring(L, method_s.c_str()); + lua_setfield(L, -2, "method"); + + // Deprecated, only for compatibility's sake + lua_pushstring(L, method_s.c_str()); lua_setfield(L, -2, "type"); + lua_pushstring(L, output.item.c_str()); lua_setfield(L, -2, "output"); } -- cgit v1.2.3 From 7f4c6f32da3cd0c5f1367af63f084775131f2ecc Mon Sep 17 00:00:00 2001 From: obneq Date: Fri, 12 Aug 2016 19:11:56 +0200 Subject: Mapblock mesh: Fix updateFastFaceRow tiling issue Increase maximum length of tiled node rows from 2 to mapblock size. --- src/mapblock_mesh.cpp | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) (limited to 'src') diff --git a/src/mapblock_mesh.cpp b/src/mapblock_mesh.cpp index e1b044271..a11fb5887 100644 --- a/src/mapblock_mesh.cpp +++ b/src/mapblock_mesh.cpp @@ -839,7 +839,7 @@ static void updateFastFaceRow( { v3s16 p = startpos; - u16 continuous_tiles_count = 0; + u16 continuous_tiles_count = 1; bool makes_face = false; v3s16 p_corrected; @@ -889,8 +889,8 @@ static void updateFastFaceRow( && (tile.material_flags & MATERIAL_FLAG_TILEABLE_HORIZONTAL) && (tile.material_flags & MATERIAL_FLAG_TILEABLE_VERTICAL)) { next_is_different = false; - } - else{ + continuous_tiles_count++; + } else { /*if(makes_face){ g_profiler->add("Meshgen: diff: next_makes_face != makes_face", next_makes_face != makes_face ? 1 : 0); @@ -915,8 +915,6 @@ static void updateFastFaceRow( g_profiler->add("Meshgen: diff: last position", 1);*/ } - continuous_tiles_count++; - if(next_is_different) { /* @@ -928,8 +926,6 @@ static void updateFastFaceRow( v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z); // Center point of face (kind of) v3f sp = pf - ((f32)continuous_tiles_count / 2.0 - 0.5) * translate_dir_f; - if(continuous_tiles_count != 1) - sp += translate_dir_f; v3f scale(1,1,1); if(translate_dir.X != 0) { @@ -952,19 +948,18 @@ static void updateFastFaceRow( } } - continuous_tiles_count = 0; - - makes_face = next_makes_face; - p_corrected = next_p_corrected; - face_dir_corrected = next_face_dir_corrected; - lights[0] = next_lights[0]; - lights[1] = next_lights[1]; - lights[2] = next_lights[2]; - lights[3] = next_lights[3]; - tile = next_tile; - light_source = next_light_source; + continuous_tiles_count = 1; } + makes_face = next_makes_face; + p_corrected = next_p_corrected; + face_dir_corrected = next_face_dir_corrected; + lights[0] = next_lights[0]; + lights[1] = next_lights[1]; + lights[2] = next_lights[2]; + lights[3] = next_lights[3]; + tile = next_tile; + light_source = next_light_source; p = p_next; } } -- cgit v1.2.3 From 6590140260d0338ab5a0fd2bbcd4c72f07dd963d Mon Sep 17 00:00:00 2001 From: Tim Date: Wed, 27 Jul 2016 18:46:02 +0200 Subject: Tidy up generate_from_settingtypes.lua a bit. * Multiline strings * Table-concat instead of String-concats * string.rep instead of loop-concat * string.format %q instead of manual quotation by gsub * Assert writeable files * Generate new settings_translation_file --- builtin/mainmenu/generate_from_settingtypes.lua | 112 ++++++++++++------------ src/settings_translation_file.cpp | 2 +- 2 files changed, 57 insertions(+), 57 deletions(-) (limited to 'src') diff --git a/builtin/mainmenu/generate_from_settingtypes.lua b/builtin/mainmenu/generate_from_settingtypes.lua index 62d11b29b..6c9ba27fb 100644 --- a/builtin/mainmenu/generate_from_settingtypes.lua +++ b/builtin/mainmenu/generate_from_settingtypes.lua @@ -1,99 +1,99 @@ local settings = ... -local function create_minetest_conf_example() - local result = "# This file contains a list of all available settings and their default value for minetest.conf\n" .. - "\n" .. - "# By default, all the settings are commented and not functional.\n" .. - "# Uncomment settings by removing the preceding #.\n" .. - "\n" .. - "# minetest.conf is read by default from:\n" .. - "# ../minetest.conf\n" .. - "# ../../minetest.conf\n" .. - "# Any other path can be chosen by passing the path as a parameter\n" .. - "# to the program, eg. \"minetest.exe --config ../minetest.conf.example\".\n" .. - "\n" .. - "# Further documentation:\n" .. - "# http://wiki.minetest.net/\n" .. - "\n" +local concat = table.concat +local insert = table.insert +local sprintf = string.format +local rep = string.rep + +local minetest_example_header = [[ +# This file contains a list of all available settings and their default value for minetest.conf + +# By default, all the settings are commented and not functional. +# Uncomment settings by removing the preceding #. + +# minetest.conf is read by default from: +# ../minetest.conf +# ../../minetest.conf +# Any other path can be chosen by passing the path as a parameter +# to the program, eg. "minetest.exe --config ../minetest.conf.example". +# Further documentation: +# http://wiki.minetest.net/ + +]] + +local function create_minetest_conf_example() + local result = { minetest_example_header } for _, entry in ipairs(settings) do if entry.type == "category" then if entry.level == 0 then - result = result .. "#\n# " .. entry.name .. "\n#\n\n" + insert(result, "#\n# " .. entry.name .. "\n#\n\n") else - for i = 1, entry.level do - result = result .. "#" - end - result = result .. "# " .. entry.name .. "\n\n" + insert(result, rep("#", entry.level)) + insert(result, "# " .. entry.name .. "\n\n") end else if entry.comment ~= "" then for _, comment_line in ipairs(entry.comment:split("\n", true)) do - result = result .."# " .. comment_line .. "\n" + insert(result, "# " .. comment_line .. "\n") end end - result = result .. "# type: " .. entry.type + insert(result, "# type: " .. entry.type) if entry.min then - result = result .. " min: " .. entry.min + insert(result, " min: " .. entry.min) end if entry.max then - result = result .. " max: " .. entry.max + insert(result, " max: " .. entry.max) end if entry.values then - result = result .. " values: " .. table.concat(entry.values, ", ") + insert(result, " values: " .. concat(entry.values, ", ")) end if entry.possible then - result = result .. " possible values: " .. entry.possible:gsub(",", ", ") + insert(result, " possible values: " .. entry.possible:gsub(",", ", ")) end - result = result .. "\n" - local append = "" + insert(result, "\n") + local append if entry.default ~= "" then append = " " .. entry.default end - result = result .. "# " .. entry.name .. " =" .. append .. "\n\n" + insert(result, sprintf("# %s =%s\n\n", entry.name, append or "")) end end - return result + return concat(result) end -local function create_translation_file() - local result = "// This file is automatically generated\n" .. - "// It conatins a bunch of fake gettext calls, to tell xgettext about the strings in config files\n" .. - "// To update it, refer to the bottom of builtin/mainmenu/tab_settings.lua\n\n" .. - "fake_function() {\n" +local translation_file_header = [[ +// This file is automatically generated +// It conatins a bunch of fake gettext calls, to tell xgettext about the strings in config files +// To update it, refer to the bottom of builtin/mainmenu/dlg_settings_advanced.lua + +fake_function() {]] +local function create_translation_file() + local result = { translation_file_header } for _, entry in ipairs(settings) do if entry.type == "category" then - local name_escaped = entry.name:gsub("\"", "\\\"") - result = result .. "\tgettext(\"" .. name_escaped .. "\");\n" + insert(result, sprintf("\tgettext(%q);", entry.name)) else if entry.readable_name then - local readable_name_escaped = entry.readable_name:gsub("\"", "\\\"") - result = result .. "\tgettext(\"" .. readable_name_escaped .. "\");\n" + insert(result, sprintf("\tgettext(%q);", entry.readable_name)) end if entry.comment ~= "" then local comment_escaped = entry.comment:gsub("\n", "\\n") comment_escaped = comment_escaped:gsub("\"", "\\\"") - result = result .. "\tgettext(\"" .. comment_escaped .. "\");\n" + insert(result, "\tgettext(\"" .. comment_escaped .. "\");") end end end - result = result .. "}\n" - return result + insert(result, "}\n") + return concat(result, "\n") end -if false then - local file = io.open("minetest.conf.example", "w") - if file then - file:write(create_minetest_conf_example()) - file:close() - end -end +local file = assert(io.open("minetest.conf.example", "w")) +file:write(create_minetest_conf_example()) +file:close() + +file = assert(io.open("src/settings_translation_file.cpp", "w")) +file:write(create_translation_file()) +file:close() -if false then - local file = io.open("src/settings_translation_file.cpp", "w") - if file then - file:write(create_translation_file()) - file:close() - end -end \ No newline at end of file diff --git a/src/settings_translation_file.cpp b/src/settings_translation_file.cpp index 3e82279cd..10e38e1a7 100644 --- a/src/settings_translation_file.cpp +++ b/src/settings_translation_file.cpp @@ -1,6 +1,6 @@ // This file is automatically generated // It conatins a bunch of fake gettext calls, to tell xgettext about the strings in config files -// To update it, refer to the bottom of builtin/mainmenu/tab_settings.lua +// To update it, refer to the bottom of builtin/mainmenu/dlg_settings_advanced.lua fake_function() { gettext("Client"); -- cgit v1.2.3 From f092dac9793b80c29a669b0d676ee3e4f55f682e Mon Sep 17 00:00:00 2001 From: est31 Date: Sat, 20 Aug 2016 21:26:44 +0200 Subject: Also support X11 icon for minetest copies installed via make install (#4407) Fixes #4323. --- CMakeLists.txt | 3 +++ src/client/clientlauncher.cpp | 3 +-- src/cmake_config.h.in | 1 + src/porting.cpp | 19 ++++++++++++++++++- src/porting.h | 4 +++- 5 files changed, 26 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/CMakeLists.txt b/CMakeLists.txt index 592feb997..fbf6bb7fa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -175,6 +175,9 @@ if(UNIX AND NOT APPLE) install(FILES "misc/minetest.desktop" DESTINATION "${XDG_APPS_DIR}") install(FILES "misc/minetest.appdata.xml" DESTINATION "${APPDATADIR}") install(FILES "misc/minetest.svg" DESTINATION "${ICONDIR}/hicolor/scalable/apps") + install(FILES "misc/minetest-xorg-icon-128.png" + DESTINATION "${ICONDIR}/hicolor/128x128/apps" + RENAME "minetest.png") endif() if(APPLE) diff --git a/src/client/clientlauncher.cpp b/src/client/clientlauncher.cpp index aa3c2d548..6145e3dde 100644 --- a/src/client/clientlauncher.cpp +++ b/src/client/clientlauncher.cpp @@ -114,8 +114,7 @@ bool ClientLauncher::run(GameParams &game_params, const Settings &cmd_args) porting::setXorgClassHint(video_driver->getExposedVideoData(), PROJECT_NAME_C); - porting::setXorgWindowIcon(device, - porting::path_share + "/misc/minetest-xorg-icon-128.png"); + porting::setXorgWindowIcon(device); /* This changes the minimum allowed number of vertices in a VBO. diff --git a/src/cmake_config.h.in b/src/cmake_config.h.in index 50f34a0b8..4b731020a 100644 --- a/src/cmake_config.h.in +++ b/src/cmake_config.h.in @@ -14,6 +14,7 @@ #define STATIC_SHAREDIR "@SHAREDIR@" #define STATIC_LOCALEDIR "@LOCALEDIR@" #define BUILD_TYPE "@CMAKE_BUILD_TYPE@" +#define ICON_DIR "@ICONDIR@" #cmakedefine01 RUN_IN_PLACE #cmakedefine01 USE_GETTEXT #cmakedefine01 USE_CURL diff --git a/src/porting.cpp b/src/porting.cpp index acd047232..ae9114ac8 100644 --- a/src/porting.cpp +++ b/src/porting.cpp @@ -611,7 +611,24 @@ void setXorgClassHint(const video::SExposedVideoData &video_data, #endif } -bool setXorgWindowIcon(IrrlichtDevice *device, +bool setXorgWindowIcon(IrrlichtDevice *device) +{ +#if RUN_IN_PLACE + return setXorgWindowIconFromPath(device, + path_share + "/misc/" PROJECT_NAME "-xorg-icon-128.png"); +#else + // We have semi-support for reading in-place data if we are + // compiled with RUN_IN_PLACE. Don't break with this and + // also try the path_share location. + return + setXorgWindowIconFromPath(device, + ICON_DIR "/hicolor/128x128/apps/" PROJECT_NAME ".png") || + setXorgWindowIconFromPath(device, + path_share + "/misc/" PROJECT_NAME "-xorg-icon-128.png"); +#endif +} + +bool setXorgWindowIconFromPath(IrrlichtDevice *device, const std::string &icon_file) { #ifdef XORG_USED diff --git a/src/porting.h b/src/porting.h index 40f6b4dc3..f5c7efcb2 100644 --- a/src/porting.h +++ b/src/porting.h @@ -367,7 +367,9 @@ inline const char *getPlatformName() void setXorgClassHint(const video::SExposedVideoData &video_data, const std::string &name); -bool setXorgWindowIcon(IrrlichtDevice *device, +bool setXorgWindowIcon(IrrlichtDevice *device); + +bool setXorgWindowIconFromPath(IrrlichtDevice *device, const std::string &icon_file); // This only needs to be called at the start of execution, since all future -- cgit v1.2.3 From cafa400fa5077e7cb9c627d79482941cb82a81cf Mon Sep 17 00:00:00 2001 From: kahrl Date: Sun, 21 Aug 2016 02:40:23 +0200 Subject: gettext.h: include before defining the gettext macro (#4452) Fixes #4446: a syntax error that happens if something else (such as or in certain libstdc++ versions) includes later, which contains a function declaration for gettext that gets mangled by the macro. See the added comment in gettext.h and the discussion in #4446/#4452 for details. --- src/gettext.h | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'src') diff --git a/src/gettext.h b/src/gettext.h index b8d27f77c..885d7ca2d 100644 --- a/src/gettext.h +++ b/src/gettext.h @@ -25,6 +25,16 @@ with this program; if not, write to the Free Software Foundation, Inc., #if USE_GETTEXT #include #else + // In certain environments, some standard headers like + // and include libintl.h. If libintl.h is included after + // we define our gettext macro below, this causes a syntax error + // at the declaration of the gettext function in libintl.h. + // Fix this by including such a header before defining the macro. + // See issue #4446. + // Note that we can't include libintl.h directly since we're in + // the USE_GETTEXT=0 case and can't assume that gettext is installed. + #include + #define gettext(String) String #endif -- cgit v1.2.3 From 0b0075e6ad0b3110cabdfc92cedb0a24d2b5ec42 Mon Sep 17 00:00:00 2001 From: Xunto Date: Mon, 22 Aug 2016 22:21:48 +0400 Subject: Move on join and on leave messages to lua (#4460) --- builtin/game/misc.lua | 14 +++++++++++--- src/server.cpp | 30 ------------------------------ 2 files changed, 11 insertions(+), 33 deletions(-) (limited to 'src') diff --git a/builtin/game/misc.lua b/builtin/game/misc.lua index 4773e0012..d7164812a 100644 --- a/builtin/game/misc.lua +++ b/builtin/game/misc.lua @@ -87,11 +87,19 @@ end local player_list = {} core.register_on_joinplayer(function(player) - player_list[player:get_player_name()] = player + local player_name = player:get_player_name() + player_list[player_name] = player + core.chat_send_all("*** " .. player_name .. " joined the game.") end) -core.register_on_leaveplayer(function(player) - player_list[player:get_player_name()] = nil +core.register_on_leaveplayer(function(player, timed_out) + local player_name = player:get_player_name() + player_list[player_name] = nil + local announcement = "*** " .. player_name .. " left the game." + if timed_out then + announcement = announcement .. " (timed out)" + end + core.chat_send_all(announcement) end) function core.get_connected_players() diff --git a/src/server.cpp b/src/server.cpp index 97a53f189..fb241f179 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -1118,23 +1118,6 @@ PlayerSAO* Server::StageTwoClientInit(u16 peer_id) if(!m_simple_singleplayer_mode) { // Send information about server to player in chat SendChatMessage(peer_id, getStatusString()); - - // Send information about joining in chat - { - std::string name = "unknown"; - Player *player = m_env->getPlayer(peer_id); - if(player != NULL) - name = player->getName(); - - std::wstring message; - message += L"*** "; - message += narrow_to_wide(name); - message += L" joined the game."; - SendChatMessage(PEER_ID_INEXISTENT,message); - if (m_admin_chat) - m_admin_chat->outgoing_queue.push_back( - new ChatEventNick(CET_NICK_ADD, name)); - } } Address addr = getPeerAddress(player->peer_id); std::string ip_str = addr.serializeString(); @@ -2660,19 +2643,6 @@ void Server::DeleteClient(u16 peer_id, ClientDeletionReason reason) Player *player = m_env->getPlayer(peer_id); - // Collect information about leaving in chat - { - if(player != NULL && reason != CDR_DENY) - { - std::wstring name = narrow_to_wide(player->getName()); - message += L"*** "; - message += name; - message += L" left the game."; - if(reason == CDR_TIMEOUT) - message += L" (timed out)"; - } - } - /* Run scripts and remove from environment */ { if(player != NULL) -- cgit v1.2.3 From d767f025cb0d5cca29c1f2147d2a0931a088b717 Mon Sep 17 00:00:00 2001 From: est31 Date: Mon, 22 Aug 2016 04:27:44 +0200 Subject: Client: disable pre v25 init sending by default Disable the ability to connect to old servers by default to improve password security. If people still want to connect to old (0.4.12 and earlier) servers, they can flip the send_pre_v25_init setting. Add the ability to detect if we've tried to connect to a server which only supports the pre v25 init protocol, and show an apropriate error message. Most times the error will already be catched at the serverlist level, the detection mechanism only acts as last resort, because the "Connection timed out" error message that would be shown otherwise would be very confusing. Automatic "fixing" of this condition is not desired, as it would allow for downgrade attacks. As already 161 of the 167 servers on the serverlist support the new srp based auth protocol (> 96%), the breakage should be minimal. Follow up of commit af30183124d40a969040d7de4b3a487feec466e4 "Add option to not send pre v25 init packet" Also change the pessimistic assumption of masterlist server versions to optimistic, in order to avoid buggy behaviour (favourites not in the serverlist would be denied to connect to, etc). --- builtin/mainmenu/common.lua | 10 +++++++--- builtin/settingtypes.txt | 2 +- minetest.conf.example | 4 ++-- src/client.h | 3 +++ src/defaultsettings.cpp | 2 +- src/game.cpp | 21 ++++++++++++++++++++- 6 files changed, 34 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/builtin/mainmenu/common.lua b/builtin/mainmenu/common.lua index 1fd89ff77..da3667828 100644 --- a/builtin/mainmenu/common.lua +++ b/builtin/mainmenu/common.lua @@ -248,14 +248,18 @@ end -------------------------------------------------------------------------------- function is_server_protocol_compat(server_proto_min, server_proto_max) - return min_supp_proto <= (server_proto_max or 24) and max_supp_proto >= (server_proto_min or 13) + if (not server_proto_min) or (not server_proto_max) then + -- There is no info. Assume the best and act as if we would be compatible. + return true + end + return min_supp_proto <= server_proto_max and max_supp_proto >= server_proto_min end -------------------------------------------------------------------------------- function is_server_protocol_compat_or_error(server_proto_min, server_proto_max) if not is_server_protocol_compat(server_proto_min, server_proto_max) then local server_prot_ver_info, client_prot_ver_info - local s_p_min = server_proto_min or 13 - local s_p_max = server_proto_max or 24 + local s_p_min = server_proto_min + local s_p_max = server_proto_max if s_p_min ~= s_p_max then server_prot_ver_info = fgettext_ne("Server supports protocol versions between $1 and $2. ", diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index 864485cce..4d354a7ef 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -247,7 +247,7 @@ remote_port (Remote port) int 30000 1 65535 # Enable if you want to connect to 0.4.12 servers and before. # Servers starting with 0.4.13 will work, 0.4.12-dev servers may work. # Disabling this option will protect your password better. -send_pre_v25_init (Support older servers) bool true +send_pre_v25_init (Support older servers) bool false # Save the map received by the client on disk. enable_local_map_saving (Saving map received from server) bool false diff --git a/minetest.conf.example b/minetest.conf.example index 03a917a39..48c54c320 100644 --- a/minetest.conf.example +++ b/minetest.conf.example @@ -261,7 +261,7 @@ # Servers starting with 0.4.13 will work, 0.4.12-dev servers may work. # Disabling this option will protect your password better. # type: bool -# send_pre_v25_init = true +# send_pre_v25_init = false # Save the map received by the client on disk. # type: bool @@ -1485,7 +1485,7 @@ # profiler.default_report_format = txt # The file path relative to your worldpath in which profiles will be saved to. -# +# # type: string # profiler.report_path = "" diff --git a/src/client.h b/src/client.h index a7eb22ad9..b479062a0 100644 --- a/src/client.h +++ b/src/client.h @@ -499,6 +499,9 @@ public: u8 getProtoVersion() { return m_proto_ver; } + bool connectedToServer() + { return m_con.Connected(); } + float mediaReceiveProgress(); void afterContentReceived(IrrlichtDevice *device); diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index 42b232afc..7c6f7ef3d 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -190,7 +190,7 @@ void set_default_settings(Settings *settings) settings->setDefault("minimap_shape_round", "true"); settings->setDefault("minimap_double_scan_height", "true"); - settings->setDefault("send_pre_v25_init", "true"); + settings->setDefault("send_pre_v25_init", "false"); settings->setDefault("curl_timeout", "5000"); settings->setDefault("curl_parallel_limit", "8"); diff --git a/src/game.cpp b/src/game.cpp index 1a036d03a..5a3b10879 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -2403,7 +2403,26 @@ bool Game::connectToServer(const std::string &playername, wait_time += dtime; // Only time out if we aren't waiting for the server we started if ((*address != "") && (wait_time > 10)) { - *error_message = "Connection timed out."; + bool sent_old_init = g_settings->getFlag("send_pre_v25_init"); + // If no pre v25 init was sent, and no answer was received, + // but the low level connection could be established + // (meaning that we have a peer id), then we probably wanted + // to connect to a legacy server. In this case, tell the user + // to enable the option to be able to connect. + if (!sent_old_init && + (client->getProtoVersion() == 0) && + client->connectedToServer()) { + *error_message = "Connection failure: init packet not " + "recognized by server.\n" + "Most likely the server uses an old protocol version ( Network -> Support older Servers'\n" + "entry in the advanced settings menu."; + } else { + *error_message = "Connection timed out."; + } errorstream << *error_message << std::endl; break; } -- cgit v1.2.3 From e58a55aa82dfc66325a694dcc3519d3c0f3388a6 Mon Sep 17 00:00:00 2001 From: Auke Kok Date: Thu, 10 Dec 2015 22:58:11 -0800 Subject: Make plantlike drawtype more fun Adds several new ways that the plantlike drawtype mesh can be changed. This requires paramtype2 = "meshoptions" to be set in the node definition. The drawtype for these nodes should be "plantlike". These modifications are all done using param2. This field is now a complex bitfield that allows some or more of the combinations to be chosen, and the mesh draw code will choose the options based as neeeded for each plantlike node. bit layout: bits 0, 1 and 2 (values 0x1 through 0x7) are for choosing the plant mesh shape: 0 - ordinary plantlike plant ("x" shaped) 1 - ordinary plant, but rotated 45 degrees ("+" shaped) 2 - a plant with 3 faces ("*" shaped) 3 - a plant with 4 faces ("#" shaped) 4 - a plant with 4 faces ("#" shaped, leaning outwards) 5 through 7 are unused and reserved for future mesh shapes. bit 3 (0x8) causes the plant to be randomly offset in the x,z plane. The plant should fall within the 1x1x1 nodebox if regularly sized. bit 4 (0x10) causes the plant mesh to grow by sqrt(2), and will cause the plant mesh to fill out 1x1x1, and appear slightly larger. Texture makers will want to make their plant texture 23x16 pixels to have the best visual fit in 1x1x1 size. bit 5 (0x20) causes each face of the plant to have a slight negative Y offset in position, descending up to 0.125 downwards into the node below. Because this is per face, this causes the plant model to be less symmetric. bit 6 (0x40) through bit 7 (0x80) are unused and reserved for future use. !(https://youtu.be/qWuI664krsI) --- doc/lua_api.txt | 16 +++++ src/content_mapblock.cpp | 152 +++++++++++++++++++++++++++++++++++++++--- src/network/networkprotocol.h | 4 +- src/nodedef.cpp | 5 +- src/nodedef.h | 2 + src/script/cpp_api/s_node.cpp | 1 + 6 files changed, 167 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 38f19aadc..440edd963 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -578,6 +578,22 @@ node definition: ^ The rotation of this node is stored in param2. Plants are rotated this way. Values range 0 - 179. The value stored in param2 is multiplied by two to get the actual rotation of the node. + paramtype2 == "meshoptions" + ^ Only valid for "plantlike". The value of param2 becomes a bitfield which can + be used to change how the client draws plantlike nodes. Bits 0, 1 and 2 form + a mesh selector. Currently the following meshes are choosable: + 0 = a "x" shaped plant (ordinary plant) + 1 = a "+" shaped plant (just rotated 45 degrees) + 2 = a "*" shaped plant with 3 faces instead of 2 + 3 = a "#" shaped plant with 4 faces instead of 2 + 4 = a "#" shaped plant with 4 faces that lean outwards + 5-7 are unused and reserved for future meshes. + Bits 3 through 7 are optional flags that can be combined and give these + effects: + bit 3 (0x08) - Makes the plant slightly vary placement horizontally + bit 4 (0x10) - Makes the plant mesh 1.4x larger + bit 5 (0x20) - Moves each face randomly a small bit down (1/8 max) + bits 6-7 are reserved for future use. collision_box = { type = "fixed", fixed = { diff --git a/src/content_mapblock.cpp b/src/content_mapblock.cpp index 6a83bd8f3..b86b97822 100644 --- a/src/content_mapblock.cpp +++ b/src/content_mapblock.cpp @@ -28,6 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include "gamedef.h" #include "log.h" +#include "noise.h" // Create a cuboid. @@ -1104,6 +1105,8 @@ void mapblock_mesh_generate_special(MeshMakeData *data, break;} case NDT_PLANTLIKE: { + PseudoRandom rng(x<<8 | z | y<<16); + TileSpec tile = getNodeTileN(n, p, 0, data); tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY; @@ -1111,9 +1114,18 @@ void mapblock_mesh_generate_special(MeshMakeData *data, video::SColor c = MapBlock_LightColor(255, l, f.light_source); float s = BS / 2 * f.visual_scale; + // add sqrt(2) visual scale + if ((f.param_type_2 == CPT2_MESHOPTIONS) && ((n.param2 & 0x10) != 0)) + s *= 1.41421; + + float random_offset_X = .0; + float random_offset_Z = .0; + if ((f.param_type_2 == CPT2_MESHOPTIONS) && ((n.param2 & 0x8) != 0)) { + random_offset_X = BS * ((rng.next() % 16 / 16.0) * 0.29 - 0.145); + random_offset_Z = BS * ((rng.next() % 16 / 16.0) * 0.29 - 0.145); + } - for (int j = 0; j < 2; j++) - { + for (int j = 0; j < 4; j++) { video::S3DVertex vertices[4] = { video::S3DVertex(-s,-BS/2, 0, 0,0,0, c, 0,1), @@ -1121,28 +1133,146 @@ void mapblock_mesh_generate_special(MeshMakeData *data, video::S3DVertex( s,-BS/2 + s*2,0, 0,0,0, c, 1,0), video::S3DVertex(-s,-BS/2 + s*2,0, 0,0,0, c, 0,0), }; + float rotate_degree = 0; + u8 p2mesh = 0; if (f.param_type_2 == CPT2_DEGROTATE) rotate_degree = n.param2 * 2; - - if (j == 0) { - for(u16 i = 0; i < 4; i++) - vertices[i].Pos.rotateXZBy(46 + rotate_degree); - } else if (j == 1) { - for(u16 i = 0; i < 4; i++) - vertices[i].Pos.rotateXZBy(-44 + rotate_degree); + else if (f.param_type_2 != CPT2_MESHOPTIONS) { + if (j == 0) { + for (u16 i = 0; i < 4; i++) + vertices[i].Pos.rotateXZBy(46 + rotate_degree); + } else if (j == 1) { + for (u16 i = 0; i < 4; i++) + vertices[i].Pos.rotateXZBy(-44 + rotate_degree); + } + } else { + p2mesh = n.param2 & 0x7; + switch (p2mesh) { + case 0: + // x + if (j == 0) { + for (u16 i = 0; i < 4; i++) + vertices[i].Pos.rotateXZBy(46); + } else if (j == 1) { + for (u16 i = 0; i < 4; i++) + vertices[i].Pos.rotateXZBy(-44); + } + break; + case 1: + // + + if (j == 0) { + for (u16 i = 0; i < 4; i++) + vertices[i].Pos.rotateXZBy(91); + } else if (j == 1) { + for (u16 i = 0; i < 4; i++) + vertices[i].Pos.rotateXZBy(1); + } + break; + case 2: + // * + if (j == 0) { + for (u16 i = 0; i < 4; i++) + vertices[i].Pos.rotateXZBy(121); + } else if (j == 1) { + for (u16 i = 0; i < 4; i++) + vertices[i].Pos.rotateXZBy(241); + } else { // (j == 2) + for (u16 i = 0; i < 4; i++) + vertices[i].Pos.rotateXZBy(1); + } + break; + case 3: + // # + switch (j) { + case 0: + for (u16 i = 0; i < 4; i++) { + vertices[i].Pos.rotateXZBy(1); + vertices[i].Pos.Z += BS / 4; + } + break; + case 1: + for (u16 i = 0; i < 4; i++) { + vertices[i].Pos.rotateXZBy(91); + vertices[i].Pos.X += BS / 4; + } + break; + case 2: + for (u16 i = 0; i < 4; i++) { + vertices[i].Pos.rotateXZBy(181); + vertices[i].Pos.Z -= BS / 4; + } + break; + case 3: + for (u16 i = 0; i < 4; i++) { + vertices[i].Pos.rotateXZBy(271); + vertices[i].Pos.X -= BS / 4; + } + break; + } + break; + case 4: + // outward leaning #-like + switch (j) { + case 0: + for (u16 i = 2; i < 4; i++) + vertices[i].Pos.Z -= BS / 2; + for (u16 i = 0; i < 4; i++) + vertices[i].Pos.rotateXZBy(1); + break; + case 1: + for (u16 i = 2; i < 4; i++) + vertices[i].Pos.Z -= BS / 2; + for (u16 i = 0; i < 4; i++) + vertices[i].Pos.rotateXZBy(91); + break; + case 2: + for (u16 i = 2; i < 4; i++) + vertices[i].Pos.Z -= BS / 2; + for (u16 i = 0; i < 4; i++) + vertices[i].Pos.rotateXZBy(181); + break; + case 3: + for (u16 i = 2; i < 4; i++) + vertices[i].Pos.Z -= BS / 2; + for (u16 i = 0; i < 4; i++) + vertices[i].Pos.rotateXZBy(271); + break; + } + break; + } } - for (int i = 0; i < 4; i++) - { + for (int i = 0; i < 4; i++) { vertices[i].Pos *= f.visual_scale; vertices[i].Pos.Y += BS/2 * (f.visual_scale - 1); vertices[i].Pos += intToFloat(p, BS); + // move to a random spot to avoid moire + if ((f.param_type_2 == CPT2_MESHOPTIONS) && ((n.param2 & 0x8) != 0)) { + vertices[i].Pos.X += random_offset_X; + vertices[i].Pos.Z += random_offset_Z; + } + // randomly move each face up/down + if ((f.param_type_2 == CPT2_MESHOPTIONS) && ((n.param2 & 0x20) != 0)) { + PseudoRandom yrng(j | x<<16 | z<<8 | y<<24 ); + vertices[i].Pos.Y -= BS * ((yrng.next() % 16 / 16.0) * 0.125); + } } u16 indices[] = {0, 1, 2, 2, 3, 0}; // Add to mesh collector collector.append(tile, vertices, 4, indices, 6); + + // stop adding faces for meshes with less than 4 faces + if (f.param_type_2 == CPT2_MESHOPTIONS) { + if (((p2mesh == 0) || (p2mesh == 1)) && (j == 1)) + break; + else if ((p2mesh == 2) && (j == 2)) + break; + } else if (j == 1) { + break; + } + } break;} case NDT_FIRELIKE: diff --git a/src/network/networkprotocol.h b/src/network/networkprotocol.h index 3a9483efb..e3fcae0c6 100644 --- a/src/network/networkprotocol.h +++ b/src/network/networkprotocol.h @@ -136,9 +136,11 @@ with this program; if not, write to the Free Software Foundation, Inc., backface_culling: backwards compatibility for playing with newer client on pre-27 servers. Add nodedef v3 - connected nodeboxes + PROTOCOL_VERSION 28: + CPT2_MESHOPTIONS */ -#define LATEST_PROTOCOL_VERSION 27 +#define LATEST_PROTOCOL_VERSION 28 // Server's supported network protocol range #define SERVER_PROTOCOL_VERSION_MIN 13 diff --git a/src/nodedef.cpp b/src/nodedef.cpp index 764203efc..646375575 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -387,7 +387,10 @@ void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const writeU8(os, post_effect_color.getGreen()); writeU8(os, post_effect_color.getBlue()); writeU8(os, param_type); - writeU8(os, param_type_2); + if ((protocol_version < 28) && (param_type_2 == CPT2_MESHOPTIONS)) + writeU8(os, CPT2_NONE); + else + writeU8(os, param_type_2); writeU8(os, is_ground_content); writeU8(os, light_propagates); writeU8(os, sunlight_propagates); diff --git a/src/nodedef.h b/src/nodedef.h index 1c2f792d8..f17c53727 100644 --- a/src/nodedef.h +++ b/src/nodedef.h @@ -65,6 +65,8 @@ enum ContentParamType2 CPT2_LEVELED, // 2D rotation for things like plants CPT2_DEGROTATE, + // Mesh options for plants + CPT2_MESHOPTIONS }; enum LiquidType diff --git a/src/script/cpp_api/s_node.cpp b/src/script/cpp_api/s_node.cpp index 17f0f0dac..379ed773f 100644 --- a/src/script/cpp_api/s_node.cpp +++ b/src/script/cpp_api/s_node.cpp @@ -58,6 +58,7 @@ struct EnumString ScriptApiNode::es_ContentParamType2[] = {CPT2_WALLMOUNTED, "wallmounted"}, {CPT2_LEVELED, "leveled"}, {CPT2_DEGROTATE, "degrotate"}, + {CPT2_MESHOPTIONS, "meshoptions"}, {0, NULL}, }; -- cgit v1.2.3 From fb20b45100490acd47038be43b6f257c1bd75d97 Mon Sep 17 00:00:00 2001 From: paramat Date: Sat, 20 Aug 2016 02:05:34 +0100 Subject: Camera: Higher frequency limit for view/hand bobbing and footsteps Rebased and tuned version of Calinou's original pull request. 'm_view_bobbing_speed' controls the frequency of view bobbing, hand bobbing and footsteps, it was limited to a maximum of 40 (walking frequency) so did not increase if player speed was modified by a 'speed buff', a sprinting mod or modified in .conf or advanced settngs. This commit raises the limit to 70 which is suitable for sprinting. --- src/camera.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/camera.cpp b/src/camera.cpp index e1fec4a3e..4feab1fe5 100644 --- a/src/camera.cpp +++ b/src/camera.cpp @@ -472,7 +472,7 @@ void Camera::update(LocalPlayer* player, f32 frametime, f32 busytime, { // Start animation m_view_bobbing_state = 1; - m_view_bobbing_speed = MYMIN(speed.getLength(), 40); + m_view_bobbing_speed = MYMIN(speed.getLength(), 70); } else if (m_view_bobbing_state == 1) { -- cgit v1.2.3 From 4330c63ea40a75d4b8fcf71dc749fed714b22a44 Mon Sep 17 00:00:00 2001 From: rubenwardy Date: Sun, 7 Aug 2016 16:01:00 +0100 Subject: Submit name of field on enter key press --- src/guiFormSpecMenu.cpp | 13 +++++++++++++ src/guiFormSpecMenu.h | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/guiFormSpecMenu.cpp b/src/guiFormSpecMenu.cpp index a9cbb6254..dd96da5a9 100644 --- a/src/guiFormSpecMenu.cpp +++ b/src/guiFormSpecMenu.cpp @@ -104,6 +104,7 @@ GUIFormSpecMenu::GUIFormSpecMenu(irr::IrrlichtDevice* dev, m_formspec_version(0), m_focused_element(""), m_joystick(joystick), + current_field_enter_pending(""), m_font(NULL), m_remap_dbl_click(remap_dbl_click) #ifdef __ANDROID__ @@ -2695,6 +2696,10 @@ void GUIFormSpecMenu::acceptInput(FormspecQuitMode quitmode=quit_mode_no) current_keys_pending.key_enter = false; } + if (!current_field_enter_pending.empty()) { + fields["key_enter_field"] = current_field_enter_pending; + } + if (current_keys_pending.key_escape) { fields["key_escape"] = "true"; current_keys_pending.key_escape = false; @@ -3625,8 +3630,16 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) if (event.GUIEvent.EventType == gui::EGET_EDITBOX_ENTER) { if (event.GUIEvent.Caller->getID() > 257) { + for (u32 i = 0; i < m_fields.size(); i++) { + FieldSpec &s = m_fields[i]; + if (s.ftype == f_Unknown && + s.fid == event.GUIEvent.Caller->getID()) { + current_field_enter_pending = s.fname; + } + } if (m_allowclose) { + current_keys_pending.key_enter = true; acceptInput(quit_mode_accept); quitMenu(); } else { diff --git a/src/guiFormSpecMenu.h b/src/guiFormSpecMenu.h index 2fb55070d..7b809df71 100644 --- a/src/guiFormSpecMenu.h +++ b/src/guiFormSpecMenu.h @@ -463,6 +463,7 @@ private: } fs_key_pendig; fs_key_pendig current_keys_pending; + std::string current_field_enter_pending; void parseElement(parserData* data,std::string element); @@ -557,4 +558,3 @@ public: }; #endif - -- cgit v1.2.3 From e10fee00011f6c1ef8ee5b884adb11013954a1c9 Mon Sep 17 00:00:00 2001 From: rubenwardy Date: Sun, 7 Aug 2016 16:22:50 +0100 Subject: Allow fields to choose whether they close on enter press --- doc/lua_api.txt | 18 +++++++++++++++--- src/guiFormSpecMenu.cpp | 28 +++++++++++++++++++++++----- src/guiFormSpecMenu.h | 12 +++++++----- 3 files changed, 45 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 440edd963..579fe796e 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -1503,17 +1503,23 @@ examples. * If `true` the background is clipped to formspec size (`x` and `y` are used as offset values, `w` and `h` are ignored) -#### `pwdfield[,;,;;