aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt25
-rw-r--r--src/activeobject.h10
-rw-r--r--src/chat.cpp4
-rw-r--r--src/client/client.cpp29
-rw-r--r--src/client/client.h7
-rw-r--r--src/client/clientenvironment.cpp7
-rw-r--r--src/client/clientlauncher.cpp10
-rw-r--r--src/client/content_mapblock.cpp4
-rw-r--r--src/client/fontengine.cpp65
-rw-r--r--src/client/fontengine.h9
-rw-r--r--src/client/game.cpp837
-rw-r--r--src/client/game.h8
-rw-r--r--src/client/gameui.h1
-rw-r--r--src/client/hud.cpp2
-rw-r--r--src/client/keycode.cpp3
-rw-r--r--src/client/mapblock_mesh.cpp7
-rw-r--r--src/client/mapblock_mesh.h2
-rw-r--r--src/client/mesh_generator_thread.h1
-rw-r--r--src/client/minimap.h1
-rw-r--r--src/client/render/factory.cpp7
-rw-r--r--src/client/renderingengine.cpp2
-rw-r--r--src/client/shader.h2
-rw-r--r--src/client/sky.h2
-rw-r--r--src/client/tile.cpp2
-rw-r--r--src/clientiface.cpp3
-rw-r--r--src/clientiface.h15
-rw-r--r--src/constants.h2
-rw-r--r--src/content/subgames.cpp24
-rw-r--r--src/database/database-files.cpp124
-rw-r--r--src/database/database-files.h9
-rw-r--r--src/defaultsettings.cpp7
-rw-r--r--src/defaultsettings.h9
-rw-r--r--src/emerge.cpp9
-rw-r--r--src/exceptions.h5
-rw-r--r--src/filesys.cpp21
-rw-r--r--src/filesys.h3
-rw-r--r--src/gui/StyleSpec.h47
-rw-r--r--src/gui/guiButtonItemImage.cpp1
-rw-r--r--src/gui/guiButtonItemImage.h1
-rw-r--r--src/gui/guiChatConsole.h2
-rw-r--r--src/gui/guiConfirmRegistration.cpp3
-rw-r--r--src/gui/guiEditBox.cpp92
-rw-r--r--src/gui/guiEditBox.h11
-rw-r--r--src/gui/guiEditBoxWithScrollbar.cpp77
-rw-r--r--src/gui/guiEditBoxWithScrollbar.h2
-rw-r--r--src/gui/guiEngine.cpp5
-rw-r--r--src/gui/guiFormSpecMenu.cpp43
-rw-r--r--src/gui/guiFormSpecMenu.h2
-rw-r--r--src/gui/guiHyperText.cpp2
-rw-r--r--src/gui/guiInventoryList.cpp2
-rw-r--r--src/gui/guiKeyChangeMenu.cpp2
-rw-r--r--src/gui/intlGUIEditBox.cpp99
-rw-r--r--src/gui/intlGUIEditBox.h9
-rw-r--r--src/gui/modalMenu.cpp171
-rw-r--r--src/gui/modalMenu.h9
-rw-r--r--src/gui/touchscreengui.h1
-rw-r--r--src/irr_ptr.h89
-rw-r--r--src/itemstackmetadata.cpp17
-rw-r--r--src/main.cpp20
-rw-r--r--src/map.cpp162
-rw-r--r--src/map.h8
-rw-r--r--src/map_settings_manager.cpp68
-rw-r--r--src/map_settings_manager.h5
-rw-r--r--src/mapblock.h9
-rw-r--r--src/mapgen/mapgen.h2
-rw-r--r--src/mapgen/mapgen_v6.cpp3
-rw-r--r--src/mapgen/mg_ore.h47
-rw-r--r--src/network/networkexceptions.h8
-rw-r--r--src/network/networkpacket.cpp49
-rw-r--r--src/network/networkpacket.h2
-rw-r--r--src/network/serverpackethandler.cpp40
-rw-r--r--src/nodedef.cpp134
-rw-r--r--src/nodedef.h65
-rw-r--r--src/pathfinder.cpp6
-rw-r--r--src/remoteplayer.cpp116
-rw-r--r--src/remoteplayer.h10
-rw-r--r--src/script/common/c_content.cpp50
-rw-r--r--src/script/cpp_api/s_async.cpp29
-rw-r--r--src/script/cpp_api/s_async.h6
-rw-r--r--src/script/cpp_api/s_node.cpp19
-rw-r--r--src/script/cpp_api/s_node.h1
-rw-r--r--src/script/cpp_api/s_player.cpp13
-rw-r--r--src/script/cpp_api/s_player.h1
-rw-r--r--src/script/lua_api/l_mainmenu.cpp217
-rw-r--r--src/script/lua_api/l_mainmenu.h6
-rw-r--r--src/script/lua_api/l_mapgen.cpp13
-rw-r--r--src/script/lua_api/l_object.cpp46
-rw-r--r--src/script/lua_api/l_server.cpp124
-rw-r--r--src/script/lua_api/l_server.h2
-rw-r--r--src/script/lua_api/l_settings.cpp2
-rw-r--r--src/script/lua_api/l_util.cpp13
-rw-r--r--src/script/lua_api/l_util.h3
-rw-r--r--src/script/scripting_mainmenu.cpp1
-rw-r--r--src/server.cpp130
-rw-r--r--src/server.h33
-rw-r--r--src/server/player_sao.cpp7
-rw-r--r--src/server/player_sao.h3
-rw-r--r--src/server/serveractiveobject.h3
-rw-r--r--src/serverenvironment.cpp7
-rw-r--r--src/serverenvironment.h10
-rw-r--r--src/serverlist.cpp162
-rw-r--r--src/serverlist.h13
-rw-r--r--src/settings.cpp300
-rw-r--r--src/settings.h44
-rw-r--r--src/settings_translation_file.cpp58
-rw-r--r--src/translation.cpp15
-rw-r--r--src/unittest/test.cpp4
-rw-r--r--src/unittest/test_connection.cpp35
-rw-r--r--src/unittest/test_map_settings_manager.cpp86
-rw-r--r--src/unittest/test_settings.cpp73
-rw-r--r--src/unittest/test_utilities.cpp19
-rw-r--r--src/util/string.cpp116
-rw-r--r--src/util/string.h34
-rw-r--r--src/version.cpp5
114 files changed, 2352 insertions, 2070 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index b6bba6e8d..7bcf8d6c7 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -532,7 +532,7 @@ set(EXECUTABLE_OUTPUT_PATH "${CMAKE_SOURCE_DIR}/bin")
if(BUILD_CLIENT)
add_executable(${PROJECT_NAME} ${client_SRCS} ${extra_windows_SRCS})
add_dependencies(${PROJECT_NAME} GenerateVersion)
- set(client_LIBS
+ target_link_libraries(
${PROJECT_NAME}
${ZLIB_LIBRARIES}
${IRRLICHT_LIBRARY}
@@ -548,9 +548,14 @@ if(BUILD_CLIENT)
${PLATFORM_LIBS}
${CLIENT_PLATFORM_LIBS}
)
- target_link_libraries(
- ${client_LIBS}
- )
+ if(NOT USE_LUAJIT)
+ set_target_properties(${PROJECT_NAME} PROPERTIES
+ # This is necessary for dynamic Lua modules
+ # to work when Lua is statically linked (issue #10806)
+ ENABLE_EXPORTS 1
+ )
+ endif()
+
if(ENABLE_GLES)
target_link_libraries(
${PROJECT_NAME}
@@ -621,7 +626,15 @@ if(BUILD_SERVER)
${PLATFORM_LIBS}
)
set_target_properties(${PROJECT_NAME}server PROPERTIES
- COMPILE_DEFINITIONS "SERVER")
+ COMPILE_DEFINITIONS "SERVER")
+ if(NOT USE_LUAJIT)
+ set_target_properties(${PROJECT_NAME}server PROPERTIES
+ # This is necessary for dynamic Lua modules
+ # to work when Lua is statically linked (issue #10806)
+ ENABLE_EXPORTS 1
+ )
+ endif()
+
if (USE_GETTEXT)
target_link_libraries(${PROJECT_NAME}server ${GETTEXT_LIBRARY})
endif()
@@ -666,7 +679,7 @@ option(APPLY_LOCALE_BLACKLIST "Use a blacklist to avoid broken locales" TRUE)
if (GETTEXTLIB_FOUND AND APPLY_LOCALE_BLACKLIST)
set(GETTEXT_USED_LOCALES "")
foreach(LOCALE ${GETTEXT_AVAILABLE_LOCALES})
- if (NOT ";${GETTEXT_BLACKLISTED_LOCALES};" MATCHES ";${LOCALE};")
+ if (NOT "${LOCALE}" IN_LIST GETTEXT_BLACKLISTED_LOCALES)
list(APPEND GETTEXT_USED_LOCALES ${LOCALE})
endif()
endforeach()
diff --git a/src/activeobject.h b/src/activeobject.h
index 0829858ad..1d8a3712b 100644
--- a/src/activeobject.h
+++ b/src/activeobject.h
@@ -28,11 +28,11 @@ enum ActiveObjectType {
ACTIVEOBJECT_TYPE_INVALID = 0,
ACTIVEOBJECT_TYPE_TEST = 1,
// Obsolete stuff
- ACTIVEOBJECT_TYPE_ITEM = 2,
-// ACTIVEOBJECT_TYPE_RAT = 3,
-// ACTIVEOBJECT_TYPE_OERKKI1 = 4,
-// ACTIVEOBJECT_TYPE_FIREFLY = 5,
- ACTIVEOBJECT_TYPE_MOBV2 = 6,
+// ACTIVEOBJECT_TYPE_ITEM = 2,
+// ACTIVEOBJECT_TYPE_RAT = 3,
+// ACTIVEOBJECT_TYPE_OERKKI1 = 4,
+// ACTIVEOBJECT_TYPE_FIREFLY = 5,
+// ACTIVEOBJECT_TYPE_MOBV2 = 6,
// End obsolete stuff
ACTIVEOBJECT_TYPE_LUAENTITY = 7,
// Special type, not stored as a static object
diff --git a/src/chat.cpp b/src/chat.cpp
index 2f65e68b3..c9317a079 100644
--- a/src/chat.cpp
+++ b/src/chat.cpp
@@ -485,8 +485,8 @@ void ChatPrompt::nickCompletion(const std::list<std::string>& names, bool backwa
// find all names that start with the selected prefix
std::vector<std::wstring> completions;
for (const std::string &name : names) {
- if (str_starts_with(narrow_to_wide(name), prefix, true)) {
- std::wstring completion = narrow_to_wide(name);
+ std::wstring completion = utf8_to_wide(name);
+ if (str_starts_with(completion, prefix, true)) {
if (prefix_start == 0)
completion += L": ";
completions.push_back(completion);
diff --git a/src/client/client.cpp b/src/client/client.cpp
index 9bbb57668..f9ecb20c6 100644
--- a/src/client/client.cpp
+++ b/src/client/client.cpp
@@ -160,20 +160,6 @@ void Client::loadMods()
scanModIntoMemory(BUILTIN_MOD_NAME, getBuiltinLuaPath());
m_script->loadModFromMemory(BUILTIN_MOD_NAME);
- // TODO Uncomment when server-sent CSM and verifying of builtin are complete
- /*
- // Don't load client-provided mods if disabled by server
- if (checkCSMRestrictionFlag(CSMRestrictionFlags::CSM_RF_LOAD_CLIENT_MODS)) {
- warningstream << "Client-provided mod loading is disabled by server." <<
- std::endl;
- // If builtin integrity is wrong, disconnect user
- if (!checkBuiltinIntegrity()) {
- // TODO disconnect user
- }
- return;
- }
- */
-
ClientModConfiguration modconf(getClientModsLuaPath());
m_mods = modconf.getMods();
// complain about mods with unsatisfied dependencies
@@ -219,12 +205,6 @@ void Client::loadMods()
m_script->on_minimap_ready(m_minimap);
}
-bool Client::checkBuiltinIntegrity()
-{
- // TODO
- return true;
-}
-
void Client::scanModSubfolder(const std::string &mod_name, const std::string &mod_path,
std::string mod_subpath)
{
@@ -1219,7 +1199,7 @@ void Client::sendChatMessage(const std::wstring &message)
if (canSendChatMessage()) {
u32 now = time(NULL);
float time_passed = now - m_last_chat_message_sent;
- m_last_chat_message_sent = time(NULL);
+ m_last_chat_message_sent = now;
m_chat_message_allowance += time_passed * (CLIENT_CHAT_MESSAGE_LIMIT_PER_10S / 8.0f);
if (m_chat_message_allowance > CLIENT_CHAT_MESSAGE_LIMIT_PER_10S)
@@ -1298,9 +1278,8 @@ void Client::sendPlayerPos(v3f pos)
// Save bandwidth by only updating position when
// player is not dead and something changed
- // FIXME: This part causes breakages in mods like 3d_armor, and has been commented for now
- // if (m_activeobjects_received && player->isDead())
- // return;
+ if (m_activeobjects_received && player->isDead())
+ return;
if (
player->last_position == pos &&
@@ -1875,7 +1854,7 @@ void Client::makeScreenshot()
sstr << "Failed to save screenshot '" << filename << "'";
}
pushToChatQueue(new ChatMessage(CHATMESSAGE_TYPE_SYSTEM,
- narrow_to_wide(sstr.str())));
+ utf8_to_wide(sstr.str())));
infostream << sstr.str() << std::endl;
image->drop();
}
diff --git a/src/client/client.h b/src/client/client.h
index 979636eba..f7a9030bc 100644
--- a/src/client/client.h
+++ b/src/client/client.h
@@ -418,11 +418,6 @@ public:
return false;
}
- u32 getCSMNodeRangeLimit() const
- {
- return m_csm_restriction_noderange;
- }
-
inline std::unordered_map<u32, u32> &getHUDTranslationMap()
{
return m_hud_server_to_client;
@@ -445,7 +440,6 @@ public:
private:
void loadMods();
- bool checkBuiltinIntegrity();
// Virtual methods from con::PeerHandler
void peerAdded(con::Peer *peer) override;
@@ -593,7 +587,6 @@ private:
// Client modding
ClientScripting *m_script = nullptr;
- bool m_modding_enabled;
std::unordered_map<std::string, ModMetadata *> m_mod_storages;
float m_mod_storage_save_timer = 10.0f;
std::vector<ModSpec> m_mods;
diff --git a/src/client/clientenvironment.cpp b/src/client/clientenvironment.cpp
index 5f44c30ac..fd56c8f44 100644
--- a/src/client/clientenvironment.cpp
+++ b/src/client/clientenvironment.cpp
@@ -334,13 +334,6 @@ GenericCAO* ClientEnvironment::getGenericCAO(u16 id)
return NULL;
}
-bool isFreeClientActiveObjectId(const u16 id,
- ClientActiveObjectMap &objects)
-{
- return id != 0 && objects.find(id) == objects.end();
-
-}
-
u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
{
// Register object. If failed return zero id
diff --git a/src/client/clientlauncher.cpp b/src/client/clientlauncher.cpp
index 29427f609..2bb0bc385 100644
--- a/src/client/clientlauncher.cpp
+++ b/src/client/clientlauncher.cpp
@@ -175,7 +175,7 @@ bool ClientLauncher::run(GameStartData &start_data, const Settings &cmd_args)
}
}
#endif
- g_fontengine = new FontEngine(g_settings, guienv);
+ g_fontengine = new FontEngine(guienv);
FATAL_ERROR_IF(g_fontengine == NULL, "Font engine creation failed.");
#if (IRRLICHT_VERSION_MAJOR >= 1 && IRRLICHT_VERSION_MINOR >= 8) || IRRLICHT_VERSION_MAJOR >= 2
@@ -487,14 +487,6 @@ bool ClientLauncher::launch_game(std::string &error_message,
start_data.socket_port = myrand_range(49152, 65535);
} else {
g_settings->set("name", start_data.name);
- if (!start_data.address.empty()) {
- ServerListSpec server;
- server["name"] = server_name;
- server["address"] = start_data.address;
- server["port"] = itos(start_data.socket_port);
- server["description"] = server_description;
- ServerList::insert(server);
- }
}
if (start_data.name.length() > PLAYERNAME_SIZE - 1) {
diff --git a/src/client/content_mapblock.cpp b/src/client/content_mapblock.cpp
index df2748212..90284ecce 100644
--- a/src/client/content_mapblock.cpp
+++ b/src/client/content_mapblock.cpp
@@ -513,10 +513,10 @@ f32 MapblockMeshGenerator::getCornerLevel(int i, int k)
count++;
} else if (content == CONTENT_AIR) {
air_count++;
- if (air_count >= 2)
- return -0.5 * BS + 0.2;
}
}
+ if (air_count >= 2)
+ return -0.5 * BS + 0.2;
if (count > 0)
return sum / count;
return 0;
diff --git a/src/client/fontengine.cpp b/src/client/fontengine.cpp
index a55420846..47218c0d9 100644
--- a/src/client/fontengine.cpp
+++ b/src/client/fontengine.cpp
@@ -42,8 +42,7 @@ static void font_setting_changed(const std::string &name, void *userdata)
}
/******************************************************************************/
-FontEngine::FontEngine(Settings* main_settings, gui::IGUIEnvironment* env) :
- m_settings(main_settings),
+FontEngine::FontEngine(gui::IGUIEnvironment* env) :
m_env(env)
{
@@ -51,34 +50,34 @@ FontEngine::FontEngine(Settings* main_settings, gui::IGUIEnvironment* env) :
i = (FontMode) FONT_SIZE_UNSPECIFIED;
}
- assert(m_settings != NULL); // pre-condition
+ assert(g_settings != NULL); // pre-condition
assert(m_env != NULL); // pre-condition
assert(m_env->getSkin() != NULL); // pre-condition
readSettings();
if (m_currentMode == FM_Standard) {
- m_settings->registerChangedCallback("font_size", font_setting_changed, NULL);
- m_settings->registerChangedCallback("font_bold", font_setting_changed, NULL);
- m_settings->registerChangedCallback("font_italic", font_setting_changed, NULL);
- m_settings->registerChangedCallback("font_path", font_setting_changed, NULL);
- m_settings->registerChangedCallback("font_path_bold", font_setting_changed, NULL);
- m_settings->registerChangedCallback("font_path_italic", font_setting_changed, NULL);
- m_settings->registerChangedCallback("font_path_bolditalic", font_setting_changed, NULL);
- m_settings->registerChangedCallback("font_shadow", font_setting_changed, NULL);
- m_settings->registerChangedCallback("font_shadow_alpha", font_setting_changed, NULL);
+ g_settings->registerChangedCallback("font_size", font_setting_changed, NULL);
+ g_settings->registerChangedCallback("font_bold", font_setting_changed, NULL);
+ g_settings->registerChangedCallback("font_italic", font_setting_changed, NULL);
+ g_settings->registerChangedCallback("font_path", font_setting_changed, NULL);
+ g_settings->registerChangedCallback("font_path_bold", font_setting_changed, NULL);
+ g_settings->registerChangedCallback("font_path_italic", font_setting_changed, NULL);
+ g_settings->registerChangedCallback("font_path_bolditalic", font_setting_changed, NULL);
+ g_settings->registerChangedCallback("font_shadow", font_setting_changed, NULL);
+ g_settings->registerChangedCallback("font_shadow_alpha", font_setting_changed, NULL);
}
else if (m_currentMode == FM_Fallback) {
- m_settings->registerChangedCallback("fallback_font_size", font_setting_changed, NULL);
- m_settings->registerChangedCallback("fallback_font_path", font_setting_changed, NULL);
- m_settings->registerChangedCallback("fallback_font_shadow", font_setting_changed, NULL);
- m_settings->registerChangedCallback("fallback_font_shadow_alpha", font_setting_changed, NULL);
+ g_settings->registerChangedCallback("fallback_font_size", font_setting_changed, NULL);
+ g_settings->registerChangedCallback("fallback_font_path", font_setting_changed, NULL);
+ g_settings->registerChangedCallback("fallback_font_shadow", font_setting_changed, NULL);
+ g_settings->registerChangedCallback("fallback_font_shadow_alpha", font_setting_changed, NULL);
}
- m_settings->registerChangedCallback("mono_font_path", font_setting_changed, NULL);
- m_settings->registerChangedCallback("mono_font_size", font_setting_changed, NULL);
- m_settings->registerChangedCallback("screen_dpi", font_setting_changed, NULL);
- m_settings->registerChangedCallback("gui_scaling", font_setting_changed, NULL);
+ g_settings->registerChangedCallback("mono_font_path", font_setting_changed, NULL);
+ g_settings->registerChangedCallback("mono_font_size", font_setting_changed, NULL);
+ g_settings->registerChangedCallback("screen_dpi", font_setting_changed, NULL);
+ g_settings->registerChangedCallback("gui_scaling", font_setting_changed, NULL);
}
/******************************************************************************/
@@ -205,9 +204,9 @@ unsigned int FontEngine::getFontSize(FontMode mode)
void FontEngine::readSettings()
{
if (USE_FREETYPE && g_settings->getBool("freetype")) {
- m_default_size[FM_Standard] = m_settings->getU16("font_size");
- m_default_size[FM_Fallback] = m_settings->getU16("fallback_font_size");
- m_default_size[FM_Mono] = m_settings->getU16("mono_font_size");
+ m_default_size[FM_Standard] = g_settings->getU16("font_size");
+ m_default_size[FM_Fallback] = g_settings->getU16("fallback_font_size");
+ m_default_size[FM_Mono] = g_settings->getU16("mono_font_size");
/*~ DO NOT TRANSLATE THIS LITERALLY!
This is a special string. Put either "no" or "yes"
@@ -220,15 +219,15 @@ void FontEngine::readSettings()
m_currentMode = is_yes(gettext("needs_fallback_font")) ?
FM_Fallback : FM_Standard;
- m_default_bold = m_settings->getBool("font_bold");
- m_default_italic = m_settings->getBool("font_italic");
+ m_default_bold = g_settings->getBool("font_bold");
+ m_default_italic = g_settings->getBool("font_italic");
} else {
m_currentMode = FM_Simple;
}
- m_default_size[FM_Simple] = m_settings->getU16("font_size");
- m_default_size[FM_SimpleMono] = m_settings->getU16("mono_font_size");
+ m_default_size[FM_Simple] = g_settings->getU16("font_size");
+ m_default_size[FM_SimpleMono] = g_settings->getU16("mono_font_size");
cleanCache();
updateFontCache();
@@ -244,7 +243,7 @@ void FontEngine::updateSkin()
m_env->getSkin()->setFont(font);
else
errorstream << "FontEngine: Default font file: " <<
- "\n\t\"" << m_settings->get("font_path") << "\"" <<
+ "\n\t\"" << g_settings->get("font_path") << "\"" <<
"\n\trequired for current screen configuration was not found" <<
" or was invalid file format." <<
"\n\tUsing irrlicht default font." << std::endl;
@@ -292,7 +291,7 @@ gui::IGUIFont *FontEngine::initFont(const FontSpec &spec)
setting_suffix.append("_italic");
u32 size = std::floor(RenderingEngine::getDisplayDensity() *
- m_settings->getFloat("gui_scaling") * spec.size);
+ g_settings->getFloat("gui_scaling") * spec.size);
if (size == 0) {
errorstream << "FontEngine: attempt to use font size 0" << std::endl;
@@ -311,8 +310,8 @@ gui::IGUIFont *FontEngine::initFont(const FontSpec &spec)
std::string fallback_settings[] = {
wanted_font_path,
- m_settings->get("fallback_font_path"),
- m_settings->getDefault(setting_prefix + "font_path")
+ g_settings->get("fallback_font_path"),
+ Settings::getLayer(SL_DEFAULTS)->get(setting_prefix + "font_path")
};
#if USE_FREETYPE
@@ -346,7 +345,7 @@ gui::IGUIFont *FontEngine::initSimpleFont(const FontSpec &spec)
assert(spec.mode == FM_Simple || spec.mode == FM_SimpleMono);
assert(spec.size != FONT_SIZE_UNSPECIFIED);
- const std::string &font_path = m_settings->get(
+ const std::string &font_path = g_settings->get(
(spec.mode == FM_SimpleMono) ? "mono_font_path" : "font_path");
size_t pos_dot = font_path.find_last_of('.');
@@ -364,7 +363,7 @@ gui::IGUIFont *FontEngine::initSimpleFont(const FontSpec &spec)
u32 size = std::floor(
RenderingEngine::getDisplayDensity() *
- m_settings->getFloat("gui_scaling") *
+ g_settings->getFloat("gui_scaling") *
spec.size);
irr::gui::IGUIFont *font = nullptr;
diff --git a/src/client/fontengine.h b/src/client/fontengine.h
index c6efa0df4..e27ef60e9 100644
--- a/src/client/fontengine.h
+++ b/src/client/fontengine.h
@@ -22,6 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <map>
#include <vector>
#include "util/basic_macros.h"
+#include "irrlichttypes.h"
#include <IGUIFont.h>
#include <IGUISkin.h>
#include <IGUIEnvironment.h>
@@ -61,7 +62,7 @@ class FontEngine
{
public:
- FontEngine(Settings* main_settings, gui::IGUIEnvironment* env);
+ FontEngine(gui::IGUIEnvironment* env);
~FontEngine();
@@ -127,9 +128,6 @@ public:
/** get font size for a specific mode */
unsigned int getFontSize(FontMode mode);
- /** initialize font engine */
- void initialize(Settings* main_settings, gui::IGUIEnvironment* env);
-
/** update internal parameters from settings */
void readSettings();
@@ -149,9 +147,6 @@ private:
/** clean cache */
void cleanCache();
- /** pointer to settings for registering callbacks or reading config */
- Settings* m_settings = nullptr;
-
/** pointer to irrlicht gui environment */
gui::IGUIEnvironment* m_env = nullptr;
diff --git a/src/client/game.cpp b/src/client/game.cpp
index 10c3a7ceb..6cf22debc 100644
--- a/src/client/game.cpp
+++ b/src/client/game.cpp
@@ -77,6 +77,843 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#else
#include "client/sound.h"
#endif
+<<<<<<< HEAD
+=======
+/*
+ Text input system
+*/
+
+struct TextDestNodeMetadata : public TextDest
+{
+ TextDestNodeMetadata(v3s16 p, Client *client)
+ {
+ m_p = p;
+ m_client = client;
+ }
+ // This is deprecated I guess? -celeron55
+ void gotText(const std::wstring &text)
+ {
+ std::string ntext = wide_to_utf8(text);
+ infostream << "Submitting 'text' field of node at (" << m_p.X << ","
+ << m_p.Y << "," << m_p.Z << "): " << ntext << std::endl;
+ StringMap fields;
+ fields["text"] = ntext;
+ m_client->sendNodemetaFields(m_p, "", fields);
+ }
+ void gotText(const StringMap &fields)
+ {
+ m_client->sendNodemetaFields(m_p, "", fields);
+ }
+
+ v3s16 m_p;
+ Client *m_client;
+};
+
+struct TextDestPlayerInventory : public TextDest
+{
+ TextDestPlayerInventory(Client *client)
+ {
+ m_client = client;
+ m_formname = "";
+ }
+ TextDestPlayerInventory(Client *client, const std::string &formname)
+ {
+ m_client = client;
+ m_formname = formname;
+ }
+ void gotText(const StringMap &fields)
+ {
+ m_client->sendInventoryFields(m_formname, fields);
+ }
+
+ Client *m_client;
+};
+
+struct LocalFormspecHandler : public TextDest
+{
+ LocalFormspecHandler(const std::string &formname)
+ {
+ m_formname = formname;
+ }
+
+ LocalFormspecHandler(const std::string &formname, Client *client):
+ m_client(client)
+ {
+ m_formname = formname;
+ }
+
+ void gotText(const StringMap &fields)
+ {
+ if (m_formname == "MT_PAUSE_MENU") {
+ if (fields.find("btn_sound") != fields.end()) {
+ g_gamecallback->changeVolume();
+ return;
+ }
+
+ if (fields.find("btn_key_config") != fields.end()) {
+ g_gamecallback->keyConfig();
+ return;
+ }
+
+ if (fields.find("btn_exit_menu") != fields.end()) {
+ g_gamecallback->disconnect();
+ return;
+ }
+
+ if (fields.find("btn_exit_os") != fields.end()) {
+ g_gamecallback->exitToOS();
+#ifndef __ANDROID__
+ RenderingEngine::get_raw_device()->closeDevice();
+#endif
+ return;
+ }
+
+ if (fields.find("btn_change_password") != fields.end()) {
+ g_gamecallback->changePassword();
+ return;
+ }
+
+ return;
+ }
+
+ if (m_formname == "MT_DEATH_SCREEN") {
+ assert(m_client != 0);
+ m_client->sendRespawn();
+ return;
+ }
+
+ if (m_client->modsLoaded())
+ m_client->getScript()->on_formspec_input(m_formname, fields);
+ }
+
+ Client *m_client = nullptr;
+};
+
+/* Form update callback */
+
+class NodeMetadataFormSource: public IFormSource
+{
+public:
+ NodeMetadataFormSource(ClientMap *map, v3s16 p):
+ m_map(map),
+ m_p(p)
+ {
+ }
+ const std::string &getForm() const
+ {
+ static const std::string empty_string = "";
+ NodeMetadata *meta = m_map->getNodeMetadata(m_p);
+
+ if (!meta)
+ return empty_string;
+
+ return meta->getString("formspec");
+ }
+
+ virtual std::string resolveText(const std::string &str)
+ {
+ NodeMetadata *meta = m_map->getNodeMetadata(m_p);
+
+ if (!meta)
+ return str;
+
+ return meta->resolveString(str);
+ }
+
+ ClientMap *m_map;
+ v3s16 m_p;
+};
+
+class PlayerInventoryFormSource: public IFormSource
+{
+public:
+ PlayerInventoryFormSource(Client *client):
+ m_client(client)
+ {
+ }
+
+ const std::string &getForm() const
+ {
+ LocalPlayer *player = m_client->getEnv().getLocalPlayer();
+ return player->inventory_formspec;
+ }
+
+ Client *m_client;
+};
+
+class NodeDugEvent: public MtEvent
+{
+public:
+ v3s16 p;
+ MapNode n;
+
+ NodeDugEvent(v3s16 p, MapNode n):
+ p(p),
+ n(n)
+ {}
+ MtEvent::Type getType() const
+ {
+ return MtEvent::NODE_DUG;
+ }
+};
+
+class SoundMaker
+{
+ ISoundManager *m_sound;
+ const NodeDefManager *m_ndef;
+public:
+ bool makes_footstep_sound;
+ float m_player_step_timer;
+ float m_player_jump_timer;
+
+ SimpleSoundSpec m_player_step_sound;
+ SimpleSoundSpec m_player_leftpunch_sound;
+ SimpleSoundSpec m_player_rightpunch_sound;
+
+ SoundMaker(ISoundManager *sound, const NodeDefManager *ndef):
+ m_sound(sound),
+ m_ndef(ndef),
+ makes_footstep_sound(true),
+ m_player_step_timer(0.0f),
+ m_player_jump_timer(0.0f)
+ {
+ }
+
+ void playPlayerStep()
+ {
+ if (m_player_step_timer <= 0 && m_player_step_sound.exists()) {
+ m_player_step_timer = 0.03;
+ if (makes_footstep_sound)
+ m_sound->playSound(m_player_step_sound, false);
+ }
+ }
+
+ void playPlayerJump()
+ {
+ if (m_player_jump_timer <= 0.0f) {
+ m_player_jump_timer = 0.2f;
+ m_sound->playSound(SimpleSoundSpec("player_jump", 0.5f), false);
+ }
+ }
+
+ static void viewBobbingStep(MtEvent *e, void *data)
+ {
+ SoundMaker *sm = (SoundMaker *)data;
+ sm->playPlayerStep();
+ }
+
+ static void playerRegainGround(MtEvent *e, void *data)
+ {
+ SoundMaker *sm = (SoundMaker *)data;
+ sm->playPlayerStep();
+ }
+
+ static void playerJump(MtEvent *e, void *data)
+ {
+ SoundMaker *sm = (SoundMaker *)data;
+ sm->playPlayerJump();
+ }
+
+ static void cameraPunchLeft(MtEvent *e, void *data)
+ {
+ SoundMaker *sm = (SoundMaker *)data;
+ sm->m_sound->playSound(sm->m_player_leftpunch_sound, false);
+ }
+
+ static void cameraPunchRight(MtEvent *e, void *data)
+ {
+ SoundMaker *sm = (SoundMaker *)data;
+ sm->m_sound->playSound(sm->m_player_rightpunch_sound, false);
+ }
+
+ static void nodeDug(MtEvent *e, void *data)
+ {
+ SoundMaker *sm = (SoundMaker *)data;
+ NodeDugEvent *nde = (NodeDugEvent *)e;
+ sm->m_sound->playSound(sm->m_ndef->get(nde->n).sound_dug, false);
+ }
+
+ static void playerDamage(MtEvent *e, void *data)
+ {
+ SoundMaker *sm = (SoundMaker *)data;
+ sm->m_sound->playSound(SimpleSoundSpec("player_damage", 0.5), false);
+ }
+
+ static void playerFallingDamage(MtEvent *e, void *data)
+ {
+ SoundMaker *sm = (SoundMaker *)data;
+ sm->m_sound->playSound(SimpleSoundSpec("player_falling_damage", 0.5), false);
+ }
+
+ void registerReceiver(MtEventManager *mgr)
+ {
+ mgr->reg(MtEvent::VIEW_BOBBING_STEP, SoundMaker::viewBobbingStep, this);
+ mgr->reg(MtEvent::PLAYER_REGAIN_GROUND, SoundMaker::playerRegainGround, this);
+ mgr->reg(MtEvent::PLAYER_JUMP, SoundMaker::playerJump, this);
+ mgr->reg(MtEvent::CAMERA_PUNCH_LEFT, SoundMaker::cameraPunchLeft, this);
+ mgr->reg(MtEvent::CAMERA_PUNCH_RIGHT, SoundMaker::cameraPunchRight, this);
+ mgr->reg(MtEvent::NODE_DUG, SoundMaker::nodeDug, this);
+ mgr->reg(MtEvent::PLAYER_DAMAGE, SoundMaker::playerDamage, this);
+ mgr->reg(MtEvent::PLAYER_FALLING_DAMAGE, SoundMaker::playerFallingDamage, this);
+ }
+
+ void step(float dtime)
+ {
+ m_player_step_timer -= dtime;
+ m_player_jump_timer -= dtime;
+ }
+};
+
+// Locally stored sounds don't need to be preloaded because of this
+class GameOnDemandSoundFetcher: public OnDemandSoundFetcher
+{
+ std::set<std::string> m_fetched;
+private:
+ void paths_insert(std::set<std::string> &dst_paths,
+ const std::string &base,
+ const std::string &name)
+ {
+ dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".ogg");
+ dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".0.ogg");
+ dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".1.ogg");
+ dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".2.ogg");
+ dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".3.ogg");
+ dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".4.ogg");
+ dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".5.ogg");
+ dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".6.ogg");
+ dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".7.ogg");
+ dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".8.ogg");
+ dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".9.ogg");
+ }
+public:
+ void fetchSounds(const std::string &name,
+ std::set<std::string> &dst_paths,
+ std::set<std::string> &dst_datas)
+ {
+ if (m_fetched.count(name))
+ return;
+
+ m_fetched.insert(name);
+
+ paths_insert(dst_paths, porting::path_share, name);
+ paths_insert(dst_paths, porting::path_user, name);
+ }
+};
+
+
+// before 1.8 there isn't a "integer interface", only float
+#if (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 8)
+typedef f32 SamplerLayer_t;
+#else
+typedef s32 SamplerLayer_t;
+#endif
+
+
+class GameGlobalShaderConstantSetter : public IShaderConstantSetter
+{
+ Sky *m_sky;
+ bool *m_force_fog_off;
+ f32 *m_fog_range;
+ bool m_fog_enabled;
+ CachedPixelShaderSetting<float, 4> m_sky_bg_color;
+ CachedPixelShaderSetting<float> m_fog_distance;
+ CachedVertexShaderSetting<float> m_animation_timer_vertex;
+ CachedPixelShaderSetting<float> m_animation_timer_pixel;
+ CachedPixelShaderSetting<float, 3> m_day_light;
+ CachedPixelShaderSetting<float, 4> m_star_color;
+ CachedPixelShaderSetting<float, 3> m_eye_position_pixel;
+ CachedVertexShaderSetting<float, 3> m_eye_position_vertex;
+ CachedPixelShaderSetting<float, 3> m_minimap_yaw;
+ CachedPixelShaderSetting<float, 3> m_camera_offset_pixel;
+ CachedPixelShaderSetting<float, 3> m_camera_offset_vertex;
+ CachedPixelShaderSetting<SamplerLayer_t> m_base_texture;
+ Client *m_client;
+
+public:
+ void onSettingsChange(const std::string &name)
+ {
+ if (name == "enable_fog")
+ m_fog_enabled = g_settings->getBool("enable_fog");
+ }
+
+ static void settingsCallback(const std::string &name, void *userdata)
+ {
+ reinterpret_cast<GameGlobalShaderConstantSetter*>(userdata)->onSettingsChange(name);
+ }
+
+ void setSky(Sky *sky) { m_sky = sky; }
+
+ GameGlobalShaderConstantSetter(Sky *sky, bool *force_fog_off,
+ f32 *fog_range, Client *client) :
+ m_sky(sky),
+ m_force_fog_off(force_fog_off),
+ m_fog_range(fog_range),
+ m_sky_bg_color("skyBgColor"),
+ m_fog_distance("fogDistance"),
+ m_animation_timer_vertex("animationTimer"),
+ m_animation_timer_pixel("animationTimer"),
+ m_day_light("dayLight"),
+ m_star_color("starColor"),
+ m_eye_position_pixel("eyePosition"),
+ m_eye_position_vertex("eyePosition"),
+ m_minimap_yaw("yawVec"),
+ m_camera_offset_pixel("cameraOffset"),
+ m_camera_offset_vertex("cameraOffset"),
+ m_base_texture("baseTexture"),
+ m_client(client)
+ {
+ g_settings->registerChangedCallback("enable_fog", settingsCallback, this);
+ m_fog_enabled = g_settings->getBool("enable_fog");
+ }
+
+ ~GameGlobalShaderConstantSetter()
+ {
+ g_settings->deregisterChangedCallback("enable_fog", settingsCallback, this);
+ }
+
+ void onSetConstants(video::IMaterialRendererServices *services) override
+ {
+ // Background color
+ video::SColor bgcolor = m_sky->getBgColor();
+ video::SColorf bgcolorf(bgcolor);
+ float bgcolorfa[4] = {
+ bgcolorf.r,
+ bgcolorf.g,
+ bgcolorf.b,
+ bgcolorf.a,
+ };
+ m_sky_bg_color.set(bgcolorfa, services);
+
+ // Fog distance
+ float fog_distance = 10000 * BS;
+
+ if (m_fog_enabled && !*m_force_fog_off)
+ fog_distance = *m_fog_range;
+
+ m_fog_distance.set(&fog_distance, services);
+
+ u32 daynight_ratio = (float)m_client->getEnv().getDayNightRatio();
+ video::SColorf sunlight;
+ get_sunlight_color(&sunlight, daynight_ratio);
+ float dnc[3] = {
+ sunlight.r,
+ sunlight.g,
+ sunlight.b };
+ m_day_light.set(dnc, services);
+
+ video::SColorf star_color = m_sky->getCurrentStarColor();
+ float clr[4] = {star_color.r, star_color.g, star_color.b, star_color.a};
+ m_star_color.set(clr, services);
+
+ u32 animation_timer = porting::getTimeMs() % 1000000;
+ float animation_timer_f = (float)animation_timer / 100000.f;
+ m_animation_timer_vertex.set(&animation_timer_f, services);
+ m_animation_timer_pixel.set(&animation_timer_f, services);
+
+ float eye_position_array[3];
+ v3f epos = m_client->getEnv().getLocalPlayer()->getEyePosition();
+#if (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 8)
+ eye_position_array[0] = epos.X;
+ eye_position_array[1] = epos.Y;
+ eye_position_array[2] = epos.Z;
+#else
+ epos.getAs3Values(eye_position_array);
+#endif
+ m_eye_position_pixel.set(eye_position_array, services);
+ m_eye_position_vertex.set(eye_position_array, services);
+
+ if (m_client->getMinimap()) {
+ float minimap_yaw_array[3];
+ v3f minimap_yaw = m_client->getMinimap()->getYawVec();
+#if (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 8)
+ minimap_yaw_array[0] = minimap_yaw.X;
+ minimap_yaw_array[1] = minimap_yaw.Y;
+ minimap_yaw_array[2] = minimap_yaw.Z;
+#else
+ minimap_yaw.getAs3Values(minimap_yaw_array);
+#endif
+ m_minimap_yaw.set(minimap_yaw_array, services);
+ }
+
+ float camera_offset_array[3];
+ v3f offset = intToFloat(m_client->getCamera()->getOffset(), BS);
+#if (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 8)
+ camera_offset_array[0] = offset.X;
+ camera_offset_array[1] = offset.Y;
+ camera_offset_array[2] = offset.Z;
+#else
+ offset.getAs3Values(camera_offset_array);
+#endif
+ m_camera_offset_pixel.set(camera_offset_array, services);
+ m_camera_offset_vertex.set(camera_offset_array, services);
+
+ SamplerLayer_t base_tex = 0;
+ m_base_texture.set(&base_tex, services);
+ }
+};
+
+
+class GameGlobalShaderConstantSetterFactory : public IShaderConstantSetterFactory
+{
+ Sky *m_sky;
+ bool *m_force_fog_off;
+ f32 *m_fog_range;
+ Client *m_client;
+ std::vector<GameGlobalShaderConstantSetter *> created_nosky;
+public:
+ GameGlobalShaderConstantSetterFactory(bool *force_fog_off,
+ f32 *fog_range, Client *client) :
+ m_sky(NULL),
+ m_force_fog_off(force_fog_off),
+ m_fog_range(fog_range),
+ m_client(client)
+ {}
+
+ void setSky(Sky *sky) {
+ m_sky = sky;
+ for (GameGlobalShaderConstantSetter *ggscs : created_nosky) {
+ ggscs->setSky(m_sky);
+ }
+ created_nosky.clear();
+ }
+
+ virtual IShaderConstantSetter* create()
+ {
+ auto *scs = new GameGlobalShaderConstantSetter(
+ m_sky, m_force_fog_off, m_fog_range, m_client);
+ if (!m_sky)
+ created_nosky.push_back(scs);
+ return scs;
+ }
+};
+
+#ifdef __ANDROID__
+#define SIZE_TAG "size[11,5.5]"
+#else
+#define SIZE_TAG "size[11,5.5,true]" // Fixed size on desktop
+#endif
+
+/****************************************************************************
+ ****************************************************************************/
+
+const float object_hit_delay = 0.2;
+
+struct FpsControl {
+ u32 last_time, busy_time, sleep_time;
+};
+
+
+/* The reason the following structs are not anonymous structs within the
+ * class is that they are not used by the majority of member functions and
+ * many functions that do require objects of thse types do not modify them
+ * (so they can be passed as a const qualified parameter)
+ */
+
+struct GameRunData {
+ u16 dig_index;
+ u16 new_playeritem;
+ PointedThing pointed_old;
+ bool digging;
+ bool punching;
+ bool btn_down_for_dig;
+ bool dig_instantly;
+ bool digging_blocked;
+ bool reset_jump_timer;
+ float nodig_delay_timer;
+ float dig_time;
+ float dig_time_complete;
+ float repeat_place_timer;
+ float object_hit_delay_timer;
+ float time_from_last_punch;
+ ClientActiveObject *selected_object;
+
+ float jump_timer;
+ float damage_flash;
+ float update_draw_list_timer;
+
+ f32 fog_range;
+
+ v3f update_draw_list_last_cam_dir;
+
+ float time_of_day_smooth;
+};
+
+class Game;
+
+struct ClientEventHandler
+{
+ void (Game::*handler)(ClientEvent *, CameraOrientation *);
+};
+
+/****************************************************************************
+ THE GAME
+ ****************************************************************************/
+
+/* This is not intended to be a public class. If a public class becomes
+ * desirable then it may be better to create another 'wrapper' class that
+ * hides most of the stuff in this class (nothing in this class is required
+ * by any other file) but exposes the public methods/data only.
+ */
+class Game {
+public:
+ Game();
+ ~Game();
+
+ bool startup(bool *kill,
+ InputHandler *input,
+ const GameStartData &game_params,
+ std::string &error_message,
+ bool *reconnect,
+ ChatBackend *chat_backend);
+
+ void run();
+ void shutdown();
+
+protected:
+
+ void extendedResourceCleanup();
+
+ // Basic initialisation
+ bool init(const std::string &map_dir, const std::string &address,
+ u16 port, const SubgameSpec &gamespec);
+ bool initSound();
+ bool createSingleplayerServer(const std::string &map_dir,
+ const SubgameSpec &gamespec, u16 port);
+
+ // Client creation
+ bool createClient(const GameStartData &start_data);
+ bool initGui();
+
+ // Client connection
+ bool connectToServer(const GameStartData &start_data,
+ bool *connect_ok, bool *aborted);
+ bool getServerContent(bool *aborted);
+
+ // Main loop
+
+ void updateInteractTimers(f32 dtime);
+ bool checkConnection();
+ bool handleCallbacks();
+ void processQueues();
+ void updateProfilers(const RunStats &stats, const FpsControl &draw_times, f32 dtime);
+ void updateStats(RunStats *stats, const FpsControl &draw_times, f32 dtime);
+ void updateProfilerGraphs(ProfilerGraph *graph);
+
+ // Input related
+ void processUserInput(f32 dtime);
+ void processKeyInput();
+ void processItemSelection(u16 *new_playeritem);
+
+ void dropSelectedItem(bool single_item = false);
+ void openInventory();
+ void openConsole(float scale, const wchar_t *line=NULL);
+ void toggleFreeMove();
+ void toggleFreeMoveAlt();
+ void togglePitchMove();
+ void toggleFast();
+ void toggleNoClip();
+ void toggleCinematic();
+ void toggleAutoforward();
+
+ void toggleMinimap(bool shift_pressed);
+ void toggleFog();
+ void toggleDebug();
+ void toggleUpdateCamera();
+
+ void increaseViewRange();
+ void decreaseViewRange();
+ void toggleFullViewRange();
+ void checkZoomEnabled();
+
+ void updateCameraDirection(CameraOrientation *cam, float dtime);
+ void updateCameraOrientation(CameraOrientation *cam, float dtime);
+ void updatePlayerControl(const CameraOrientation &cam);
+ void step(f32 *dtime);
+ void processClientEvents(CameraOrientation *cam);
+ void updateCamera(u32 busy_time, f32 dtime);
+ void updateSound(f32 dtime);
+ void processPlayerInteraction(f32 dtime, bool show_hud, bool show_debug);
+ /*!
+ * Returns the object or node the player is pointing at.
+ * Also updates the selected thing in the Hud.
+ *
+ * @param[in] shootline the shootline, starting from
+ * the camera position. This also gives the maximal distance
+ * of the search.
+ * @param[in] liquids_pointable if false, liquids are ignored
+ * @param[in] look_for_object if false, objects are ignored
+ * @param[in] camera_offset offset of the camera
+ * @param[out] selected_object the selected object or
+ * NULL if not found
+ */
+ PointedThing updatePointedThing(
+ const core::line3d<f32> &shootline, bool liquids_pointable,
+ bool look_for_object, const v3s16 &camera_offset);
+ void handlePointingAtNothing(const ItemStack &playerItem);
+ void handlePointingAtNode(const PointedThing &pointed,
+ const ItemStack &selected_item, const ItemStack &hand_item, f32 dtime);
+ void handlePointingAtObject(const PointedThing &pointed, const ItemStack &playeritem,
+ const v3f &player_position, bool show_debug);
+ void handleDigging(const PointedThing &pointed, const v3s16 &nodepos,
+ const ItemStack &selected_item, const ItemStack &hand_item, f32 dtime);
+ void updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime,
+ const CameraOrientation &cam);
+
+ // Misc
+ void limitFps(FpsControl *fps_timings, f32 *dtime);
+
+ void showOverlayMessage(const char *msg, float dtime, int percent,
+ bool draw_clouds = true);
+
+ static void settingChangedCallback(const std::string &setting_name, void *data);
+ void readSettings();
+
+ inline bool isKeyDown(GameKeyType k)
+ {
+ return input->isKeyDown(k);
+ }
+ inline bool wasKeyDown(GameKeyType k)
+ {
+ return input->wasKeyDown(k);
+ }
+ inline bool wasKeyPressed(GameKeyType k)
+ {
+ return input->wasKeyPressed(k);
+ }
+ inline bool wasKeyReleased(GameKeyType k)
+ {
+ return input->wasKeyReleased(k);
+ }
+
+#ifdef __ANDROID__
+ void handleAndroidChatInput();
+#endif
+
+private:
+ struct Flags {
+ bool force_fog_off = false;
+ bool disable_camera_update = false;
+ };
+
+ void showDeathFormspec();
+ void showPauseMenu();
+
+ // ClientEvent handlers
+ void handleClientEvent_None(ClientEvent *event, CameraOrientation *cam);
+ void handleClientEvent_PlayerDamage(ClientEvent *event, CameraOrientation *cam);
+ void handleClientEvent_PlayerForceMove(ClientEvent *event, CameraOrientation *cam);
+ void handleClientEvent_Deathscreen(ClientEvent *event, CameraOrientation *cam);
+ void handleClientEvent_ShowFormSpec(ClientEvent *event, CameraOrientation *cam);
+ void handleClientEvent_ShowLocalFormSpec(ClientEvent *event, CameraOrientation *cam);
+ void handleClientEvent_HandleParticleEvent(ClientEvent *event,
+ CameraOrientation *cam);
+ void handleClientEvent_HudAdd(ClientEvent *event, CameraOrientation *cam);
+ void handleClientEvent_HudRemove(ClientEvent *event, CameraOrientation *cam);
+ void handleClientEvent_HudChange(ClientEvent *event, CameraOrientation *cam);
+ void handleClientEvent_SetSky(ClientEvent *event, CameraOrientation *cam);
+ void handleClientEvent_SetSun(ClientEvent *event, CameraOrientation *cam);
+ void handleClientEvent_SetMoon(ClientEvent *event, CameraOrientation *cam);
+ void handleClientEvent_SetStars(ClientEvent *event, CameraOrientation *cam);
+ void handleClientEvent_OverrideDayNigthRatio(ClientEvent *event,
+ CameraOrientation *cam);
+ void handleClientEvent_CloudParams(ClientEvent *event, CameraOrientation *cam);
+
+ void updateChat(f32 dtime, const v2u32 &screensize);
+
+ bool nodePlacement(const ItemDefinition &selected_def, const ItemStack &selected_item,
+ const v3s16 &nodepos, const v3s16 &neighbourpos, const PointedThing &pointed,
+ const NodeMetadata *meta);
+ static const ClientEventHandler clientEventHandler[CLIENTEVENT_MAX];
+
+ InputHandler *input = nullptr;
+
+ Client *client = nullptr;
+ Server *server = nullptr;
+
+ IWritableTextureSource *texture_src = nullptr;
+ IWritableShaderSource *shader_src = nullptr;
+
+ // When created, these will be filled with data received from the server
+ IWritableItemDefManager *itemdef_manager = nullptr;
+ NodeDefManager *nodedef_manager = nullptr;
+
+ GameOnDemandSoundFetcher soundfetcher; // useful when testing
+ ISoundManager *sound = nullptr;
+ bool sound_is_dummy = false;
+ SoundMaker *soundmaker = nullptr;
+
+ ChatBackend *chat_backend = nullptr;
+ LogOutputBuffer m_chat_log_buf;
+
+ EventManager *eventmgr = nullptr;
+ QuicktuneShortcutter *quicktune = nullptr;
+ bool registration_confirmation_shown = false;
+
+ std::unique_ptr<GameUI> m_game_ui;
+ GUIChatConsole *gui_chat_console = nullptr; // Free using ->Drop()
+ MapDrawControl *draw_control = nullptr;
+ Camera *camera = nullptr;
+ Clouds *clouds = nullptr; // Free using ->Drop()
+ Sky *sky = nullptr; // Free using ->Drop()
+ Hud *hud = nullptr;
+ Minimap *mapper = nullptr;
+
+ GameRunData runData;
+ Flags m_flags;
+
+ /* 'cache'
+ This class does take ownership/responsibily for cleaning up etc of any of
+ these items (e.g. device)
+ */
+ IrrlichtDevice *device;
+ video::IVideoDriver *driver;
+ scene::ISceneManager *smgr;
+ bool *kill;
+ std::string *error_message;
+ bool *reconnect_requested;
+ scene::ISceneNode *skybox;
+
+ bool simple_singleplayer_mode;
+ /* End 'cache' */
+
+ /* Pre-calculated values
+ */
+ int crack_animation_length;
+
+ IntervalLimiter profiler_interval;
+
+ /*
+ * TODO: Local caching of settings is not optimal and should at some stage
+ * be updated to use a global settings object for getting thse values
+ * (as opposed to the this local caching). This can be addressed in
+ * a later release.
+ */
+ 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;
+ bool m_cache_enable_noclip;
+ bool m_cache_enable_free_move;
+ f32 m_cache_mouse_sensitivity;
+ f32 m_cache_joystick_frustum_sensitivity;
+ f32 m_repeat_place_time;
+ f32 m_cache_cam_smoothing;
+ f32 m_cache_fog_start;
+
+ bool m_invert_mouse = false;
+ bool m_first_loop_after_window_activation = false;
+ bool m_camera_offset_changed = false;
+
+ bool m_does_lost_focus_pause_game = false;
+
+ int m_reset_HW_buffer_counter = 0;
+#ifdef __ANDROID__
+ bool m_cache_hold_aux1;
+ bool m_android_chat_open;
+#endif
+};
+>>>>>>> 9736b9cea5f841bb0e9bb2c9c05c3b2560327064
Game::Game() :
m_chat_log_buf(g_logger),
diff --git a/src/client/game.h b/src/client/game.h
index fd93ef82d..af0b7ef54 100644
--- a/src/client/game.h
+++ b/src/client/game.h
@@ -188,13 +188,7 @@ struct LocalFormspecHandler : public TextDest
return;
}
- if (fields.find("quit") != fields.end()) {
- return;
- }
-
- if (fields.find("btn_continue") != fields.end()) {
- return;
- }
+ return;
}
if (m_formname == "MT_DEATH_SCREEN") {
diff --git a/src/client/gameui.h b/src/client/gameui.h
index 8a1b5650d..f04fd97b8 100644
--- a/src/client/gameui.h
+++ b/src/client/gameui.h
@@ -20,6 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#pragma once
+#include "irrlichttypes.h"
#include <IGUIEnvironment.h>
#include "gui/guiFormSpecMenu.h"
#include "util/enriched_string.h"
diff --git a/src/client/hud.cpp b/src/client/hud.cpp
index e956c2738..46736b325 100644
--- a/src/client/hud.cpp
+++ b/src/client/hud.cpp
@@ -571,8 +571,6 @@ void Hud::drawCompassTranslate(HudElement *e, video::ITexture *texture,
void Hud::drawCompassRotate(HudElement *e, video::ITexture *texture,
const core::rect<s32> &rect, int angle)
{
- core::dimension2di imgsize(texture->getOriginalSize());
-
core::rect<s32> oldViewPort = driver->getViewPort();
core::matrix4 oldProjMat = driver->getTransform(video::ETS_PROJECTION);
core::matrix4 oldViewMat = driver->getTransform(video::ETS_VIEW);
diff --git a/src/client/keycode.cpp b/src/client/keycode.cpp
index 6a0e9f569..ce5214f54 100644
--- a/src/client/keycode.cpp
+++ b/src/client/keycode.cpp
@@ -316,7 +316,8 @@ KeyPress::KeyPress(const char *name)
int chars_read = mbtowc(&Char, name, 1);
FATAL_ERROR_IF(chars_read != 1, "Unexpected multibyte character");
m_name = "";
- warningstream << "KeyPress: Unknown key '" << name << "', falling back to first char.";
+ warningstream << "KeyPress: Unknown key '" << name
+ << "', falling back to first char." << std::endl;
}
KeyPress::KeyPress(const irr::SEvent::SKeyInput &in, bool prefer_character)
diff --git a/src/client/mapblock_mesh.cpp b/src/client/mapblock_mesh.cpp
index b7c5ea16a..d8e6ee054 100644
--- a/src/client/mapblock_mesh.cpp
+++ b/src/client/mapblock_mesh.cpp
@@ -1244,13 +1244,6 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
}
if (m_mesh[layer]) {
-#if 0
- // Usually 1-700 faces and 1-7 materials
- std::cout << "Updated MapBlock has " << fastfaces_new.size()
- << " faces and uses " << m_mesh[layer]->getMeshBufferCount()
- << " materials (meshbuffers)" << std::endl;
-#endif
-
// Use VBO for mesh (this just would set this for ever buffer)
if (m_enable_vbo)
m_mesh[layer]->setHardwareMappingHint(scene::EHM_STATIC);
diff --git a/src/client/mapblock_mesh.h b/src/client/mapblock_mesh.h
index 6f454d348..80075fce2 100644
--- a/src/client/mapblock_mesh.h
+++ b/src/client/mapblock_mesh.h
@@ -125,8 +125,6 @@ public:
m_animation_force_timer--;
}
- void updateCameraOffset(v3s16 camera_offset);
-
std::set<v3s16> esp_nodes;
private:
diff --git a/src/client/mesh_generator_thread.h b/src/client/mesh_generator_thread.h
index 8e13dec3e..f393e2368 100644
--- a/src/client/mesh_generator_thread.h
+++ b/src/client/mesh_generator_thread.h
@@ -40,7 +40,6 @@ struct QueuedMeshUpdate
{
v3s16 p = v3s16(-1337, -1337, -1337);
bool ack_block_to_server = false;
- bool urgent = false;
int crack_level = -1;
v3s16 crack_pos;
MeshMakeData *data = nullptr; // This is generated in MeshUpdateQueue::pop()
diff --git a/src/client/minimap.h b/src/client/minimap.h
index 4a2c462f8..87c9668ee 100644
--- a/src/client/minimap.h
+++ b/src/client/minimap.h
@@ -138,7 +138,6 @@ public:
size_t getMaxModeIndex() const { return m_modes.size() - 1; };
void nextMode();
- void setModesFromString(std::string modes_string);
MinimapModeDef getModeDef() const { return data->mode; }
video::ITexture *getMinimapTexture();
diff --git a/src/client/render/factory.cpp b/src/client/render/factory.cpp
index 30f9480fc..7fcec40dd 100644
--- a/src/client/render/factory.cpp
+++ b/src/client/render/factory.cpp
@@ -19,7 +19,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
*/
#include "factory.h"
-#include <stdexcept>
+#include "log.h"
#include "plain.h"
#include "anaglyph.h"
#include "interlaced.h"
@@ -45,5 +45,8 @@ RenderingCore *createRenderingCore(const std::string &stereo_mode, IrrlichtDevic
return new RenderingCoreSideBySide(device, client, hud, true);
if (stereo_mode == "crossview")
return new RenderingCoreSideBySide(device, client, hud, false, true);
- throw std::invalid_argument("Invalid rendering mode: " + stereo_mode);
+
+ // fallback to plain renderer
+ errorstream << "Invalid rendering mode: " << stereo_mode << std::endl;
+ return new RenderingCorePlain(device, client, hud);
}
diff --git a/src/client/renderingengine.cpp b/src/client/renderingengine.cpp
index 22c8a8102..5987ef4aa 100644
--- a/src/client/renderingengine.cpp
+++ b/src/client/renderingengine.cpp
@@ -153,7 +153,7 @@ RenderingEngine::RenderingEngine(IEventReceiver *receiver)
RenderingEngine::~RenderingEngine()
{
core.reset();
- m_device->drop();
+ m_device->closeDevice();
s_singleton = nullptr;
}
diff --git a/src/client/shader.h b/src/client/shader.h
index d99182693..38ab76704 100644
--- a/src/client/shader.h
+++ b/src/client/shader.h
@@ -20,8 +20,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#pragma once
-#include <IMaterialRendererServices.h>
#include "irrlichttypes_bloated.h"
+#include <IMaterialRendererServices.h>
#include <string>
#include "tile.h"
#include "nodedef.h"
diff --git a/src/client/sky.h b/src/client/sky.h
index 10e1cd976..342a97596 100644
--- a/src/client/sky.h
+++ b/src/client/sky.h
@@ -17,10 +17,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
+#include "irrlichttypes_extrabloated.h"
#include <ISceneNode.h>
#include <array>
#include "camera.h"
-#include "irrlichttypes_extrabloated.h"
#include "irr_ptr.h"
#include "shader.h"
#include "skyparams.h"
diff --git a/src/client/tile.cpp b/src/client/tile.cpp
index 37836d0df..aad956ada 100644
--- a/src/client/tile.cpp
+++ b/src/client/tile.cpp
@@ -429,7 +429,6 @@ private:
// Cached settings needed for making textures from meshes
bool m_setting_trilinear_filter;
bool m_setting_bilinear_filter;
- bool m_setting_anisotropic_filter;
};
IWritableTextureSource *createTextureSource()
@@ -450,7 +449,6 @@ TextureSource::TextureSource()
// for these settings to take effect
m_setting_trilinear_filter = g_settings->getBool("trilinear_filter");
m_setting_bilinear_filter = g_settings->getBool("bilinear_filter");
- m_setting_anisotropic_filter = g_settings->getBool("anisotropic_filter");
}
TextureSource::~TextureSource()
diff --git a/src/clientiface.cpp b/src/clientiface.cpp
index 01852c5d1..797afd3c1 100644
--- a/src/clientiface.cpp
+++ b/src/clientiface.cpp
@@ -452,9 +452,6 @@ void RemoteClient::notifyEvent(ClientStateEvent event)
case CSE_Hello:
m_state = CS_HelloSent;
break;
- case CSE_InitLegacy:
- m_state = CS_AwaitingInit2;
- break;
case CSE_Disconnect:
m_state = CS_Disconnecting;
break;
diff --git a/src/clientiface.h b/src/clientiface.h
index eabffb0b6..cc5292b71 100644
--- a/src/clientiface.h
+++ b/src/clientiface.h
@@ -25,6 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "serialization.h" // for SER_FMT_VER_INVALID
#include "network/networkpacket.h"
#include "network/networkprotocol.h"
+#include "network/address.h"
#include "porting.h"
#include <list>
@@ -188,7 +189,6 @@ enum ClientStateEvent
{
CSE_Hello,
CSE_AuthAccept,
- CSE_InitLegacy,
CSE_GotInit2,
CSE_SetDenied,
CSE_SetDefinitionsSent,
@@ -338,17 +338,24 @@ public:
u8 getMajor() const { return m_version_major; }
u8 getMinor() const { return m_version_minor; }
u8 getPatch() const { return m_version_patch; }
- const std::string &getFull() const { return m_full_version; }
+ const std::string &getFullVer() const { return m_full_version; }
void setLangCode(const std::string &code) { m_lang_code = code; }
const std::string &getLangCode() const { return m_lang_code; }
+
+ void setCachedAddress(const Address &addr) { m_addr = addr; }
+ const Address &getAddress() const { return m_addr; }
+
private:
// Version is stored in here after INIT before INIT2
u8 m_pending_serialization_version = SER_FMT_VER_INVALID;
/* current state of client */
ClientState m_state = CS_Created;
-
+
+ // Cached here so retrieval doesn't have to go to connection API
+ Address m_addr;
+
// Client sent language code
std::string m_lang_code;
@@ -412,7 +419,7 @@ private:
/*
client information
- */
+ */
u8 m_version_major = 0;
u8 m_version_minor = 0;
u8 m_version_patch = 0;
diff --git a/src/constants.h b/src/constants.h
index c17f3b6af..3cc3af094 100644
--- a/src/constants.h
+++ b/src/constants.h
@@ -89,7 +89,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
// Size of player's main inventory
#define PLAYER_INVENTORY_SIZE (8 * 4)
-// Default maximum hit points of a player
+// Default maximum health points of a player
#define PLAYER_MAX_HP_DEFAULT 20
// Default maximal breath of a player
diff --git a/src/content/subgames.cpp b/src/content/subgames.cpp
index c6350f2dd..e9dc609b0 100644
--- a/src/content/subgames.cpp
+++ b/src/content/subgames.cpp
@@ -329,18 +329,16 @@ void loadGameConfAndInitWorld(const std::string &path, const std::string &name,
}
}
- // Override defaults with those provided by the game.
- // We clear and reload the defaults because the defaults
- // might have been overridden by other subgame config
- // files that were loaded before.
- g_settings->clearDefaults();
- set_default_settings(g_settings);
-
- Settings game_defaults;
- getGameMinetestConfig(gamespec.path, game_defaults);
- game_defaults.removeSecureSettings();
+ Settings *game_settings = Settings::getLayer(SL_GAME);
+ const bool new_game_settings = (game_settings == nullptr);
+ if (new_game_settings) {
+ // Called by main-menu without a Server instance running
+ // -> create and free manually
+ game_settings = Settings::createLayer(SL_GAME);
+ }
- g_settings->overrideDefaults(&game_defaults);
+ getGameMinetestConfig(gamespec.path, *game_settings);
+ game_settings->removeSecureSettings();
infostream << "Initializing world at " << final_path << std::endl;
@@ -381,4 +379,8 @@ void loadGameConfAndInitWorld(const std::string &path, const std::string &name,
fs::safeWriteToFile(map_meta_path, oss.str());
}
+
+ // The Settings object is no longer needed for created worlds
+ if (new_game_settings)
+ delete game_settings;
}
diff --git a/src/database/database-files.cpp b/src/database/database-files.cpp
index d2b0b1543..d9d113b4e 100644
--- a/src/database/database-files.cpp
+++ b/src/database/database-files.cpp
@@ -19,6 +19,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <cassert>
#include <json/json.h>
+#include "convert_json.h"
#include "database-files.h"
#include "remoteplayer.h"
#include "settings.h"
@@ -36,29 +37,116 @@ PlayerDatabaseFiles::PlayerDatabaseFiles(const std::string &savedir) : m_savedir
fs::CreateDir(m_savedir);
}
-void PlayerDatabaseFiles::serialize(std::ostringstream &os, RemotePlayer *player)
+void PlayerDatabaseFiles::deSerialize(RemotePlayer *p, std::istream &is,
+ const std::string &playername, PlayerSAO *sao)
+{
+ Settings args("PlayerArgsEnd");
+
+ if (!args.parseConfigLines(is)) {
+ throw SerializationError("PlayerArgsEnd of player " + playername + " not found!");
+ }
+
+ p->m_dirty = true;
+ //args.getS32("version"); // Version field value not used
+ const std::string &name = args.get("name");
+ strlcpy(p->m_name, name.c_str(), PLAYERNAME_SIZE);
+
+ if (sao) {
+ try {
+ sao->setHPRaw(args.getU16("hp"));
+ } catch(SettingNotFoundException &e) {
+ sao->setHPRaw(PLAYER_MAX_HP_DEFAULT);
+ }
+
+ try {
+ sao->setBasePosition(args.getV3F("position"));
+ } catch (SettingNotFoundException &e) {}
+
+ try {
+ sao->setLookPitch(args.getFloat("pitch"));
+ } catch (SettingNotFoundException &e) {}
+ try {
+ sao->setPlayerYaw(args.getFloat("yaw"));
+ } catch (SettingNotFoundException &e) {}
+
+ try {
+ sao->setBreath(args.getU16("breath"), false);
+ } catch (SettingNotFoundException &e) {}
+
+ try {
+ const std::string &extended_attributes = args.get("extended_attributes");
+ std::istringstream iss(extended_attributes);
+ Json::CharReaderBuilder builder;
+ builder.settings_["collectComments"] = false;
+ std::string errs;
+
+ Json::Value attr_root;
+ Json::parseFromStream(builder, iss, &attr_root, &errs);
+
+ const Json::Value::Members attr_list = attr_root.getMemberNames();
+ for (const auto &it : attr_list) {
+ Json::Value attr_value = attr_root[it];
+ sao->getMeta().setString(it, attr_value.asString());
+ }
+ sao->getMeta().setModified(false);
+ } catch (SettingNotFoundException &e) {}
+ }
+
+ try {
+ p->inventory.deSerialize(is);
+ } catch (SerializationError &e) {
+ errorstream << "Failed to deserialize player inventory. player_name="
+ << name << " " << e.what() << std::endl;
+ }
+
+ if (!p->inventory.getList("craftpreview") && p->inventory.getList("craftresult")) {
+ // Convert players without craftpreview
+ p->inventory.addList("craftpreview", 1);
+
+ bool craftresult_is_preview = true;
+ if(args.exists("craftresult_is_preview"))
+ craftresult_is_preview = args.getBool("craftresult_is_preview");
+ if(craftresult_is_preview)
+ {
+ // Clear craftresult
+ p->inventory.getList("craftresult")->changeItem(0, ItemStack());
+ }
+ }
+}
+
+void PlayerDatabaseFiles::serialize(RemotePlayer *p, std::ostream &os)
{
// Utilize a Settings object for storing values
- Settings args;
+ Settings args("PlayerArgsEnd");
args.setS32("version", 1);
- args.set("name", player->getName());
+ args.set("name", p->m_name);
- sanity_check(player->getPlayerSAO());
- args.setU16("hp", player->getPlayerSAO()->getHP());
- args.setV3F("position", player->getPlayerSAO()->getBasePosition());
- args.setFloat("pitch", player->getPlayerSAO()->getLookPitch());
- args.setFloat("yaw", player->getPlayerSAO()->getRotation().Y);
- args.setU16("breath", player->getPlayerSAO()->getBreath());
+ PlayerSAO *sao = p->getPlayerSAO();
+ // This should not happen
+ sanity_check(sao);
+ args.setU16("hp", sao->getHP());
+ args.setV3F("position", sao->getBasePosition());
+ args.setFloat("pitch", sao->getLookPitch());
+ args.setFloat("yaw", sao->getRotation().Y);
+ args.setU16("breath", sao->getBreath());
std::string extended_attrs;
- player->serializeExtraAttributes(extended_attrs);
+ {
+ // serializeExtraAttributes
+ Json::Value json_root;
+
+ const StringMap &attrs = sao->getMeta().getStrings();
+ for (const auto &attr : attrs) {
+ json_root[attr.first] = attr.second;
+ }
+
+ extended_attrs = fastWriteJson(json_root);
+ }
args.set("extended_attributes", extended_attrs);
args.writeLines(os);
- os << "PlayerArgsEnd\n";
-
- player->inventory.serialize(os);
+ p->inventory.serialize(os);
}
void PlayerDatabaseFiles::savePlayer(RemotePlayer *player)
@@ -83,7 +171,7 @@ void PlayerDatabaseFiles::savePlayer(RemotePlayer *player)
return;
}
- testplayer.deSerialize(is, path, NULL);
+ deSerialize(&testplayer, is, path, NULL);
is.close();
if (strcmp(testplayer.getName(), player->getName()) == 0) {
path_found = true;
@@ -101,7 +189,7 @@ void PlayerDatabaseFiles::savePlayer(RemotePlayer *player)
// Open and serialize file
std::ostringstream ss(std::ios_base::binary);
- serialize(ss, player);
+ serialize(player, ss);
if (!fs::safeWriteToFile(path, ss.str())) {
infostream << "Failed to write " << path << std::endl;
}
@@ -121,7 +209,7 @@ bool PlayerDatabaseFiles::removePlayer(const std::string &name)
if (!is.good())
continue;
- temp_player.deSerialize(is, path, NULL);
+ deSerialize(&temp_player, is, path, NULL);
is.close();
if (temp_player.getName() == name) {
@@ -147,7 +235,7 @@ bool PlayerDatabaseFiles::loadPlayer(RemotePlayer *player, PlayerSAO *sao)
if (!is.good())
continue;
- player->deSerialize(is, path, sao);
+ deSerialize(player, is, path, sao);
is.close();
if (player->getName() == player_to_load)
@@ -180,7 +268,7 @@ void PlayerDatabaseFiles::listPlayers(std::vector<std::string> &res)
// Null env & dummy peer_id
PlayerSAO playerSAO(NULL, &player, 15789, false);
- player.deSerialize(is, "", &playerSAO);
+ deSerialize(&player, is, "", &playerSAO);
is.close();
res.emplace_back(player.getName());
diff --git a/src/database/database-files.h b/src/database/database-files.h
index cb830a3ed..e647a2e24 100644
--- a/src/database/database-files.h
+++ b/src/database/database-files.h
@@ -38,7 +38,14 @@ public:
void listPlayers(std::vector<std::string> &res);
private:
- void serialize(std::ostringstream &os, RemotePlayer *player);
+ void deSerialize(RemotePlayer *p, std::istream &is, const std::string &playername,
+ PlayerSAO *sao);
+ /*
+ serialize() writes a bunch of text that can contain
+ any characters except a '\0', and such an ending that
+ deSerialize stops reading exactly at the right point.
+ */
+ void serialize(RemotePlayer *p, std::ostream &os);
std::string m_savedir;
};
diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp
index 23e06ab05..6e4e348d0 100644
--- a/src/defaultsettings.cpp
+++ b/src/defaultsettings.cpp
@@ -27,8 +27,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "mapgen/mapgen.h" // Mapgen::setDefaultSettings
#include "util/string.h"
-void set_default_settings(Settings *settings)
+void set_default_settings()
{
+ Settings *settings = Settings::createLayer(SL_DEFAULTS);
+
// Client and server
settings->setDefault("language", "");
settings->setDefault("name", "");
@@ -351,7 +353,7 @@ void set_default_settings(Settings *settings)
// Main menu
settings->setDefault("main_menu_path", "");
- settings->setDefault("serverlist_file", "favoriteservers.txt");
+ settings->setDefault("serverlist_file", "favoriteservers.json");
#if USE_FREETYPE
settings->setDefault("freetype", "true");
@@ -527,7 +529,6 @@ void set_default_settings(Settings *settings)
settings->setDefault("screen_h", "0");
settings->setDefault("fullscreen", "true");
settings->setDefault("touchtarget", "true");
- settings->setDefault("TMPFolder", porting::path_cache);
settings->setDefault("touchscreen_threshold","20");
settings->setDefault("fixed_virtual_joystick", "false");
settings->setDefault("virtual_joystick_triggers_aux", "false");
diff --git a/src/defaultsettings.h b/src/defaultsettings.h
index c81e88502..c239b3c12 100644
--- a/src/defaultsettings.h
+++ b/src/defaultsettings.h
@@ -25,11 +25,4 @@ class Settings;
* initialize basic default settings
* @param settings pointer to settings
*/
-void set_default_settings(Settings *settings);
-
-/**
- * override a default settings by settings from another settings element
- * @param settings target settings pointer
- * @param from source settings pointer
- */
-void override_default_settings(Settings *settings, Settings *from);
+void set_default_settings();
diff --git a/src/emerge.cpp b/src/emerge.cpp
index 12e407797..e0dc5628e 100644
--- a/src/emerge.cpp
+++ b/src/emerge.cpp
@@ -396,14 +396,7 @@ int EmergeManager::getGroundLevelAtPoint(v2s16 p)
// TODO(hmmmm): Move this to ServerMap
bool EmergeManager::isBlockUnderground(v3s16 blockpos)
{
-#if 0
- v2s16 p = v2s16((blockpos.X * MAP_BLOCKSIZE) + MAP_BLOCKSIZE / 2,
- (blockpos.Y * MAP_BLOCKSIZE) + MAP_BLOCKSIZE / 2);
- int ground_level = getGroundLevelAtPoint(p);
- return blockpos.Y * (MAP_BLOCKSIZE + 1) <= min(water_level, ground_level);
-#endif
-
- // Use a simple heuristic; the above method is wildly inaccurate anyway.
+ // Use a simple heuristic
return blockpos.Y * (MAP_BLOCKSIZE + 1) <= mgparams->water_level;
}
diff --git a/src/exceptions.h b/src/exceptions.h
index c54307653..a558adc5d 100644
--- a/src/exceptions.h
+++ b/src/exceptions.h
@@ -72,11 +72,6 @@ public:
SettingNotFoundException(const std::string &s): BaseException(s) {}
};
-class InvalidFilenameException : public BaseException {
-public:
- InvalidFilenameException(const std::string &s): BaseException(s) {}
-};
-
class ItemNotFoundException : public BaseException {
public:
ItemNotFoundException(const std::string &s): BaseException(s) {}
diff --git a/src/filesys.cpp b/src/filesys.cpp
index 28a33f4d0..5ffb4506e 100644
--- a/src/filesys.cpp
+++ b/src/filesys.cpp
@@ -27,9 +27,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "log.h"
#include "config.h"
#include "porting.h"
-#ifdef __ANDROID__
-#include "settings.h" // For g_settings
-#endif
namespace fs
{
@@ -359,8 +356,9 @@ std::string TempPath()
compatible with lua's os.tmpname which under the default
configuration hardcodes mkstemp("/tmp/lua_XXXXXX").
*/
+
#ifdef __ANDROID__
- return g_settings->get("TMPFolder");
+ return porting::path_cache;
#else
return DIR_DELIM "tmp";
#endif
@@ -401,21 +399,6 @@ void GetRecursiveSubPaths(const std::string &path,
}
}
-bool DeletePaths(const std::vector<std::string> &paths)
-{
- bool success = true;
- // Go backwards to succesfully delete the output of GetRecursiveSubPaths
- for(int i=paths.size()-1; i>=0; i--){
- const std::string &path = paths[i];
- bool did = DeleteSingleFileOrEmptyDirectory(path);
- if(!did){
- errorstream<<"Failed to delete "<<path<<std::endl;
- success = false;
- }
- }
- return success;
-}
-
bool RecursiveDeleteContent(const std::string &path)
{
infostream<<"Removing content of \""<<path<<"\""<<std::endl;
diff --git a/src/filesys.h b/src/filesys.h
index b2c800c55..cfbfa02bf 100644
--- a/src/filesys.h
+++ b/src/filesys.h
@@ -85,9 +85,6 @@ void GetRecursiveSubPaths(const std::string &path,
bool list_files,
const std::set<char> &ignore = {});
-// Tries to delete all, returns false if any failed
-bool DeletePaths(const std::vector<std::string> &paths);
-
// Only pass full paths to this one. True on success.
bool RecursiveDeleteContent(const std::string &path);
diff --git a/src/gui/StyleSpec.h b/src/gui/StyleSpec.h
index f2844ce28..fc92a861b 100644
--- a/src/gui/StyleSpec.h
+++ b/src/gui/StyleSpec.h
@@ -55,6 +55,8 @@ public:
BORDERCOLORS,
BORDERWIDTHS,
SOUND,
+ SPACING,
+ SIZE,
NUM_PROPERTIES,
NONE
};
@@ -119,6 +121,10 @@ public:
return BORDERWIDTHS;
} else if (name == "sound") {
return SOUND;
+ } else if (name == "spacing") {
+ return SPACING;
+ } else if (name == "size") {
+ return SIZE;
} else {
return NONE;
}
@@ -259,27 +265,40 @@ public:
return rect;
}
- irr::core::vector2d<s32> getVector2i(Property prop, irr::core::vector2d<s32> def) const
+ v2f32 getVector2f(Property prop, v2f32 def) const
{
const auto &val = properties[prop];
if (val.empty())
return def;
- irr::core::vector2d<s32> vec;
- if (!parseVector2i(val, &vec))
+ v2f32 vec;
+ if (!parseVector2f(val, &vec))
return def;
return vec;
}
- irr::core::vector2d<s32> getVector2i(Property prop) const
+ v2s32 getVector2i(Property prop, v2s32 def) const
+ {
+ const auto &val = properties[prop];
+ if (val.empty())
+ return def;
+
+ v2f32 vec;
+ if (!parseVector2f(val, &vec))
+ return def;
+
+ return v2s32(vec.X, vec.Y);
+ }
+
+ v2s32 getVector2i(Property prop) const
{
const auto &val = properties[prop];
FATAL_ERROR_IF(val.empty(), "Unexpected missing property");
- irr::core::vector2d<s32> vec;
- parseVector2i(val, &vec);
- return vec;
+ v2f32 vec;
+ parseVector2f(val, &vec);
+ return v2s32(vec.X, vec.Y);
}
gui::IGUIFont *getFont() const
@@ -432,22 +451,20 @@ private:
return true;
}
- bool parseVector2i(const std::string &value, irr::core::vector2d<s32> *parsed_vec) const
+ bool parseVector2f(const std::string &value, v2f32 *parsed_vec) const
{
- irr::core::vector2d<s32> vec;
+ v2f32 vec;
std::vector<std::string> v_vector = split(value, ',');
if (v_vector.size() == 1) {
- s32 x = stoi(v_vector[0]);
+ f32 x = stof(v_vector[0]);
vec.X = x;
vec.Y = x;
} else if (v_vector.size() == 2) {
- s32 x = stoi(v_vector[0]);
- s32 y = stoi(v_vector[1]);
- vec.X = x;
- vec.Y = y;
+ vec.X = stof(v_vector[0]);
+ vec.Y = stof(v_vector[1]);
} else {
- warningstream << "Invalid vector2d string format: \"" << value
+ warningstream << "Invalid 2d vector string format: \"" << value
<< "\"" << std::endl;
return false;
}
diff --git a/src/gui/guiButtonItemImage.cpp b/src/gui/guiButtonItemImage.cpp
index d8b9042ac..39272fe37 100644
--- a/src/gui/guiButtonItemImage.cpp
+++ b/src/gui/guiButtonItemImage.cpp
@@ -39,7 +39,6 @@ GUIButtonItemImage::GUIButtonItemImage(gui::IGUIEnvironment *environment,
item, getActiveFont(), client);
sendToBack(m_image);
- m_item_name = item;
m_client = client;
}
diff --git a/src/gui/guiButtonItemImage.h b/src/gui/guiButtonItemImage.h
index aad923bda..b90ac757e 100644
--- a/src/gui/guiButtonItemImage.h
+++ b/src/gui/guiButtonItemImage.h
@@ -42,7 +42,6 @@ public:
Client *client);
private:
- std::string m_item_name;
Client *m_client;
GUIItemImage *m_image;
};
diff --git a/src/gui/guiChatConsole.h b/src/gui/guiChatConsole.h
index 204f9f9cc..896342ab0 100644
--- a/src/gui/guiChatConsole.h
+++ b/src/gui/guiChatConsole.h
@@ -68,8 +68,6 @@ public:
// Irrlicht draw method
virtual void draw();
- bool canTakeFocus(gui::IGUIElement* element) { return false; }
-
virtual bool OnEvent(const SEvent& event);
virtual void setVisible(bool visible);
diff --git a/src/gui/guiConfirmRegistration.cpp b/src/gui/guiConfirmRegistration.cpp
index 020a2796a..4a798c39b 100644
--- a/src/gui/guiConfirmRegistration.cpp
+++ b/src/gui/guiConfirmRegistration.cpp
@@ -192,8 +192,7 @@ void GUIConfirmRegistration::acceptInput()
bool GUIConfirmRegistration::processInput()
{
- std::wstring m_password_ws = narrow_to_wide(m_password);
- if (m_password_ws != m_pass_confirm) {
+ if (utf8_to_wide(m_password) != m_pass_confirm) {
gui::IGUIElement *e = getElementFromId(ID_message);
if (e)
e->setVisible(true);
diff --git a/src/gui/guiEditBox.cpp b/src/gui/guiEditBox.cpp
index 11d080be9..79979dbc3 100644
--- a/src/gui/guiEditBox.cpp
+++ b/src/gui/guiEditBox.cpp
@@ -689,6 +689,46 @@ bool GUIEditBox::onKeyDelete(const SEvent &event, s32 &mark_begin, s32 &mark_end
return true;
}
+void GUIEditBox::inputChar(wchar_t c)
+{
+ if (!isEnabled() || !m_writable)
+ return;
+
+ if (c != 0) {
+ if (Text.size() < m_max || m_max == 0) {
+ core::stringw s;
+
+ if (m_mark_begin != m_mark_end) {
+ // clang-format off
+ // replace marked text
+ s32 real_begin = m_mark_begin < m_mark_end ? m_mark_begin : m_mark_end;
+ s32 real_end = m_mark_begin < m_mark_end ? m_mark_end : m_mark_begin;
+
+ s = Text.subString(0, real_begin);
+ s.append(c);
+ s.append(Text.subString(real_end, Text.size() - real_end));
+ Text = s;
+ m_cursor_pos = real_begin + 1;
+ // clang-format on
+ } else {
+ // add new character
+ s = Text.subString(0, m_cursor_pos);
+ s.append(c);
+ s.append(Text.subString(m_cursor_pos,
+ Text.size() - m_cursor_pos));
+ Text = s;
+ ++m_cursor_pos;
+ }
+
+ m_blink_start_time = porting::getTimeMs();
+ setTextMarkers(0, 0);
+ }
+ }
+ breakText();
+ sendGuiEvent(EGET_EDITBOX_CHANGED);
+ calculateScrollPos();
+}
+
bool GUIEditBox::processMouse(const SEvent &event)
{
switch (event.MouseInput.Event) {
@@ -747,6 +787,7 @@ bool GUIEditBox::processMouse(const SEvent &event)
s32 pos = m_vscrollbar->getPos();
s32 step = m_vscrollbar->getSmallStep();
m_vscrollbar->setPos(pos - event.MouseInput.Wheel * step);
+ return true;
}
break;
default:
@@ -817,3 +858,54 @@ void GUIEditBox::updateVScrollBar()
}
}
}
+
+void GUIEditBox::deserializeAttributes(
+ io::IAttributes *in, io::SAttributeReadWriteOptions *options = 0)
+{
+ IGUIEditBox::deserializeAttributes(in, options);
+
+ setOverrideColor(in->getAttributeAsColor("OverrideColor"));
+ enableOverrideColor(in->getAttributeAsBool("OverrideColorEnabled"));
+ setMax(in->getAttributeAsInt("MaxChars"));
+ setWordWrap(in->getAttributeAsBool("WordWrap"));
+ setMultiLine(in->getAttributeAsBool("MultiLine"));
+ setAutoScroll(in->getAttributeAsBool("AutoScroll"));
+ core::stringw ch = in->getAttributeAsStringW("PasswordChar");
+
+ if (ch.empty())
+ setPasswordBox(in->getAttributeAsBool("PasswordBox"));
+ else
+ setPasswordBox(in->getAttributeAsBool("PasswordBox"), ch[0]);
+
+ setTextAlignment((EGUI_ALIGNMENT)in->getAttributeAsEnumeration(
+ "HTextAlign", GUIAlignmentNames),
+ (EGUI_ALIGNMENT)in->getAttributeAsEnumeration(
+ "VTextAlign", GUIAlignmentNames));
+
+ setWritable(in->getAttributeAsBool("Writable"));
+ // setOverrideFont(in->getAttributeAsFont("OverrideFont"));
+}
+
+//! Writes attributes of the element.
+void GUIEditBox::serializeAttributes(
+ io::IAttributes *out, io::SAttributeReadWriteOptions *options = 0) const
+{
+ // IGUIEditBox::serializeAttributes(out,options);
+
+ out->addBool("OverrideColorEnabled", m_override_color_enabled);
+ out->addColor("OverrideColor", m_override_color);
+ // out->addFont("OverrideFont",m_override_font);
+ out->addInt("MaxChars", m_max);
+ out->addBool("WordWrap", m_word_wrap);
+ out->addBool("MultiLine", m_multiline);
+ out->addBool("AutoScroll", m_autoscroll);
+ out->addBool("PasswordBox", m_passwordbox);
+ core::stringw ch = L" ";
+ ch[0] = m_passwordchar;
+ out->addString("PasswordChar", ch.c_str());
+ out->addEnum("HTextAlign", m_halign, GUIAlignmentNames);
+ out->addEnum("VTextAlign", m_valign, GUIAlignmentNames);
+ out->addBool("Writable", m_writable);
+
+ IGUIEditBox::serializeAttributes(out, options);
+}
diff --git a/src/gui/guiEditBox.h b/src/gui/guiEditBox.h
index 3e41c7e51..c616d75d1 100644
--- a/src/gui/guiEditBox.h
+++ b/src/gui/guiEditBox.h
@@ -19,6 +19,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#pragma once
+#include "irrlichttypes.h"
#include "IGUIEditBox.h"
#include "IOSOperator.h"
#include "guiScrollBar.h"
@@ -129,6 +130,14 @@ public:
//! called if an event happened.
virtual bool OnEvent(const SEvent &event);
+ //! 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);
+
protected:
virtual void breakText() = 0;
@@ -147,7 +156,7 @@ protected:
virtual s32 getCursorPos(s32 x, s32 y) = 0;
bool processKey(const SEvent &event);
- virtual void inputChar(wchar_t c) = 0;
+ virtual void inputChar(wchar_t c);
//! returns the line number that the cursor is on
s32 getLineFromPos(s32 pos);
diff --git a/src/gui/guiEditBoxWithScrollbar.cpp b/src/gui/guiEditBoxWithScrollbar.cpp
index 707dbb7db..c72070787 100644
--- a/src/gui/guiEditBoxWithScrollbar.cpp
+++ b/src/gui/guiEditBoxWithScrollbar.cpp
@@ -481,44 +481,6 @@ void GUIEditBoxWithScrollBar::setTextRect(s32 line)
m_current_text_rect += m_frame_rect.UpperLeftCorner;
}
-
-void GUIEditBoxWithScrollBar::inputChar(wchar_t c)
-{
- if (!isEnabled())
- return;
-
- if (c != 0) {
- if (Text.size() < m_max || m_max == 0) {
- core::stringw s;
-
- if (m_mark_begin != m_mark_end) {
- // replace marked text
- const s32 realmbgn = m_mark_begin < m_mark_end ? m_mark_begin : m_mark_end;
- const s32 realmend = m_mark_begin < m_mark_end ? m_mark_end : m_mark_begin;
-
- s = Text.subString(0, realmbgn);
- s.append(c);
- s.append(Text.subString(realmend, Text.size() - realmend));
- Text = s;
- m_cursor_pos = realmbgn + 1;
- } else {
- // add new character
- s = Text.subString(0, m_cursor_pos);
- s.append(c);
- s.append(Text.subString(m_cursor_pos, Text.size() - m_cursor_pos));
- Text = s;
- ++m_cursor_pos;
- }
-
- m_blink_start_time = porting::getTimeMs();
- setTextMarkers(0, 0);
- }
- }
- breakText();
- calculateScrollPos();
- sendGuiEvent(EGET_EDITBOX_CHANGED);
-}
-
// calculate autoscroll
void GUIEditBoxWithScrollBar::calculateScrollPos()
{
@@ -682,54 +644,21 @@ void GUIEditBoxWithScrollBar::setBackgroundColor(const video::SColor &bg_color)
//! Writes attributes of the element.
void GUIEditBoxWithScrollBar::serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options = 0) const
{
- // IGUIEditBox::serializeAttributes(out,options);
-
out->addBool("Border", m_border);
out->addBool("Background", m_background);
- out->addBool("OverrideColorEnabled", m_override_color_enabled);
- out->addColor("OverrideColor", m_override_color);
// out->addFont("OverrideFont", OverrideFont);
- out->addInt("MaxChars", m_max);
- out->addBool("WordWrap", m_word_wrap);
- out->addBool("MultiLine", m_multiline);
- out->addBool("AutoScroll", m_autoscroll);
- out->addBool("PasswordBox", m_passwordbox);
- core::stringw ch = L" ";
- ch[0] = m_passwordchar;
- out->addString("PasswordChar", ch.c_str());
- out->addEnum("HTextAlign", m_halign, GUIAlignmentNames);
- out->addEnum("VTextAlign", m_valign, GUIAlignmentNames);
- out->addBool("Writable", m_writable);
-
- IGUIEditBox::serializeAttributes(out, options);
+
+ GUIEditBox::serializeAttributes(out, options);
}
//! Reads attributes of the element
void GUIEditBoxWithScrollBar::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options = 0)
{
- IGUIEditBox::deserializeAttributes(in, options);
+ GUIEditBox::deserializeAttributes(in, options);
setDrawBorder(in->getAttributeAsBool("Border"));
setDrawBackground(in->getAttributeAsBool("Background"));
- setOverrideColor(in->getAttributeAsColor("OverrideColor"));
- enableOverrideColor(in->getAttributeAsBool("OverrideColorEnabled"));
- setMax(in->getAttributeAsInt("MaxChars"));
- setWordWrap(in->getAttributeAsBool("WordWrap"));
- setMultiLine(in->getAttributeAsBool("MultiLine"));
- setAutoScroll(in->getAttributeAsBool("AutoScroll"));
- core::stringw ch = in->getAttributeAsStringW("PasswordChar");
-
- if (!ch.size())
- setPasswordBox(in->getAttributeAsBool("PasswordBox"));
- else
- setPasswordBox(in->getAttributeAsBool("PasswordBox"), ch[0]);
-
- setTextAlignment((EGUI_ALIGNMENT)in->getAttributeAsEnumeration("HTextAlign", GUIAlignmentNames),
- (EGUI_ALIGNMENT)in->getAttributeAsEnumeration("VTextAlign", GUIAlignmentNames));
-
- // setOverrideFont(in->getAttributeAsFont("OverrideFont"));
- setWritable(in->getAttributeAsBool("Writable"));
}
bool GUIEditBoxWithScrollBar::isDrawBackgroundEnabled() const { return false; }
diff --git a/src/gui/guiEditBoxWithScrollbar.h b/src/gui/guiEditBoxWithScrollbar.h
index b863ee614..3f7450dcb 100644
--- a/src/gui/guiEditBoxWithScrollbar.h
+++ b/src/gui/guiEditBoxWithScrollbar.h
@@ -49,8 +49,6 @@ protected:
virtual void breakText();
//! sets the area of the given line
virtual void setTextRect(s32 line);
- //! adds a letter to the edit box
- virtual void inputChar(wchar_t c);
//! calculates the current scroll position
void calculateScrollPos();
//! calculated the FrameRect
diff --git a/src/gui/guiEngine.cpp b/src/gui/guiEngine.cpp
index c5ad5c323..93463ad70 100644
--- a/src/gui/guiEngine.cpp
+++ b/src/gui/guiEngine.cpp
@@ -75,8 +75,6 @@ video::ITexture *MenuTextureSource::getTexture(const std::string &name, u32 *id)
if (name.empty())
return NULL;
- m_to_delete.insert(name);
-
#if ENABLE_GLES
video::ITexture *retval = m_driver->findTexture(name.c_str());
if (retval)
@@ -88,6 +86,7 @@ video::ITexture *MenuTextureSource::getTexture(const std::string &name, u32 *id)
image = Align2Npot2(image, m_driver);
retval = m_driver->addTexture(name.c_str(), image);
+ m_to_delete.insert(name);
image->drop();
return retval;
#else
@@ -486,8 +485,6 @@ void GUIEngine::drawHeader(video::IVideoDriver *driver)
splashrect += v2s32((screensize.Width/2)-(splashsize.X/2),
((free_space/2)-splashsize.Y/2)+10);
- video::SColor bgcolor(255,50,50,50);
-
draw2DImageFilterScaled(driver, texture, splashrect,
core::rect<s32>(core::position2d<s32>(0,0),
core::dimension2di(texture->getOriginalSize())),
diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp
index 973fc60a8..5aa6dc9ae 100644
--- a/src/gui/guiFormSpecMenu.cpp
+++ b/src/gui/guiFormSpecMenu.cpp
@@ -19,6 +19,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <cstdlib>
+#include <cmath>
#include <algorithm>
#include <iterator>
#include <limits>
@@ -497,19 +498,39 @@ void GUIFormSpecMenu::parseList(parserData *data, const std::string &element)
3
);
- v2f32 slot_spacing = data->real_coordinates ?
- v2f32(imgsize.X * 1.25f, imgsize.Y * 1.25f) : spacing;
+ auto style = getDefaultStyleForElement("list", spec.fname);
- v2s32 pos = data->real_coordinates ? getRealCoordinateBasePos(v_pos)
- : getElementBasePos(&v_pos);
+ v2f32 slot_scale = style.getVector2f(StyleSpec::SIZE, v2f32(0, 0));
+ v2f32 slot_size(
+ slot_scale.X <= 0 ? imgsize.X : std::max<f32>(slot_scale.X * imgsize.X, 1),
+ slot_scale.Y <= 0 ? imgsize.Y : std::max<f32>(slot_scale.Y * imgsize.Y, 1)
+ );
+
+ v2f32 slot_spacing = style.getVector2f(StyleSpec::SPACING, v2f32(-1, -1));
+ v2f32 default_spacing = data->real_coordinates ?
+ v2f32(imgsize.X * 0.25f, imgsize.Y * 0.25f) :
+ v2f32(spacing.X - imgsize.X, spacing.Y - imgsize.Y);
+
+ slot_spacing.X = slot_spacing.X < 0 ? default_spacing.X :
+ imgsize.X * slot_spacing.X;
+ slot_spacing.Y = slot_spacing.Y < 0 ? default_spacing.Y :
+ imgsize.Y * slot_spacing.Y;
+
+ slot_spacing += slot_size;
+
+ v2s32 pos = data->real_coordinates ? getRealCoordinateBasePos(v_pos) :
+ getElementBasePos(&v_pos);
core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y,
- pos.X + (geom.X - 1) * slot_spacing.X + imgsize.X,
- pos.Y + (geom.Y - 1) * slot_spacing.Y + imgsize.Y);
+ pos.X + (geom.X - 1) * slot_spacing.X + slot_size.X,
+ pos.Y + (geom.Y - 1) * slot_spacing.Y + slot_size.Y);
GUIInventoryList *e = new GUIInventoryList(Environment, data->current_parent,
- spec.fid, rect, m_invmgr, loc, listname, geom, start_i, imgsize,
- slot_spacing, this, data->inventorylist_options, m_font);
+ spec.fid, rect, m_invmgr, loc, listname, geom, start_i,
+ v2s32(slot_size.X, slot_size.Y), slot_spacing, this,
+ data->inventorylist_options, m_font);
+
+ e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false));
m_inventorylists.push_back(e);
m_fields.push_back(spec);
@@ -907,7 +928,7 @@ void GUIFormSpecMenu::parseAnimatedImage(parserData *data, const std::string &el
core::rect<s32> rect = core::rect<s32>(pos, pos + geom);
- GUIAnimatedImage *e = new GUIAnimatedImage(Environment, this, spec.fid,
+ GUIAnimatedImage *e = new GUIAnimatedImage(Environment, data->current_parent, spec.fid,
rect, texture_name, frame_count, frame_duration, m_tsrc);
if (parts.size() >= 7)
@@ -3505,8 +3526,6 @@ bool GUIFormSpecMenu::getAndroidUIInput()
GUIInventoryList::ItemSpec GUIFormSpecMenu::getItemAtPos(v2s32 p) const
{
- core::rect<s32> imgrect(0, 0, imgsize.X, imgsize.Y);
-
for (const GUIInventoryList *e : m_inventorylists) {
s32 item_index = e->getItemIndexAtPos(p);
if (item_index != -1)
@@ -3837,7 +3856,7 @@ ItemStack GUIFormSpecMenu::verifySelectedItem()
return ItemStack();
}
-void GUIFormSpecMenu::acceptInput(FormspecQuitMode quitmode=quit_mode_no)
+void GUIFormSpecMenu::acceptInput(FormspecQuitMode quitmode)
{
if(m_text_dst)
{
diff --git a/src/gui/guiFormSpecMenu.h b/src/gui/guiFormSpecMenu.h
index 37106cb65..d658aba7b 100644
--- a/src/gui/guiFormSpecMenu.h
+++ b/src/gui/guiFormSpecMenu.h
@@ -253,7 +253,7 @@ public:
void updateSelectedItem();
ItemStack verifySelectedItem();
- void acceptInput(FormspecQuitMode quitmode);
+ void acceptInput(FormspecQuitMode quitmode=quit_mode_no);
bool preprocessEvent(const SEvent& event);
bool OnEvent(const SEvent& event);
bool doPause;
diff --git a/src/gui/guiHyperText.cpp b/src/gui/guiHyperText.cpp
index 88931cdf9..ccfdcb81d 100644
--- a/src/gui/guiHyperText.cpp
+++ b/src/gui/guiHyperText.cpp
@@ -1088,7 +1088,7 @@ bool GUIHyperText::OnEvent(const SEvent &event)
if (event.MouseInput.Event == EMIE_MOUSE_MOVED)
checkHover(event.MouseInput.X, event.MouseInput.Y);
- if (event.MouseInput.Event == EMIE_MOUSE_WHEEL) {
+ if (event.MouseInput.Event == EMIE_MOUSE_WHEEL && m_vscrollbar->isVisible()) {
m_vscrollbar->setPos(m_vscrollbar->getPos() -
event.MouseInput.Wheel * m_vscrollbar->getSmallStep());
m_text_scrollpos.Y = -m_vscrollbar->getPos();
diff --git a/src/gui/guiInventoryList.cpp b/src/gui/guiInventoryList.cpp
index 58d7ae771..290ae40e7 100644
--- a/src/gui/guiInventoryList.cpp
+++ b/src/gui/guiInventoryList.cpp
@@ -101,8 +101,6 @@ void GUIInventoryList::draw()
&& m_invmgr->getInventory(selected_item->inventoryloc) == inv
&& selected_item->listname == m_listname
&& selected_item->i == item_i;
- core::rect<s32> clipped_rect(rect);
- clipped_rect.clipAgainst(AbsoluteClippingRect);
bool hovering = m_hovered_i == item_i;
ItemRotationKind rotation_kind = selected ? IT_ROT_SELECTED :
(hovering ? IT_ROT_HOVERED : IT_ROT_NONE);
diff --git a/src/gui/guiKeyChangeMenu.cpp b/src/gui/guiKeyChangeMenu.cpp
index a7270da06..16ca64763 100644
--- a/src/gui/guiKeyChangeMenu.cpp
+++ b/src/gui/guiKeyChangeMenu.cpp
@@ -258,7 +258,7 @@ bool GUIKeyChangeMenu::acceptInput()
{
for (key_setting *k : key_settings) {
std::string default_key;
- g_settings->getDefaultNoEx(k->setting_name, default_key);
+ Settings::getLayer(SL_DEFAULTS)->getNoEx(k->setting_name, default_key);
if (k->key.sym() != default_key)
g_settings->set(k->setting_name, k->key.sym());
diff --git a/src/gui/intlGUIEditBox.cpp b/src/gui/intlGUIEditBox.cpp
index a61619148..0f09ea746 100644
--- a/src/gui/intlGUIEditBox.cpp
+++ b/src/gui/intlGUIEditBox.cpp
@@ -318,10 +318,7 @@ void intlGUIEditBox::draw()
s32 intlGUIEditBox::getCursorPos(s32 x, s32 y)
{
- IGUIFont* font = m_override_font;
- IGUISkin* skin = Environment->getSkin();
- if (!m_override_font)
- font = skin->getFont();
+ IGUIFont* font = getActiveFont();
const u32 lineCount = (m_word_wrap || m_multiline) ? m_broken_text.size() : 1;
@@ -547,49 +544,6 @@ void intlGUIEditBox::setTextRect(s32 line)
}
-void intlGUIEditBox::inputChar(wchar_t c)
-{
- if (!isEnabled() || !m_writable)
- return;
-
- if (c != 0)
- {
- if (Text.size() < m_max || m_max == 0)
- {
- core::stringw s;
-
- if (m_mark_begin != m_mark_end)
- {
- // replace marked text
- const s32 realmbgn = m_mark_begin < m_mark_end ? m_mark_begin : m_mark_end;
- const s32 realmend = m_mark_begin < m_mark_end ? m_mark_end : m_mark_begin;
-
- s = Text.subString(0, realmbgn);
- s.append(c);
- s.append( Text.subString(realmend, Text.size()-realmend) );
- Text = s;
- m_cursor_pos = realmbgn+1;
- }
- else
- {
- // add new character
- s = Text.subString(0, m_cursor_pos);
- s.append(c);
- s.append( Text.subString(m_cursor_pos, Text.size()-m_cursor_pos) );
- Text = s;
- ++m_cursor_pos;
- }
-
- m_blink_start_time = porting::getTimeMs();
- setTextMarkers(0, 0);
- }
- }
- breakText();
- sendGuiEvent(EGET_EDITBOX_CHANGED);
- calculateScrollPos();
-}
-
-
void intlGUIEditBox::calculateScrollPos()
{
if (!m_autoscroll)
@@ -668,56 +622,5 @@ void intlGUIEditBox::createVScrollBar()
m_vscrollbar->setLargeStep(10 * fontHeight);
}
-
-//! Writes attributes of the element.
-void intlGUIEditBox::serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options=0) const
-{
- // IGUIEditBox::serializeAttributes(out,options);
-
- out->addBool ("OverrideColorEnabled", m_override_color_enabled );
- out->addColor ("OverrideColor", m_override_color);
- // out->addFont("OverrideFont",m_override_font);
- out->addInt ("MaxChars", m_max);
- out->addBool ("WordWrap", m_word_wrap);
- out->addBool ("MultiLine", m_multiline);
- out->addBool ("AutoScroll", m_autoscroll);
- out->addBool ("PasswordBox", m_passwordbox);
- core::stringw ch = L" ";
- ch[0] = m_passwordchar;
- out->addString("PasswordChar", ch.c_str());
- out->addEnum ("HTextAlign", m_halign, GUIAlignmentNames);
- out->addEnum ("VTextAlign", m_valign, GUIAlignmentNames);
- out->addBool ("Writable", m_writable);
-
- IGUIEditBox::serializeAttributes(out,options);
-}
-
-
-//! Reads attributes of the element
-void intlGUIEditBox::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options=0)
-{
- IGUIEditBox::deserializeAttributes(in,options);
-
- setOverrideColor(in->getAttributeAsColor("OverrideColor"));
- enableOverrideColor(in->getAttributeAsBool("OverrideColorEnabled"));
- setMax(in->getAttributeAsInt("MaxChars"));
- setWordWrap(in->getAttributeAsBool("WordWrap"));
- setMultiLine(in->getAttributeAsBool("MultiLine"));
- setAutoScroll(in->getAttributeAsBool("AutoScroll"));
- core::stringw ch = in->getAttributeAsStringW("PasswordChar");
-
- if (ch.empty())
- setPasswordBox(in->getAttributeAsBool("PasswordBox"));
- else
- setPasswordBox(in->getAttributeAsBool("PasswordBox"), ch[0]);
-
- setTextAlignment( (EGUI_ALIGNMENT) in->getAttributeAsEnumeration("HTextAlign", GUIAlignmentNames),
- (EGUI_ALIGNMENT) in->getAttributeAsEnumeration("VTextAlign", GUIAlignmentNames));
-
- setWritable(in->getAttributeAsBool("Writable"));
- // setOverrideFont(in->getAttributeAsFont("OverrideFont"));
-}
-
-
} // end namespace gui
} // end namespace irr
diff --git a/src/gui/intlGUIEditBox.h b/src/gui/intlGUIEditBox.h
index 2abc12d1a..007fe1c93 100644
--- a/src/gui/intlGUIEditBox.h
+++ b/src/gui/intlGUIEditBox.h
@@ -38,12 +38,6 @@ namespace gui
//! Updates the absolute position, splits text if required
virtual void updateAbsolutePosition();
- //! 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 void setCursorChar(const wchar_t cursorChar) {}
virtual wchar_t getCursorChar() const { return L'|'; }
@@ -57,8 +51,7 @@ namespace gui
virtual void breakText();
//! sets the area of the given line
virtual void setTextRect(s32 line);
- //! adds a letter to the edit box
- virtual void inputChar(wchar_t c);
+
//! calculates the current scroll position
void calculateScrollPos();
diff --git a/src/gui/modalMenu.cpp b/src/gui/modalMenu.cpp
index 9b1e6dd9c..0d3fb55f0 100644
--- a/src/gui/modalMenu.cpp
+++ b/src/gui/modalMenu.cpp
@@ -183,6 +183,64 @@ static bool isChild(gui::IGUIElement *tocheck, gui::IGUIElement *parent)
return false;
}
+#ifdef __ANDROID__
+
+bool GUIModalMenu::simulateMouseEvent(
+ gui::IGUIElement *target, ETOUCH_INPUT_EVENT touch_event)
+{
+ SEvent mouse_event{}; // value-initialized, not unitialized
+ mouse_event.EventType = EET_MOUSE_INPUT_EVENT;
+ mouse_event.MouseInput.X = m_pointer.X;
+ mouse_event.MouseInput.Y = m_pointer.Y;
+ switch (touch_event) {
+ case ETIE_PRESSED_DOWN:
+ mouse_event.MouseInput.Event = EMIE_LMOUSE_PRESSED_DOWN;
+ mouse_event.MouseInput.ButtonStates = EMBSM_LEFT;
+ break;
+ case ETIE_MOVED:
+ mouse_event.MouseInput.Event = EMIE_MOUSE_MOVED;
+ mouse_event.MouseInput.ButtonStates = EMBSM_LEFT;
+ break;
+ case ETIE_LEFT_UP:
+ mouse_event.MouseInput.Event = EMIE_LMOUSE_LEFT_UP;
+ mouse_event.MouseInput.ButtonStates = 0;
+ break;
+ default:
+ return false;
+ }
+ if (preprocessEvent(mouse_event))
+ return true;
+ if (!target)
+ return false;
+ return target->OnEvent(mouse_event);
+}
+
+void GUIModalMenu::enter(gui::IGUIElement *hovered)
+{
+ sanity_check(!m_hovered);
+ m_hovered.grab(hovered);
+ SEvent gui_event{};
+ gui_event.EventType = EET_GUI_EVENT;
+ gui_event.GUIEvent.Caller = m_hovered.get();
+ gui_event.GUIEvent.EventType = EGET_ELEMENT_HOVERED;
+ gui_event.GUIEvent.Element = gui_event.GUIEvent.Caller;
+ m_hovered->OnEvent(gui_event);
+}
+
+void GUIModalMenu::leave()
+{
+ if (!m_hovered)
+ return;
+ SEvent gui_event{};
+ gui_event.EventType = EET_GUI_EVENT;
+ gui_event.GUIEvent.Caller = m_hovered.get();
+ gui_event.GUIEvent.EventType = EGET_ELEMENT_LEFT;
+ m_hovered->OnEvent(gui_event);
+ m_hovered.reset();
+}
+
+#endif
+
bool GUIModalMenu::preprocessEvent(const SEvent &event)
{
#ifdef __ANDROID__
@@ -230,89 +288,50 @@ bool GUIModalMenu::preprocessEvent(const SEvent &event)
}
if (event.EventType == EET_TOUCH_INPUT_EVENT) {
- SEvent translated;
- memset(&translated, 0, sizeof(SEvent));
- translated.EventType = EET_MOUSE_INPUT_EVENT;
- gui::IGUIElement *root = Environment->getRootGUIElement();
-
- if (!root) {
- errorstream << "GUIModalMenu::preprocessEvent"
- << " unable to get root element" << std::endl;
- return false;
- }
- gui::IGUIElement *hovered =
- root->getElementFromPoint(core::position2d<s32>(
- event.TouchInput.X, event.TouchInput.Y));
-
- translated.MouseInput.X = event.TouchInput.X;
- translated.MouseInput.Y = event.TouchInput.Y;
- translated.MouseInput.Control = false;
+ irr_ptr<GUIModalMenu> holder;
+ holder.grab(this); // keep this alive until return (it might be dropped downstream [?])
- if (event.TouchInput.touchedCount == 1) {
- switch (event.TouchInput.Event) {
- case ETIE_PRESSED_DOWN:
+ switch ((int)event.TouchInput.touchedCount) {
+ case 1: {
+ if (event.TouchInput.Event == ETIE_PRESSED_DOWN || event.TouchInput.Event == ETIE_MOVED)
m_pointer = v2s32(event.TouchInput.X, event.TouchInput.Y);
- translated.MouseInput.Event = EMIE_LMOUSE_PRESSED_DOWN;
- translated.MouseInput.ButtonStates = EMBSM_LEFT;
+ if (event.TouchInput.Event == ETIE_PRESSED_DOWN)
m_down_pos = m_pointer;
- break;
- case ETIE_MOVED:
- m_pointer = v2s32(event.TouchInput.X, event.TouchInput.Y);
- translated.MouseInput.Event = EMIE_MOUSE_MOVED;
- translated.MouseInput.ButtonStates = EMBSM_LEFT;
- break;
- case ETIE_LEFT_UP:
- translated.MouseInput.Event = EMIE_LMOUSE_LEFT_UP;
- translated.MouseInput.ButtonStates = 0;
- hovered = root->getElementFromPoint(m_down_pos);
- // we don't have a valid pointer element use last
- // known pointer pos
- translated.MouseInput.X = m_pointer.X;
- translated.MouseInput.Y = m_pointer.Y;
-
- // reset down pos
- m_down_pos = v2s32(0, 0);
- break;
- default:
- break;
+ gui::IGUIElement *hovered = Environment->getRootGUIElement()->getElementFromPoint(core::position2d<s32>(m_pointer));
+ if (event.TouchInput.Event == ETIE_PRESSED_DOWN)
+ Environment->setFocus(hovered);
+ if (m_hovered != hovered) {
+ leave();
+ enter(hovered);
}
- } else if ((event.TouchInput.touchedCount == 2) &&
- (event.TouchInput.Event == ETIE_PRESSED_DOWN)) {
- hovered = root->getElementFromPoint(m_down_pos);
-
- translated.MouseInput.Event = EMIE_RMOUSE_PRESSED_DOWN;
- translated.MouseInput.ButtonStates = EMBSM_LEFT | EMBSM_RIGHT;
- translated.MouseInput.X = m_pointer.X;
- translated.MouseInput.Y = m_pointer.Y;
- if (hovered)
- hovered->OnEvent(translated);
-
- translated.MouseInput.Event = EMIE_RMOUSE_LEFT_UP;
- translated.MouseInput.ButtonStates = EMBSM_LEFT;
-
- if (hovered)
- hovered->OnEvent(translated);
-
- return true;
- } else {
- // ignore unhandled 2 touch events (accidental moving for example)
+ gui::IGUIElement *focused = Environment->getFocus();
+ bool ret = simulateMouseEvent(focused, event.TouchInput.Event);
+ if (!ret && m_hovered != focused)
+ ret = simulateMouseEvent(m_hovered.get(), event.TouchInput.Event);
+ if (event.TouchInput.Event == ETIE_LEFT_UP)
+ leave();
+ return ret;
+ }
+ case 2: {
+ if (event.TouchInput.Event != ETIE_PRESSED_DOWN)
+ return true; // ignore
+ auto focused = Environment->getFocus();
+ if (!focused)
+ return true;
+ SEvent rclick_event{};
+ rclick_event.EventType = EET_MOUSE_INPUT_EVENT;
+ rclick_event.MouseInput.Event = EMIE_RMOUSE_PRESSED_DOWN;
+ rclick_event.MouseInput.ButtonStates = EMBSM_LEFT | EMBSM_RIGHT;
+ rclick_event.MouseInput.X = m_pointer.X;
+ rclick_event.MouseInput.Y = m_pointer.Y;
+ focused->OnEvent(rclick_event);
+ rclick_event.MouseInput.Event = EMIE_RMOUSE_LEFT_UP;
+ rclick_event.MouseInput.ButtonStates = EMBSM_LEFT;
+ focused->OnEvent(rclick_event);
return true;
}
-
- // check if translated event needs to be preprocessed again
- if (preprocessEvent(translated))
+ default: // ignored
return true;
-
- if (hovered) {
- grab();
- bool retval = hovered->OnEvent(translated);
-
- if (event.TouchInput.Event == ETIE_LEFT_UP)
- // reset pointer
- m_pointer = v2s32(0, 0);
-
- drop();
- return retval;
}
}
#endif
diff --git a/src/gui/modalMenu.h b/src/gui/modalMenu.h
index 1cb687f82..ed0da3205 100644
--- a/src/gui/modalMenu.h
+++ b/src/gui/modalMenu.h
@@ -20,6 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#pragma once
#include "irrlichttypes_extrabloated.h"
+#include "irr_ptr.h"
#include "util/string.h"
class GUIModalMenu;
@@ -100,4 +101,12 @@ private:
// This might be necessary to expose to the implementation if it
// wants to launch other menus
bool m_allow_focus_removal = false;
+
+#ifdef __ANDROID__
+ irr_ptr<gui::IGUIElement> m_hovered;
+
+ bool simulateMouseEvent(gui::IGUIElement *target, ETOUCH_INPUT_EVENT touch_event);
+ void enter(gui::IGUIElement *element);
+ void leave();
+#endif
};
diff --git a/src/gui/touchscreengui.h b/src/gui/touchscreengui.h
index 761d33207..0349624fa 100644
--- a/src/gui/touchscreengui.h
+++ b/src/gui/touchscreengui.h
@@ -18,6 +18,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#pragma once
+#include "irrlichttypes.h"
#include <IEventReceiver.h>
#include <IGUIButton.h>
#include <IGUIEnvironment.h>
diff --git a/src/irr_ptr.h b/src/irr_ptr.h
index 5022adb9d..42b409676 100644
--- a/src/irr_ptr.h
+++ b/src/irr_ptr.h
@@ -27,9 +27,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
* It should only be used for user-managed objects, i.e. those created with
* the @c new operator or @c create* functions, like:
* `irr_ptr<scene::IMeshBuffer> buf{new scene::SMeshBuffer()};`
+ * The reference counting is *not* balanced as new objects have reference
+ * count set to one, and the @c irr_ptr constructor (and @c reset) assumes
+ * ownership of that reference.
*
- * It should *never* be used for engine-managed objects, including
- * those created with @c addTexture and similar methods.
+ * It shouldn’t be used for engine-managed objects, including those created
+ * with @c addTexture and similar methods. Constructing @c irr_ptr directly
+ * from such object is a bug and may lead to a crash. Indirect construction
+ * is possible though; see the @c grab free function for details and use cases.
*/
template <class ReferenceCounted,
class = typename std::enable_if<std::is_base_of<IReferenceCounted,
@@ -38,16 +43,6 @@ class irr_ptr
{
ReferenceCounted *value = nullptr;
- /** Drops stored pointer replacing it with the given one.
- * @note Copy semantics: reference counter *is* increased.
- */
- void grab(ReferenceCounted *object)
- {
- if (object)
- object->grab();
- reset(object);
- }
-
public:
irr_ptr() {}
@@ -71,8 +66,10 @@ public:
reset(b.release());
}
- /** Constructs a shared pointer out of a plain one
+ /** Constructs a shared pointer out of a plain one to control object lifetime.
+ * @param object The object, usually returned by some @c create* function.
* @note Move semantics: reference counter is *not* increased.
+ * @warning Never wrap any @c add* function with this!
*/
explicit irr_ptr(ReferenceCounted *object) noexcept { reset(object); }
@@ -134,4 +131,70 @@ public:
value->drop();
value = object;
}
+
+ /** Drops stored pointer replacing it with the given one.
+ * @note Copy semantics: reference counter *is* increased.
+ */
+ void grab(ReferenceCounted *object) noexcept
+ {
+ if (object)
+ object->grab();
+ reset(object);
+ }
};
+
+// clang-format off
+// ^ dislikes long lines
+
+/** Constructs a shared pointer as a *secondary* reference to an object
+ *
+ * This function is intended to make a temporary reference to an object which
+ * is owned elsewhere so that it is not destroyed too early. To acheive that
+ * it does balanced reference counting, i.e. reference count is increased
+ * in this function and decreased when the returned pointer is destroyed.
+ */
+template <class ReferenceCounted>
+irr_ptr<ReferenceCounted> grab(ReferenceCounted *object) noexcept
+{
+ irr_ptr<ReferenceCounted> ptr;
+ ptr.grab(object);
+ return ptr;
+}
+
+template <typename ReferenceCounted>
+bool operator==(const irr_ptr<ReferenceCounted> &a, const irr_ptr<ReferenceCounted> &b) noexcept
+{
+ return a.get() == b.get();
+}
+
+template <typename ReferenceCounted>
+bool operator==(const irr_ptr<ReferenceCounted> &a, const ReferenceCounted *b) noexcept
+{
+ return a.get() == b;
+}
+
+template <typename ReferenceCounted>
+bool operator==(const ReferenceCounted *a, const irr_ptr<ReferenceCounted> &b) noexcept
+{
+ return a == b.get();
+}
+
+template <typename ReferenceCounted>
+bool operator!=(const irr_ptr<ReferenceCounted> &a, const irr_ptr<ReferenceCounted> &b) noexcept
+{
+ return a.get() != b.get();
+}
+
+template <typename ReferenceCounted>
+bool operator!=(const irr_ptr<ReferenceCounted> &a, const ReferenceCounted *b) noexcept
+{
+ return a.get() != b;
+}
+
+template <typename ReferenceCounted>
+bool operator!=(const ReferenceCounted *a, const irr_ptr<ReferenceCounted> &b) noexcept
+{
+ return a != b.get();
+}
+
+// clang-format on
diff --git a/src/itemstackmetadata.cpp b/src/itemstackmetadata.cpp
index 4aa1a0903..7a26fbb0e 100644
--- a/src/itemstackmetadata.cpp
+++ b/src/itemstackmetadata.cpp
@@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "itemstackmetadata.h"
#include "util/serialize.h"
#include "util/strfnd.h"
+#include <algorithm>
#define DESERIALIZE_START '\x01'
#define DESERIALIZE_KV_DELIM '\x02'
@@ -37,10 +38,22 @@ void ItemStackMetadata::clear()
updateToolCapabilities();
}
+static void sanitize_string(std::string &str)
+{
+ str.erase(std::remove(str.begin(), str.end(), DESERIALIZE_START), str.end());
+ str.erase(std::remove(str.begin(), str.end(), DESERIALIZE_KV_DELIM), str.end());
+ str.erase(std::remove(str.begin(), str.end(), DESERIALIZE_PAIR_DELIM), str.end());
+}
+
bool ItemStackMetadata::setString(const std::string &name, const std::string &var)
{
- bool result = Metadata::setString(name, var);
- if (name == TOOLCAP_KEY)
+ std::string clean_name = name;
+ std::string clean_var = var;
+ sanitize_string(clean_name);
+ sanitize_string(clean_var);
+
+ bool result = Metadata::setString(clean_name, clean_var);
+ if (clean_name == TOOLCAP_KEY)
updateToolCapabilities();
return result;
}
diff --git a/src/main.cpp b/src/main.cpp
index af6d307dc..39b441d2c 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -47,11 +47,19 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "gui/guiEngine.h"
#include "gui/mainmenumanager.h"
#endif
-
#ifdef HAVE_TOUCHSCREENGUI
#include "gui/touchscreengui.h"
#endif
+// for version information only
+extern "C" {
+#if USE_LUAJIT
+ #include <luajit.h>
+#else
+ #include <lua.h>
+#endif
+}
+
#if !defined(SERVER) && \
(IRRLICHT_VERSION_MAJOR == 1) && \
(IRRLICHT_VERSION_MINOR == 8) && \
@@ -351,6 +359,11 @@ static void print_version()
#ifndef SERVER
std::cout << "Using Irrlicht " IRRLICHT_SDK_VERSION << std::endl;
#endif
+#if USE_LUAJIT
+ std::cout << "Using " << LUAJIT_VERSION << std::endl;
+#else
+ std::cout << "Using " << LUA_RELEASE << std::endl;
+#endif
std::cout << g_build_info << std::endl;
}
@@ -474,12 +487,15 @@ static bool create_userdata_path()
static bool init_common(const Settings &cmd_args, int argc, char *argv[])
{
startup_message();
- set_default_settings(g_settings);
+ set_default_settings();
// Initialize sockets
sockets_init();
atexit(sockets_cleanup);
+ // Initialize g_settings
+ Settings::createLayer(SL_GLOBAL);
+
if (!read_config_file(cmd_args))
return false;
diff --git a/src/map.cpp b/src/map.cpp
index a80e7ff92..eeaf5c140 100644
--- a/src/map.cpp
+++ b/src/map.cpp
@@ -545,23 +545,6 @@ void Map::transformLiquids(std::map<v3s16, MapBlock*> &modified_blocks,
u32 liquid_loop_max = g_settings->getS32("liquid_loop_max");
u32 loop_max = liquid_loop_max;
-#if 0
-
- /* If liquid_loop_max is not keeping up with the queue size increase
- * loop_max up to a maximum of liquid_loop_max * dedicated_server_step.
- */
- if (m_transforming_liquid.size() > loop_max * 2) {
- // "Burst" mode
- float server_step = g_settings->getFloat("dedicated_server_step");
- if (m_transforming_liquid_loop_count_multiplier - 1.0 < server_step)
- m_transforming_liquid_loop_count_multiplier *= 1.0 + server_step / 10;
- } else {
- m_transforming_liquid_loop_count_multiplier = 1.0;
- }
-
- loop_max *= m_transforming_liquid_loop_count_multiplier;
-#endif
-
while (m_transforming_liquid.size() != 0)
{
// This should be done here so that it is done when continue is used
@@ -1216,7 +1199,7 @@ bool Map::isBlockOccluded(MapBlock *block, v3s16 cam_pos_nodes)
ServerMap::ServerMap(const std::string &savedir, IGameDef *gamedef,
EmergeManager *emerge, MetricsBackend *mb):
Map(gamedef),
- settings_mgr(g_settings, savedir + DIR_DELIM + "map_meta.txt"),
+ settings_mgr(savedir + DIR_DELIM + "map_meta.txt"),
m_emerge(emerge)
{
verbosestream<<FUNCTION_NAME<<std::endl;
@@ -1317,18 +1300,6 @@ ServerMap::~ServerMap()
*/
delete dbase;
delete dbase_ro;
-
-#if 0
- /*
- Free all MapChunks
- */
- core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
- for(; i.atEnd() == false; i++)
- {
- MapChunk *chunk = i.getNode()->getValue();
- delete chunk;
- }
-#endif
}
MapgenParams *ServerMap::getMapgenParams()
@@ -1417,25 +1388,6 @@ bool ServerMap::initBlockMake(v3s16 blockpos, BlockMakeData *data)
data->vmanip = new MMVManip(this);
data->vmanip->initialEmerge(full_bpmin, full_bpmax);
- // Note: we may need this again at some point.
-#if 0
- // Ensure none of the blocks to be generated were marked as
- // containing CONTENT_IGNORE
- for (s16 z = blockpos_min.Z; z <= blockpos_max.Z; z++) {
- for (s16 y = blockpos_min.Y; y <= blockpos_max.Y; y++) {
- for (s16 x = blockpos_min.X; x <= blockpos_max.X; x++) {
- core::map<v3s16, u8>::Node *n;
- n = data->vmanip->m_loaded_blocks.find(v3s16(x, y, z));
- if (n == NULL)
- continue;
- u8 flags = n->getValue();
- flags &= ~VMANIP_BLOCK_CONTAINS_CIGNORE;
- n->setValue(flags);
- }
- }
- }
-#endif
-
// Data is ready now.
return true;
}
@@ -1446,8 +1398,6 @@ void ServerMap::finishBlockMake(BlockMakeData *data,
v3s16 bpmin = data->blockpos_min;
v3s16 bpmax = data->blockpos_max;
- v3s16 extra_borders(1, 1, 1);
-
bool enable_mapgen_debug_info = m_emerge->enable_mapgen_debug_info;
EMERGE_DBG_OUT("finishBlockMake(): " PP(bpmin) " - " PP(bpmax));
@@ -1540,116 +1490,6 @@ MapSector *ServerMap::createSector(v2s16 p2d)
return sector;
}
-#if 0
-/*
- This is a quick-hand function for calling makeBlock().
-*/
-MapBlock * ServerMap::generateBlock(
- v3s16 p,
- std::map<v3s16, MapBlock*> &modified_blocks
-)
-{
- bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
-
- TimeTaker timer("generateBlock");
-
- //MapBlock *block = original_dummy;
-
- v2s16 p2d(p.X, p.Z);
- v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
-
- /*
- Do not generate over-limit
- */
- if(blockpos_over_limit(p))
- {
- infostream<<FUNCTION_NAME<<": Block position over limit"<<std::endl;
- throw InvalidPositionException("generateBlock(): pos. over limit");
- }
-
- /*
- Create block make data
- */
- BlockMakeData data;
- initBlockMake(&data, p);
-
- /*
- Generate block
- */
- {
- TimeTaker t("mapgen::make_block()");
- mapgen->makeChunk(&data);
- //mapgen::make_block(&data);
-
- if(enable_mapgen_debug_info == false)
- t.stop(true); // Hide output
- }
-
- /*
- Blit data back on map, update lighting, add mobs and whatever this does
- */
- finishBlockMake(&data, modified_blocks);
-
- /*
- Get central block
- */
- MapBlock *block = getBlockNoCreateNoEx(p);
-
-#if 0
- /*
- Check result
- */
- if(block)
- {
- bool erroneus_content = false;
- for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
- for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
- for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
- {
- v3s16 p(x0,y0,z0);
- MapNode n = block->getNode(p);
- if(n.getContent() == CONTENT_IGNORE)
- {
- infostream<<"CONTENT_IGNORE at "
- <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
- <<std::endl;
- erroneus_content = true;
- assert(0);
- }
- }
- if(erroneus_content)
- {
- assert(0);
- }
- }
-#endif
-
-#if 0
- /*
- Generate a completely empty block
- */
- if(block)
- {
- for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
- for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
- {
- for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
- {
- MapNode n;
- n.setContent(CONTENT_AIR);
- block->setNode(v3s16(x0,y0,z0), n);
- }
- }
- }
-#endif
-
- if(enable_mapgen_debug_info == false)
- timer.stop(true); // Hide output
-
- return block;
-}
-#endif
-
MapBlock * ServerMap::createBlock(v3s16 p)
{
/*
diff --git a/src/map.h b/src/map.h
index 821e638c1..e0db2e262 100644
--- a/src/map.h
+++ b/src/map.h
@@ -418,13 +418,7 @@ private:
bool m_map_saving_enabled;
int m_map_compression_level;
-#if 0
- // Chunk size in MapSectors
- // If 0, chunks are disabled.
- s16 m_chunksize;
- // Chunks
- core::map<v2s16, MapChunk*> m_chunks;
-#endif
+
std::set<v3s16> m_chunks_in_progress;
/*
diff --git a/src/map_settings_manager.cpp b/src/map_settings_manager.cpp
index 9c447b3d0..99e3cb0e6 100644
--- a/src/map_settings_manager.cpp
+++ b/src/map_settings_manager.cpp
@@ -25,17 +25,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "map_settings_manager.h"
-MapSettingsManager::MapSettingsManager(Settings *user_settings,
- const std::string &map_meta_path):
- m_map_meta_path(map_meta_path),
- m_map_settings(new Settings()),
- m_user_settings(user_settings)
+MapSettingsManager::MapSettingsManager(const std::string &map_meta_path):
+ m_map_meta_path(map_meta_path)
{
- assert(m_user_settings != NULL);
-
- Mapgen::setDefaultSettings(m_map_settings);
- // This inherits the combined defaults provided by loadGameConfAndInitWorld.
- m_map_settings->overrideDefaults(user_settings);
+ m_map_settings = Settings::createLayer(SL_MAP, "[end_of_params]");
+ Mapgen::setDefaultSettings(Settings::getLayer(SL_DEFAULTS));
}
@@ -49,22 +43,23 @@ MapSettingsManager::~MapSettingsManager()
bool MapSettingsManager::getMapSetting(
const std::string &name, std::string *value_out)
{
+ // Get from map_meta.txt, then try from all other sources
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);
+ if (name == "seed")
+ return Settings::getLayer(SL_GLOBAL)->getNoEx("fixed_map_seed", *value_out);
- return m_user_settings->getNoEx(name, *value_out);
+ return false;
}
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);
+ // TODO: Rename to "getNoiseParams"
+ return m_map_settings->getNoiseParams(name, *value_out);
}
@@ -77,7 +72,7 @@ bool MapSettingsManager::setMapSetting(
if (override_meta)
m_map_settings->set(name, value);
else
- m_map_settings->setDefault(name, value);
+ Settings::getLayer(SL_GLOBAL)->set(name, value);
return true;
}
@@ -89,7 +84,11 @@ bool MapSettingsManager::setMapSettingNoiseParams(
if (mapgen_params)
return false;
- m_map_settings->setNoiseParams(name, *value, !override_meta);
+ if (override_meta)
+ m_map_settings->setNoiseParams(name, *value);
+ else
+ Settings::getLayer(SL_GLOBAL)->setNoiseParams(name, *value);
+
return true;
}
@@ -104,8 +103,8 @@ bool MapSettingsManager::loadMapMeta()
return false;
}
- if (!m_map_settings->parseConfigLines(is, "[end_of_params]")) {
- errorstream << "loadMapMeta: [end_of_params] not found!" << std::endl;
+ if (!m_map_settings->parseConfigLines(is)) {
+ errorstream << "loadMapMeta: Format error. '[end_of_params]' missing?" << std::endl;
return false;
}
@@ -116,28 +115,23 @@ bool MapSettingsManager::loadMapMeta()
bool MapSettingsManager::saveMapMeta()
{
// If mapgen params haven't been created yet; abort
- if (!mapgen_params)
+ if (!mapgen_params) {
+ infostream << "saveMapMeta: mapgen_params not present! "
+ << "Server startup was probably interrupted." << std::endl;
return false;
+ }
+ // Paths set up by subgames.cpp, but not in unittests
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(m_map_settings);
+ mapgen_params->writeParams(m_map_settings);
- 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())) {
+ if (!m_map_settings->updateConfigFile(m_map_meta_path.c_str())) {
errorstream << "saveMapMeta: could not write "
<< m_map_meta_path << std::endl;
return false;
@@ -152,23 +146,21 @@ 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
+ // 1). SL_MAP 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
+ // 2). SL_GLOBAL containing all user-specified config file
// settings
- // 4). g_settings->m_defaults containing any low-priority settings from
+ // 3). SL_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 " <<
diff --git a/src/map_settings_manager.h b/src/map_settings_manager.h
index 5baa38455..9258d3032 100644
--- a/src/map_settings_manager.h
+++ b/src/map_settings_manager.h
@@ -44,8 +44,7 @@ struct MapgenParams;
*/
class MapSettingsManager {
public:
- MapSettingsManager(Settings *user_settings,
- const std::string &map_meta_path);
+ MapSettingsManager(const std::string &map_meta_path);
~MapSettingsManager();
// Finalized map generation parameters
@@ -71,6 +70,6 @@ public:
private:
std::string m_map_meta_path;
+ // TODO: Rename to "m_settings"
Settings *m_map_settings;
- Settings *m_user_settings;
};
diff --git a/src/mapblock.h b/src/mapblock.h
index 641a1b69b..7b82301e9 100644
--- a/src/mapblock.h
+++ b/src/mapblock.h
@@ -340,15 +340,6 @@ public:
// is not valid on this MapBlock.
bool isValidPositionParent(v3s16 p);
MapNode getNodeParent(v3s16 p, bool *is_valid_position = NULL);
- void setNodeParent(v3s16 p, MapNode & n);
-
- inline void drawbox(s16 x0, s16 y0, s16 z0, s16 w, s16 h, s16 d, MapNode node)
- {
- for (u16 z = 0; z < d; z++)
- for (u16 y = 0; y < h; y++)
- for (u16 x = 0; x < w; x++)
- setNode(x0 + x, y0 + y, z0 + z, node);
- }
// Copies data to VoxelManipulator to getPosRelative()
void copyTo(VoxelManipulator &dst);
diff --git a/src/mapgen/mapgen.h b/src/mapgen/mapgen.h
index 1487731e2..61db4f3b9 100644
--- a/src/mapgen/mapgen.h
+++ b/src/mapgen/mapgen.h
@@ -30,10 +30,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define MAPGEN_DEFAULT_NAME "v7"
/////////////////// Mapgen flags
-#define MG_TREES 0x01 // Obsolete. Moved into mgv6 flags
#define MG_CAVES 0x02
#define MG_DUNGEONS 0x04
-#define MG_FLAT 0x08 // Obsolete. Moved into mgv6 flags
#define MG_LIGHT 0x10
#define MG_DECORATIONS 0x20
#define MG_BIOMES 0x40
diff --git a/src/mapgen/mapgen_v6.cpp b/src/mapgen/mapgen_v6.cpp
index e04180f96..bce9cee81 100644
--- a/src/mapgen/mapgen_v6.cpp
+++ b/src/mapgen/mapgen_v6.cpp
@@ -792,7 +792,7 @@ void MapgenV6::flowMud(s16 &mudflow_minpos, s16 &mudflow_maxpos)
v3s16(0, 0, -1), // Front
v3s16(-1, 0, 0), // Left
};
-
+
// Iterate twice
for (s16 k = 0; k < 2; k++) {
for (s16 z = mudflow_minpos; z <= mudflow_maxpos; z++)
@@ -1055,7 +1055,6 @@ void MapgenV6::growGrass() // Add surface nodes
MapNode n_dirt_with_grass(c_dirt_with_grass);
MapNode n_dirt_with_snow(c_dirt_with_snow);
MapNode n_snowblock(c_snowblock);
- MapNode n_snow(c_snow);
const v3s16 &em = vm->m_area.getExtent();
u32 index = 0;
diff --git a/src/mapgen/mg_ore.h b/src/mapgen/mg_ore.h
index 76420fab4..a58fa9bfe 100644
--- a/src/mapgen/mg_ore.h
+++ b/src/mapgen/mg_ore.h
@@ -52,7 +52,7 @@ extern FlagDesc flagdesc_ore[];
class Ore : public ObjDef, public NodeResolver {
public:
- static const bool NEEDS_NOISE = false;
+ const bool needs_noise;
content_t c_ore; // the node to place
std::vector<content_t> c_wherein; // the nodes to be placed in
@@ -68,7 +68,7 @@ public:
Noise *noise = nullptr;
std::unordered_set<biome_t> biomes;
- Ore() = default;;
+ explicit Ore(bool needs_noise): needs_noise(needs_noise) {}
virtual ~Ore();
virtual void resolveNodeNames();
@@ -83,17 +83,17 @@ protected:
class OreScatter : public Ore {
public:
- static const bool NEEDS_NOISE = false;
+ OreScatter() : Ore(false) {}
ObjDef *clone() const;
- virtual void generate(MMVManip *vm, int mapseed, u32 blockseed,
- v3s16 nmin, v3s16 nmax, biome_t *biomemap);
+ void generate(MMVManip *vm, int mapseed, u32 blockseed,
+ v3s16 nmin, v3s16 nmax, biome_t *biomemap) override;
};
class OreSheet : public Ore {
public:
- static const bool NEEDS_NOISE = true;
+ OreSheet() : Ore(true) {}
ObjDef *clone() const;
@@ -101,14 +101,12 @@ public:
u16 column_height_max;
float column_midpoint_factor;
- virtual void generate(MMVManip *vm, int mapseed, u32 blockseed,
- v3s16 nmin, v3s16 nmax, biome_t *biomemap);
+ void generate(MMVManip *vm, int mapseed, u32 blockseed,
+ v3s16 nmin, v3s16 nmax, biome_t *biomemap) override;
};
class OrePuff : public Ore {
public:
- static const bool NEEDS_NOISE = true;
-
ObjDef *clone() const;
NoiseParams np_puff_top;
@@ -116,55 +114,50 @@ public:
Noise *noise_puff_top = nullptr;
Noise *noise_puff_bottom = nullptr;
- OrePuff() = default;
+ OrePuff() : Ore(true) {}
virtual ~OrePuff();
- virtual void generate(MMVManip *vm, int mapseed, u32 blockseed,
- v3s16 nmin, v3s16 nmax, biome_t *biomemap);
+ void generate(MMVManip *vm, int mapseed, u32 blockseed,
+ v3s16 nmin, v3s16 nmax, biome_t *biomemap) override;
};
class OreBlob : public Ore {
public:
- static const bool NEEDS_NOISE = true;
-
ObjDef *clone() const;
- virtual void generate(MMVManip *vm, int mapseed, u32 blockseed,
- v3s16 nmin, v3s16 nmax, biome_t *biomemap);
+ OreBlob() : Ore(true) {}
+ void generate(MMVManip *vm, int mapseed, u32 blockseed,
+ v3s16 nmin, v3s16 nmax, biome_t *biomemap) override;
};
class OreVein : public Ore {
public:
- static const bool NEEDS_NOISE = true;
-
ObjDef *clone() const;
float random_factor;
Noise *noise2 = nullptr;
int sizey_prev = 0;
- OreVein() = default;
+ OreVein() : Ore(true) {}
virtual ~OreVein();
- virtual void generate(MMVManip *vm, int mapseed, u32 blockseed,
- v3s16 nmin, v3s16 nmax, biome_t *biomemap);
+ void generate(MMVManip *vm, int mapseed, u32 blockseed,
+ v3s16 nmin, v3s16 nmax, biome_t *biomemap) override;
};
class OreStratum : public Ore {
public:
- static const bool NEEDS_NOISE = false;
-
ObjDef *clone() const;
NoiseParams np_stratum_thickness;
Noise *noise_stratum_thickness = nullptr;
u16 stratum_thickness;
- OreStratum() = default;
+ OreStratum() : Ore(false) {}
virtual ~OreStratum();
- virtual void generate(MMVManip *vm, int mapseed, u32 blockseed,
- v3s16 nmin, v3s16 nmax, biome_t *biomemap);
+ void generate(MMVManip *vm, int mapseed, u32 blockseed,
+ v3s16 nmin, v3s16 nmax, biome_t *biomemap) override;
};
class OreManager : public ObjDefManager {
diff --git a/src/network/networkexceptions.h b/src/network/networkexceptions.h
index f4913928c..58a3bb490 100644
--- a/src/network/networkexceptions.h
+++ b/src/network/networkexceptions.h
@@ -56,12 +56,6 @@ public:
InvalidIncomingDataException(const char *s) : BaseException(s) {}
};
-class InvalidOutgoingDataException : public BaseException
-{
-public:
- InvalidOutgoingDataException(const char *s) : BaseException(s) {}
-};
-
class NoIncomingDataException : public BaseException
{
public:
@@ -103,4 +97,4 @@ class SendFailedException : public BaseException
{
public:
SendFailedException(const std::string &s) : BaseException(s) {}
-}; \ No newline at end of file
+};
diff --git a/src/network/networkpacket.cpp b/src/network/networkpacket.cpp
index 6d0abb12c..a71e26572 100644
--- a/src/network/networkpacket.cpp
+++ b/src/network/networkpacket.cpp
@@ -50,7 +50,7 @@ void NetworkPacket::checkReadOffset(u32 from_offset, u32 field_size)
}
}
-void NetworkPacket::putRawPacket(u8 *data, u32 datasize, session_t peer_id)
+void NetworkPacket::putRawPacket(const u8 *data, u32 datasize, session_t peer_id)
{
// If a m_command is already set, we are rewriting on same packet
// This is not permitted
@@ -145,6 +145,8 @@ void NetworkPacket::putLongString(const std::string &src)
putRawString(src.c_str(), msgsize);
}
+static constexpr bool NEED_SURROGATE_CODING = sizeof(wchar_t) > 2;
+
NetworkPacket& NetworkPacket::operator>>(std::wstring& dst)
{
checkReadOffset(m_read_offset, 2);
@@ -160,9 +162,16 @@ NetworkPacket& NetworkPacket::operator>>(std::wstring& dst)
checkReadOffset(m_read_offset, strLen * 2);
dst.reserve(strLen);
- for(u16 i=0; i<strLen; i++) {
- wchar_t c16 = readU16(&m_data[m_read_offset]);
- dst.append(&c16, 1);
+ for (u16 i = 0; i < strLen; i++) {
+ wchar_t c = readU16(&m_data[m_read_offset]);
+ if (NEED_SURROGATE_CODING && c >= 0xD800 && c < 0xDC00 && i+1 < strLen) {
+ i++;
+ m_read_offset += sizeof(u16);
+
+ wchar_t c2 = readU16(&m_data[m_read_offset]);
+ c = 0x10000 + ( ((c & 0x3ff) << 10) | (c2 & 0x3ff) );
+ }
+ dst.push_back(c);
m_read_offset += sizeof(u16);
}
@@ -175,15 +184,37 @@ NetworkPacket& NetworkPacket::operator<<(const std::wstring &src)
throw PacketError("String too long");
}
- u16 msgsize = src.size();
+ if (!NEED_SURROGATE_CODING || src.size() == 0) {
+ *this << static_cast<u16>(src.size());
+ for (u16 i = 0; i < src.size(); i++)
+ *this << static_cast<u16>(src[i]);
- *this << msgsize;
+ return *this;
+ }
- // Write string
- for (u16 i=0; i<msgsize; i++) {
- *this << (u16) src[i];
+ // write dummy value, to be overwritten later
+ const u32 len_offset = m_read_offset;
+ u32 written = 0;
+ *this << static_cast<u16>(0xfff0);
+
+ for (u16 i = 0; i < src.size(); i++) {
+ wchar_t c = src[i];
+ if (c > 0xffff) {
+ // Encode high code-points as surrogate pairs
+ u32 n = c - 0x10000;
+ *this << static_cast<u16>(0xD800 | (n >> 10))
+ << static_cast<u16>(0xDC00 | (n & 0x3ff));
+ written += 2;
+ } else {
+ *this << static_cast<u16>(c);
+ written++;
+ }
}
+ if (written > WIDE_STRING_MAX_LEN)
+ throw PacketError("String too long");
+ writeU16(&m_data[len_offset], written);
+
return *this;
}
diff --git a/src/network/networkpacket.h b/src/network/networkpacket.h
index e77bfb744..c7ff03b8e 100644
--- a/src/network/networkpacket.h
+++ b/src/network/networkpacket.h
@@ -34,7 +34,7 @@ public:
~NetworkPacket();
- void putRawPacket(u8 *data, u32 datasize, session_t peer_id);
+ void putRawPacket(const u8 *data, u32 datasize, session_t peer_id);
void clear();
// Getters
diff --git a/src/network/serverpackethandler.cpp b/src/network/serverpackethandler.cpp
index c636d01e1..270b8e01f 100644
--- a/src/network/serverpackethandler.cpp
+++ b/src/network/serverpackethandler.cpp
@@ -56,12 +56,12 @@ void Server::handleCommand_Init(NetworkPacket* pkt)
session_t peer_id = pkt->getPeerId();
RemoteClient *client = getClient(peer_id, CS_Created);
+ Address addr;
std::string addr_s;
try {
- Address address = getPeerAddress(peer_id);
- addr_s = address.serializeString();
- }
- catch (con::PeerNotFoundException &e) {
+ addr = m_con->GetPeerAddress(peer_id);
+ addr_s = addr.serializeString();
+ } catch (con::PeerNotFoundException &e) {
/*
* no peer for this packet found
* most common reason is peer timeout, e.g. peer didn't
@@ -73,13 +73,14 @@ void Server::handleCommand_Init(NetworkPacket* pkt)
return;
}
- // If net_proto_version is set, this client has already been handled
if (client->getState() > CS_Created) {
verbosestream << "Server: Ignoring multiple TOSERVER_INITs from " <<
addr_s << " (peer_id=" << peer_id << ")" << std::endl;
return;
}
+ client->setCachedAddress(addr);
+
verbosestream << "Server: Got TOSERVER_INIT from " << addr_s <<
" (peer_id=" << peer_id << ")" << std::endl;
@@ -437,18 +438,20 @@ void Server::handleCommand_GotBlocks(NetworkPacket* pkt)
u8 count;
*pkt >> count;
- RemoteClient *client = getClient(pkt->getPeerId());
-
if ((s16)pkt->getSize() < 1 + (int)count * 6) {
throw con::InvalidIncomingDataException
("GOTBLOCKS length is too short");
}
+ m_clients.lock();
+ RemoteClient *client = m_clients.lockedGetClientNoEx(pkt->getPeerId());
+
for (u16 i = 0; i < count; i++) {
v3s16 p;
*pkt >> p;
client->GotBlock(p);
}
+ m_clients.unlock();
}
void Server::process_PlayerPos(RemotePlayer *player, PlayerSAO *playersao,
@@ -749,21 +752,8 @@ void Server::handleCommand_InventoryAction(NetworkPacket* pkt)
void Server::handleCommand_ChatMessage(NetworkPacket* pkt)
{
- /*
- u16 command
- u16 length
- wstring message
- */
- u16 len;
- *pkt >> len;
-
std::wstring message;
- for (u16 i = 0; i < len; i++) {
- u16 tmp_wchar;
- *pkt >> tmp_wchar;
-
- message += (wchar_t)tmp_wchar;
- }
+ *pkt >> message;
session_t peer_id = pkt->getPeerId();
RemotePlayer *player = m_env->getPlayer(peer_id);
@@ -775,15 +765,13 @@ void Server::handleCommand_ChatMessage(NetworkPacket* pkt)
return;
}
- // Get player name of this client
std::string name = player->getName();
- std::wstring wname = narrow_to_wide(name);
- std::wstring answer_to_sender = handleChat(name, wname, message, true, player);
+ std::wstring answer_to_sender = handleChat(name, message, true, player);
if (!answer_to_sender.empty()) {
// Send the answer to sender
- SendChatMessage(peer_id, ChatMessage(CHATMESSAGE_TYPE_NORMAL,
- answer_to_sender, wname));
+ SendChatMessage(peer_id, ChatMessage(CHATMESSAGE_TYPE_SYSTEM,
+ answer_to_sender));
}
}
diff --git a/src/nodedef.cpp b/src/nodedef.cpp
index bc00d5637..f1c2ba507 100644
--- a/src/nodedef.cpp
+++ b/src/nodedef.cpp
@@ -360,7 +360,7 @@ void ContentFeatures::reset()
i = TileDef();
for (auto &j : tiledef_special)
j = TileDef();
- alpha = 255;
+ alpha = ALPHAMODE_OPAQUE;
post_effect_color = video::SColor(0, 0, 0, 0);
param_type = CPT_NONE;
param_type_2 = CPT2_NONE;
@@ -405,6 +405,31 @@ void ContentFeatures::reset()
node_dig_prediction = "air";
}
+void ContentFeatures::setAlphaFromLegacy(u8 legacy_alpha)
+{
+ // No special handling for nodebox/mesh here as it doesn't make sense to
+ // throw warnings when the server is too old to support the "correct" way
+ switch (drawtype) {
+ case NDT_NORMAL:
+ alpha = legacy_alpha == 255 ? ALPHAMODE_OPAQUE : ALPHAMODE_CLIP;
+ break;
+ case NDT_LIQUID:
+ case NDT_FLOWINGLIQUID:
+ alpha = legacy_alpha == 255 ? ALPHAMODE_OPAQUE : ALPHAMODE_BLEND;
+ break;
+ default:
+ alpha = legacy_alpha == 255 ? ALPHAMODE_CLIP : ALPHAMODE_BLEND;
+ break;
+ }
+}
+
+u8 ContentFeatures::getAlphaForLegacy() const
+{
+ // This is so simple only because 255 and 0 mean wildly different things
+ // depending on drawtype...
+ return alpha == ALPHAMODE_OPAQUE ? 255 : 0;
+}
+
void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
{
const u8 version = CONTENTFEATURES_VERSION;
@@ -433,7 +458,7 @@ void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
for (const TileDef &td : tiledef_special) {
td.serialize(os, protocol_version);
}
- writeU8(os, alpha);
+ writeU8(os, getAlphaForLegacy());
writeU8(os, color.getRed());
writeU8(os, color.getGreen());
writeU8(os, color.getBlue());
@@ -489,21 +514,7 @@ void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
os << serializeString16(node_dig_prediction);
writeU8(os, leveled_max);
-}
-
-void ContentFeatures::correctAlpha(TileDef *tiles, int length)
-{
- // alpha == 0 means that the node is using texture alpha
- if (alpha == 0 || alpha == 255)
- return;
-
- for (int i = 0; i < length; i++) {
- if (tiles[i].name.empty())
- continue;
- std::stringstream s;
- s << tiles[i].name << "^[noalpha^[opacity:" << ((int)alpha);
- tiles[i].name = s.str();
- }
+ writeU8(os, alpha);
}
void ContentFeatures::deSerialize(std::istream &is)
@@ -539,7 +550,7 @@ void ContentFeatures::deSerialize(std::istream &is)
throw SerializationError("unsupported CF_SPECIAL_COUNT");
for (TileDef &td : tiledef_special)
td.deSerialize(is, version, drawtype);
- alpha = readU8(is);
+ setAlphaFromLegacy(readU8(is));
color.setRed(readU8(is));
color.setGreen(readU8(is));
color.setBlue(readU8(is));
@@ -597,10 +608,16 @@ void ContentFeatures::deSerialize(std::istream &is)
try {
node_dig_prediction = deSerializeString16(is);
- u8 tmp_leveled_max = readU8(is);
+
+ u8 tmp = readU8(is);
if (is.eof()) /* readU8 doesn't throw exceptions so we have to do this */
throw SerializationError("");
- leveled_max = tmp_leveled_max;
+ leveled_max = tmp;
+
+ tmp = readU8(is);
+ if (is.eof())
+ throw SerializationError("");
+ alpha = static_cast<enum AlphaMode>(tmp);
} catch(SerializationError &e) {};
}
@@ -692,6 +709,7 @@ bool ContentFeatures::textureAlphaCheck(ITextureSource *tsrc, const TileDef *til
video::IVideoDriver *driver = RenderingEngine::get_video_driver();
static thread_local bool long_warning_printed = false;
std::set<std::string> seen;
+
for (int i = 0; i < length; i++) {
if (seen.find(tiles[i].name) != seen.end())
continue;
@@ -716,20 +734,21 @@ bool ContentFeatures::textureAlphaCheck(ITextureSource *tsrc, const TileDef *til
break_loop:
image->drop();
- if (!ok) {
- warningstream << "Texture \"" << tiles[i].name << "\" of "
- << name << " has transparent pixels, assuming "
- "use_texture_alpha = true." << std::endl;
- if (!long_warning_printed) {
- warningstream << " This warning can be a false-positive if "
- "unused pixels in the texture are transparent. However if "
- "it is meant to be transparent, you *MUST* update the "
- "nodedef and set use_texture_alpha = true! This compatibility "
- "code will be removed in a few releases." << std::endl;
- long_warning_printed = true;
- }
- return true;
+ if (ok)
+ continue;
+ warningstream << "Texture \"" << tiles[i].name << "\" of "
+ << name << " has transparency, assuming "
+ "use_texture_alpha = \"clip\"." << std::endl;
+ if (!long_warning_printed) {
+ warningstream << " This warning can be a false-positive if "
+ "unused pixels in the texture are transparent. However if "
+ "it is meant to be transparent, you *MUST* update the "
+ "nodedef and set use_texture_alpha = \"clip\"! This "
+ "compatibility code will be removed in a few releases."
+ << std::endl;
+ long_warning_printed = true;
}
+ return true;
}
return false;
}
@@ -774,31 +793,33 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc
bool is_liquid = false;
- MaterialType material_type = (alpha == 255) ?
- TILE_MATERIAL_BASIC : TILE_MATERIAL_ALPHA;
+ if (alpha == ALPHAMODE_LEGACY_COMPAT) {
+ // Before working with the alpha mode, resolve any legacy kludges
+ alpha = textureAlphaCheck(tsrc, tdef, 6) ? ALPHAMODE_CLIP : ALPHAMODE_OPAQUE;
+ }
+
+ MaterialType material_type = alpha == ALPHAMODE_OPAQUE ?
+ TILE_MATERIAL_OPAQUE : (alpha == ALPHAMODE_CLIP ? TILE_MATERIAL_BASIC :
+ TILE_MATERIAL_ALPHA);
switch (drawtype) {
default:
case NDT_NORMAL:
- material_type = (alpha == 255) ?
- TILE_MATERIAL_OPAQUE : TILE_MATERIAL_ALPHA;
solidness = 2;
break;
case NDT_AIRLIKE:
solidness = 0;
break;
case NDT_LIQUID:
- assert(liquid_type == LIQUID_SOURCE);
if (tsettings.opaque_water)
- alpha = 255;
+ alpha = ALPHAMODE_OPAQUE;
solidness = 1;
is_liquid = true;
break;
case NDT_FLOWINGLIQUID:
- assert(liquid_type == LIQUID_FLOWING);
solidness = 0;
if (tsettings.opaque_water)
- alpha = 255;
+ alpha = ALPHAMODE_OPAQUE;
is_liquid = true;
break;
case NDT_GLASSLIKE:
@@ -850,19 +871,16 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc
break;
case NDT_MESH:
case NDT_NODEBOX:
- if (alpha == 255 && textureAlphaCheck(tsrc, tdef, 6))
- alpha = 0;
-
solidness = 0;
- if (waving == 1)
+ if (waving == 1) {
material_type = TILE_MATERIAL_WAVING_PLANTS;
- else if (waving == 2)
+ } else if (waving == 2) {
material_type = TILE_MATERIAL_WAVING_LEAVES;
- else if (waving == 3)
- material_type = (alpha == 255) ? TILE_MATERIAL_WAVING_LIQUID_OPAQUE :
- TILE_MATERIAL_WAVING_LIQUID_BASIC;
- else if (alpha == 255)
- material_type = TILE_MATERIAL_OPAQUE;
+ } else if (waving == 3) {
+ material_type = alpha == ALPHAMODE_OPAQUE ?
+ TILE_MATERIAL_WAVING_LIQUID_OPAQUE : (alpha == ALPHAMODE_CLIP ?
+ TILE_MATERIAL_WAVING_LIQUID_BASIC : TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT);
+ }
break;
case NDT_TORCHLIKE:
case NDT_SIGNLIKE:
@@ -876,16 +894,12 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc
}
if (is_liquid) {
- // Vertex alpha is no longer supported, correct if necessary.
- correctAlpha(tdef, 6);
- correctAlpha(tdef_overlay, 6);
- correctAlpha(tdef_spec, CF_SPECIAL_COUNT);
-
if (waving == 3) {
- material_type = (alpha == 255) ? TILE_MATERIAL_WAVING_LIQUID_OPAQUE :
- TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT;
+ material_type = alpha == ALPHAMODE_OPAQUE ?
+ TILE_MATERIAL_WAVING_LIQUID_OPAQUE : (alpha == ALPHAMODE_CLIP ?
+ TILE_MATERIAL_WAVING_LIQUID_BASIC : TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT);
} else {
- material_type = (alpha == 255) ? TILE_MATERIAL_LIQUID_OPAQUE :
+ material_type = alpha == ALPHAMODE_OPAQUE ? TILE_MATERIAL_LIQUID_OPAQUE :
TILE_MATERIAL_LIQUID_TRANSPARENT;
}
}
@@ -1612,7 +1626,7 @@ static void removeDupes(std::vector<content_t> &list)
void NodeDefManager::resolveCrossrefs()
{
for (ContentFeatures &f : m_content_features) {
- if (f.liquid_type != LIQUID_NONE) {
+ if (f.liquid_type != LIQUID_NONE || f.drawtype == NDT_LIQUID || f.drawtype == NDT_FLOWINGLIQUID) {
f.liquid_alternative_flowing_id = getId(f.liquid_alternative_flowing);
f.liquid_alternative_source_id = getId(f.liquid_alternative_source);
continue;
diff --git a/src/nodedef.h b/src/nodedef.h
index 66c21cc07..6fc20518d 100644
--- a/src/nodedef.h
+++ b/src/nodedef.h
@@ -231,6 +231,14 @@ enum AlignStyle : u8 {
ALIGN_STYLE_USER_DEFINED,
};
+enum AlphaMode : u8 {
+ ALPHAMODE_BLEND,
+ ALPHAMODE_CLIP,
+ ALPHAMODE_OPAQUE,
+ ALPHAMODE_LEGACY_COMPAT, /* means either opaque or clip */
+};
+
+
/*
Stand-alone definition of a TileSpec (basically a server-side TileSpec)
*/
@@ -315,9 +323,7 @@ struct ContentFeatures
// These will be drawn over the base tiles.
TileDef tiledef_overlay[6];
TileDef tiledef_special[CF_SPECIAL_COUNT]; // eg. flowing liquid
- // If 255, the node is opaque.
- // Otherwise it uses texture alpha.
- u8 alpha;
+ AlphaMode alpha;
// The color of the node.
video::SColor color;
std::string palette_name;
@@ -418,29 +424,27 @@ struct ContentFeatures
void serialize(std::ostream &os, u16 protocol_version) const;
void deSerialize(std::istream &is);
- /*!
- * Since vertex alpha is no longer supported, this method
- * adds opacity directly to the texture pixels.
- *
- * \param tiles array of the tile definitions.
- * \param length length of tiles
- */
- void correctAlpha(TileDef *tiles, int length);
-
-#ifndef SERVER
- /*
- * Checks if any tile texture has any transparent pixels.
- * Prints a warning and returns true if that is the case, false otherwise.
- * This is supposed to be used for use_texture_alpha backwards compatibility.
- */
- bool textureAlphaCheck(ITextureSource *tsrc, const TileDef *tiles,
- int length);
-#endif
-
-
/*
Some handy methods
*/
+ void setDefaultAlphaMode()
+ {
+ switch (drawtype) {
+ case NDT_NORMAL:
+ case NDT_LIQUID:
+ case NDT_FLOWINGLIQUID:
+ alpha = ALPHAMODE_OPAQUE;
+ break;
+ case NDT_NODEBOX:
+ case NDT_MESH:
+ alpha = ALPHAMODE_LEGACY_COMPAT; // this should eventually be OPAQUE
+ break;
+ default:
+ alpha = ALPHAMODE_CLIP;
+ break;
+ }
+ }
+
bool needsBackfaceCulling() const
{
switch (drawtype) {
@@ -474,6 +478,21 @@ struct ContentFeatures
void updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc,
scene::IMeshManipulator *meshmanip, Client *client, const TextureSettings &tsettings);
#endif
+
+private:
+#ifndef SERVER
+ /*
+ * Checks if any tile texture has any transparent pixels.
+ * Prints a warning and returns true if that is the case, false otherwise.
+ * This is supposed to be used for use_texture_alpha backwards compatibility.
+ */
+ bool textureAlphaCheck(ITextureSource *tsrc, const TileDef *tiles,
+ int length);
+#endif
+
+ void setAlphaFromLegacy(u8 legacy_alpha);
+
+ u8 getAlphaForLegacy() const;
};
/*!
diff --git a/src/pathfinder.cpp b/src/pathfinder.cpp
index 3f0b98c10..1cb84997a 100644
--- a/src/pathfinder.cpp
+++ b/src/pathfinder.cpp
@@ -157,9 +157,8 @@ public:
ArrayGridNodeContainer(Pathfinder *pathf, v3s16 dimensions);
virtual PathGridnode &access(v3s16 p);
-private:
- v3s16 m_dimensions;
+private:
int m_x_stride;
int m_y_stride;
std::vector<PathGridnode> m_nodes_array;
@@ -306,8 +305,6 @@ private:
int m_max_index_y = 0; /**< max index of search area in y direction */
int m_max_index_z = 0; /**< max index of search area in z direction */
-
- int m_searchdistance = 0; /**< max distance to search in each direction */
int m_maxdrop = 0; /**< maximum number of blocks a path may drop */
int m_maxjump = 0; /**< maximum number of blocks a path may jump */
int m_min_target_distance = 0; /**< current smalest path to target */
@@ -619,7 +616,6 @@ std::vector<v3s16> Pathfinder::getPath(v3s16 source,
std::vector<v3s16> retval;
//initialization
- m_searchdistance = searchdistance;
m_maxjump = max_jump;
m_maxdrop = max_drop;
m_start = source;
diff --git a/src/remoteplayer.cpp b/src/remoteplayer.cpp
index bef60c792..925ad001b 100644
--- a/src/remoteplayer.cpp
+++ b/src/remoteplayer.cpp
@@ -83,122 +83,6 @@ RemotePlayer::RemotePlayer(const char *name, IItemDefManager *idef):
m_star_params = sky_defaults.getStarDefaults();
}
-void RemotePlayer::serializeExtraAttributes(std::string &output)
-{
- assert(m_sao);
- Json::Value json_root;
-
- const StringMap &attrs = m_sao->getMeta().getStrings();
- for (const auto &attr : attrs) {
- json_root[attr.first] = attr.second;
- }
-
- output = fastWriteJson(json_root);
-}
-
-
-void RemotePlayer::deSerialize(std::istream &is, const std::string &playername,
- PlayerSAO *sao)
-{
- Settings args;
-
- if (!args.parseConfigLines(is, "PlayerArgsEnd")) {
- throw SerializationError("PlayerArgsEnd of player " + playername + " not found!");
- }
-
- m_dirty = true;
- //args.getS32("version"); // Version field value not used
- const std::string &name = args.get("name");
- strlcpy(m_name, name.c_str(), PLAYERNAME_SIZE);
-
- if (sao) {
- try {
- sao->setHPRaw(args.getU16("hp"));
- } catch(SettingNotFoundException &e) {
- sao->setHPRaw(PLAYER_MAX_HP_DEFAULT);
- }
-
- try {
- sao->setBasePosition(args.getV3F("position"));
- } catch (SettingNotFoundException &e) {}
-
- try {
- sao->setLookPitch(args.getFloat("pitch"));
- } catch (SettingNotFoundException &e) {}
- try {
- sao->setPlayerYaw(args.getFloat("yaw"));
- } catch (SettingNotFoundException &e) {}
-
- try {
- sao->setBreath(args.getU16("breath"), false);
- } catch (SettingNotFoundException &e) {}
-
- try {
- const std::string &extended_attributes = args.get("extended_attributes");
- std::istringstream iss(extended_attributes);
- Json::CharReaderBuilder builder;
- builder.settings_["collectComments"] = false;
- std::string errs;
-
- Json::Value attr_root;
- Json::parseFromStream(builder, iss, &attr_root, &errs);
-
- const Json::Value::Members attr_list = attr_root.getMemberNames();
- for (const auto &it : attr_list) {
- Json::Value attr_value = attr_root[it];
- sao->getMeta().setString(it, attr_value.asString());
- }
- sao->getMeta().setModified(false);
- } catch (SettingNotFoundException &e) {}
- }
-
- try {
- inventory.deSerialize(is);
- } catch (SerializationError &e) {
- errorstream << "Failed to deserialize player inventory. player_name="
- << name << " " << e.what() << std::endl;
- }
-
- if (!inventory.getList("craftpreview") && inventory.getList("craftresult")) {
- // Convert players without craftpreview
- inventory.addList("craftpreview", 1);
-
- bool craftresult_is_preview = true;
- if(args.exists("craftresult_is_preview"))
- craftresult_is_preview = args.getBool("craftresult_is_preview");
- if(craftresult_is_preview)
- {
- // Clear craftresult
- inventory.getList("craftresult")->changeItem(0, ItemStack());
- }
- }
-}
-
-void RemotePlayer::serialize(std::ostream &os)
-{
- // Utilize a Settings object for storing values
- Settings args;
- args.setS32("version", 1);
- args.set("name", m_name);
-
- // This should not happen
- assert(m_sao);
- args.setU16("hp", m_sao->getHP());
- args.setV3F("position", m_sao->getBasePosition());
- args.setFloat("pitch", m_sao->getLookPitch());
- args.setFloat("yaw", m_sao->getRotation().Y);
- args.setU16("breath", m_sao->getBreath());
-
- std::string extended_attrs;
- serializeExtraAttributes(extended_attrs);
- args.set("extended_attributes", extended_attrs);
-
- args.writeLines(os);
-
- os<<"PlayerArgsEnd\n";
-
- inventory.serialize(os);
-}
const RemotePlayerChatResult RemotePlayer::canSendChatMessage()
{
diff --git a/src/remoteplayer.h b/src/remoteplayer.h
index e4209c54f..8d086fc5a 100644
--- a/src/remoteplayer.h
+++ b/src/remoteplayer.h
@@ -44,8 +44,6 @@ public:
RemotePlayer(const char *name, IItemDefManager *idef);
virtual ~RemotePlayer() = default;
- void deSerialize(std::istream &is, const std::string &playername, PlayerSAO *sao);
-
PlayerSAO *getPlayerSAO() { return m_sao; }
void setPlayerSAO(PlayerSAO *sao) { m_sao = sao; }
@@ -142,14 +140,6 @@ public:
void onSuccessfulSave();
private:
- /*
- serialize() writes a bunch of text that can contain
- any characters except a '\0', and such an ending that
- deSerialize stops reading exactly at the right point.
- */
- void serialize(std::ostream &os);
- void serializeExtraAttributes(std::string &output);
-
PlayerSAO *m_sao = nullptr;
bool m_dirty = false;
diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp
index 72361bdb5..e9090e733 100644
--- a/src/script/common/c_content.cpp
+++ b/src/script/common/c_content.cpp
@@ -84,9 +84,6 @@ void read_item_definition(lua_State* L, int index,
getboolfield(L, index, "liquids_pointable", def.liquids_pointable);
- warn_if_field_exists(L, index, "tool_digging_properties",
- "Obsolete; use tool_capabilities");
-
lua_getfield(L, index, "tool_capabilities");
if(lua_istable(L, -1)){
def.tool_capabilities = new ToolCapabilities(
@@ -624,22 +621,39 @@ void read_content_features(lua_State *L, ContentFeatures &f, int index)
}
lua_pop(L, 1);
- f.alpha = getintfield_default(L, index, "alpha", 255);
+ /* alpha & use_texture_alpha */
+ // This is a bit complicated due to compatibility
+
+ f.setDefaultAlphaMode();
+
+ warn_if_field_exists(L, index, "alpha",
+ "Obsolete, only limited compatibility provided; "
+ "replaced by \"use_texture_alpha\"");
+ if (getintfield_default(L, index, "alpha", 255) != 255)
+ f.alpha = ALPHAMODE_BLEND;
+
+ lua_getfield(L, index, "use_texture_alpha");
+ if (lua_isboolean(L, -1)) {
+ warn_if_field_exists(L, index, "use_texture_alpha",
+ "Boolean values are deprecated; use the new choices");
+ if (lua_toboolean(L, -1))
+ f.alpha = (f.drawtype == NDT_NORMAL) ? ALPHAMODE_CLIP : ALPHAMODE_BLEND;
+ } else if (check_field_or_nil(L, -1, LUA_TSTRING, "use_texture_alpha")) {
+ int result = f.alpha;
+ string_to_enum(ScriptApiNode::es_TextureAlphaMode, result,
+ std::string(lua_tostring(L, -1)));
+ f.alpha = static_cast<enum AlphaMode>(result);
+ }
+ lua_pop(L, 1);
- bool usealpha = getboolfield_default(L, index,
- "use_texture_alpha", false);
- if (usealpha)
- f.alpha = 0;
+ /* Other stuff */
- // Read node color.
lua_getfield(L, index, "color");
read_color(L, -1, &f.color);
lua_pop(L, 1);
getstringfield(L, index, "palette", f.palette_name);
- /* Other stuff */
-
lua_getfield(L, index, "post_effect_color");
read_color(L, -1, &f.post_effect_color);
lua_pop(L, 1);
@@ -656,20 +670,6 @@ void read_content_features(lua_State *L, ContentFeatures &f, int index)
warningstream << "Node " << f.name.c_str()
<< " has a palette, but not a suitable paramtype2." << std::endl;
- // Warn about some obsolete fields
- warn_if_field_exists(L, index, "wall_mounted",
- "Obsolete; use paramtype2 = 'wallmounted'");
- warn_if_field_exists(L, index, "light_propagates",
- "Obsolete; determined from paramtype");
- warn_if_field_exists(L, index, "dug_item",
- "Obsolete; use 'drop' field");
- warn_if_field_exists(L, index, "extra_dug_item",
- "Obsolete; use 'drop' field");
- warn_if_field_exists(L, index, "extra_dug_item_rarity",
- "Obsolete; use 'drop' field");
- warn_if_field_exists(L, index, "metadata_name",
- "Obsolete; use on_add and metadata callbacks");
-
// True for all ground-like things like stone and mud, false for eg. trees
getboolfield(L, index, "is_ground_content", f.is_ground_content);
f.light_propagates = (f.param_type == CPT_LIGHT);
diff --git a/src/script/cpp_api/s_async.cpp b/src/script/cpp_api/s_async.cpp
index 5f1f9297e..0619b32c0 100644
--- a/src/script/cpp_api/s_async.cpp
+++ b/src/script/cpp_api/s_async.cpp
@@ -158,35 +158,6 @@ void AsyncEngine::step(lua_State *L)
}
/******************************************************************************/
-void AsyncEngine::pushFinishedJobs(lua_State* L) {
- // Result Table
- MutexAutoLock l(resultQueueMutex);
-
- unsigned int index = 1;
- lua_createtable(L, resultQueue.size(), 0);
- int top = lua_gettop(L);
-
- while (!resultQueue.empty()) {
- LuaJobInfo jobDone = resultQueue.front();
- resultQueue.pop_front();
-
- lua_createtable(L, 0, 2); // Pre-allocate space for two map fields
- int top_lvl2 = lua_gettop(L);
-
- lua_pushstring(L, "jobid");
- lua_pushnumber(L, jobDone.id);
- lua_settable(L, top_lvl2);
-
- lua_pushstring(L, "retval");
- lua_pushlstring(L, jobDone.serializedResult.data(),
- jobDone.serializedResult.size());
- lua_settable(L, top_lvl2);
-
- lua_rawseti(L, top, index++);
- }
-}
-
-/******************************************************************************/
void AsyncEngine::prepareEnvironment(lua_State* L, int top)
{
for (StateInitializer &stateInitializer : stateInitializers) {
diff --git a/src/script/cpp_api/s_async.h b/src/script/cpp_api/s_async.h
index b1f4bf45f..99a4f891c 100644
--- a/src/script/cpp_api/s_async.h
+++ b/src/script/cpp_api/s_async.h
@@ -98,12 +98,6 @@ public:
*/
void step(lua_State *L);
- /**
- * Push a list of finished jobs onto the stack
- * @param L The Lua stack
- */
- void pushFinishedJobs(lua_State *L);
-
protected:
/**
* Get a Job from queue to be processed
diff --git a/src/script/cpp_api/s_node.cpp b/src/script/cpp_api/s_node.cpp
index e0f9bcd78..f23fbfbde 100644
--- a/src/script/cpp_api/s_node.cpp
+++ b/src/script/cpp_api/s_node.cpp
@@ -93,6 +93,14 @@ struct EnumString ScriptApiNode::es_NodeBoxType[] =
{0, NULL},
};
+struct EnumString ScriptApiNode::es_TextureAlphaMode[] =
+ {
+ {ALPHAMODE_OPAQUE, "opaque"},
+ {ALPHAMODE_CLIP, "clip"},
+ {ALPHAMODE_BLEND, "blend"},
+ {0, NULL},
+ };
+
bool ScriptApiNode::node_on_punch(v3s16 p, MapNode node,
ServerActiveObject *puncher, const PointedThing &pointed)
{
@@ -133,9 +141,14 @@ bool ScriptApiNode::node_on_dig(v3s16 p, MapNode node,
push_v3s16(L, p);
pushnode(L, node, ndef);
objectrefGetOrCreate(L, digger);
- PCALL_RES(lua_pcall(L, 3, 0, error_handler));
- lua_pop(L, 1); // Pop error handler
- return true;
+ PCALL_RES(lua_pcall(L, 3, 1, error_handler));
+
+ // nil is treated as true for backwards compat
+ bool result = lua_isnil(L, -1) || lua_toboolean(L, -1);
+
+ lua_pop(L, 2); // Pop error handler and result
+
+ return result;
}
void ScriptApiNode::node_on_construct(v3s16 p, MapNode node)
diff --git a/src/script/cpp_api/s_node.h b/src/script/cpp_api/s_node.h
index 81b44f0f0..3f771c838 100644
--- a/src/script/cpp_api/s_node.h
+++ b/src/script/cpp_api/s_node.h
@@ -54,4 +54,5 @@ public:
static struct EnumString es_ContentParamType2[];
static struct EnumString es_LiquidType[];
static struct EnumString es_NodeBoxType[];
+ static struct EnumString es_TextureAlphaMode[];
};
diff --git a/src/script/cpp_api/s_player.cpp b/src/script/cpp_api/s_player.cpp
index 712120c61..d3e6138dc 100644
--- a/src/script/cpp_api/s_player.cpp
+++ b/src/script/cpp_api/s_player.cpp
@@ -77,6 +77,19 @@ bool ScriptApiPlayer::on_punchplayer(ServerActiveObject *player,
return readParam<bool>(L, -1);
}
+void ScriptApiPlayer::on_rightclickplayer(ServerActiveObject *player,
+ ServerActiveObject *clicker)
+{
+ SCRIPTAPI_PRECHECKHEADER
+ // Get core.registered_on_rightclickplayers
+ lua_getglobal(L, "core");
+ lua_getfield(L, -1, "registered_on_rightclickplayers");
+ // Call callbacks
+ objectrefGetOrCreate(L, player);
+ objectrefGetOrCreate(L, clicker);
+ runCallbacks(2, RUN_CALLBACKS_MODE_FIRST);
+}
+
s32 ScriptApiPlayer::on_player_hpchange(ServerActiveObject *player,
s32 hp_change, const PlayerHPChangeReason &reason)
{
diff --git a/src/script/cpp_api/s_player.h b/src/script/cpp_api/s_player.h
index a337f975b..c0f141862 100644
--- a/src/script/cpp_api/s_player.h
+++ b/src/script/cpp_api/s_player.h
@@ -47,6 +47,7 @@ public:
bool on_punchplayer(ServerActiveObject *player, ServerActiveObject *hitter,
float time_from_last_punch, const ToolCapabilities *toolcap,
v3f dir, s16 damage);
+ void on_rightclickplayer(ServerActiveObject *player, ServerActiveObject *clicker);
s32 on_player_hpchange(ServerActiveObject *player, s32 hp_change,
const PlayerHPChangeReason &reason);
void on_playerReceiveFields(ServerActiveObject *player,
diff --git a/src/script/lua_api/l_mainmenu.cpp b/src/script/lua_api/l_mainmenu.cpp
index 3ea5eb4ba..4d437b967 100644
--- a/src/script/lua_api/l_mainmenu.cpp
+++ b/src/script/lua_api/l_mainmenu.cpp
@@ -275,207 +275,6 @@ int ModApiMainMenu::l_get_worlds(lua_State *L)
}
/******************************************************************************/
-int ModApiMainMenu::l_get_favorites(lua_State *L)
-{
- std::string listtype = "local";
-
- if (!lua_isnone(L, 1)) {
- listtype = luaL_checkstring(L, 1);
- }
-
- std::vector<ServerListSpec> servers;
-
- if(listtype == "online") {
- servers = ServerList::getOnline();
- } else {
- servers = ServerList::getLocal();
- }
-
- lua_newtable(L);
- int top = lua_gettop(L);
- unsigned int index = 1;
-
- for (const Json::Value &server : servers) {
-
- lua_pushnumber(L, index);
-
- lua_newtable(L);
- int top_lvl2 = lua_gettop(L);
-
- if (!server["clients"].asString().empty()) {
- std::string clients_raw = server["clients"].asString();
- char* endptr = 0;
- int numbervalue = strtol(clients_raw.c_str(), &endptr,10);
-
- if ((!clients_raw.empty()) && (*endptr == 0)) {
- lua_pushstring(L, "clients");
- lua_pushnumber(L, numbervalue);
- lua_settable(L, top_lvl2);
- }
- }
-
- if (!server["clients_max"].asString().empty()) {
-
- std::string clients_max_raw = server["clients_max"].asString();
- char* endptr = 0;
- int numbervalue = strtol(clients_max_raw.c_str(), &endptr,10);
-
- if ((!clients_max_raw.empty()) && (*endptr == 0)) {
- lua_pushstring(L, "clients_max");
- lua_pushnumber(L, numbervalue);
- lua_settable(L, top_lvl2);
- }
- }
-
- if (!server["version"].asString().empty()) {
- lua_pushstring(L, "version");
- std::string topush = server["version"].asString();
- lua_pushstring(L, topush.c_str());
- lua_settable(L, top_lvl2);
- }
-
- if (!server["proto_min"].asString().empty()) {
- lua_pushstring(L, "proto_min");
- lua_pushinteger(L, server["proto_min"].asInt());
- lua_settable(L, top_lvl2);
- }
-
- if (!server["proto_max"].asString().empty()) {
- lua_pushstring(L, "proto_max");
- lua_pushinteger(L, server["proto_max"].asInt());
- lua_settable(L, top_lvl2);
- }
-
- if (!server["password"].asString().empty()) {
- lua_pushstring(L, "password");
- lua_pushboolean(L, server["password"].asBool());
- lua_settable(L, top_lvl2);
- }
-
- if (!server["creative"].asString().empty()) {
- lua_pushstring(L, "creative");
- lua_pushboolean(L, server["creative"].asBool());
- lua_settable(L, top_lvl2);
- }
-
- if (!server["damage"].asString().empty()) {
- lua_pushstring(L, "damage");
- lua_pushboolean(L, server["damage"].asBool());
- lua_settable(L, top_lvl2);
- }
-
- if (!server["pvp"].asString().empty()) {
- lua_pushstring(L, "pvp");
- lua_pushboolean(L, server["pvp"].asBool());
- lua_settable(L, top_lvl2);
- }
-
- if (!server["description"].asString().empty()) {
- lua_pushstring(L, "description");
- std::string topush = server["description"].asString();
- lua_pushstring(L, topush.c_str());
- lua_settable(L, top_lvl2);
- }
-
- if (!server["name"].asString().empty()) {
- lua_pushstring(L, "name");
- std::string topush = server["name"].asString();
- lua_pushstring(L, topush.c_str());
- lua_settable(L, top_lvl2);
- }
-
- if (!server["address"].asString().empty()) {
- lua_pushstring(L, "address");
- std::string topush = server["address"].asString();
- lua_pushstring(L, topush.c_str());
- lua_settable(L, top_lvl2);
- }
-
- if (!server["port"].asString().empty()) {
- lua_pushstring(L, "port");
- std::string topush = server["port"].asString();
- lua_pushstring(L, topush.c_str());
- lua_settable(L, top_lvl2);
- }
-
- if (server.isMember("ping")) {
- float ping = server["ping"].asFloat();
- lua_pushstring(L, "ping");
- lua_pushnumber(L, ping);
- lua_settable(L, top_lvl2);
- }
-
- if (server["clients_list"].isArray()) {
- unsigned int index_lvl2 = 1;
- lua_pushstring(L, "clients_list");
- lua_newtable(L);
- int top_lvl3 = lua_gettop(L);
- for (const Json::Value &client : server["clients_list"]) {
- lua_pushnumber(L, index_lvl2);
- std::string topush = client.asString();
- lua_pushstring(L, topush.c_str());
- lua_settable(L, top_lvl3);
- index_lvl2++;
- }
- lua_settable(L, top_lvl2);
- }
-
- if (server["mods"].isArray()) {
- unsigned int index_lvl2 = 1;
- lua_pushstring(L, "mods");
- lua_newtable(L);
- int top_lvl3 = lua_gettop(L);
- for (const Json::Value &mod : server["mods"]) {
-
- lua_pushnumber(L, index_lvl2);
- std::string topush = mod.asString();
- lua_pushstring(L, topush.c_str());
- lua_settable(L, top_lvl3);
- index_lvl2++;
- }
- lua_settable(L, top_lvl2);
- }
-
- lua_settable(L, top);
- index++;
- }
- return 1;
-}
-
-/******************************************************************************/
-int ModApiMainMenu::l_delete_favorite(lua_State *L)
-{
- std::vector<ServerListSpec> servers;
-
- std::string listtype = "local";
-
- if (!lua_isnone(L,2)) {
- listtype = luaL_checkstring(L,2);
- }
-
- if ((listtype != "local") &&
- (listtype != "online"))
- return 0;
-
-
- if(listtype == "online") {
- servers = ServerList::getOnline();
- } else {
- servers = ServerList::getLocal();
- }
-
- int fav_idx = luaL_checkinteger(L,1) -1;
-
- if ((fav_idx >= 0) &&
- (fav_idx < (int) servers.size())) {
-
- ServerList::deleteEntry(servers[fav_idx]);
- }
-
- return 0;
-}
-
-/******************************************************************************/
int ModApiMainMenu::l_get_games(lua_State *L)
{
std::vector<SubgameSpec> games = getAvailableGames();
@@ -730,6 +529,7 @@ int ModApiMainMenu::l_get_texturepath(lua_State *L)
return 1;
}
+/******************************************************************************/
int ModApiMainMenu::l_get_texturepath_share(lua_State *L)
{
std::string gamepath = fs::RemoveRelativePathComponents(
@@ -738,6 +538,7 @@ int ModApiMainMenu::l_get_texturepath_share(lua_State *L)
return 1;
}
+/******************************************************************************/
int ModApiMainMenu::l_get_cache_path(lua_State *L)
{
lua_pushstring(L, fs::RemoveRelativePathComponents(porting::path_cache).c_str());
@@ -745,6 +546,13 @@ int ModApiMainMenu::l_get_cache_path(lua_State *L)
}
/******************************************************************************/
+int ModApiMainMenu::l_get_temp_path(lua_State *L)
+{
+ lua_pushstring(L, fs::TempPath().c_str());
+ return 1;
+}
+
+/******************************************************************************/
int ModApiMainMenu::l_create_dir(lua_State *L) {
const char *path = luaL_checkstring(L, 1);
@@ -1133,11 +941,9 @@ void ModApiMainMenu::Initialize(lua_State *L, int top)
API_FCT(get_content_info);
API_FCT(start);
API_FCT(close);
- API_FCT(get_favorites);
API_FCT(show_keys_menu);
API_FCT(create_world);
API_FCT(delete_world);
- API_FCT(delete_favorite);
API_FCT(set_background);
API_FCT(set_topleft_text);
API_FCT(get_mapgen_names);
@@ -1148,6 +954,7 @@ void ModApiMainMenu::Initialize(lua_State *L, int top)
API_FCT(get_texturepath);
API_FCT(get_texturepath_share);
API_FCT(get_cache_path);
+ API_FCT(get_temp_path);
API_FCT(create_dir);
API_FCT(delete_dir);
API_FCT(copy_dir);
@@ -1173,7 +980,6 @@ void ModApiMainMenu::InitializeAsync(lua_State *L, int top)
{
API_FCT(get_worlds);
API_FCT(get_games);
- API_FCT(get_favorites);
API_FCT(get_mapgen_names);
API_FCT(get_user_path);
API_FCT(get_modpath);
@@ -1182,6 +988,7 @@ void ModApiMainMenu::InitializeAsync(lua_State *L, int top)
API_FCT(get_texturepath);
API_FCT(get_texturepath_share);
API_FCT(get_cache_path);
+ API_FCT(get_temp_path);
API_FCT(create_dir);
API_FCT(delete_dir);
API_FCT(copy_dir);
@@ -1189,5 +996,7 @@ void ModApiMainMenu::InitializeAsync(lua_State *L, int top)
//API_FCT(extract_zip); //TODO remove dependency to GuiEngine
API_FCT(may_modify_path);
API_FCT(download_file);
+ API_FCT(get_min_supp_proto);
+ API_FCT(get_max_supp_proto);
//API_FCT(gettext); (gettext lib isn't threadsafe)
}
diff --git a/src/script/lua_api/l_mainmenu.h b/src/script/lua_api/l_mainmenu.h
index 0b02ed892..49ce7c251 100644
--- a/src/script/lua_api/l_mainmenu.h
+++ b/src/script/lua_api/l_mainmenu.h
@@ -74,10 +74,6 @@ private:
static int l_get_mapgen_names(lua_State *L);
- static int l_get_favorites(lua_State *L);
-
- static int l_delete_favorite(lua_State *L);
-
static int l_gettext(lua_State *L);
//packages
@@ -126,6 +122,8 @@ private:
static int l_get_cache_path(lua_State *L);
+ static int l_get_temp_path(lua_State *L);
+
static int l_create_dir(lua_State *L);
static int l_delete_dir(lua_State *L);
diff --git a/src/script/lua_api/l_mapgen.cpp b/src/script/lua_api/l_mapgen.cpp
index 834938e56..12a497b1e 100644
--- a/src/script/lua_api/l_mapgen.cpp
+++ b/src/script/lua_api/l_mapgen.cpp
@@ -873,9 +873,6 @@ int ModApiMapgen::l_set_mapgen_params(lua_State *L)
if (lua_isnumber(L, -1))
settingsmgr->setMapSetting("chunksize", readParam<std::string>(L, -1), true);
- warn_if_field_exists(L, 1, "flagmask",
- "Obsolete: flags field now includes unset flags.");
-
lua_getfield(L, 1, "flags");
if (lua_isstring(L, -1))
settingsmgr->setMapSetting("mg_flags", readParam<std::string>(L, -1), true);
@@ -985,7 +982,7 @@ int ModApiMapgen::l_set_noiseparams(lua_State *L)
bool set_default = !lua_isboolean(L, 3) || readParam<bool>(L, 3);
- g_settings->setNoiseParams(name, np, set_default);
+ Settings::getLayer(set_default ? SL_DEFAULTS : SL_GLOBAL)->setNoiseParams(name, np);
return 0;
}
@@ -1338,11 +1335,9 @@ int ModApiMapgen::l_register_ore(lua_State *L)
lua_getfield(L, index, "noise_params");
if (read_noiseparams(L, -1, &ore->np)) {
ore->flags |= OREFLAG_USE_NOISE;
- } else if (ore->NEEDS_NOISE) {
- errorstream << "register_ore: specified ore type requires valid "
- "'noise_params' parameter" << std::endl;
- delete ore;
- return 0;
+ } else if (ore->needs_noise) {
+ log_deprecated(L,
+ "register_ore: ore type requires 'noise_params' but it is not specified, falling back to defaults");
}
lua_pop(L, 1);
diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp
index f52e4892e..07aa3f7c9 100644
--- a/src/script/lua_api/l_object.cpp
+++ b/src/script/lua_api/l_object.cpp
@@ -355,6 +355,15 @@ int ObjectRef::l_set_armor_groups(lua_State *L)
ItemGroupList groups;
read_groups(L, 2, groups);
+ if (sao->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
+ if (!g_settings->getBool("enable_damage") && !itemgroup_get(groups, "immortal")) {
+ warningstream << "Mod tried to enable damage for a player, but it's "
+ "disabled globally. Ignoring." << std::endl;
+ infostream << script_get_backtrace(L) << std::endl;
+ groups["immortal"] = 1;
+ }
+ }
+
sao->setArmorGroups(groups);
return 0;
}
@@ -399,7 +408,7 @@ int ObjectRef::l_get_animation(lua_State *L)
if (sao == nullptr)
return 0;
- v2f frames = v2f(1,1);
+ v2f frames = v2f(1, 1);
float frame_speed = 15;
float frame_blend = 0;
bool frame_loop = true;
@@ -463,8 +472,8 @@ int ObjectRef::l_set_eye_offset(lua_State *L)
if (player == nullptr)
return 0;
- v3f offset_first = read_v3f(L, 2);
- v3f offset_third = read_v3f(L, 3);
+ v3f offset_first = readParam<v3f>(L, 2, v3f(0, 0, 0));
+ v3f offset_third = readParam<v3f>(L, 3, v3f(0, 0, 0));
// Prevent abuse of offset values (keep player always visible)
offset_third.X = rangelim(offset_third.X,-10,10);
@@ -537,9 +546,9 @@ int ObjectRef::l_set_bone_position(lua_State *L)
if (sao == nullptr)
return 0;
- std::string bone = readParam<std::string>(L, 2);
- v3f position = check_v3f(L, 3);
- v3f rotation = check_v3f(L, 4);
+ std::string bone = readParam<std::string>(L, 2, "");
+ v3f position = readParam<v3f>(L, 3, v3f(0, 0, 0));
+ v3f rotation = readParam<v3f>(L, 4, v3f(0, 0, 0));
sao->setBonePosition(bone, position, rotation);
return 0;
@@ -554,7 +563,7 @@ int ObjectRef::l_get_bone_position(lua_State *L)
if (sao == nullptr)
return 0;
- std::string bone = readParam<std::string>(L, 2);
+ std::string bone = readParam<std::string>(L, 2, "");
v3f position = v3f(0, 0, 0);
v3f rotation = v3f(0, 0, 0);
@@ -578,10 +587,10 @@ int ObjectRef::l_set_attach(lua_State *L)
if (sao == parent)
throw LuaError("ObjectRef::set_attach: attaching object to itself is not allowed.");
- int parent_id = 0;
+ int parent_id;
std::string bone;
- v3f position = v3f(0, 0, 0);
- v3f rotation = v3f(0, 0, 0);
+ v3f position;
+ v3f rotation;
bool force_visible;
sao->getAttachment(&parent_id, &bone, &position, &rotation, &force_visible);
@@ -590,9 +599,9 @@ int ObjectRef::l_set_attach(lua_State *L)
old_parent->removeAttachmentChild(sao->getId());
}
- bone = readParam<std::string>(L, 3, "");
- position = read_v3f(L, 4);
- rotation = read_v3f(L, 5);
+ bone = readParam<std::string>(L, 3, "");
+ position = readParam<v3f>(L, 4, v3f(0, 0, 0));
+ rotation = readParam<v3f>(L, 5, v3f(0, 0, 0));
force_visible = readParam<bool>(L, 6, false);
sao->setAttachment(parent->getId(), bone, position, rotation, force_visible);
@@ -609,10 +618,10 @@ int ObjectRef::l_get_attach(lua_State *L)
if (sao == nullptr)
return 0;
- int parent_id = 0;
+ int parent_id;
std::string bone;
- v3f position = v3f(0, 0, 0);
- v3f rotation = v3f(0, 0, 0);
+ v3f position;
+ v3f rotation;
bool force_visible;
sao->getAttachment(&parent_id, &bone, &position, &rotation, &force_visible);
@@ -892,9 +901,6 @@ int ObjectRef::l_set_yaw(lua_State *L)
if (entitysao == nullptr)
return 0;
- if (isNaN(L, 2))
- throw LuaError("ObjectRef::set_yaw: NaN value is not allowed.");
-
float yaw = readParam<float>(L, 2) * core::RADTODEG;
entitysao->setRotation(v3f(0, yaw, 0));
@@ -2199,7 +2205,7 @@ int ObjectRef::l_set_minimap_modes(lua_State *L)
luaL_checktype(L, 2, LUA_TTABLE);
std::vector<MinimapMode> modes;
- s16 selected_mode = luaL_checkint(L, 3);
+ s16 selected_mode = readParam<s16>(L, 3);
lua_pushnil(L);
while (lua_next(L, 2) != 0) {
diff --git a/src/script/lua_api/l_server.cpp b/src/script/lua_api/l_server.cpp
index 64ae924d2..026f5282c 100644
--- a/src/script/lua_api/l_server.cpp
+++ b/src/script/lua_api/l_server.cpp
@@ -44,7 +44,7 @@ int ModApiServer::l_request_shutdown(lua_State *L)
int ModApiServer::l_get_server_status(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
- lua_pushstring(L, wide_to_narrow(getServer(L)->getStatusString()).c_str());
+ lua_pushstring(L, getServer(L)->getStatusString().c_str());
return 1;
}
@@ -116,24 +116,18 @@ int ModApiServer::l_get_player_privs(lua_State *L)
int ModApiServer::l_get_player_ip(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
- const char * name = luaL_checkstring(L, 1);
- RemotePlayer *player = dynamic_cast<ServerEnvironment *>(getEnv(L))->getPlayer(name);
- if(player == NULL)
- {
+
+ Server *server = getServer(L);
+
+ const char *name = luaL_checkstring(L, 1);
+ RemotePlayer *player = server->getEnv().getPlayer(name);
+ if (!player) {
lua_pushnil(L); // no such player
return 1;
}
- try
- {
- Address addr = getServer(L)->getPeerAddress(player->getPeerId());
- std::string ip_str = addr.serializeString();
- lua_pushstring(L, ip_str.c_str());
- return 1;
- } catch (const con::PeerNotFoundException &) {
- dstream << FUNCTION_NAME << ": peer was not found" << std::endl;
- lua_pushnil(L); // error
- return 1;
- }
+
+ lua_pushstring(L, server->getPeerAddress(player->getPeerId()).serializeString().c_str());
+ return 1;
}
// get_player_information(name)
@@ -150,26 +144,18 @@ int ModApiServer::l_get_player_information(lua_State *L)
return 1;
}
- Address addr;
- try {
- addr = server->getPeerAddress(player->getPeerId());
- } catch (const con::PeerNotFoundException &) {
- dstream << FUNCTION_NAME << ": peer was not found" << std::endl;
- lua_pushnil(L); // error
- return 1;
- }
-
- float min_rtt, max_rtt, avg_rtt, min_jitter, max_jitter, avg_jitter;
- ClientState state;
- u32 uptime;
- u16 prot_vers;
- u8 ser_vers, major, minor, patch;
- std::string vers_string, lang_code;
+ /*
+ Be careful not to introduce a depdendency on the connection to
+ the peer here. This function is >>REQUIRED<< to still be able to return
+ values even when the peer unexpectedly disappears.
+ Hence all the ConInfo values here are optional.
+ */
auto getConInfo = [&] (con::rtt_stat_type type, float *value) -> bool {
return server->getClientConInfo(player->getPeerId(), type, value);
};
+ float min_rtt, max_rtt, avg_rtt, min_jitter, max_jitter, avg_jitter;
bool have_con_info =
getConInfo(con::MIN_RTT, &min_rtt) &&
getConInfo(con::MAX_RTT, &max_rtt) &&
@@ -178,11 +164,9 @@ int ModApiServer::l_get_player_information(lua_State *L)
getConInfo(con::MAX_JITTER, &max_jitter) &&
getConInfo(con::AVG_JITTER, &avg_jitter);
- bool r = server->getClientInfo(player->getPeerId(), &state, &uptime,
- &ser_vers, &prot_vers, &major, &minor, &patch, &vers_string,
- &lang_code);
- if (!r) {
- dstream << FUNCTION_NAME << ": peer was not found" << std::endl;
+ ClientInfo info;
+ if (!server->getClientInfo(player->getPeerId(), info)) {
+ warningstream << FUNCTION_NAME << ": no client info?!" << std::endl;
lua_pushnil(L); // error
return 1;
}
@@ -191,13 +175,13 @@ int ModApiServer::l_get_player_information(lua_State *L)
int table = lua_gettop(L);
lua_pushstring(L,"address");
- lua_pushstring(L, addr.serializeString().c_str());
+ lua_pushstring(L, info.addr.serializeString().c_str());
lua_settable(L, table);
lua_pushstring(L,"ip_version");
- if (addr.getFamily() == AF_INET) {
+ if (info.addr.getFamily() == AF_INET) {
lua_pushnumber(L, 4);
- } else if (addr.getFamily() == AF_INET6) {
+ } else if (info.addr.getFamily() == AF_INET6) {
lua_pushnumber(L, 6);
} else {
lua_pushnumber(L, 0);
@@ -231,11 +215,11 @@ int ModApiServer::l_get_player_information(lua_State *L)
}
lua_pushstring(L,"connection_uptime");
- lua_pushnumber(L, uptime);
+ lua_pushnumber(L, info.uptime);
lua_settable(L, table);
lua_pushstring(L,"protocol_version");
- lua_pushnumber(L, prot_vers);
+ lua_pushnumber(L, info.prot_vers);
lua_settable(L, table);
lua_pushstring(L, "formspec_version");
@@ -243,32 +227,32 @@ int ModApiServer::l_get_player_information(lua_State *L)
lua_settable(L, table);
lua_pushstring(L, "lang_code");
- lua_pushstring(L, lang_code.c_str());
+ lua_pushstring(L, info.lang_code.c_str());
lua_settable(L, table);
#ifndef NDEBUG
lua_pushstring(L,"serialization_version");
- lua_pushnumber(L, ser_vers);
+ lua_pushnumber(L, info.ser_vers);
lua_settable(L, table);
lua_pushstring(L,"major");
- lua_pushnumber(L, major);
+ lua_pushnumber(L, info.major);
lua_settable(L, table);
lua_pushstring(L,"minor");
- lua_pushnumber(L, minor);
+ lua_pushnumber(L, info.minor);
lua_settable(L, table);
lua_pushstring(L,"patch");
- lua_pushnumber(L, patch);
+ lua_pushnumber(L, info.patch);
lua_settable(L, table);
lua_pushstring(L,"version_string");
- lua_pushstring(L, vers_string.c_str());
+ lua_pushstring(L, info.vers_string.c_str());
lua_settable(L, table);
lua_pushstring(L,"state");
- lua_pushstring(L,ClientInterface::state2Name(state).c_str());
+ lua_pushstring(L, ClientInterface::state2Name(info.state).c_str());
lua_settable(L, table);
#endif
@@ -296,23 +280,18 @@ int ModApiServer::l_get_ban_description(lua_State *L)
int ModApiServer::l_ban_player(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
- const char * name = luaL_checkstring(L, 1);
- RemotePlayer *player = dynamic_cast<ServerEnvironment *>(getEnv(L))->getPlayer(name);
- if (player == NULL) {
+
+ Server *server = getServer(L);
+
+ const char *name = luaL_checkstring(L, 1);
+ RemotePlayer *player = server->getEnv().getPlayer(name);
+ if (!player) {
lua_pushboolean(L, false); // no such player
return 1;
}
- try
- {
- Address addr = getServer(L)->getPeerAddress(
- dynamic_cast<ServerEnvironment *>(getEnv(L))->getPlayer(name)->getPeerId());
- std::string ip_str = addr.serializeString();
- getServer(L)->setIpBanned(ip_str, name);
- } catch(const con::PeerNotFoundException &) {
- dstream << FUNCTION_NAME << ": peer was not found" << std::endl;
- lua_pushboolean(L, false); // error
- return 1;
- }
+
+ std::string ip_str = server->getPeerAddress(player->getPeerId()).serializeString();
+ server->setIpBanned(ip_str, name);
lua_pushboolean(L, true);
return 1;
}
@@ -474,19 +453,30 @@ int ModApiServer::l_sound_fade(lua_State *L)
}
// dynamic_add_media(filepath)
-int ModApiServer::l_dynamic_add_media(lua_State *L)
+int ModApiServer::l_dynamic_add_media_raw(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
- // Reject adding media before the server has started up
if (!getEnv(L))
throw LuaError("Dynamic media cannot be added before server has started up");
std::string filepath = readParam<std::string>(L, 1);
CHECK_SECURE_PATH(L, filepath.c_str(), false);
- bool ok = getServer(L)->dynamicAddMedia(filepath);
- lua_pushboolean(L, ok);
+ std::vector<RemotePlayer*> sent_to;
+ bool ok = getServer(L)->dynamicAddMedia(filepath, sent_to);
+ if (ok) {
+ // (see wrapper code in builtin)
+ lua_createtable(L, sent_to.size(), 0);
+ int i = 0;
+ for (RemotePlayer *player : sent_to) {
+ lua_pushstring(L, player->getName());
+ lua_rawseti(L, -2, ++i);
+ }
+ } else {
+ lua_pushboolean(L, false);
+ }
+
return 1;
}
@@ -554,7 +544,7 @@ void ModApiServer::Initialize(lua_State *L, int top)
API_FCT(sound_play);
API_FCT(sound_stop);
API_FCT(sound_fade);
- API_FCT(dynamic_add_media);
+ API_FCT(dynamic_add_media_raw);
API_FCT(get_player_information);
API_FCT(get_player_privs);
diff --git a/src/script/lua_api/l_server.h b/src/script/lua_api/l_server.h
index 938bfa8ef..2df180b17 100644
--- a/src/script/lua_api/l_server.h
+++ b/src/script/lua_api/l_server.h
@@ -71,7 +71,7 @@ private:
static int l_sound_fade(lua_State *L);
// dynamic_add_media(filepath)
- static int l_dynamic_add_media(lua_State *L);
+ static int l_dynamic_add_media_raw(lua_State *L);
// get_player_privs(name, text)
static int l_get_player_privs(lua_State *L);
diff --git a/src/script/lua_api/l_settings.cpp b/src/script/lua_api/l_settings.cpp
index 33eb02392..bcbaf15fa 100644
--- a/src/script/lua_api/l_settings.cpp
+++ b/src/script/lua_api/l_settings.cpp
@@ -197,7 +197,7 @@ int LuaSettings::l_set_np_group(lua_State *L)
SET_SECURITY_CHECK(L, key);
- o->m_settings->setNoiseParams(key, value, false);
+ o->m_settings->setNoiseParams(key, value);
return 0;
}
diff --git a/src/script/lua_api/l_util.cpp b/src/script/lua_api/l_util.cpp
index e2730c6d9..60ae7871c 100644
--- a/src/script/lua_api/l_util.cpp
+++ b/src/script/lua_api/l_util.cpp
@@ -250,6 +250,17 @@ int ModApiUtil::l_get_builtin_path(lua_State *L)
return 1;
}
+// get_user_path()
+int ModApiUtil::l_get_user_path(lua_State *L)
+{
+ NO_MAP_LOCK_REQUIRED;
+
+ std::string path = porting::path_user;
+ lua_pushstring(L, path.c_str());
+
+ return 1;
+}
+
// compress(data, method, level)
int ModApiUtil::l_compress(lua_State *L)
{
@@ -486,6 +497,7 @@ void ModApiUtil::Initialize(lua_State *L, int top)
API_FCT(is_yes);
API_FCT(get_builtin_path);
+ API_FCT(get_user_path);
API_FCT(compress);
API_FCT(decompress);
@@ -544,6 +556,7 @@ void ModApiUtil::InitializeAsync(lua_State *L, int top)
API_FCT(is_yes);
API_FCT(get_builtin_path);
+ API_FCT(get_user_path);
API_FCT(compress);
API_FCT(decompress);
diff --git a/src/script/lua_api/l_util.h b/src/script/lua_api/l_util.h
index b6c1b58af..dbdd62b99 100644
--- a/src/script/lua_api/l_util.h
+++ b/src/script/lua_api/l_util.h
@@ -68,6 +68,9 @@ private:
// get_builtin_path()
static int l_get_builtin_path(lua_State *L);
+ // get_user_path()
+ static int l_get_user_path(lua_State *L);
+
// compress(data, method, ...)
static int l_compress(lua_State *L);
diff --git a/src/script/scripting_mainmenu.cpp b/src/script/scripting_mainmenu.cpp
index 0f672f917..b102a66a1 100644
--- a/src/script/scripting_mainmenu.cpp
+++ b/src/script/scripting_mainmenu.cpp
@@ -31,7 +31,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
extern "C" {
#include "lualib.h"
}
-
#define MAINMENU_NUM_ASYNC_THREADS 4
diff --git a/src/server.cpp b/src/server.cpp
index b5352749c..af4eb17e2 100644
--- a/src/server.cpp
+++ b/src/server.cpp
@@ -351,6 +351,8 @@ Server::~Server()
// Deinitialize scripting
infostream << "Server: Deinitializing scripting" << std::endl;
delete m_script;
+ delete m_startup_server_map; // if available
+ delete m_game_settings;
while (!m_unsent_map_edit_queue.empty()) {
delete m_unsent_map_edit_queue.front();
@@ -368,6 +370,8 @@ void Server::init()
infostream << "- world: " << m_path_world << std::endl;
infostream << "- game: " << m_gamespec.path << std::endl;
+ m_game_settings = Settings::createLayer(SL_GAME);
+
// Create world if it doesn't exist
try {
loadGameConfAndInitWorld(m_path_world,
@@ -396,6 +400,7 @@ void Server::init()
// Create the Map (loads map_meta.txt, overriding configured mapgen params)
ServerMap *servermap = new ServerMap(m_path_world, this, m_emerge, m_metrics_backend.get());
+ m_startup_server_map = servermap;
// Initialize scripting
infostream << "Server: Initializing Lua" << std::endl;
@@ -437,6 +442,7 @@ void Server::init()
m_craftdef->initHashes(this);
// Initialize Environment
+ m_startup_server_map = nullptr; // Ownership moved to ServerEnvironment
m_env = new ServerEnvironment(servermap, m_script, this, m_path_world);
m_inventory_mgr->setEnv(m_env);
@@ -1239,20 +1245,8 @@ bool Server::getClientConInfo(session_t peer_id, con::rtt_stat_type type, float*
return *retval != -1;
}
-bool Server::getClientInfo(
- session_t peer_id,
- ClientState* state,
- u32* uptime,
- u8* ser_vers,
- u16* prot_vers,
- u8* major,
- u8* minor,
- u8* patch,
- std::string* vers_string,
- std::string* lang_code
- )
-{
- *state = m_clients.getClientState(peer_id);
+bool Server::getClientInfo(session_t peer_id, ClientInfo &ret)
+{
m_clients.lock();
RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_Invalid);
@@ -1261,15 +1255,18 @@ bool Server::getClientInfo(
return false;
}
- *uptime = client->uptime();
- *ser_vers = client->serialization_version;
- *prot_vers = client->net_proto_version;
+ ret.state = client->getState();
+ ret.addr = client->getAddress();
+ ret.uptime = client->uptime();
+ ret.ser_vers = client->serialization_version;
+ ret.prot_vers = client->net_proto_version;
+
+ ret.major = client->getMajor();
+ ret.minor = client->getMinor();
+ ret.patch = client->getPatch();
+ ret.vers_string = client->getFullVer();
- *major = client->getMajor();
- *minor = client->getMinor();
- *patch = client->getPatch();
- *vers_string = client->getFull();
- *lang_code = client->getLangCode();
+ ret.lang_code = client->getLangCode();
m_clients.unlock();
@@ -1355,7 +1352,7 @@ void Server::SendPlayerHPOrDie(PlayerSAO *playersao, const PlayerHPChangeReason
return;
session_t peer_id = playersao->getPeerID();
- bool is_alive = playersao->getHP() > 0;
+ bool is_alive = !playersao->isDead();
if (is_alive)
SendPlayerHP(peer_id);
@@ -1488,7 +1485,8 @@ void Server::SendChatMessage(session_t peer_id, const ChatMessage &message)
NetworkPacket pkt(TOCLIENT_CHAT_MESSAGE, 0, peer_id);
u8 version = 1;
u8 type = message.type;
- pkt << version << type << std::wstring(L"") << message.message << (u64)message.timestamp;
+ pkt << version << type << message.sender << message.message
+ << static_cast<u64>(message.timestamp);
if (peer_id != PEER_ID_INEXISTENT) {
RemotePlayer *player = m_env->getPlayer(peer_id);
@@ -2961,7 +2959,7 @@ void Server::handleChatInterfaceEvent(ChatEvent *evt)
}
}
-std::wstring Server::handleChat(const std::string &name, const std::wstring &wname,
+std::wstring Server::handleChat(const std::string &name,
std::wstring wmessage, bool check_shout_priv, RemotePlayer *player)
{
// If something goes wrong, this player is to blame
@@ -2999,7 +2997,7 @@ std::wstring Server::handleChat(const std::string &name, const std::wstring &wna
auto message = trim(wide_to_utf8(wmessage));
if (message.find_first_of("\n\r") != std::wstring::npos) {
- return L"New lines are not permitted in chat messages";
+ return L"Newlines are not permitted in chat messages";
}
// Run script hook, exit if script ate the chat message
@@ -3020,10 +3018,10 @@ std::wstring Server::handleChat(const std::string &name, const std::wstring &wna
the Cyrillic alphabet and some characters on older Android devices
*/
#ifdef __ANDROID__
- line += L"<" + wname + L"> " + wmessage;
+ line += L"<" + utf8_to_wide(name) + L"> " + wmessage;
#else
- line += narrow_to_wide(m_script->formatChatMessage(name,
- wide_to_narrow(wmessage)));
+ line += utf8_to_wide(m_script->formatChatMessage(name,
+ wide_to_utf8(wmessage)));
#endif
}
@@ -3036,35 +3034,23 @@ std::wstring Server::handleChat(const std::string &name, const std::wstring &wna
/*
Send the message to others
*/
- actionstream << "CHAT: " << wide_to_narrow(unescape_enriched(line)) << std::endl;
+ actionstream << "CHAT: " << wide_to_utf8(unescape_enriched(line)) << std::endl;
- std::vector<session_t> clients = m_clients.getClientIDs();
+ ChatMessage chatmsg(line);
- /*
- Send the message back to the inital sender
- if they are using protocol version >= 29
- */
-
- session_t peer_id_to_avoid_sending =
- (player ? player->getPeerId() : PEER_ID_INEXISTENT);
-
- if (player && player->protocol_version >= 29)
- peer_id_to_avoid_sending = PEER_ID_INEXISTENT;
+ std::vector<session_t> clients = m_clients.getClientIDs();
+ for (u16 cid : clients)
+ SendChatMessage(cid, chatmsg);
- for (u16 cid : clients) {
- if (cid != peer_id_to_avoid_sending)
- SendChatMessage(cid, ChatMessage(line));
- }
return L"";
}
void Server::handleAdminChat(const ChatEventChat *evt)
{
std::string name = evt->nick;
- std::wstring wname = utf8_to_wide(name);
std::wstring wmessage = evt->evt_msg;
- std::wstring answer = handleChat(name, wname, wmessage);
+ std::wstring answer = handleChat(name, wmessage);
// If asked to send answer to sender
if (!answer.empty()) {
@@ -3101,46 +3087,43 @@ PlayerSAO *Server::getPlayerSAO(session_t peer_id)
return player->getPlayerSAO();
}
-std::wstring Server::getStatusString()
+std::string Server::getStatusString()
{
- std::wostringstream os(std::ios_base::binary);
- os << L"# Server: ";
+ std::ostringstream os(std::ios_base::binary);
+ os << "# Server: ";
// Version
- os << L"version=" << narrow_to_wide(g_version_string);
+ os << "version=" << g_version_string;
// Uptime
- os << L", uptime=" << m_uptime_counter->get();
+ os << ", uptime=" << m_uptime_counter->get();
// Max lag estimate
- os << L", max_lag=" << (m_env ? m_env->getMaxLagEstimate() : 0);
+ os << ", max_lag=" << (m_env ? m_env->getMaxLagEstimate() : 0);
// Information about clients
bool first = true;
- os << L", clients={";
+ os << ", clients={";
if (m_env) {
std::vector<session_t> clients = m_clients.getClientIDs();
for (session_t client_id : clients) {
RemotePlayer *player = m_env->getPlayer(client_id);
// Get name of player
- std::wstring name = L"unknown";
- if (player)
- name = narrow_to_wide(player->getName());
+ const char *name = player ? player->getName() : "<unknown>";
// Add name to information string
if (!first)
- os << L", ";
+ os << ", ";
else
first = false;
-
os << name;
}
}
- os << L"}";
+ os << "}";
if (m_env && !((ServerMap*)(&m_env->getMap()))->isSavingEnabled())
- os << std::endl << L"# Server: " << " WARNING: Map saving is disabled.";
+ os << std::endl << "# Server: " << " WARNING: Map saving is disabled.";
if (!g_settings->get("motd").empty())
- os << std::endl << L"# Server: " << narrow_to_wide(g_settings->get("motd"));
+ os << std::endl << "# Server: " << g_settings->get("motd");
return os.str();
}
@@ -3336,7 +3319,8 @@ void Server::hudSetHotbarSelectedImage(RemotePlayer *player, const std::string &
Address Server::getPeerAddress(session_t peer_id)
{
- return m_con->GetPeerAddress(peer_id);
+ // Note that this is only set after Init was received in Server::handleCommand_Init
+ return getClient(peer_id, CS_Invalid)->getAddress();
}
void Server::setLocalPlayerAnimations(RemotePlayer *player,
@@ -3470,7 +3454,8 @@ void Server::deleteParticleSpawner(const std::string &playername, u32 id)
SendDeleteParticleSpawner(peer_id, id);
}
-bool Server::dynamicAddMedia(const std::string &filepath)
+bool Server::dynamicAddMedia(const std::string &filepath,
+ std::vector<RemotePlayer*> &sent_to)
{
std::string filename = fs::GetFilenameFromPath(filepath.c_str());
if (m_media.find(filename) != m_media.end()) {
@@ -3490,9 +3475,17 @@ bool Server::dynamicAddMedia(const std::string &filepath)
pkt << raw_hash << filename << (bool) true;
pkt.putLongString(filedata);
- auto client_ids = m_clients.getClientIDs(CS_DefinitionsSent);
- for (session_t client_id : client_ids) {
+ m_clients.lock();
+ for (auto &pair : m_clients.getClientList()) {
+ if (pair.second->getState() < CS_DefinitionsSent)
+ continue;
+ if (pair.second->net_proto_version < 39)
+ continue;
+
+ if (auto player = m_env->getPlayer(pair.second->peer_id))
+ sent_to.emplace_back(player);
/*
+ FIXME: this is a very awful hack
The network layer only guarantees ordered delivery inside a channel.
Since the very next packet could be one that uses the media, we have
to push the media over ALL channels to ensure it is processed before
@@ -3501,9 +3494,10 @@ bool Server::dynamicAddMedia(const std::string &filepath)
- channel 1 (HUD)
- channel 0 (everything else: e.g. play_sound, object messages)
*/
- m_clients.send(client_id, 1, &pkt, true);
- m_clients.send(client_id, 0, &pkt, true);
+ m_clients.send(pair.second->peer_id, 1, &pkt, true);
+ m_clients.send(pair.second->peer_id, 0, &pkt, true);
}
+ m_clients.unlock();
return true;
}
diff --git a/src/server.h b/src/server.h
index 4b3ac5cf7..9857215d0 100644
--- a/src/server.h
+++ b/src/server.h
@@ -126,6 +126,17 @@ struct MinimapMode {
u16 scale = 1;
};
+// structure for everything getClientInfo returns, for convenience
+struct ClientInfo {
+ ClientState state;
+ Address addr;
+ u32 uptime;
+ u8 ser_vers;
+ u16 prot_vers;
+ u8 major, minor, patch;
+ std::string vers_string, lang_code;
+};
+
class Server : public con::PeerHandler, public MapEventReceiver,
public IGameDef
{
@@ -208,7 +219,7 @@ public:
void onMapEditEvent(const MapEditEvent &event);
// Connection must be locked when called
- std::wstring getStatusString();
+ std::string getStatusString();
inline double getUptime() const { return m_uptime_counter->get(); }
// read shutdown state
@@ -246,7 +257,7 @@ public:
void deleteParticleSpawner(const std::string &playername, u32 id);
- bool dynamicAddMedia(const std::string &filepath);
+ bool dynamicAddMedia(const std::string &filepath, std::vector<RemotePlayer*> &sent_to);
ServerInventoryManager *getInventoryMgr() const { return m_inventory_mgr.get(); }
void sendDetachedInventory(Inventory *inventory, const std::string &name, session_t peer_id);
@@ -326,9 +337,7 @@ public:
void DenyAccess_Legacy(session_t peer_id, const std::wstring &reason);
void DisconnectPeer(session_t peer_id);
bool getClientConInfo(session_t peer_id, con::rtt_stat_type type, float *retval);
- bool getClientInfo(session_t peer_id, ClientState *state, u32 *uptime,
- u8* ser_vers, u16* prot_vers, u8* major, u8* minor, u8* patch,
- std::string* vers_string, std::string* lang_code);
+ bool getClientInfo(session_t peer_id, ClientInfo &ret);
void printToConsoleOnly(const std::string &text);
@@ -486,10 +495,8 @@ private:
void handleChatInterfaceEvent(ChatEvent *evt);
// This returns the answer to the sender of wmessage, or "" if there is none
- std::wstring handleChat(const std::string &name, const std::wstring &wname,
- std::wstring wmessage_input,
- bool check_shout_priv = false,
- RemotePlayer *player = NULL);
+ std::wstring handleChat(const std::string &name, std::wstring wmessage_input,
+ bool check_shout_priv = false, RemotePlayer *player = nullptr);
void handleAdminChat(const ChatEventChat *evt);
// When called, connection mutex should be locked
@@ -524,6 +531,7 @@ private:
u16 m_max_chatmessage_length;
// For "dedicated" server list flag
bool m_dedicated;
+ Settings *m_game_settings = nullptr;
// Thread can set; step() will throw as ServerError
MutexedVariable<std::string> m_async_fatal_error;
@@ -539,6 +547,10 @@ private:
// Environment
ServerEnvironment *m_env = nullptr;
+ // Reference to the server map until ServerEnvironment is initialized
+ // after that this variable must be a nullptr
+ ServerMap *m_startup_server_map = nullptr;
+
// server connection
std::shared_ptr<con::Connection> m_con;
@@ -564,9 +576,6 @@ private:
// Craft definition manager
IWritableCraftDefManager *m_craftdef;
- // Event manager
- EventManager *m_event;
-
// Mods
std::unique_ptr<ServerModManager> m_modmgr;
diff --git a/src/server/player_sao.cpp b/src/server/player_sao.cpp
index 232c6a01d..110d2010d 100644
--- a/src/server/player_sao.cpp
+++ b/src/server/player_sao.cpp
@@ -148,7 +148,7 @@ std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
void PlayerSAO::getStaticData(std::string * result) const
{
- FATAL_ERROR("Obsolete function");
+ FATAL_ERROR("This function shall not be called for PlayerSAO");
}
void PlayerSAO::step(float dtime, bool send_recommended)
@@ -456,6 +456,11 @@ u16 PlayerSAO::punch(v3f dir,
return hitparams.wear;
}
+void PlayerSAO::rightClick(ServerActiveObject *clicker)
+{
+ m_env->getScriptIface()->on_rightclickplayer(this, clicker);
+}
+
void PlayerSAO::setHP(s32 hp, const PlayerHPChangeReason &reason)
{
if (hp == (s32)m_hp)
diff --git a/src/server/player_sao.h b/src/server/player_sao.h
index 3e178d4fc..8e2d8803f 100644
--- a/src/server/player_sao.h
+++ b/src/server/player_sao.h
@@ -111,10 +111,9 @@ public:
u16 punch(v3f dir, const ToolCapabilities *toolcap, ServerActiveObject *puncher,
float time_from_last_punch);
- void rightClick(ServerActiveObject *clicker) {}
+ void rightClick(ServerActiveObject *clicker);
void setHP(s32 hp, const PlayerHPChangeReason &reason);
void setHPRaw(u16 hp) { m_hp = hp; }
- s16 readDamage();
u16 getBreath() const { return m_breath; }
void setBreath(const u16 breath, bool send = true);
diff --git a/src/server/serveractiveobject.h b/src/server/serveractiveobject.h
index 25653a1ad..51f445914 100644
--- a/src/server/serveractiveobject.h
+++ b/src/server/serveractiveobject.h
@@ -162,8 +162,6 @@ public:
{}
virtual const ItemGroupList &getArmorGroups() const
{ static ItemGroupList rv; return rv; }
- virtual void setPhysicsOverride(float physics_override_speed, float physics_override_jump, float physics_override_gravity)
- {}
virtual void setAnimation(v2f frames, float frame_speed, float frame_blend, bool frame_loop)
{}
virtual void getAnimation(v2f *frames, float *frame_speed, float *frame_blend, bool *frame_loop)
@@ -206,7 +204,6 @@ public:
}
std::string generateUpdateInfantCommand(u16 infant_id, u16 protocol_version);
- std::string generateUpdateNametagAttributesCommand(const video::SColor &color) const;
void dumpAOMessagesToQueue(std::queue<ActiveObjectMessage> &queue);
diff --git a/src/serverenvironment.cpp b/src/serverenvironment.cpp
index 56dbb0632..3d9ba132b 100644
--- a/src/serverenvironment.cpp
+++ b/src/serverenvironment.cpp
@@ -632,7 +632,7 @@ void ServerEnvironment::saveMeta()
// Open file and serialize
std::ostringstream ss(std::ios_base::binary);
- Settings args;
+ Settings args("EnvArgsEnd");
args.setU64("game_time", m_game_time);
args.setU64("time_of_day", getTimeOfDay());
args.setU64("last_clear_objects_time", m_last_clear_objects_time);
@@ -641,7 +641,6 @@ void ServerEnvironment::saveMeta()
m_lbm_mgr.createIntroductionTimesString());
args.setU64("day_count", m_day_count);
args.writeLines(ss);
- ss<<"EnvArgsEnd\n";
if(!fs::safeWriteToFile(path, ss.str()))
{
@@ -676,9 +675,9 @@ void ServerEnvironment::loadMeta()
throw SerializationError("Couldn't load env meta");
}
- Settings args;
+ Settings args("EnvArgsEnd");
- if (!args.parseConfigLines(is, "EnvArgsEnd")) {
+ if (!args.parseConfigLines(is)) {
throw SerializationError("ServerEnvironment::loadMeta(): "
"EnvArgsEnd not found!");
}
diff --git a/src/serverenvironment.h b/src/serverenvironment.h
index c76d34a37..a11c814ed 100644
--- a/src/serverenvironment.h
+++ b/src/serverenvironment.h
@@ -190,14 +190,6 @@ enum ClearObjectsMode {
CLEAR_OBJECTS_MODE_QUICK,
};
-/*
- The server-side environment.
-
- This is not thread-safe. Server uses an environment mutex.
-*/
-
-typedef std::unordered_map<u16, ServerActiveObject *> ServerActiveObjectMap;
-
class ServerEnvironment : public Environment
{
public:
@@ -331,7 +323,7 @@ public:
{
return m_ao_manager.getObjectsInsideRadius(pos, radius, objects, include_obj_cb);
}
-
+
// Find all active objects inside a box
void getObjectsInArea(std::vector<ServerActiveObject *> &objects, const aabb3f &box,
std::function<bool(ServerActiveObject *obj)> include_obj_cb)
diff --git a/src/serverlist.cpp b/src/serverlist.cpp
index 80a8c2f1a..3bcab3d58 100644
--- a/src/serverlist.cpp
+++ b/src/serverlist.cpp
@@ -17,181 +17,19 @@ with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#include <fstream>
#include <iostream>
-#include <sstream>
-#include <algorithm>
-
#include "version.h"
#include "settings.h"
#include "serverlist.h"
#include "filesys.h"
-#include "porting.h"
#include "log.h"
#include "network/networkprotocol.h"
#include <json/json.h>
#include "convert_json.h"
#include "httpfetch.h"
-#include "util/string.h"
namespace ServerList
{
-
-std::string getFilePath()
-{
- std::string serverlist_file = g_settings->get("serverlist_file");
-
- std::string dir_path = "client" DIR_DELIM "serverlist" DIR_DELIM;
- fs::CreateDir(porting::path_user + DIR_DELIM "client");
- fs::CreateDir(porting::path_user + DIR_DELIM + dir_path);
- return porting::path_user + DIR_DELIM + dir_path + serverlist_file;
-}
-
-
-std::vector<ServerListSpec> getLocal()
-{
- std::string path = ServerList::getFilePath();
- std::string liststring;
- fs::ReadFile(path, liststring);
-
- return deSerialize(liststring);
-}
-
-
-std::vector<ServerListSpec> getOnline()
-{
- std::ostringstream geturl;
-
- u16 proto_version_min = CLIENT_PROTOCOL_VERSION_MIN;
-
- geturl << g_settings->get("serverlist_url") <<
- "/list?proto_version_min=" << proto_version_min <<
- "&proto_version_max=" << CLIENT_PROTOCOL_VERSION_MAX;
- Json::Value root = fetchJsonValue(geturl.str(), NULL);
-
- std::vector<ServerListSpec> server_list;
-
- if (!root.isObject()) {
- return server_list;
- }
-
- root = root["list"];
- if (!root.isArray()) {
- return server_list;
- }
-
- for (const Json::Value &i : root) {
- if (i.isObject()) {
- server_list.push_back(i);
- }
- }
-
- return server_list;
-}
-
-
-// Delete a server from the local favorites list
-bool deleteEntry(const ServerListSpec &server)
-{
- std::vector<ServerListSpec> serverlist = ServerList::getLocal();
- for (std::vector<ServerListSpec>::iterator it = serverlist.begin();
- it != serverlist.end();) {
- if ((*it)["address"] == server["address"] &&
- (*it)["port"] == server["port"]) {
- it = serverlist.erase(it);
- } else {
- ++it;
- }
- }
-
- std::string path = ServerList::getFilePath();
- std::ostringstream ss(std::ios_base::binary);
- ss << ServerList::serialize(serverlist);
- if (!fs::safeWriteToFile(path, ss.str()))
- return false;
- return true;
-}
-
-// Insert a server to the local favorites list
-bool insert(const ServerListSpec &server)
-{
- // Remove duplicates
- ServerList::deleteEntry(server);
-
- std::vector<ServerListSpec> serverlist = ServerList::getLocal();
-
- // Insert new server at the top of the list
- serverlist.insert(serverlist.begin(), server);
-
- std::string path = ServerList::getFilePath();
- std::ostringstream ss(std::ios_base::binary);
- ss << ServerList::serialize(serverlist);
- if (!fs::safeWriteToFile(path, ss.str()))
- return false;
-
- return true;
-}
-
-std::vector<ServerListSpec> deSerialize(const std::string &liststring)
-{
- std::vector<ServerListSpec> serverlist;
- std::istringstream stream(liststring);
- std::string line, tmp;
- while (std::getline(stream, line)) {
- std::transform(line.begin(), line.end(), line.begin(), ::toupper);
- if (line == "[SERVER]") {
- ServerListSpec server;
- std::getline(stream, tmp);
- server["name"] = tmp;
- std::getline(stream, tmp);
- server["address"] = tmp;
- std::getline(stream, tmp);
- server["port"] = tmp;
- bool unique = true;
- for (const ServerListSpec &added : serverlist) {
- if (server["name"] == added["name"]
- && server["port"] == added["port"]) {
- unique = false;
- break;
- }
- }
- if (!unique)
- continue;
- std::getline(stream, tmp);
- server["description"] = tmp;
- serverlist.push_back(server);
- }
- }
- return serverlist;
-}
-
-const std::string serialize(const std::vector<ServerListSpec> &serverlist)
-{
- std::string liststring;
- for (const ServerListSpec &it : serverlist) {
- liststring += "[server]\n";
- liststring += it["name"].asString() + '\n';
- liststring += it["address"].asString() + '\n';
- liststring += it["port"].asString() + '\n';
- liststring += it["description"].asString() + '\n';
- liststring += '\n';
- }
- return liststring;
-}
-
-const std::string serializeJson(const std::vector<ServerListSpec> &serverlist)
-{
- Json::Value root;
- Json::Value list(Json::arrayValue);
- for (const ServerListSpec &it : serverlist) {
- list.append(it);
- }
- root["list"] = list;
-
- return fastWriteJson(root);
-}
-
-
#if USE_CURL
void sendAnnounce(AnnounceAction action,
const u16 port,
diff --git a/src/serverlist.h b/src/serverlist.h
index 2b82b7431..4a0bd5efa 100644
--- a/src/serverlist.h
+++ b/src/serverlist.h
@@ -24,21 +24,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#pragma once
-typedef Json::Value ServerListSpec;
-
namespace ServerList
{
-std::vector<ServerListSpec> getLocal();
-std::vector<ServerListSpec> getOnline();
-
-bool deleteEntry(const ServerListSpec &server);
-bool insert(const ServerListSpec &server);
-
-std::vector<ServerListSpec> deSerialize(const std::string &liststring);
-const std::string serialize(const std::vector<ServerListSpec> &serverlist);
-std::vector<ServerListSpec> deSerializeJson(const std::string &liststring);
-const std::string serializeJson(const std::vector<ServerListSpec> &serverlist);
-
#if USE_CURL
enum AnnounceAction {AA_START, AA_UPDATE, AA_DELETE};
void sendAnnounce(AnnounceAction, u16 port,
diff --git a/src/settings.cpp b/src/settings.cpp
index f30ef34e9..3415ff818 100644
--- a/src/settings.cpp
+++ b/src/settings.cpp
@@ -33,27 +33,50 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <cctype>
#include <algorithm>
-static Settings main_settings;
-Settings *g_settings = &main_settings;
+Settings *g_settings = nullptr; // Populated in main()
std::string g_settings_path;
-Settings::~Settings()
+Settings *Settings::s_layers[SL_TOTAL_COUNT] = {0}; // Zeroed by compiler
+std::unordered_map<std::string, const FlagDesc *> Settings::s_flags;
+
+
+Settings *Settings::createLayer(SettingsLayer sl, const std::string &end_tag)
{
- clear();
+ if ((int)sl < 0 || sl >= SL_TOTAL_COUNT)
+ throw new BaseException("Invalid settings layer");
+
+ Settings *&pos = s_layers[(size_t)sl];
+ if (pos)
+ throw new BaseException("Setting layer " + std::to_string(sl) + " already exists");
+
+ pos = new Settings(end_tag);
+ pos->m_settingslayer = sl;
+
+ if (sl == SL_GLOBAL)
+ g_settings = pos;
+ return pos;
}
-Settings & Settings::operator += (const Settings &other)
+Settings *Settings::getLayer(SettingsLayer sl)
{
- if (&other == this)
- return *this;
+ sanity_check((int)sl >= 0 && sl < SL_TOTAL_COUNT);
+ return s_layers[(size_t)sl];
+}
+
+Settings::~Settings()
+{
MutexAutoLock lock(m_mutex);
- MutexAutoLock lock2(other.m_mutex);
- updateNoLock(other);
+ if (m_settingslayer < SL_TOTAL_COUNT)
+ s_layers[(size_t)m_settingslayer] = nullptr;
- return *this;
+ // Compatibility
+ if (m_settingslayer == SL_GLOBAL)
+ g_settings = nullptr;
+
+ clearNoLock();
}
@@ -62,11 +85,16 @@ Settings & Settings::operator = (const Settings &other)
if (&other == this)
return *this;
+ // TODO: Avoid copying Settings objects. Make this private.
+ FATAL_ERROR_IF(m_settingslayer != SL_TOTAL_COUNT && other.m_settingslayer != SL_TOTAL_COUNT,
+ ("Tried to copy unique Setting layer " + std::to_string(m_settingslayer)).c_str());
+
MutexAutoLock lock(m_mutex);
MutexAutoLock lock2(other.m_mutex);
clearNoLock();
- updateNoLock(other);
+ m_settings = other.m_settings;
+ m_callbacks = other.m_callbacks;
return *this;
}
@@ -130,11 +158,11 @@ bool Settings::readConfigFile(const char *filename)
if (!is.good())
return false;
- return parseConfigLines(is, "");
+ return parseConfigLines(is);
}
-bool Settings::parseConfigLines(std::istream &is, const std::string &end)
+bool Settings::parseConfigLines(std::istream &is)
{
MutexAutoLock lock(m_mutex);
@@ -142,7 +170,7 @@ bool Settings::parseConfigLines(std::istream &is, const std::string &end)
while (is.good()) {
std::getline(is, line);
- SettingsParseEvent event = parseConfigObject(line, end, name, value);
+ SettingsParseEvent event = parseConfigObject(line, name, value);
switch (event) {
case SPE_NONE:
@@ -155,8 +183,8 @@ bool Settings::parseConfigLines(std::istream &is, const std::string &end)
case SPE_END:
return true;
case SPE_GROUP: {
- Settings *group = new Settings;
- if (!group->parseConfigLines(is, "}")) {
+ Settings *group = new Settings("}");
+ if (!group->parseConfigLines(is)) {
delete group;
return false;
}
@@ -169,7 +197,8 @@ bool Settings::parseConfigLines(std::istream &is, const std::string &end)
}
}
- return end.empty();
+ // false (failure) if end tag not found
+ return m_end_tag.empty();
}
@@ -179,6 +208,14 @@ void Settings::writeLines(std::ostream &os, u32 tab_depth) const
for (const auto &setting_it : m_settings)
printEntry(os, setting_it.first, setting_it.second, tab_depth);
+
+ // For groups this must be "}" !
+ if (!m_end_tag.empty()) {
+ for (u32 i = 0; i < tab_depth; i++)
+ os << "\t";
+
+ os << m_end_tag << "\n";
+ }
}
@@ -193,9 +230,7 @@ void Settings::printEntry(std::ostream &os, const std::string &name,
entry.group->writeLines(os, tab_depth + 1);
- for (u32 i = 0; i != tab_depth; i++)
- os << "\t";
- os << "}\n";
+ // Closing bracket handled by writeLines
} else {
os << name << " = ";
@@ -207,8 +242,7 @@ void Settings::printEntry(std::ostream &os, const std::string &name,
}
-bool Settings::updateConfigObject(std::istream &is, std::ostream &os,
- const std::string &end, u32 tab_depth)
+bool Settings::updateConfigObject(std::istream &is, std::ostream &os, u32 tab_depth)
{
SettingEntries::const_iterator it;
std::set<std::string> present_entries;
@@ -220,11 +254,11 @@ bool Settings::updateConfigObject(std::istream &is, std::ostream &os,
// in the object if existing
while (is.good() && !end_found) {
std::getline(is, line);
- SettingsParseEvent event = parseConfigObject(line, end, name, value);
+ SettingsParseEvent event = parseConfigObject(line, name, value);
switch (event) {
case SPE_END:
- os << line << (is.eof() ? "" : "\n");
+ // Skip end tag. Append later.
end_found = true;
break;
case SPE_MULTILINE:
@@ -252,14 +286,13 @@ bool Settings::updateConfigObject(std::istream &is, std::ostream &os,
if (it != m_settings.end() && it->second.is_group) {
os << line << "\n";
sanity_check(it->second.group != NULL);
- was_modified |= it->second.group->updateConfigObject(is, os,
- "}", tab_depth + 1);
+ was_modified |= it->second.group->updateConfigObject(is, os, tab_depth + 1);
} else if (it == m_settings.end()) {
// Remove by skipping
was_modified = true;
- Settings removed_group; // Move 'is' to group end
+ Settings removed_group("}"); // Move 'is' to group end
std::stringstream ss;
- removed_group.updateConfigObject(is, ss, "}", tab_depth + 1);
+ removed_group.updateConfigObject(is, ss, tab_depth + 1);
break;
} else {
printEntry(os, name, it->second, tab_depth);
@@ -273,6 +306,9 @@ bool Settings::updateConfigObject(std::istream &is, std::ostream &os,
}
}
+ if (!line.empty() && is.eof())
+ os << "\n";
+
// Add any settings in the object that don't exist in the config file yet
for (it = m_settings.begin(); it != m_settings.end(); ++it) {
if (present_entries.find(it->first) != present_entries.end())
@@ -282,6 +318,12 @@ bool Settings::updateConfigObject(std::istream &is, std::ostream &os,
was_modified = true;
}
+ // Append ending tag
+ if (!m_end_tag.empty()) {
+ os << m_end_tag << "\n";
+ was_modified |= !end_found;
+ }
+
return was_modified;
}
@@ -293,7 +335,7 @@ bool Settings::updateConfigFile(const char *filename)
std::ifstream is(filename);
std::ostringstream os(std::ios_base::binary);
- bool was_modified = updateConfigObject(is, os, "");
+ bool was_modified = updateConfigObject(is, os);
is.close();
if (!was_modified)
@@ -366,29 +408,37 @@ bool Settings::parseCommandLine(int argc, char *argv[],
* Getters *
***********/
-
-const SettingsEntry &Settings::getEntry(const std::string &name) const
+Settings *Settings::getParent() const
{
- MutexAutoLock lock(m_mutex);
+ // If the Settings object is within the hierarchy structure,
+ // iterate towards the origin (0) to find the next fallback layer
+ if (m_settingslayer >= SL_TOTAL_COUNT)
+ return nullptr;
- SettingEntries::const_iterator n;
- if ((n = m_settings.find(name)) == m_settings.end()) {
- if ((n = m_defaults.find(name)) == m_defaults.end())
- throw SettingNotFoundException("Setting [" + name + "] not found.");
+ for (int i = (int)m_settingslayer - 1; i >= 0; --i) {
+ if (s_layers[i])
+ return s_layers[i];
}
- return n->second;
+
+ // No parent
+ return nullptr;
}
-const SettingsEntry &Settings::getEntryDefault(const std::string &name) const
+const SettingsEntry &Settings::getEntry(const std::string &name) const
{
- MutexAutoLock lock(m_mutex);
+ {
+ MutexAutoLock lock(m_mutex);
- SettingEntries::const_iterator n;
- if ((n = m_defaults.find(name)) == m_defaults.end()) {
- throw SettingNotFoundException("Setting [" + name + "] not found.");
+ SettingEntries::const_iterator n;
+ if ((n = m_settings.find(name)) != m_settings.end())
+ return n->second;
}
- return n->second;
+
+ if (auto parent = getParent())
+ return parent->getEntry(name);
+
+ throw SettingNotFoundException("Setting [" + name + "] not found.");
}
@@ -410,15 +460,6 @@ const std::string &Settings::get(const std::string &name) const
}
-const std::string &Settings::getDefault(const std::string &name) const
-{
- const SettingsEntry &entry = getEntryDefault(name);
- if (entry.is_group)
- throw SettingNotFoundException("Setting [" + name + "] is a group.");
- return entry.value;
-}
-
-
bool Settings::getBool(const std::string &name) const
{
return is_yes(get(name));
@@ -491,29 +532,25 @@ u32 Settings::getFlagStr(const std::string &name, const FlagDesc *flagdesc,
u32 *flagmask) const
{
u32 flags = 0;
- u32 mask_default = 0;
- std::string value;
// Read default value (if there is any)
- if (getDefaultNoEx(name, value)) {
- flags = std::isdigit(value[0])
- ? stoi(value)
- : readFlagString(value, flagdesc, &mask_default);
- }
+ if (auto parent = getParent())
+ flags = parent->getFlagStr(name, flagdesc, flagmask);
// Apply custom flags "on top"
- value = get(name);
- u32 flags_user;
- u32 mask_user = U32_MAX;
- flags_user = std::isdigit(value[0])
- ? stoi(value) // Override default
- : readFlagString(value, flagdesc, &mask_user);
-
- flags &= ~mask_user;
- flags |= flags_user;
-
- if (flagmask)
- *flagmask = mask_default | mask_user;
+ if (m_settings.find(name) != m_settings.end()) {
+ std::string value = get(name);
+ u32 flags_user;
+ u32 mask_user = U32_MAX;
+ flags_user = std::isdigit(value[0])
+ ? stoi(value) // Override default
+ : readFlagString(value, flagdesc, &mask_user);
+
+ flags &= ~mask_user;
+ flags |= flags_user;
+ if (flagmask)
+ *flagmask |= mask_user;
+ }
return flags;
}
@@ -521,7 +558,12 @@ u32 Settings::getFlagStr(const std::string &name, const FlagDesc *flagdesc,
bool Settings::getNoiseParams(const std::string &name, NoiseParams &np) const
{
- return getNoiseParamsFromGroup(name, np) || getNoiseParamsFromValue(name, np);
+ if (getNoiseParamsFromGroup(name, np) || getNoiseParamsFromValue(name, np))
+ return true;
+ if (auto parent = getParent())
+ return parent->getNoiseParams(name, np);
+
+ return false;
}
@@ -583,13 +625,18 @@ bool Settings::exists(const std::string &name) const
{
MutexAutoLock lock(m_mutex);
- return (m_settings.find(name) != m_settings.end() ||
- m_defaults.find(name) != m_defaults.end());
+ if (m_settings.find(name) != m_settings.end())
+ return true;
+ if (auto parent = getParent())
+ return parent->exists(name);
+ return false;
}
std::vector<std::string> Settings::getNames() const
{
+ MutexAutoLock lock(m_mutex);
+
std::vector<std::string> names;
for (const auto &settings_it : m_settings) {
names.push_back(settings_it.first);
@@ -625,17 +672,6 @@ bool Settings::getNoEx(const std::string &name, std::string &val) const
}
-bool Settings::getDefaultNoEx(const std::string &name, std::string &val) const
-{
- try {
- val = getDefault(name);
- return true;
- } catch (SettingNotFoundException &e) {
- return false;
- }
-}
-
-
bool Settings::getFlag(const std::string &name) const
{
try {
@@ -746,24 +782,25 @@ bool Settings::getFlagStrNoEx(const std::string &name, u32 &val,
***********/
bool Settings::setEntry(const std::string &name, const void *data,
- bool set_group, bool set_default)
+ bool set_group)
{
- Settings *old_group = NULL;
-
if (!checkNameValid(name))
return false;
if (!set_group && !checkValueValid(*(const std::string *)data))
return false;
+ Settings *old_group = NULL;
{
MutexAutoLock lock(m_mutex);
- SettingsEntry &entry = set_default ? m_defaults[name] : m_settings[name];
+ SettingsEntry &entry = m_settings[name];
old_group = entry.group;
entry.value = set_group ? "" : *(const std::string *)data;
entry.group = set_group ? *(Settings **)data : NULL;
entry.is_group = set_group;
+ if (set_group)
+ entry.group->m_end_tag = "}";
}
delete old_group;
@@ -774,7 +811,7 @@ bool Settings::setEntry(const std::string &name, const void *data,
bool Settings::set(const std::string &name, const std::string &value)
{
- if (!setEntry(name, &value, false, false))
+ if (!setEntry(name, &value, false))
return false;
doCallbacks(name);
@@ -782,9 +819,10 @@ bool Settings::set(const std::string &name, const std::string &value)
}
+// TODO: Remove this function
bool Settings::setDefault(const std::string &name, const std::string &value)
{
- return setEntry(name, &value, false, true);
+ return getLayer(SL_DEFAULTS)->set(name, value);
}
@@ -794,17 +832,7 @@ bool Settings::setGroup(const std::string &name, const Settings &group)
// avoid double-free by copying the source
Settings *copy = new Settings();
*copy = group;
- return setEntry(name, &copy, true, false);
-}
-
-
-bool Settings::setGroupDefault(const std::string &name, const Settings &group)
-{
- // Settings must own the group pointer
- // avoid double-free by copying the source
- Settings *copy = new Settings();
- *copy = group;
- return setEntry(name, &copy, true, true);
+ return setEntry(name, &copy, true);
}
@@ -874,8 +902,7 @@ bool Settings::setFlagStr(const std::string &name, u32 flags,
}
-bool Settings::setNoiseParams(const std::string &name,
- const NoiseParams &np, bool set_default)
+bool Settings::setNoiseParams(const std::string &name, const NoiseParams &np)
{
Settings *group = new Settings;
@@ -888,7 +915,7 @@ bool Settings::setNoiseParams(const std::string &name,
group->setFloat("lacunarity", np.lacunarity);
group->setFlagStr("flags", np.flags, flagdesc_noiseparams, np.flags);
- return setEntry(name, &group, true, set_default);
+ return setEntry(name, &group, true);
}
@@ -912,20 +939,8 @@ bool Settings::remove(const std::string &name)
}
-void Settings::clear()
-{
- MutexAutoLock lock(m_mutex);
- clearNoLock();
-}
-
-void Settings::clearDefaults()
-{
- MutexAutoLock lock(m_mutex);
- clearDefaultsNoLock();
-}
-
SettingsParseEvent Settings::parseConfigObject(const std::string &line,
- const std::string &end, std::string &name, std::string &value)
+ std::string &name, std::string &value)
{
std::string trimmed_line = trim(line);
@@ -933,7 +948,7 @@ SettingsParseEvent Settings::parseConfigObject(const std::string &line,
return SPE_NONE;
if (trimmed_line[0] == '#')
return SPE_COMMENT;
- if (trimmed_line == end)
+ if (trimmed_line == m_end_tag)
return SPE_END;
size_t pos = trimmed_line.find('=');
@@ -952,67 +967,26 @@ SettingsParseEvent Settings::parseConfigObject(const std::string &line,
}
-void Settings::updateNoLock(const Settings &other)
-{
- m_settings.insert(other.m_settings.begin(), other.m_settings.end());
- m_defaults.insert(other.m_defaults.begin(), other.m_defaults.end());
-}
-
-
void Settings::clearNoLock()
{
-
for (SettingEntries::const_iterator it = m_settings.begin();
it != m_settings.end(); ++it)
delete it->second.group;
m_settings.clear();
-
- clearDefaultsNoLock();
}
-void Settings::clearDefaultsNoLock()
-{
- for (SettingEntries::const_iterator it = m_defaults.begin();
- it != m_defaults.end(); ++it)
- delete it->second.group;
- m_defaults.clear();
-}
void Settings::setDefault(const std::string &name, const FlagDesc *flagdesc,
u32 flags)
{
- m_flags[name] = flagdesc;
+ s_flags[name] = flagdesc;
setDefault(name, writeFlagString(flags, flagdesc, U32_MAX));
}
-void Settings::overrideDefaults(Settings *other)
-{
- for (const auto &setting : other->m_settings) {
- if (setting.second.is_group) {
- setGroupDefault(setting.first, *setting.second.group);
- continue;
- }
- const FlagDesc *flagdesc = getFlagDescFallback(setting.first);
- if (flagdesc) {
- // Flags cannot be copied directly.
- // 1) Get the current set flags
- u32 flags = getFlagStr(setting.first, flagdesc, nullptr);
- // 2) Set the flags as defaults
- other->setDefault(setting.first, flagdesc, flags);
- // 3) Get the newly set flags and override the default setting value
- setDefault(setting.first, flagdesc,
- other->getFlagStr(setting.first, flagdesc, nullptr));
- continue;
- }
- // Also covers FlagDesc settings
- setDefault(setting.first, setting.second.value);
- }
-}
-
const FlagDesc *Settings::getFlagDescFallback(const std::string &name) const
{
- auto it = m_flags.find(name);
- return it == m_flags.end() ? nullptr : it->second;
+ auto it = s_flags.find(name);
+ return it == s_flags.end() ? nullptr : it->second;
}
void Settings::registerChangedCallback(const std::string &name,
diff --git a/src/settings.h b/src/settings.h
index 6db2f9481..b5e859ee0 100644
--- a/src/settings.h
+++ b/src/settings.h
@@ -30,7 +30,7 @@ class Settings;
struct NoiseParams;
// Global objects
-extern Settings *g_settings;
+extern Settings *g_settings; // Same as Settings::getLayer(SL_GLOBAL);
extern std::string g_settings_path;
// Type for a settings changed callback function
@@ -60,6 +60,14 @@ enum SettingsParseEvent {
SPE_MULTILINE,
};
+enum SettingsLayer {
+ SL_DEFAULTS,
+ SL_GAME,
+ SL_GLOBAL,
+ SL_MAP,
+ SL_TOTAL_COUNT
+};
+
struct ValueSpec {
ValueSpec(ValueType a_type, const char *a_help=NULL)
{
@@ -92,8 +100,13 @@ typedef std::unordered_map<std::string, SettingsEntry> SettingEntries;
class Settings {
public:
- Settings() = default;
+ static Settings *createLayer(SettingsLayer sl, const std::string &end_tag = "");
+ static Settings *getLayer(SettingsLayer sl);
+ SettingsLayer getLayerType() const { return m_settingslayer; }
+ Settings(const std::string &end_tag = "") :
+ m_end_tag(end_tag)
+ {}
~Settings();
Settings & operator += (const Settings &other);
@@ -110,7 +123,7 @@ public:
// NOTE: Types of allowed_options are ignored. Returns success.
bool parseCommandLine(int argc, char *argv[],
std::map<std::string, ValueSpec> &allowed_options);
- bool parseConfigLines(std::istream &is, const std::string &end = "");
+ bool parseConfigLines(std::istream &is);
void writeLines(std::ostream &os, u32 tab_depth=0) const;
/***********
@@ -119,7 +132,6 @@ public:
Settings *getGroup(const std::string &name) const;
const std::string &get(const std::string &name) const;
- const std::string &getDefault(const std::string &name) const;
bool getBool(const std::string &name) const;
u16 getU16(const std::string &name) const;
s16 getS16(const std::string &name) const;
@@ -146,7 +158,6 @@ public:
bool getGroupNoEx(const std::string &name, Settings *&val) const;
bool getNoEx(const std::string &name, std::string &val) const;
- bool getDefaultNoEx(const std::string &name, std::string &val) const;
bool getFlag(const std::string &name) const;
bool getU16NoEx(const std::string &name, u16 &val) const;
bool getS16NoEx(const std::string &name, s16 &val) const;
@@ -170,11 +181,10 @@ public:
// N.B. Groups not allocated with new must be set to NULL in the settings
// tree before object destruction.
bool setEntry(const std::string &name, const void *entry,
- bool set_group, bool set_default);
+ bool set_group);
bool set(const std::string &name, const std::string &value);
bool setDefault(const std::string &name, const std::string &value);
bool setGroup(const std::string &name, const Settings &group);
- bool setGroupDefault(const std::string &name, const Settings &group);
bool setBool(const std::string &name, bool value);
bool setS16(const std::string &name, s16 value);
bool setU16(const std::string &name, u16 value);
@@ -185,21 +195,16 @@ public:
bool setV3F(const std::string &name, v3f value);
bool setFlagStr(const std::string &name, u32 flags,
const FlagDesc *flagdesc = nullptr, u32 flagmask = U32_MAX);
- bool setNoiseParams(const std::string &name, const NoiseParams &np,
- bool set_default=false);
+ bool setNoiseParams(const std::string &name, const NoiseParams &np);
// remove a setting
bool remove(const std::string &name);
- void clear();
- void clearDefaults();
/**************
* Miscellany *
**************/
void setDefault(const std::string &name, const FlagDesc *flagdesc, u32 flags);
- // Takes the provided setting values and uses them as new defaults
- void overrideDefaults(Settings *other);
const FlagDesc *getFlagDescFallback(const std::string &name) const;
void registerChangedCallback(const std::string &name,
@@ -215,9 +220,9 @@ private:
***********************/
SettingsParseEvent parseConfigObject(const std::string &line,
- const std::string &end, std::string &name, std::string &value);
+ std::string &name, std::string &value);
bool updateConfigObject(std::istream &is, std::ostream &os,
- const std::string &end, u32 tab_depth=0);
+ u32 tab_depth=0);
static bool checkNameValid(const std::string &name);
static bool checkValueValid(const std::string &value);
@@ -228,9 +233,9 @@ private:
/***********
* Getters *
***********/
+ Settings *getParent() const;
const SettingsEntry &getEntry(const std::string &name) const;
- const SettingsEntry &getEntryDefault(const std::string &name) const;
// Allow TestSettings to run sanity checks using private functions.
friend class TestSettings;
@@ -242,14 +247,15 @@ private:
void doCallbacks(const std::string &name) const;
SettingEntries m_settings;
- SettingEntries m_defaults;
- std::unordered_map<std::string, const FlagDesc *> m_flags;
-
SettingsCallbackMap m_callbacks;
+ std::string m_end_tag;
mutable std::mutex m_callback_mutex;
// All methods that access m_settings/m_defaults directly should lock this.
mutable std::mutex m_mutex;
+ static Settings *s_layers[SL_TOTAL_COUNT];
+ SettingsLayer m_settingslayer = SL_TOTAL_COUNT;
+ static std::unordered_map<std::string, const FlagDesc *> s_flags;
};
diff --git a/src/settings_translation_file.cpp b/src/settings_translation_file.cpp
index 0cd772337..8ce323ff6 100644
--- a/src/settings_translation_file.cpp
+++ b/src/settings_translation_file.cpp
@@ -30,8 +30,8 @@ fake_function() {
gettext("Double-tapping the jump key toggles fly mode.");
gettext("Always fly and fast");
gettext("If disabled, \"special\" key is used to fly fast if both fly and fast mode are\nenabled.");
- gettext("Rightclick repetition interval");
- gettext("The time in seconds it takes between repeated right clicks when holding the right\nmouse button.");
+ gettext("Place repetition interval");
+ gettext("The time in seconds it takes between repeated node placements when holding\nthe place button.");
gettext("Automatic jumping");
gettext("Automatically jump up single-node obstacles.");
gettext("Safe digging and placing");
@@ -54,6 +54,8 @@ fake_function() {
gettext("The type of joystick");
gettext("Joystick button repetition interval");
gettext("The time in seconds it takes between repeated events\nwhen holding down a joystick button combination.");
+ gettext("Joystick deadzone");
+ gettext("The deadzone of the joystick");
gettext("Joystick frustum sensitivity");
gettext("The sensitivity of the joystick axes for moving the\ningame view frustum around.");
gettext("Forward key");
@@ -68,6 +70,10 @@ fake_function() {
gettext("Key for jumping.\nSee http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3");
gettext("Sneak key");
gettext("Key for sneaking.\nAlso used for climbing down and descending in water if aux1_descends is disabled.\nSee http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3");
+ gettext("Dig key");
+ gettext("Key for digging.\nSee http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3");
+ gettext("Place key");
+ gettext("Key for placing.\nSee http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3");
gettext("Inventory key");
gettext("Key for opening the inventory.\nSee http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3");
gettext("Special key");
@@ -229,7 +235,7 @@ fake_function() {
gettext("Minimum texture size");
gettext("When using bilinear/trilinear/anisotropic filters, low-resolution textures\ncan be blurred, so automatically upscale them with nearest-neighbor\ninterpolation to preserve crisp pixels. This sets the minimum texture size\nfor the upscaled textures; higher values look sharper, but require more\nmemory. Powers of 2 are recommended. Setting this higher than 1 may not\nhave a visible effect unless bilinear/trilinear/anisotropic filtering is\nenabled.\nThis is also used as the base node texture size for world-aligned\ntexture autoscaling.");
gettext("FSAA");
- gettext("Experimental option, might cause visible spaces between blocks\nwhen set to higher number than 0.");
+ gettext("Use multi-sample antialiasing (MSAA) to smooth out block edges.\nThis algorithm smooths out the 3D viewport while keeping the image sharp,\nbut it doesn't affect the insides of textures\n(which is especially noticeable with transparent textures).\nVisible spaces appear between nodes when shaders are disabled.\nIf set to 0, MSAA is disabled.\nA restart is required after changing this option.");
gettext("Undersampling");
gettext("Undersampling is similar to using a lower screen resolution, but it applies\nto the game world only, keeping the GUI intact.\nIt should give a significant performance boost at the cost of less detailed image.\nHigher values result in a less detailed image.");
gettext("Shaders");
@@ -240,20 +246,6 @@ fake_function() {
gettext("Tone Mapping");
gettext("Filmic tone mapping");
gettext("Enables Hable's 'Uncharted 2' filmic tone mapping.\nSimulates the tone curve of photographic film and how this approximates the\nappearance of high dynamic range images. Mid-range contrast is slightly\nenhanced, highlights and shadows are gradually compressed.");
- gettext("Bumpmapping");
- gettext("Bumpmapping");
- gettext("Enables bumpmapping for textures. Normalmaps need to be supplied by the texture pack.\nRequires shaders to be enabled.");
- gettext("Parallax Occlusion");
- gettext("Parallax occlusion");
- gettext("Enables parallax occlusion mapping.\nRequires shaders to be enabled.");
- gettext("Parallax occlusion mode");
- gettext("0 = parallax occlusion with slope information (faster).\n1 = relief mapping (slower, more accurate).");
- gettext("Parallax occlusion iterations");
- gettext("Number of parallax occlusion iterations.");
- gettext("Parallax occlusion scale");
- gettext("Overall scale of parallax occlusion effect.");
- gettext("Parallax occlusion bias");
- gettext("Overall bias of parallax occlusion effect, usually scale/2.");
gettext("Waving Nodes");
gettext("Waving liquids");
gettext("Set to true to enable waving liquids (like water).\nRequires shaders to be enabled.");
@@ -272,8 +264,8 @@ fake_function() {
gettext("Arm inertia, gives a more realistic movement of\nthe arm when the camera moves.");
gettext("Maximum FPS");
gettext("If FPS would go higher than this, limit it by sleeping\nto not waste CPU power for no benefit.");
- gettext("FPS in pause menu");
- gettext("Maximum FPS when game is paused.");
+ gettext("FPS when unfocused or paused");
+ gettext("Maximum FPS when the window is not focused, or when the game is paused.");
gettext("Pause on lost window focus");
gettext("Open the pause menu when the window's focus is lost. Does not pause if a formspec is\nopen.");
gettext("Viewing range");
@@ -309,7 +301,7 @@ fake_function() {
gettext("Texture path");
gettext("Path to texture directory. All textures are first searched from here.");
gettext("Video driver");
- gettext("The rendering back-end for Irrlicht.\nA restart is required after changing this.\nNote: On Android, stick with OGLES1 if unsure! App may fail to start otherwise.\nOn other platforms, OpenGL is recommended, and it’s the only driver with\nshader support currently.");
+ gettext("The rendering back-end for Irrlicht.\nA restart is required after changing this.\nNote: On Android, stick with OGLES1 if unsure! App may fail to start otherwise.\nOn other platforms, OpenGL is recommended.\nShaders are supported by OpenGL (desktop only) and OGLES2 (experimental)");
gettext("Cloud radius");
gettext("Radius of cloud area stated in number of 64 node cloud squares.\nValues larger than 26 will start to produce sharp cutoffs at cloud area corners.");
gettext("View bobbing factor");
@@ -339,9 +331,9 @@ fake_function() {
gettext("Selection box width");
gettext("Width of the selection box lines around nodes.");
gettext("Crosshair color");
- gettext("Crosshair color (R,G,B).");
+ gettext("Crosshair color (R,G,B).\nAlso controls the object crosshair color");
gettext("Crosshair alpha");
- gettext("Crosshair alpha (opaqueness, between 0 and 255).");
+ gettext("Crosshair alpha (opaqueness, between 0 and 255).\nAlso controls the object crosshair color");
gettext("Recent Chat Messages");
gettext("Maximum number of recent chat messages to show");
gettext("Desynchronize block animation");
@@ -377,7 +369,7 @@ fake_function() {
gettext("Autoscaling mode");
gettext("World-aligned textures may be scaled to span several nodes. However,\nthe server may not send the scale you want, especially if you use\na specially-designed texture pack; with this option, the client tries\nto determine the scale automatically basing on the texture size.\nSee also texture_min_size.\nWarning: This option is EXPERIMENTAL!");
gettext("Show entity selection boxes");
- gettext("Show entity selection boxes");
+ gettext("Show entity selection boxes\nA restart is required after changing this.");
gettext("Menus");
gettext("Clouds in menu");
gettext("Use a cloud animation for the main menu background.");
@@ -503,6 +495,8 @@ fake_function() {
gettext("To reduce lag, block transfers are slowed down when a player is building something.\nThis determines how long they are slowed down after placing or removing a node.");
gettext("Max. packets per iteration");
gettext("Maximum number of packets sent per send step, if you have a slow connection\ntry reducing it, but don't reduce it to a number below double of targeted\nclient number.");
+ gettext("Map Compression Level for Network Transfer");
+ gettext("ZLib compression level to use when sending mapblocks to the client.\n-1 - Zlib's default compression level\n0 - no compresson, fastest\n9 - best compression, slowest\n(levels 1-3 use Zlib's \"fast\" method, 4-9 use the normal method)");
gettext("Game");
gettext("Default game");
gettext("Default game when creating a new world.\nThis will be overridden when creating a world from the main menu.");
@@ -601,7 +595,7 @@ fake_function() {
gettext("Acceleration of gravity, in nodes per second per second.");
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("Handling for deprecated Lua API calls:\n- none: Do not log deprecated calls\n- log: mimic and log backtrace of deprecated call (default).\n- error: abort on usage of deprecated call (suggested for mod 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");
@@ -610,12 +604,16 @@ fake_function() {
gettext("Maximum number of statically stored objects in a block.");
gettext("Synchronous SQLite");
gettext("See https://www.sqlite.org/pragma.html#pragma_synchronous");
+ gettext("Map Compression Level for Disk Storage");
+ gettext("ZLib compression level to use when saving mapblocks to disk.\n-1 - Zlib's default compression level\n0 - no compresson, fastest\n9 - best compression, slowest\n(levels 1-3 use Zlib's \"fast\" method, 4-9 use the normal method)");
gettext("Dedicated server step");
gettext("Length of a server tick and the interval at which objects are generally updated over\nnetwork.");
gettext("Active block management interval");
gettext("Length of time between active block management cycles");
gettext("ABM interval");
gettext("Length of time between Active Block Modifier (ABM) execution cycles");
+ gettext("ABM time budget");
+ gettext("The time budget allowed for ABMs to execute on each step\n(as a fraction of the ABM Interval)");
gettext("NodeTimer interval");
gettext("Length of time between NodeTimer execution cycles");
gettext("Ignore world errors");
@@ -687,8 +685,6 @@ fake_function() {
gettext("Maximum time in ms a file download (e.g. a mod download) may take.");
gettext("High-precision FPU");
gettext("Makes DirectX work with LuaJIT. Disable if it causes troubles.");
- gettext("Main menu style");
- gettext("Changes the main menu UI:\n- Full: Multiple singleplayer worlds, game choice, texture pack chooser, etc.\n- Simple: One singleplayer world, no game or texture pack choosers. May be\nnecessary for smaller screens.");
gettext("Main menu script");
gettext("Replaces the default main menu with a custom one.");
gettext("Engine profiling data print interval");
@@ -958,6 +954,12 @@ fake_function() {
gettext("Terrain noise threshold for hills.\nControls proportion of world area covered by hills.\nAdjust towards 0.0 for a larger proportion.");
gettext("Hill steepness");
gettext("Controls steepness/height of hills.");
+ gettext("Cavern limit");
+ gettext("Y-level of cavern upper limit.");
+ gettext("Cavern taper");
+ gettext("Y-distance over which caverns expand to full size.");
+ gettext("Cavern threshold");
+ gettext("Defines full size of caverns, smaller values create larger caverns.");
gettext("Dungeon minimum Y");
gettext("Lower Y limit of dungeons.");
gettext("Dungeon maximum Y");
@@ -971,6 +973,8 @@ fake_function() {
gettext("First of two 3D noises that together define tunnels.");
gettext("Cave2 noise");
gettext("Second of two 3D noises that together define tunnels.");
+ gettext("Cavern noise");
+ gettext("3D noise defining giant caverns.");
gettext("Dungeon noise");
gettext("3D noise that determines number of dungeons per mapchunk.");
gettext("Mapgen Fractal");
@@ -1097,4 +1101,6 @@ fake_function() {
gettext("The URL for the content repository");
gettext("ContentDB Flag Blacklist");
gettext("Comma-separated list of flags to hide in the content repository.\n\"nonfree\" can be used to hide packages which do not qualify as 'free software',\nas defined by the Free Software Foundation.\nYou can also specify content ratings.\nThese flags are independent from Minetest versions,\nso see a full list at https://content.minetest.net/help/content_flags/");
+ gettext("ContentDB Max Concurrent Downloads");
+ gettext("Maximum number of concurrent downloads. Downloads exceeding this limit will be queued.\nThis should be lower than curl_parallel_limit.");
}
diff --git a/src/translation.cpp b/src/translation.cpp
index 82e813a5d..55c958fa2 100644
--- a/src/translation.cpp
+++ b/src/translation.cpp
@@ -144,14 +144,13 @@ void Translations::loadTranslation(const std::string &data)
}
std::wstring oword1 = word1.str(), oword2 = word2.str();
- if (oword2.empty()) {
- oword2 = oword1;
- errorstream << "Ignoring empty translation for \""
- << wide_to_utf8(oword1) << "\"" << std::endl;
+ if (!oword2.empty()) {
+ std::wstring translation_index = textdomain + L"|";
+ translation_index.append(oword1);
+ m_translations[translation_index] = oword2;
+ } else {
+ infostream << "Ignoring empty translation for \""
+ << wide_to_utf8(oword1) << "\"" << std::endl;
}
-
- std::wstring translation_index = textdomain + L"|";
- translation_index.append(oword1);
- m_translations[translation_index] = oword2;
}
}
diff --git a/src/unittest/test.cpp b/src/unittest/test.cpp
index 0f6b36649..d4841d559 100644
--- a/src/unittest/test.cpp
+++ b/src/unittest/test.cpp
@@ -182,7 +182,7 @@ void TestGameDef::defineSomeNodes()
"{default_water.png";
f = ContentFeatures();
f.name = itemdef.name;
- f.alpha = 128;
+ f.alpha = ALPHAMODE_BLEND;
f.liquid_type = LIQUID_SOURCE;
f.liquid_viscosity = 4;
f.is_ground_content = true;
@@ -203,7 +203,7 @@ void TestGameDef::defineSomeNodes()
"{default_lava.png";
f = ContentFeatures();
f.name = itemdef.name;
- f.alpha = 128;
+ f.alpha = ALPHAMODE_OPAQUE;
f.liquid_type = LIQUID_SOURCE;
f.liquid_viscosity = 7;
f.light_source = LIGHT_MAX-1;
diff --git a/src/unittest/test_connection.cpp b/src/unittest/test_connection.cpp
index c5e4085e1..c3aacc536 100644
--- a/src/unittest/test_connection.cpp
+++ b/src/unittest/test_connection.cpp
@@ -39,6 +39,7 @@ public:
void runTests(IGameDef *gamedef);
+ void testNetworkPacketSerialize();
void testHelpers();
void testConnectSendReceive();
};
@@ -47,6 +48,7 @@ static TestConnection g_test_instance;
void TestConnection::runTests(IGameDef *gamedef)
{
+ TEST(testNetworkPacketSerialize);
TEST(testHelpers);
TEST(testConnectSendReceive);
}
@@ -78,6 +80,39 @@ struct Handler : public con::PeerHandler
const char *name;
};
+void TestConnection::testNetworkPacketSerialize()
+{
+ const static u8 expected[] = {
+ 0x00, 0x7b,
+ 0x00, 0x02, 0xd8, 0x42, 0xdf, 0x9a
+ };
+
+ if (sizeof(wchar_t) == 2)
+ warningstream << __func__ << " may fail on this platform." << std::endl;
+
+ {
+ NetworkPacket pkt(123, 0);
+
+ // serializing wide strings should do surrogate encoding, we test that here
+ pkt << std::wstring(L"\U00020b9a");
+
+ SharedBuffer<u8> buf = pkt.oldForgePacket();
+ UASSERTEQ(int, buf.getSize(), sizeof(expected));
+ UASSERT(!memcmp(expected, &buf[0], buf.getSize()));
+ }
+
+ {
+ NetworkPacket pkt;
+ pkt.putRawPacket(expected, sizeof(expected), 0);
+
+ // same for decoding
+ std::wstring pkt_s;
+ pkt >> pkt_s;
+
+ UASSERT(pkt_s == L"\U00020b9a");
+ }
+}
+
void TestConnection::testHelpers()
{
// Some constants for testing
diff --git a/src/unittest/test_map_settings_manager.cpp b/src/unittest/test_map_settings_manager.cpp
index 3d642a9b4..81ca68705 100644
--- a/src/unittest/test_map_settings_manager.cpp
+++ b/src/unittest/test_map_settings_manager.cpp
@@ -30,7 +30,7 @@ public:
TestMapSettingsManager() { TestManager::registerTestModule(this); }
const char *getName() { return "TestMapSettingsManager"; }
- void makeUserConfig(Settings *conf);
+ void makeUserConfig();
std::string makeMetaFile(bool make_corrupt);
void runTests(IGameDef *gamedef);
@@ -65,8 +65,11 @@ void check_noise_params(const NoiseParams *np1, const NoiseParams *np2)
}
-void TestMapSettingsManager::makeUserConfig(Settings *conf)
+void TestMapSettingsManager::makeUserConfig()
{
+ delete Settings::getLayer(SL_GLOBAL);
+ Settings *conf = Settings::createLayer(SL_GLOBAL);
+
conf->set("mg_name", "v7");
conf->set("seed", "5678");
conf->set("water_level", "20");
@@ -103,12 +106,11 @@ std::string TestMapSettingsManager::makeMetaFile(bool make_corrupt)
void TestMapSettingsManager::testMapSettingsManager()
{
- Settings user_settings;
- makeUserConfig(&user_settings);
+ makeUserConfig();
std::string test_mapmeta_path = makeMetaFile(false);
- MapSettingsManager mgr(&user_settings, test_mapmeta_path);
+ MapSettingsManager mgr(test_mapmeta_path);
std::string value;
UASSERT(mgr.getMapSetting("mg_name", &value));
@@ -140,6 +142,12 @@ void TestMapSettingsManager::testMapSettingsManager()
mgr.setMapSettingNoiseParams("mgv5_np_height", &script_np_height);
mgr.setMapSettingNoiseParams("mgv5_np_factor", &script_np_factor);
+ {
+ NoiseParams dummy;
+ mgr.getMapSettingNoiseParams("mgv5_np_factor", &dummy);
+ check_noise_params(&dummy, &script_np_factor);
+ }
+
// Now make our Params and see if the values are correctly sourced
MapgenParams *params = mgr.makeMapgenParams();
UASSERT(params->mgtype == MAPGEN_V5);
@@ -188,50 +196,66 @@ void TestMapSettingsManager::testMapSettingsManager()
void TestMapSettingsManager::testMapMetaSaveLoad()
{
- Settings conf;
std::string path = getTestTempDirectory()
+ DIR_DELIM + "foobar" + DIR_DELIM + "map_meta.txt";
+ makeUserConfig();
+ Settings &conf = *Settings::getLayer(SL_GLOBAL);
+
+ // There cannot be two MapSettingsManager
+ // copy the mapgen params to compare them
+ MapgenParams params1, params2;
// 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());
+ {
+ conf.set("seed", "12345");
+ conf.set("water_level", "5");
+ MapSettingsManager mgr(path);
+ MapgenParams *params = mgr.makeMapgenParams();
+ UASSERT(params);
+ params1 = *params;
+ params1.bparams = nullptr; // No double-free
+ UASSERT(mgr.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);
+ {
+ conf.set("seed", "67890");
+ conf.set("water_level", "32");
+ MapSettingsManager mgr(path);
+ UASSERT(mgr.loadMapMeta());
+ MapgenParams *params = mgr.makeMapgenParams();
+ UASSERT(params);
+ params2 = *params;
+ params2.bparams = nullptr; // No double-free
+ }
// 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);
+ 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));
+ {
+ test_mapmeta_path = "woobawooba/fgdfg/map_meta.txt";
+ UASSERT(!fs::PathExists(test_mapmeta_path));
- MapSettingsManager mgr1(&conf, test_mapmeta_path);
- UASSERT(!mgr1.loadMapMeta());
+ MapSettingsManager mgr1(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));
+ {
+ test_mapmeta_path = makeMetaFile(true);
+ UASSERT(fs::PathExists(test_mapmeta_path));
- MapSettingsManager mgr2(&conf, test_mapmeta_path);
- UASSERT(!mgr2.loadMapMeta());
+ MapSettingsManager mgr2(test_mapmeta_path);
+ UASSERT(!mgr2.loadMapMeta());
+ }
}
diff --git a/src/unittest/test_settings.cpp b/src/unittest/test_settings.cpp
index f91ba5b67..6b493c9e4 100644
--- a/src/unittest/test_settings.cpp
+++ b/src/unittest/test_settings.cpp
@@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <cmath>
#include "settings.h"
+#include "defaultsettings.h"
#include "noise.h"
class TestSettings : public TestBase {
@@ -31,6 +32,7 @@ public:
void runTests(IGameDef *gamedef);
void testAllSettings();
+ void testDefaults();
void testFlagDesc();
static const char *config_text_before;
@@ -42,6 +44,7 @@ static TestSettings g_test_instance;
void TestSettings::runTests(IGameDef *gamedef)
{
TEST(testAllSettings);
+ TEST(testDefaults);
TEST(testFlagDesc);
}
@@ -70,7 +73,8 @@ const char *TestSettings::config_text_before =
" with leading whitespace!\n"
"\"\"\"\n"
"np_terrain = 5, 40, (250, 250, 250), 12341, 5, 0.7, 2.4\n"
- "zoop = true";
+ "zoop = true\n"
+ "[dummy_eof_end_tag]\n";
const std::string TestSettings::config_text_after =
"leet = 1337\n"
@@ -111,12 +115,34 @@ const std::string TestSettings::config_text_after =
" animals = cute\n"
" num_apples = 4\n"
" num_oranges = 53\n"
- "}\n";
+ "}\n"
+ "[dummy_eof_end_tag]";
+
+void compare_settings(const std::string &name, Settings *a, Settings *b)
+{
+ auto keys = a->getNames();
+ Settings *group1, *group2;
+ std::string value1, value2;
+ for (auto &key : keys) {
+ if (a->getGroupNoEx(key, group1)) {
+ UASSERT(b->getGroupNoEx(key, group2));
+
+ compare_settings(name + "->" + key, group1, group2);
+ continue;
+ }
+
+ UASSERT(b->getNoEx(key, value1));
+ // For identification
+ value1 = name + "->" + key + "=" + value1;
+ value2 = name + "->" + key + "=" + a->get(key);
+ UASSERTCMP(std::string, ==, value2, value1);
+ }
+}
void TestSettings::testAllSettings()
{
try {
- Settings s;
+ Settings s("[dummy_eof_end_tag]");
// Test reading of settings
std::istringstream is(config_text_before);
@@ -197,21 +223,44 @@ void TestSettings::testAllSettings()
is.clear();
is.seekg(0);
- UASSERT(s.updateConfigObject(is, os, "", 0) == true);
- //printf(">>>> expected config:\n%s\n", TEST_CONFIG_TEXT_AFTER);
- //printf(">>>> actual config:\n%s\n", os.str().c_str());
-#if __cplusplus < 201103L
- // This test only works in older C++ versions than C++11 because we use unordered_map
- UASSERT(os.str() == config_text_after);
-#endif
+ UASSERT(s.updateConfigObject(is, os, 0) == true);
+
+ {
+ // Confirm settings
+ Settings s2("[dummy_eof_end_tag]");
+ std::istringstream is(config_text_after, std::ios_base::binary);
+ UASSERT(s2.parseConfigLines(is) == true);
+
+ compare_settings("(main)", &s, &s2);
+ }
+
} catch (SettingNotFoundException &e) {
UASSERT(!"Setting not found!");
}
}
+void TestSettings::testDefaults()
+{
+ Settings *game = Settings::createLayer(SL_GAME);
+ Settings *def = Settings::getLayer(SL_DEFAULTS);
+
+ def->set("name", "FooBar");
+ UASSERT(def->get("name") == "FooBar");
+ UASSERT(game->get("name") == "FooBar");
+
+ game->set("name", "Baz");
+ UASSERT(game->get("name") == "Baz");
+
+ delete game;
+
+ // Restore default settings
+ delete Settings::getLayer(SL_DEFAULTS);
+ set_default_settings();
+}
+
void TestSettings::testFlagDesc()
{
- Settings s;
+ Settings &s = *Settings::createLayer(SL_GAME);
FlagDesc flagdesc[] = {
{ "biomes", 0x01 },
{ "trees", 0x02 },
@@ -242,4 +291,6 @@ void TestSettings::testFlagDesc()
// Enabled: tables
s.set("test_flags", "16");
UASSERT(s.getFlagStr("test_flags", flagdesc, nullptr) == 0x10);
+
+ delete &s;
}
diff --git a/src/unittest/test_utilities.cpp b/src/unittest/test_utilities.cpp
index 447b591e1..93ba3f844 100644
--- a/src/unittest/test_utilities.cpp
+++ b/src/unittest/test_utilities.cpp
@@ -247,8 +247,8 @@ void TestUtilities::testStartsWith()
void TestUtilities::testStrEqual()
{
- UASSERT(str_equal(narrow_to_wide("abc"), narrow_to_wide("abc")));
- UASSERT(str_equal(narrow_to_wide("ABC"), narrow_to_wide("abc"), true));
+ UASSERT(str_equal(utf8_to_wide("abc"), utf8_to_wide("abc")));
+ UASSERT(str_equal(utf8_to_wide("ABC"), utf8_to_wide("abc"), true));
}
@@ -302,9 +302,18 @@ void TestUtilities::testAsciiPrintableHelper()
void TestUtilities::testUTF8()
{
- UASSERT(wide_to_utf8(utf8_to_wide("")) == "");
- UASSERT(wide_to_utf8(utf8_to_wide("the shovel dug a crumbly node!"))
- == "the shovel dug a crumbly node!");
+ UASSERT(utf8_to_wide("¤") == L"¤");
+
+ UASSERT(wide_to_utf8(L"¤") == "¤");
+
+ UASSERTEQ(std::string, wide_to_utf8(utf8_to_wide("")), "");
+ UASSERTEQ(std::string, wide_to_utf8(utf8_to_wide("the shovel dug a crumbly node!")),
+ "the shovel dug a crumbly node!");
+ UASSERTEQ(std::string, wide_to_utf8(utf8_to_wide("-ä-")),
+ "-ä-");
+ UASSERTEQ(std::string, wide_to_utf8(utf8_to_wide("-\xF0\xA0\x80\x8B-")),
+ "-\xF0\xA0\x80\x8B-");
+
}
void TestUtilities::testRemoveEscapes()
diff --git a/src/util/string.cpp b/src/util/string.cpp
index 3ac3b8cf0..611ad35cb 100644
--- a/src/util/string.cpp
+++ b/src/util/string.cpp
@@ -50,8 +50,8 @@ static bool parseNamedColorString(const std::string &value, video::SColor &color
#ifndef _WIN32
-bool convert(const char *to, const char *from, char *outbuf,
- size_t outbuf_size, char *inbuf, size_t inbuf_size)
+static bool convert(const char *to, const char *from, char *outbuf,
+ size_t *outbuf_size, char *inbuf, size_t inbuf_size)
{
iconv_t cd = iconv_open(to, from);
@@ -60,15 +60,14 @@ bool convert(const char *to, const char *from, char *outbuf,
#else
char *inbuf_ptr = inbuf;
#endif
-
char *outbuf_ptr = outbuf;
size_t *inbuf_left_ptr = &inbuf_size;
- size_t *outbuf_left_ptr = &outbuf_size;
+ const size_t old_outbuf_size = *outbuf_size;
size_t old_size = inbuf_size;
while (inbuf_size > 0) {
- iconv(cd, &inbuf_ptr, inbuf_left_ptr, &outbuf_ptr, outbuf_left_ptr);
+ iconv(cd, &inbuf_ptr, inbuf_left_ptr, &outbuf_ptr, outbuf_size);
if (inbuf_size == old_size) {
iconv_close(cd);
return false;
@@ -77,11 +76,12 @@ bool convert(const char *to, const char *from, char *outbuf,
}
iconv_close(cd);
+ *outbuf_size = old_outbuf_size - *outbuf_size;
return true;
}
#ifdef __ANDROID__
-// Android need manual caring to support the full character set possible with wchar_t
+// On Android iconv disagrees how big a wchar_t is for whatever reason
const char *DEFAULT_ENCODING = "UTF-32LE";
#else
const char *DEFAULT_ENCODING = "WCHAR_T";
@@ -89,58 +89,52 @@ const char *DEFAULT_ENCODING = "WCHAR_T";
std::wstring utf8_to_wide(const std::string &input)
{
- size_t inbuf_size = input.length() + 1;
+ const size_t inbuf_size = input.length();
// maximum possible size, every character is sizeof(wchar_t) bytes
- size_t outbuf_size = (input.length() + 1) * sizeof(wchar_t);
+ size_t outbuf_size = input.length() * sizeof(wchar_t);
- char *inbuf = new char[inbuf_size];
+ char *inbuf = new char[inbuf_size]; // intentionally NOT null-terminated
memcpy(inbuf, input.c_str(), inbuf_size);
- char *outbuf = new char[outbuf_size];
- memset(outbuf, 0, outbuf_size);
+ std::wstring out;
+ out.resize(outbuf_size / sizeof(wchar_t));
#ifdef __ANDROID__
- // Android need manual caring to support the full character set possible with wchar_t
SANITY_CHECK(sizeof(wchar_t) == 4);
#endif
- if (!convert(DEFAULT_ENCODING, "UTF-8", outbuf, outbuf_size, inbuf, inbuf_size)) {
+ char *outbuf = reinterpret_cast<char*>(&out[0]);
+ if (!convert(DEFAULT_ENCODING, "UTF-8", outbuf, &outbuf_size, inbuf, inbuf_size)) {
infostream << "Couldn't convert UTF-8 string 0x" << hex_encode(input)
<< " into wstring" << std::endl;
delete[] inbuf;
- delete[] outbuf;
return L"<invalid UTF-8 string>";
}
- std::wstring out((wchar_t *)outbuf);
-
delete[] inbuf;
- delete[] outbuf;
+ out.resize(outbuf_size / sizeof(wchar_t));
return out;
}
std::string wide_to_utf8(const std::wstring &input)
{
- size_t inbuf_size = (input.length() + 1) * sizeof(wchar_t);
- // maximum possible size: utf-8 encodes codepoints using 1 up to 6 bytes
- size_t outbuf_size = (input.length() + 1) * 6;
+ const size_t inbuf_size = input.length() * sizeof(wchar_t);
+ // maximum possible size: utf-8 encodes codepoints using 1 up to 4 bytes
+ size_t outbuf_size = input.length() * 4;
- char *inbuf = new char[inbuf_size];
+ char *inbuf = new char[inbuf_size]; // intentionally NOT null-terminated
memcpy(inbuf, input.c_str(), inbuf_size);
- char *outbuf = new char[outbuf_size];
- memset(outbuf, 0, outbuf_size);
+ std::string out;
+ out.resize(outbuf_size);
- if (!convert("UTF-8", DEFAULT_ENCODING, outbuf, outbuf_size, inbuf, inbuf_size)) {
+ if (!convert("UTF-8", DEFAULT_ENCODING, &out[0], &outbuf_size, inbuf, inbuf_size)) {
infostream << "Couldn't convert wstring 0x" << hex_encode(inbuf, inbuf_size)
<< " into UTF-8 string" << std::endl;
delete[] inbuf;
- delete[] outbuf;
- return "<invalid wstring>";
+ return "<invalid wide string>";
}
- std::string out(outbuf);
-
delete[] inbuf;
- delete[] outbuf;
+ out.resize(outbuf_size);
return out;
}
@@ -172,74 +166,15 @@ std::string wide_to_utf8(const std::wstring &input)
#endif // _WIN32
-// You must free the returned string!
-// The returned string is allocated using new
wchar_t *utf8_to_wide_c(const char *str)
{
std::wstring ret = utf8_to_wide(std::string(str));
size_t len = ret.length();
wchar_t *ret_c = new wchar_t[len + 1];
- memset(ret_c, 0, (len + 1) * sizeof(wchar_t));
- memcpy(ret_c, ret.c_str(), len * sizeof(wchar_t));
+ memcpy(ret_c, ret.c_str(), (len + 1) * sizeof(wchar_t));
return ret_c;
}
-// You must free the returned string!
-// The returned string is allocated using new
-wchar_t *narrow_to_wide_c(const char *str)
-{
- wchar_t *nstr = nullptr;
-#if defined(_WIN32)
- int nResult = MultiByteToWideChar(CP_UTF8, 0, (LPCSTR) str, -1, 0, 0);
- if (nResult == 0) {
- errorstream<<"gettext: MultiByteToWideChar returned null"<<std::endl;
- } else {
- nstr = new wchar_t[nResult];
- MultiByteToWideChar(CP_UTF8, 0, (LPCSTR) str, -1, (WCHAR *) nstr, nResult);
- }
-#else
- size_t len = strlen(str);
- nstr = new wchar_t[len + 1];
-
- std::wstring intermediate = narrow_to_wide(str);
- memset(nstr, 0, (len + 1) * sizeof(wchar_t));
- memcpy(nstr, intermediate.c_str(), len * sizeof(wchar_t));
-#endif
-
- return nstr;
-}
-
-std::wstring narrow_to_wide(const std::string &mbs) {
-#ifdef __ANDROID__
- return utf8_to_wide(mbs);
-#else
- size_t wcl = mbs.size();
- Buffer<wchar_t> wcs(wcl + 1);
- size_t len = mbstowcs(*wcs, mbs.c_str(), wcl);
- if (len == (size_t)(-1))
- return L"<invalid multibyte string>";
- wcs[len] = 0;
- return *wcs;
-#endif
-}
-
-
-std::string wide_to_narrow(const std::wstring &wcs)
-{
-#ifdef __ANDROID__
- return wide_to_utf8(wcs);
-#else
- size_t mbl = wcs.size() * 4;
- SharedBuffer<char> mbs(mbl+1);
- size_t len = wcstombs(*mbs, wcs.c_str(), mbl);
- if (len == (size_t)(-1))
- return "Character conversion failed!";
-
- mbs[len] = 0;
- return *mbs;
-#endif
-}
-
std::string urlencode(const std::string &str)
{
@@ -766,7 +701,8 @@ void translate_string(const std::wstring &s, Translations *translations,
} else {
// This is an escape sequence *inside* the template string to translate itself.
// This should not happen, show an error message.
- errorstream << "Ignoring escape sequence '" << wide_to_narrow(escape_sequence) << "' in translation" << std::endl;
+ errorstream << "Ignoring escape sequence '"
+ << wide_to_utf8(escape_sequence) << "' in translation" << std::endl;
}
}
diff --git a/src/util/string.h b/src/util/string.h
index 6fd11fadc..d4afcaec8 100644
--- a/src/util/string.h
+++ b/src/util/string.h
@@ -64,22 +64,14 @@ struct FlagDesc {
u32 flag;
};
-// try not to convert between wide/utf8 encodings; this can result in data loss
-// try to only convert between them when you need to input/output stuff via Irrlicht
+// Try to avoid converting between wide and UTF-8 unless you need to
+// input/output stuff via Irrlicht
std::wstring utf8_to_wide(const std::string &input);
std::string wide_to_utf8(const std::wstring &input);
-wchar_t *utf8_to_wide_c(const char *str);
-
-// NEVER use those two functions unless you have a VERY GOOD reason to
-// they just convert between wide and multibyte encoding
-// multibyte encoding depends on current locale, this is no good, especially on Windows
-
// You must free the returned string!
-// The returned string is allocated using new
-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);
+// The returned string is allocated using new[]
+wchar_t *utf8_to_wide_c(const char *str);
std::string urlencode(const std::string &str);
std::string urldecode(const std::string &str);
@@ -353,11 +345,6 @@ inline s32 mystoi(const std::string &str, s32 min, s32 max)
return i;
}
-
-// MSVC2010 includes it's own versions of these
-//#if !defined(_MSC_VER) || _MSC_VER < 1600
-
-
/**
* Returns a 32-bit value reprensented by the string \p str (decimal).
* @see atoi(3) for further limitations
@@ -367,17 +354,6 @@ inline s32 mystoi(const std::string &str)
return atoi(str.c_str());
}
-
-/**
- * Returns s 32-bit value represented by the wide string \p str (decimal).
- * @see atoi(3) for further limitations
- */
-inline s32 mystoi(const std::wstring &str)
-{
- return mystoi(wide_to_narrow(str));
-}
-
-
/**
* Returns a float reprensented by the string \p str (decimal).
* @see atof(3)
@@ -387,8 +363,6 @@ inline float mystof(const std::string &str)
return atof(str.c_str());
}
-//#endif
-
#define stoi mystoi
#define stof mystof
diff --git a/src/version.cpp b/src/version.cpp
index 241228a6a..c555f30af 100644
--- a/src/version.cpp
+++ b/src/version.cpp
@@ -33,11 +33,12 @@ const char *g_version_hash = VERSION_GITHASH;
const char *g_build_info =
"BUILD_TYPE=" BUILD_TYPE "\n"
"RUN_IN_PLACE=" STR(RUN_IN_PLACE) "\n"
+ "USE_CURL=" STR(USE_CURL) "\n"
+#ifndef SERVER
"USE_GETTEXT=" STR(USE_GETTEXT) "\n"
"USE_SOUND=" STR(USE_SOUND) "\n"
- "USE_CURL=" STR(USE_CURL) "\n"
"USE_FREETYPE=" STR(USE_FREETYPE) "\n"
- "USE_LUAJIT=" STR(USE_LUAJIT) "\n"
+#endif
"STATIC_SHAREDIR=" STR(STATIC_SHAREDIR)
#if USE_GETTEXT && defined(STATIC_LOCALEDIR)
"\n" "STATIC_LOCALEDIR=" STR(STATIC_LOCALEDIR)