mirror of
https://github.com/luanti-org/luanti.git
synced 2025-08-06 17:41:04 +00:00
Merge remote-tracking branch 'upstream/master' into Visuals-Vol-2
This commit is contained in:
commit
cd6e304cfa
172 changed files with 27747 additions and 18949 deletions
|
@ -48,6 +48,25 @@ if(NOT (BUILD_CLIENT OR BUILD_SERVER))
|
|||
endif()
|
||||
|
||||
|
||||
option(PRECOMPILE_HEADERS "Precompile some headers (experimental; requires CMake 3.16 or later)" FALSE)
|
||||
set(PRECOMPILED_HEADERS_PATH "" CACHE FILEPATH "Path to a file listing all headers to precompile")
|
||||
|
||||
if(PRECOMPILE_HEADERS)
|
||||
if(${CMAKE_VERSION} VERSION_LESS 3.16)
|
||||
message(FATAL_ERROR "PRECOMPILE_HEADERS is on, but precompiled headers require at least CMake 3.16.")
|
||||
endif()
|
||||
if(PRECOMPILED_HEADERS_PATH)
|
||||
set(PRECOMPILED_HEADERS ${PRECOMPILED_HEADERS_PATH})
|
||||
else()
|
||||
set(PRECOMPILED_HEADERS "${CMAKE_SOURCE_DIR}/src/precompiled_headers.txt")
|
||||
endif()
|
||||
message(STATUS "Reading headers to precompile from: ${PRECOMPILED_HEADERS}")
|
||||
# ignore lines that begin with # and empty lines
|
||||
file(STRINGS ${PRECOMPILED_HEADERS} PRECOMPILED_HEADERS_LIST REGEX "^[^#].*$")
|
||||
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${PRECOMPILED_HEADERS})
|
||||
endif()
|
||||
|
||||
|
||||
option(ENABLE_CURL "Enable cURL support for fetching media" TRUE)
|
||||
set(USE_CURL FALSE)
|
||||
|
||||
|
@ -332,6 +351,12 @@ endif()
|
|||
include(CheckSymbolExists)
|
||||
check_symbol_exists(strlcpy "string.h" HAVE_STRLCPY)
|
||||
|
||||
if(UNIX)
|
||||
check_symbol_exists(malloc_trim "malloc.h" HAVE_MALLOC_TRIM)
|
||||
else()
|
||||
set(HAVE_MALLOC_TRIM FALSE)
|
||||
endif()
|
||||
|
||||
check_include_files(endian.h HAVE_ENDIAN_H)
|
||||
|
||||
configure_file(
|
||||
|
@ -449,6 +474,10 @@ if(BUILD_BENCHMARKS)
|
|||
set(common_SRCS ${common_SRCS} ${BENCHMARK_SRCS})
|
||||
endif()
|
||||
|
||||
if(BUILD_UNITTESTS OR BUILD_BENCHMARKS)
|
||||
set(common_SRCS ${common_SRCS} catch.cpp)
|
||||
endif()
|
||||
|
||||
# This gives us the icon and file version information
|
||||
if(WIN32)
|
||||
set(WINRESOURCE_FILE "${CMAKE_CURRENT_SOURCE_DIR}/../misc/winresource.rc")
|
||||
|
@ -565,6 +594,7 @@ if(BUILD_CLIENT)
|
|||
${GMP_LIBRARY}
|
||||
${JSON_LIBRARY}
|
||||
${LUA_BIT_LIBRARY}
|
||||
sha256
|
||||
${FREETYPE_LIBRARY}
|
||||
${PLATFORM_LIBS}
|
||||
# on Android, Minetest depends on SDL2 directly
|
||||
|
@ -615,8 +645,12 @@ if(BUILD_CLIENT)
|
|||
if (USE_SPATIAL)
|
||||
target_link_libraries(${PROJECT_NAME} ${SPATIAL_LIBRARY})
|
||||
endif()
|
||||
if(BUILD_BENCHMARKS)
|
||||
target_link_libraries(${PROJECT_NAME} catch2)
|
||||
if(BUILD_UNITTESTS OR BUILD_BENCHMARKS)
|
||||
target_link_libraries(${PROJECT_NAME} Catch2::Catch2)
|
||||
endif()
|
||||
|
||||
if(PRECOMPILE_HEADERS)
|
||||
target_precompile_headers(${PROJECT_NAME} PRIVATE ${PRECOMPILED_HEADERS_LIST})
|
||||
endif()
|
||||
endif(BUILD_CLIENT)
|
||||
|
||||
|
@ -637,6 +671,7 @@ if(BUILD_SERVER)
|
|||
${JSON_LIBRARY}
|
||||
${LUA_LIBRARY}
|
||||
${LUA_BIT_LIBRARY}
|
||||
sha256
|
||||
${GMP_LIBRARY}
|
||||
${PLATFORM_LIBS}
|
||||
)
|
||||
|
@ -677,8 +712,12 @@ if(BUILD_SERVER)
|
|||
${CURL_LIBRARY}
|
||||
)
|
||||
endif()
|
||||
if(BUILD_BENCHMARKS)
|
||||
target_link_libraries(${PROJECT_NAME}server catch2)
|
||||
if(BUILD_UNITTESTS OR BUILD_BENCHMARKS)
|
||||
target_link_libraries(${PROJECT_NAME}server Catch2::Catch2)
|
||||
endif()
|
||||
|
||||
if(PRECOMPILE_HEADERS)
|
||||
target_precompile_headers(${PROJECT_NAME}server PRIVATE ${PRECOMPILED_HEADERS_LIST})
|
||||
endif()
|
||||
endif(BUILD_SERVER)
|
||||
|
||||
|
|
|
@ -31,7 +31,6 @@ class TestServerActiveObjectMgr;
|
|||
template <typename T>
|
||||
class ActiveObjectMgr
|
||||
{
|
||||
friend class ::TestClientActiveObjectMgr;
|
||||
friend class ::TestServerActiveObjectMgr;
|
||||
|
||||
public:
|
||||
|
|
|
@ -19,9 +19,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
|
||||
#include "benchmark/benchmark.h"
|
||||
|
||||
// This must be set in just this file
|
||||
#define CATCH_CONFIG_RUNNER
|
||||
#include "benchmark_setup.h"
|
||||
#define CATCH_CONFIG_ENABLE_BENCHMARKING
|
||||
#include "catch.h"
|
||||
|
||||
bool run_benchmarks(const char *arg)
|
||||
{
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// Minetest
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
#include "benchmark_setup.h"
|
||||
#include "catch.h"
|
||||
#include "server/activeobjectmgr.h"
|
||||
#include "util/numeric.h"
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "benchmark_setup.h"
|
||||
#include "catch.h"
|
||||
#include "voxelalgorithms.h"
|
||||
#include "dummygamedef.h"
|
||||
#include "dummymap.h"
|
||||
|
|
|
@ -17,7 +17,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "benchmark_setup.h"
|
||||
#include "catch.h"
|
||||
#include "mapblock.h"
|
||||
#include <vector>
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "benchmark_setup.h"
|
||||
#include "catch.h"
|
||||
#include "util/container.h"
|
||||
|
||||
// Testing the standard library is not useful except to compare
|
||||
|
|
|
@ -17,7 +17,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "benchmark_setup.h"
|
||||
#include "catch.h"
|
||||
#include "util/serialize.h"
|
||||
#include <sstream>
|
||||
#include <ios>
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
/*
|
||||
Minetest
|
||||
Copyright (C) 2022 Minetest Authors
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#define CATCH_CONFIG_ENABLE_BENCHMARKING
|
||||
#define CATCH_CONFIG_CONSOLE_WIDTH 160
|
||||
#include <catch.hpp>
|
18
src/catch.cpp
Normal file
18
src/catch.cpp
Normal file
|
@ -0,0 +1,18 @@
|
|||
// Minetest
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
#include "catch.h"
|
||||
|
||||
#include "log.h"
|
||||
|
||||
namespace Catch {
|
||||
std::ostream& cout() {
|
||||
return rawstream;
|
||||
}
|
||||
std::ostream& clog() {
|
||||
return rawstream;
|
||||
}
|
||||
std::ostream& cerr() {
|
||||
return rawstream;
|
||||
}
|
||||
}
|
14
src/catch.h
Normal file
14
src/catch.h
Normal file
|
@ -0,0 +1,14 @@
|
|||
// Minetest
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
// We want to have catch write to rawstream (stderr) instead of stdout.
|
||||
// This should be included instead of <catch_amalgamated.hpp>
|
||||
// to patch the output streams accordingly.
|
||||
#define CATCH_CONFIG_NOSTDOUT
|
||||
#include <catch_amalgamated.hpp>
|
||||
|
||||
namespace Catch {
|
||||
std::ostream& cout();
|
||||
std::ostream& clog();
|
||||
std::ostream& cerr();
|
||||
}
|
|
@ -376,6 +376,13 @@ u32 ChatBuffer::formatChatLine(const ChatLine &line, u32 cols,
|
|||
tempchar = linestring[in_pos+frag_length];
|
||||
}
|
||||
|
||||
// Remove tailing punctuation characters
|
||||
static const std::wstring tailing_chars = L",.";
|
||||
tempchar = linestring[in_pos+frag_length - 1];
|
||||
if (tailing_chars.find(tempchar) != std::wstring::npos) {
|
||||
frag_length--;
|
||||
}
|
||||
|
||||
space_pos = frag_length - 1;
|
||||
// This frag may need to be force-split. That's ok, urls aren't "words"
|
||||
if (frag_length >= remaining_in_output) {
|
||||
|
|
|
@ -278,9 +278,7 @@ void Client::scanModSubfolder(const std::string &mod_name, const std::string &mo
|
|||
<< "\" as \"" << vfs_path << "\"." << std::endl;
|
||||
|
||||
std::string contents;
|
||||
if (!fs::ReadFile(real_path, contents)) {
|
||||
errorstream << "Client::scanModSubfolder(): Can't read file \""
|
||||
<< real_path << "\"." << std::endl;
|
||||
if (!fs::ReadFile(real_path, contents, true)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -1945,8 +1943,7 @@ void Client::makeScreenshot()
|
|||
|
||||
while (serial < SCREENSHOT_MAX_SERIAL_TRIES) {
|
||||
filename = filename_base + (serial > 0 ? ("_" + itos(serial)) : "") + filename_ext;
|
||||
std::ifstream tmp(filename.c_str());
|
||||
if (!tmp.good())
|
||||
if (!fs::PathExists(filename))
|
||||
break; // File did not apparently exist, we'll go with it
|
||||
serial++;
|
||||
}
|
||||
|
|
|
@ -36,6 +36,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
#include "network/networkexceptions.h"
|
||||
#include <IGUISpriteBank.h>
|
||||
#include <ICameraSceneNode.h>
|
||||
#include <unordered_map>
|
||||
|
||||
#if USE_SOUND
|
||||
#include "sound/sound_openal.h"
|
||||
|
@ -68,6 +69,7 @@ static void dump_start_data(const GameStartData &data)
|
|||
ClientLauncher::~ClientLauncher()
|
||||
{
|
||||
delete input;
|
||||
g_settings->deregisterChangedCallback("gui_scaling", setting_changed_callback, this);
|
||||
|
||||
delete g_fontengine;
|
||||
g_fontengine = nullptr;
|
||||
|
@ -126,7 +128,8 @@ bool ClientLauncher::run(GameStartData &start_data, const Settings &cmd_args)
|
|||
setAttribute(scene::ALLOW_ZWRITE_ON_TRANSPARENT, true);
|
||||
|
||||
guienv = m_rendering_engine->get_gui_env();
|
||||
init_guienv(guienv);
|
||||
config_guienv();
|
||||
g_settings->registerChangedCallback("gui_scaling", setting_changed_callback, this);
|
||||
|
||||
g_fontengine = new FontEngine(guienv);
|
||||
|
||||
|
@ -326,7 +329,12 @@ void ClientLauncher::init_input()
|
|||
}
|
||||
}
|
||||
|
||||
void ClientLauncher::init_guienv(gui::IGUIEnvironment *guienv)
|
||||
void ClientLauncher::setting_changed_callback(const std::string &name, void *data)
|
||||
{
|
||||
static_cast<ClientLauncher*>(data)->config_guienv();
|
||||
}
|
||||
|
||||
void ClientLauncher::config_guienv()
|
||||
{
|
||||
gui::IGUISkin *skin = guienv->getSkin();
|
||||
|
||||
|
@ -343,25 +351,37 @@ void ClientLauncher::init_guienv(gui::IGUIEnvironment *guienv)
|
|||
float density = rangelim(g_settings->getFloat("gui_scaling"), 0.5f, 20) *
|
||||
RenderingEngine::getDisplayDensity();
|
||||
skin->setSize(gui::EGDS_CHECK_BOX_WIDTH, (s32)(17.0f * density));
|
||||
skin->setSize(gui::EGDS_SCROLLBAR_SIZE, (s32)(14.0f * density));
|
||||
skin->setSize(gui::EGDS_SCROLLBAR_SIZE, (s32)(21.0f * density));
|
||||
skin->setSize(gui::EGDS_WINDOW_BUTTON_WIDTH, (s32)(15.0f * density));
|
||||
|
||||
static u32 orig_sprite_id = skin->getIcon(gui::EGDI_CHECK_BOX_CHECKED);
|
||||
static std::unordered_map<std::string, u32> sprite_ids;
|
||||
|
||||
if (density > 1.5f) {
|
||||
std::string sprite_path = porting::path_share + "/textures/base/pack/";
|
||||
if (density > 3.5f)
|
||||
sprite_path.append("checkbox_64.png");
|
||||
else if (density > 2.0f)
|
||||
sprite_path.append("checkbox_32.png");
|
||||
else
|
||||
sprite_path.append("checkbox_16.png");
|
||||
// Texture dimensions should be a power of 2
|
||||
gui::IGUISpriteBank *sprites = skin->getSpriteBank();
|
||||
video::IVideoDriver *driver = m_rendering_engine->get_video_driver();
|
||||
video::ITexture *sprite_texture = driver->getTexture(sprite_path.c_str());
|
||||
if (sprite_texture) {
|
||||
s32 sprite_id = sprites->addTextureAsSprite(sprite_texture);
|
||||
if (sprite_id != -1)
|
||||
skin->setIcon(gui::EGDI_CHECK_BOX_CHECKED, sprite_id);
|
||||
std::string path = porting::path_share + "/textures/base/pack/";
|
||||
if (density > 3.5f)
|
||||
path.append("checkbox_64.png");
|
||||
else if (density > 2.0f)
|
||||
path.append("checkbox_32.png");
|
||||
else
|
||||
path.append("checkbox_16.png");
|
||||
|
||||
auto cached_id = sprite_ids.find(path);
|
||||
if (cached_id != sprite_ids.end()) {
|
||||
skin->setIcon(gui::EGDI_CHECK_BOX_CHECKED, cached_id->second);
|
||||
} else {
|
||||
gui::IGUISpriteBank *sprites = skin->getSpriteBank();
|
||||
video::IVideoDriver *driver = m_rendering_engine->get_video_driver();
|
||||
video::ITexture *texture = driver->getTexture(path.c_str());
|
||||
s32 id = sprites->addTextureAsSprite(texture);
|
||||
if (id != -1) {
|
||||
skin->setIcon(gui::EGDI_CHECK_BOX_CHECKED, id);
|
||||
sprite_ids.emplace(path, id);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
skin->setIcon(gui::EGDI_CHECK_BOX_CHECKED, orig_sprite_id);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -38,7 +38,9 @@ private:
|
|||
void init_args(GameStartData &start_data, const Settings &cmd_args);
|
||||
bool init_engine();
|
||||
void init_input();
|
||||
void init_guienv(gui::IGUIEnvironment *guienv);
|
||||
|
||||
static void setting_changed_callback(const std::string &name, void *data);
|
||||
void config_guienv();
|
||||
|
||||
bool launch_game(std::string &error_message, bool reconnect_requested,
|
||||
GameStartData &start_data, const Settings &cmd_args);
|
||||
|
|
|
@ -38,13 +38,9 @@ void FileCache::createDir()
|
|||
|
||||
bool FileCache::loadByPath(const std::string &path, std::ostream &os)
|
||||
{
|
||||
std::ifstream fis(path.c_str(), std::ios_base::binary);
|
||||
|
||||
if(!fis.good()){
|
||||
verbosestream<<"FileCache: File not found in cache: "
|
||||
<<path<<std::endl;
|
||||
auto fis = open_ifstream(path.c_str(), false);
|
||||
if (!fis.good())
|
||||
return false;
|
||||
}
|
||||
|
||||
bool bad = false;
|
||||
for(;;){
|
||||
|
@ -70,15 +66,10 @@ bool FileCache::loadByPath(const std::string &path, std::ostream &os)
|
|||
bool FileCache::updateByPath(const std::string &path, std::string_view data)
|
||||
{
|
||||
createDir();
|
||||
std::ofstream file(path.c_str(), std::ios_base::binary |
|
||||
std::ios_base::trunc);
|
||||
|
||||
if(!file.good())
|
||||
{
|
||||
errorstream<<"FileCache: Can't write to file at "
|
||||
<<path<<std::endl;
|
||||
auto file = open_ofstream(path.c_str(), true);
|
||||
if (!file.good())
|
||||
return false;
|
||||
}
|
||||
|
||||
file << data;
|
||||
file.close();
|
||||
|
@ -101,8 +92,7 @@ bool FileCache::load(const std::string &name, std::ostream &os)
|
|||
bool FileCache::exists(const std::string &name)
|
||||
{
|
||||
std::string path = m_dir + DIR_DELIM + name;
|
||||
std::ifstream fis(path.c_str(), std::ios_base::binary);
|
||||
return fis.good();
|
||||
return fs::PathExists(path);
|
||||
}
|
||||
|
||||
bool FileCache::updateCopyFile(const std::string &name, const std::string &src_path)
|
||||
|
|
|
@ -801,7 +801,15 @@ protected:
|
|||
|
||||
// Misc
|
||||
void showOverlayMessage(const char *msg, float dtime, int percent,
|
||||
bool draw_clouds = true);
|
||||
float *indef_pos = nullptr);
|
||||
|
||||
inline bool fogEnabled()
|
||||
{
|
||||
// Client setting only takes effect if fog distance unlimited or debug priv
|
||||
if (sky->getFogDistance() < 0 || client->checkPrivilege("debug"))
|
||||
return m_cache_enable_fog;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void settingChangedCallback(const std::string &setting_name, void *data);
|
||||
void readSettings();
|
||||
|
@ -969,6 +977,8 @@ private:
|
|||
#ifdef __ANDROID__
|
||||
bool m_android_chat_open;
|
||||
#endif
|
||||
|
||||
float m_shutdown_progress = 0.0f;
|
||||
};
|
||||
|
||||
Game::Game() :
|
||||
|
@ -1165,7 +1175,8 @@ void Game::run()
|
|||
g_settings->getU16("screen_w"),
|
||||
g_settings->getU16("screen_h")
|
||||
);
|
||||
const bool initial_window_maximized = g_settings->getBool("window_maximized");
|
||||
const bool initial_window_maximized = !g_settings->getBool("fullscreen") &&
|
||||
g_settings->getBool("window_maximized");
|
||||
|
||||
while (m_rendering_engine->run()
|
||||
&& !(*kill || g_gamecallback->shutdown_requested
|
||||
|
@ -1248,7 +1259,9 @@ void Game::shutdown()
|
|||
if (g_touchscreengui)
|
||||
g_touchscreengui->hide();
|
||||
|
||||
showOverlayMessage(N_("Shutting down..."), 0, 0, false);
|
||||
// only if the shutdown progress bar isn't shown yet
|
||||
if (m_shutdown_progress == 0.0f)
|
||||
showOverlayMessage(N_("Shutting down..."), 0, 0);
|
||||
|
||||
if (clouds)
|
||||
clouds->drop();
|
||||
|
@ -1300,7 +1313,7 @@ void Game::shutdown()
|
|||
m_rendering_engine->run();
|
||||
f32 dtime;
|
||||
fps_control.limit(device, &dtime);
|
||||
showOverlayMessage(N_("Shutting down..."), dtime, 0, false);
|
||||
showOverlayMessage(N_("Shutting down..."), dtime, 0, &m_shutdown_progress);
|
||||
}
|
||||
|
||||
stop_thread->rethrow();
|
||||
|
@ -1432,7 +1445,7 @@ bool Game::createSingleplayerServer(const std::string &map_dir,
|
|||
if (success)
|
||||
showOverlayMessage(N_("Creating server..."), dtime, 5);
|
||||
else
|
||||
showOverlayMessage(N_("Shutting down..."), dtime, 0, false);
|
||||
showOverlayMessage(N_("Shutting down..."), dtime, 0, &m_shutdown_progress);
|
||||
}
|
||||
|
||||
start_thread->rethrow();
|
||||
|
@ -2426,9 +2439,6 @@ void Game::toggleBlockBounds()
|
|||
case Hud::BLOCK_BOUNDS_NEAR:
|
||||
m_game_ui->showTranslatedStatusText("Block bounds shown for nearby blocks");
|
||||
break;
|
||||
case Hud::BLOCK_BOUNDS_MAX:
|
||||
m_game_ui->showTranslatedStatusText("Block bounds shown for all blocks");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -2479,15 +2489,12 @@ void Game::toggleMinimap(bool shift_pressed)
|
|||
|
||||
void Game::toggleFog()
|
||||
{
|
||||
bool flag;
|
||||
// do not modify setting if no privilege
|
||||
if (!client->checkPrivilege("debug")) {
|
||||
flag = true;
|
||||
} else {
|
||||
flag = !g_settings->getBool("enable_fog");
|
||||
g_settings->setBool("enable_fog", flag);
|
||||
}
|
||||
if (flag)
|
||||
bool flag = !g_settings->getBool("enable_fog");
|
||||
g_settings->setBool("enable_fog", flag);
|
||||
bool allowed = sky->getFogDistance() < 0 || client->checkPrivilege("debug");
|
||||
if (!allowed)
|
||||
m_game_ui->showTranslatedStatusText("Fog enabled by game or mod");
|
||||
else if (flag)
|
||||
m_game_ui->showTranslatedStatusText("Fog enabled");
|
||||
else
|
||||
m_game_ui->showTranslatedStatusText("Fog disabled");
|
||||
|
@ -4242,8 +4249,7 @@ void Game::updateClouds(float dtime)
|
|||
camera_node_position.Y = camera_node_position.Y + camera_offset.Y * BS;
|
||||
camera_node_position.Z = camera_node_position.Z + camera_offset.Z * BS;
|
||||
this->clouds->update(camera_node_position, this->sky->getCloudColor());
|
||||
bool enable_fog = this->m_cache_enable_fog || !client->checkPrivilege("debug");
|
||||
if (this->clouds->isCameraInsideCloud() && enable_fog) {
|
||||
if (this->clouds->isCameraInsideCloud() && this->fogEnabled()) {
|
||||
// If camera is inside cloud and fog is enabled, use cloud's colors as sky colors.
|
||||
video::SColor clouds_dark = this->clouds->getColor().getInterpolated(
|
||||
video::SColor(255, 0, 0, 0), 0.9);
|
||||
|
@ -4303,7 +4309,7 @@ void Game::drawScene(ProfilerGraph *graph, RunStats *stats)
|
|||
/*
|
||||
Fog
|
||||
*/
|
||||
if (this->m_cache_enable_fog || !client->checkPrivilege("debug")) {
|
||||
if (this->fogEnabled()) {
|
||||
this->driver->setFog(
|
||||
fog_color,
|
||||
video::EFT_FOG_LINEAR,
|
||||
|
@ -4374,10 +4380,10 @@ void Game::drawScene(ProfilerGraph *graph, RunStats *stats)
|
|||
Misc
|
||||
****************************************************************************/
|
||||
|
||||
void Game::showOverlayMessage(const char *msg, float dtime, int percent, bool draw_sky)
|
||||
void Game::showOverlayMessage(const char *msg, float dtime, int percent, float *indef_pos)
|
||||
{
|
||||
m_rendering_engine->draw_load_screen(wstrgettext(msg), guienv, texture_src,
|
||||
dtime, percent, draw_sky);
|
||||
dtime, percent, indef_pos);
|
||||
}
|
||||
|
||||
void Game::settingChangedCallback(const std::string &setting_name, void *data)
|
||||
|
|
|
@ -913,7 +913,7 @@ enum Hud::BlockBoundsMode Hud::toggleBlockBounds()
|
|||
{
|
||||
m_block_bounds_mode = static_cast<BlockBoundsMode>(m_block_bounds_mode + 1);
|
||||
|
||||
if (m_block_bounds_mode >= BLOCK_BOUNDS_MAX) {
|
||||
if (m_block_bounds_mode > BLOCK_BOUNDS_NEAR) {
|
||||
m_block_bounds_mode = BLOCK_BOUNDS_OFF;
|
||||
}
|
||||
return m_block_bounds_mode;
|
||||
|
|
|
@ -40,7 +40,6 @@ public:
|
|||
BLOCK_BOUNDS_OFF,
|
||||
BLOCK_BOUNDS_CURRENT,
|
||||
BLOCK_BOUNDS_NEAR,
|
||||
BLOCK_BOUNDS_MAX
|
||||
} m_block_bounds_mode = BLOCK_BOUNDS_OFF;
|
||||
|
||||
video::SColor crosshair_argb;
|
||||
|
|
|
@ -18,6 +18,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "settings.h"
|
||||
#include "util/numeric.h"
|
||||
#include "inputhandler.h"
|
||||
#include "gui/mainmenumanager.h"
|
||||
|
@ -113,6 +114,19 @@ bool MyEventReceiver::OnEvent(const SEvent &event)
|
|||
return true;
|
||||
}
|
||||
|
||||
// This is separate from other keyboard handling so that it also works in menus.
|
||||
if (event.EventType == EET_KEY_INPUT_EVENT) {
|
||||
const KeyPress keyCode(event.KeyInput);
|
||||
if (keyCode == getKeySetting("keymap_fullscreen")) {
|
||||
if (event.KeyInput.PressedDown && !fullscreen_is_down) {
|
||||
bool fullscreen = RenderingEngine::get_raw_device()->isFullscreen();
|
||||
g_settings->setBool("fullscreen", !fullscreen);
|
||||
}
|
||||
fullscreen_is_down = event.KeyInput.PressedDown;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Let the menu handle events, if one is active.
|
||||
if (isMenuActive()) {
|
||||
if (g_touchscreengui)
|
||||
|
|
|
@ -220,6 +220,9 @@ private:
|
|||
// often changing keys, and keysListenedFor is expected
|
||||
// to change seldomly but contain lots of keys.
|
||||
KeyList keysListenedFor;
|
||||
|
||||
// Intentionally not reset by clearInput/releaseAllKeys.
|
||||
bool fullscreen_is_down = false;
|
||||
};
|
||||
|
||||
class InputHandler
|
||||
|
|
|
@ -437,15 +437,18 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d,
|
|||
m_speed.Z = 0.0f;
|
||||
}
|
||||
|
||||
if (y_diff > 0 && m_speed.Y <= 0.0f &&
|
||||
(physics_override.sneak_glitch || y_diff < BS * 0.6f)) {
|
||||
if (y_diff > 0 && m_speed.Y <= 0.0f) {
|
||||
// Move player to the maximal height when falling or when
|
||||
// the ledge is climbed on the next step.
|
||||
|
||||
// Smoothen the movement (based on 'position.Y = bmax.Y')
|
||||
position.Y += y_diff * dtime * 22.0f + BS * 0.01f;
|
||||
position.Y = std::min(position.Y, bmax.Y);
|
||||
m_speed.Y = 0.0f;
|
||||
v3f check_pos = position;
|
||||
check_pos.Y += y_diff * dtime * 22.0f + BS * 0.01f;
|
||||
if (y_diff < BS * 0.6f || (physics_override.sneak_glitch
|
||||
&& !collision_check_intersection(env, m_client, m_collisionbox, check_pos))) {
|
||||
// Smoothen the movement (based on 'position.Y = bmax.Y')
|
||||
position.Y = std::min(check_pos.Y, bmax.Y);
|
||||
m_speed.Y = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
// Allow jumping on node edges while sneaking
|
||||
|
|
|
@ -329,32 +329,6 @@ void final_color_blend(video::SColor *result,
|
|||
Mesh generation helpers
|
||||
*/
|
||||
|
||||
// This table is moved outside getNodeVertexDirs to avoid the compiler using
|
||||
// a mutex to initialize this table at runtime right in the hot path.
|
||||
// For details search the internet for "cxa_guard_acquire".
|
||||
static const v3s16 vertex_dirs_table[] = {
|
||||
// ( 1, 0, 0)
|
||||
v3s16( 1,-1, 1), v3s16( 1,-1,-1),
|
||||
v3s16( 1, 1,-1), v3s16( 1, 1, 1),
|
||||
// ( 0, 1, 0)
|
||||
v3s16( 1, 1,-1), v3s16(-1, 1,-1),
|
||||
v3s16(-1, 1, 1), v3s16( 1, 1, 1),
|
||||
// ( 0, 0, 1)
|
||||
v3s16(-1,-1, 1), v3s16( 1,-1, 1),
|
||||
v3s16( 1, 1, 1), v3s16(-1, 1, 1),
|
||||
// invalid
|
||||
v3s16(), v3s16(), v3s16(), v3s16(),
|
||||
// ( 0, 0,-1)
|
||||
v3s16( 1,-1,-1), v3s16(-1,-1,-1),
|
||||
v3s16(-1, 1,-1), v3s16( 1, 1,-1),
|
||||
// ( 0,-1, 0)
|
||||
v3s16( 1,-1, 1), v3s16(-1,-1, 1),
|
||||
v3s16(-1,-1,-1), v3s16( 1,-1,-1),
|
||||
// (-1, 0, 0)
|
||||
v3s16(-1,-1,-1), v3s16(-1,-1, 1),
|
||||
v3s16(-1, 1, 1), v3s16(-1, 1,-1)
|
||||
};
|
||||
|
||||
/*
|
||||
Gets nth node tile (0 <= n <= 5).
|
||||
*/
|
||||
|
@ -826,11 +800,16 @@ MapBlockMesh::MapBlockMesh(Client *client, MeshMakeData *data, v3s16 camera_offs
|
|||
|
||||
MapBlockMesh::~MapBlockMesh()
|
||||
{
|
||||
size_t sz = 0;
|
||||
for (scene::IMesh *m : m_mesh) {
|
||||
for (u32 i = 0; i < m->getMeshBufferCount(); i++)
|
||||
sz += m->getMeshBuffer(i)->getSize();
|
||||
m->drop();
|
||||
}
|
||||
for (MinimapMapblock *block : m_minimap_mapblocks)
|
||||
delete block;
|
||||
|
||||
porting::TrackFreedMemory(sz);
|
||||
}
|
||||
|
||||
bool MapBlockMesh::animate(bool faraway, float time, int crack,
|
||||
|
@ -1006,7 +985,6 @@ video::SColor encode_light(u16 light, u8 emissive_light)
|
|||
u8 get_solid_sides(MeshMakeData *data)
|
||||
{
|
||||
std::unordered_map<v3s16, u8> results;
|
||||
v3s16 ofs;
|
||||
v3s16 blockpos_nodes = data->m_blockpos * MAP_BLOCKSIZE;
|
||||
const NodeDefManager *ndef = data->nodedef;
|
||||
|
||||
|
|
|
@ -1008,7 +1008,6 @@ video::SMaterial ParticleManager::getMaterialForParticle(const ClientParticleTex
|
|||
video::EMFN_MODULATE_1X,
|
||||
video::EAS_TEXTURE | video::EAS_VERTEX_COLOR);
|
||||
material.BlendOperation = blendop;
|
||||
assert(texture.ref);
|
||||
material.setTexture(0, texture.ref);
|
||||
|
||||
return material;
|
||||
|
|
|
@ -207,7 +207,12 @@ RenderingEngine::RenderingEngine(IEventReceiver *receiver)
|
|||
#else
|
||||
u16 screen_w = std::max<u16>(g_settings->getU16("screen_w"), 1);
|
||||
u16 screen_h = std::max<u16>(g_settings->getU16("screen_h"), 1);
|
||||
bool window_maximized = g_settings->getBool("window_maximized");
|
||||
// If I…
|
||||
// 1. … set fullscreen = true and window_maximized = true on startup
|
||||
// 2. … set fullscreen = false later
|
||||
// on Linux with SDL, everything breaks.
|
||||
// => Don't do it.
|
||||
bool window_maximized = !fullscreen && g_settings->getBool("window_maximized");
|
||||
#endif
|
||||
|
||||
// bpp, fsaa, vsync
|
||||
|
@ -249,18 +254,40 @@ RenderingEngine::RenderingEngine(IEventReceiver *receiver)
|
|||
gui::EGST_WINDOWS_METALLIC, driver);
|
||||
m_device->getGUIEnvironment()->setSkin(skin);
|
||||
skin->drop();
|
||||
|
||||
g_settings->registerChangedCallback("fullscreen", settingChangedCallback, this);
|
||||
g_settings->registerChangedCallback("window_maximized", settingChangedCallback, this);
|
||||
}
|
||||
|
||||
RenderingEngine::~RenderingEngine()
|
||||
{
|
||||
sanity_check(s_singleton == this);
|
||||
|
||||
g_settings->deregisterChangedCallback("fullscreen", settingChangedCallback, this);
|
||||
g_settings->deregisterChangedCallback("window_maximized", settingChangedCallback, this);
|
||||
|
||||
core.reset();
|
||||
m_device->closeDevice();
|
||||
m_device->drop();
|
||||
s_singleton = nullptr;
|
||||
}
|
||||
|
||||
void RenderingEngine::settingChangedCallback(const std::string &name, void *data)
|
||||
{
|
||||
IrrlichtDevice *device = static_cast<RenderingEngine*>(data)->m_device;
|
||||
if (name == "fullscreen") {
|
||||
device->setFullscreen(g_settings->getBool("fullscreen"));
|
||||
|
||||
} else if (name == "window_maximized") {
|
||||
if (!device->isFullscreen()) {
|
||||
if (g_settings->getBool("window_maximized"))
|
||||
device->maximizeWindow();
|
||||
else
|
||||
device->restoreWindow();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
v2u32 RenderingEngine::_getWindowSize() const
|
||||
{
|
||||
if (core)
|
||||
|
@ -308,7 +335,7 @@ bool RenderingEngine::setWindowIcon()
|
|||
*/
|
||||
void RenderingEngine::draw_load_screen(const std::wstring &text,
|
||||
gui::IGUIEnvironment *guienv, ITextureSource *tsrc, float dtime,
|
||||
int percent, bool sky)
|
||||
int percent, float *indef_pos)
|
||||
{
|
||||
v2u32 screensize = getWindowSize();
|
||||
|
||||
|
@ -322,18 +349,22 @@ void RenderingEngine::draw_load_screen(const std::wstring &text,
|
|||
|
||||
auto *driver = get_video_driver();
|
||||
|
||||
if (sky) {
|
||||
driver->beginScene(true, true, RenderingEngine::MENU_SKY_COLOR);
|
||||
if (g_settings->getBool("menu_clouds")) {
|
||||
g_menuclouds->step(dtime * 3);
|
||||
g_menucloudsmgr->drawAll();
|
||||
}
|
||||
} else {
|
||||
driver->beginScene(true, true, video::SColor(255, 0, 0, 0));
|
||||
driver->setFog(RenderingEngine::MENU_SKY_COLOR);
|
||||
driver->beginScene(true, true, RenderingEngine::MENU_SKY_COLOR);
|
||||
if (g_settings->getBool("menu_clouds")) {
|
||||
g_menuclouds->step(dtime * 3);
|
||||
g_menucloudsmgr->drawAll();
|
||||
}
|
||||
|
||||
int percent_min = 0;
|
||||
int percent_max = percent;
|
||||
if (indef_pos) {
|
||||
*indef_pos = fmodf(*indef_pos + (dtime * 50.0f), 140.0f);
|
||||
percent_max = std::min((int) *indef_pos, 100);
|
||||
percent_min = std::max((int) *indef_pos - 40, 0);
|
||||
}
|
||||
// draw progress bar
|
||||
if ((percent >= 0) && (percent <= 100)) {
|
||||
if ((percent_min >= 0) && (percent_max <= 100)) {
|
||||
video::ITexture *progress_img = tsrc->getTexture("progress_bar.png");
|
||||
video::ITexture *progress_img_bg =
|
||||
tsrc->getTexture("progress_bar_bg.png");
|
||||
|
@ -364,11 +395,11 @@ void RenderingEngine::draw_load_screen(const std::wstring &text,
|
|||
0, 0, true);
|
||||
|
||||
draw2DImageFilterScaled(get_video_driver(), progress_img,
|
||||
core::rect<s32>(img_pos.X, img_pos.Y,
|
||||
img_pos.X + (percent * imgW) / 100,
|
||||
core::rect<s32>(img_pos.X + (percent_min * imgW) / 100, img_pos.Y,
|
||||
img_pos.X + (percent_max * imgW) / 100,
|
||||
img_pos.Y + imgH),
|
||||
core::rect<s32>(0, 0,
|
||||
(percent * img_size.Width) / 100,
|
||||
core::rect<s32>(percent_min * img_size.Width / 100, 0,
|
||||
percent_max * img_size.Width / 100,
|
||||
img_size.Height),
|
||||
0, 0, true);
|
||||
}
|
||||
|
|
|
@ -137,9 +137,11 @@ public:
|
|||
return m_device->getGUIEnvironment();
|
||||
}
|
||||
|
||||
// If "indef_pos" is given, the value of "percent" is ignored and an indefinite
|
||||
// progress bar is drawn.
|
||||
void draw_load_screen(const std::wstring &text,
|
||||
gui::IGUIEnvironment *guienv, ITextureSource *tsrc,
|
||||
float dtime = 0, int percent = 0, bool sky = true);
|
||||
float dtime = 0, int percent = 0, float *indef_pos = nullptr);
|
||||
|
||||
void draw_scene(video::SColor skycolor, bool show_hud,
|
||||
bool draw_wield_tool, bool draw_crosshair);
|
||||
|
@ -166,6 +168,7 @@ public:
|
|||
const bool initial_window_maximized);
|
||||
|
||||
private:
|
||||
static void settingChangedCallback(const std::string &name, void *data);
|
||||
v2u32 _getWindowSize() const;
|
||||
|
||||
std::unique_ptr<RenderingCore> core;
|
||||
|
|
|
@ -160,10 +160,10 @@ public:
|
|||
private:
|
||||
StringMap m_programs;
|
||||
|
||||
std::string readFile(const std::string &path)
|
||||
inline std::string readFile(const std::string &path)
|
||||
{
|
||||
std::string ret;
|
||||
if (!fs::ReadFile(path, ret))
|
||||
if (!fs::ReadFile(path, ret, true))
|
||||
ret.clear();
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -42,7 +42,6 @@ static v3f quantizeDirection(v3f direction, float step)
|
|||
void DirectionalLight::createSplitMatrices(const Camera *cam)
|
||||
{
|
||||
static const float COS_15_DEG = 0.965926f;
|
||||
v3f newCenter;
|
||||
v3f look = cam->getDirection().normalize();
|
||||
|
||||
// if current look direction is < 15 degrees away from the captured
|
||||
|
|
|
@ -710,7 +710,8 @@ std::string ShadowRenderer::readShaderFile(const std::string &path)
|
|||
prefix.append("#line 0\n");
|
||||
|
||||
std::string content;
|
||||
fs::ReadFile(path, content);
|
||||
if (!fs::ReadFile(path, content, true))
|
||||
return "";
|
||||
|
||||
return prefix + content;
|
||||
}
|
||||
|
|
|
@ -490,9 +490,9 @@ void Sky::update(float time_of_day, float time_brightness,
|
|||
);
|
||||
} else {
|
||||
pointcolor_moon_f = video::SColorf(
|
||||
(m_sky_params.fog_moon_tint.getRed() / 255) * pointcolor_light,
|
||||
(m_sky_params.fog_moon_tint.getGreen() / 255) * pointcolor_light,
|
||||
(m_sky_params.fog_moon_tint.getBlue() / 255) * pointcolor_light,
|
||||
(m_sky_params.fog_moon_tint.getRed() / 255.0f) * pointcolor_light,
|
||||
(m_sky_params.fog_moon_tint.getGreen() / 255.0f) * pointcolor_light,
|
||||
(m_sky_params.fog_moon_tint.getBlue() / 255.0f) * pointcolor_light,
|
||||
1
|
||||
);
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
|
||||
#include "ogg_file.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cstring> // memcpy
|
||||
|
||||
namespace sound {
|
||||
|
@ -98,18 +99,31 @@ const ov_callbacks OggVorbisBufferSource::s_ov_callbacks = {
|
|||
std::optional<OggFileDecodeInfo> RAIIOggFile::getDecodeInfo(const std::string &filename_for_logging)
|
||||
{
|
||||
OggFileDecodeInfo ret;
|
||||
|
||||
vorbis_info *pInfo = ov_info(&m_file, -1);
|
||||
if (!pInfo)
|
||||
return std::nullopt;
|
||||
|
||||
ret.name_for_logging = filename_for_logging;
|
||||
|
||||
if (pInfo->channels == 1) {
|
||||
long num_logical_bitstreams = ov_streams(&m_file);
|
||||
if (num_logical_bitstreams < 1) {
|
||||
warningstream << "Audio: Can't decode. Has not even one logical bitstream (but "
|
||||
<< num_logical_bitstreams << "): "
|
||||
<< ret.name_for_logging << std::endl;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// We only support sounds where the sample rate and channel count doesn't
|
||||
// change.
|
||||
// Read the info for the first logical bitstream:
|
||||
vorbis_info *info0 = ov_info(&m_file, 0);
|
||||
if (!info0) {
|
||||
warningstream << "Audio: Can't decode. ov_info failed for: "
|
||||
<< ret.name_for_logging << std::endl;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (info0->channels == 1) {
|
||||
ret.is_stereo = false;
|
||||
ret.format = AL_FORMAT_MONO16;
|
||||
ret.bytes_per_sample = 2;
|
||||
} else if (pInfo->channels == 2) {
|
||||
} else if (info0->channels == 2) {
|
||||
ret.is_stereo = true;
|
||||
ret.format = AL_FORMAT_STEREO16;
|
||||
ret.bytes_per_sample = 4;
|
||||
|
@ -119,10 +133,42 @@ std::optional<OggFileDecodeInfo> RAIIOggFile::getDecodeInfo(const std::string &f
|
|||
return std::nullopt;
|
||||
}
|
||||
|
||||
ret.freq = pInfo->rate;
|
||||
ret.freq = info0->rate;
|
||||
|
||||
ret.length_samples = static_cast<ALuint>(ov_pcm_total(&m_file, -1));
|
||||
ret.length_seconds = static_cast<f32>(ov_time_total(&m_file, -1));
|
||||
if (!ov_seekable(&m_file)) {
|
||||
warningstream << "Audio: Can't decode. Sound is not seekable, can't get length: "
|
||||
<< ret.name_for_logging << std::endl;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto pcm_total = ov_pcm_total(&m_file, -1);
|
||||
assert(pcm_total >= 0);
|
||||
auto time_total = ov_time_total(&m_file, -1);
|
||||
assert(time_total >= 0);
|
||||
ret.length_samples = static_cast<ALuint>(pcm_total);
|
||||
ret.length_seconds = static_cast<f32>(time_total);
|
||||
|
||||
// Check that channel count and sample rate do indeed not change
|
||||
for (int strm = 1; strm < num_logical_bitstreams; ++strm) {
|
||||
vorbis_info *info = ov_info(&m_file, strm);
|
||||
if (!info) {
|
||||
warningstream << "Audio: Can't decode. ov_info failed for: "
|
||||
<< ret.name_for_logging << std::endl;
|
||||
return std::nullopt;
|
||||
}
|
||||
if (info->channels != info0->channels) {
|
||||
warningstream << "Audio: Can't decode. Channel count changes from "
|
||||
<< info0->channels << " to " << info->channels << ": "
|
||||
<< ret.name_for_logging << std::endl;
|
||||
return std::nullopt;
|
||||
}
|
||||
if (info->rate != info0->rate) {
|
||||
warningstream << "Audio: Can't decode. Sample rate changes from "
|
||||
<< info0->rate << " to " << info->rate << ": "
|
||||
<< ret.name_for_logging << std::endl;
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -134,13 +180,19 @@ RAIIALSoundBuffer RAIIOggFile::loadBuffer(const OggFileDecodeInfo &decode_info,
|
|||
constexpr int word_size = 2; // we use s16 samples
|
||||
constexpr int word_signed = 1; // ^
|
||||
|
||||
assert(pcm_end <= decode_info.length_samples);
|
||||
assert(pcm_start <= decode_info.length_samples);
|
||||
|
||||
// seek
|
||||
if (ov_pcm_tell(&m_file) != pcm_start) {
|
||||
s64 current_pcm = ov_pcm_tell(&m_file);
|
||||
if (current_pcm != pcm_start) {
|
||||
if (ov_pcm_seek(&m_file, pcm_start) != 0) {
|
||||
warningstream << "Audio: Error decoding (could not seek) "
|
||||
warningstream << "Audio: Error decoding (could not seek): "
|
||||
<< decode_info.name_for_logging << std::endl;
|
||||
return RAIIALSoundBuffer();
|
||||
}
|
||||
assert(ov_pcm_tell(&m_file) == pcm_start);
|
||||
current_pcm = pcm_start;
|
||||
}
|
||||
|
||||
const size_t size = static_cast<size_t>(pcm_end - pcm_start)
|
||||
|
@ -149,19 +201,48 @@ RAIIALSoundBuffer RAIIOggFile::loadBuffer(const OggFileDecodeInfo &decode_info,
|
|||
std::unique_ptr<char[]> snd_buffer(new char[size]);
|
||||
|
||||
// read size bytes
|
||||
s64 last_byte_offset = current_pcm * decode_info.bytes_per_sample;
|
||||
size_t read_count = 0;
|
||||
int bitStream;
|
||||
int bitstream;
|
||||
while (read_count < size) {
|
||||
// Read up to a buffer's worth of decoded sound data
|
||||
long num_bytes = ov_read(&m_file, &snd_buffer[read_count], size - read_count,
|
||||
endian, word_size, word_signed, &bitStream);
|
||||
endian, word_size, word_signed, &bitstream);
|
||||
|
||||
if (num_bytes <= 0) {
|
||||
warningstream << "Audio: Error decoding "
|
||||
std::string_view errstr = [&]{
|
||||
switch (num_bytes) {
|
||||
case 0: return "EOF";
|
||||
case OV_HOLE: return "OV_HOLE";
|
||||
case OV_EBADLINK: return "OV_EBADLINK";
|
||||
case OV_EINVAL: return "OV_EINVAL";
|
||||
default: return "unknown error";
|
||||
}
|
||||
}();
|
||||
warningstream << "Audio: Error decoding (" << errstr << "): "
|
||||
<< decode_info.name_for_logging << std::endl;
|
||||
return RAIIALSoundBuffer();
|
||||
}
|
||||
|
||||
// This usually doesn't happen, but for some sounds ov_read seems to skip
|
||||
// some samples, see #14453.
|
||||
s64 current_byte_offset = ov_pcm_tell(&m_file) * decode_info.bytes_per_sample;
|
||||
if (current_byte_offset != last_byte_offset + num_bytes) {
|
||||
infostream << "Audio: ov_read skipped "
|
||||
<< current_byte_offset - (last_byte_offset + num_bytes)
|
||||
<< " bytes, re-seeking: "
|
||||
<< decode_info.name_for_logging << std::endl;
|
||||
s64 expected_offset = (last_byte_offset + num_bytes) / decode_info.bytes_per_sample;
|
||||
if (ov_pcm_seek(&m_file, expected_offset) != 0) {
|
||||
warningstream << "Audio: Error decoding (could not seek): "
|
||||
<< decode_info.name_for_logging << std::endl;
|
||||
return RAIIALSoundBuffer();
|
||||
}
|
||||
assert(ov_pcm_tell(&m_file) == expected_offset);
|
||||
current_byte_offset = last_byte_offset + num_bytes;
|
||||
}
|
||||
last_byte_offset = current_byte_offset;
|
||||
|
||||
read_count += num_bytes;
|
||||
}
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#cmakedefine01 USE_REDIS
|
||||
#cmakedefine01 HAVE_ENDIAN_H
|
||||
#cmakedefine01 HAVE_STRLCPY
|
||||
#cmakedefine01 HAVE_MALLOC_TRIM
|
||||
#cmakedefine01 CURSES_HAVE_CURSES_H
|
||||
#cmakedefine01 CURSES_HAVE_NCURSES_H
|
||||
#cmakedefine01 CURSES_HAVE_NCURSES_NCURSES_H
|
||||
|
|
|
@ -218,25 +218,131 @@ bool wouldCollideWithCeiling(
|
|||
return false;
|
||||
}
|
||||
|
||||
static inline void getNeighborConnectingFace(const v3s16 &p,
|
||||
const NodeDefManager *nodedef, Map *map, MapNode n, int v, int *neighbors)
|
||||
static bool add_area_node_boxes(const v3s16 min, const v3s16 max, IGameDef *gamedef,
|
||||
Environment *env, std::vector<NearbyCollisionInfo> &cinfo)
|
||||
{
|
||||
MapNode n2 = map->getNode(p);
|
||||
if (nodedef->nodeboxConnects(n, n2, v))
|
||||
*neighbors |= v;
|
||||
const auto *nodedef = gamedef->getNodeDefManager();
|
||||
bool any_position_valid = false;
|
||||
|
||||
thread_local std::vector<aabb3f> nodeboxes;
|
||||
Map *map = &env->getMap();
|
||||
|
||||
v3s16 p;
|
||||
for (p.Z = min.Z; p.Z <= max.Z; p.Z++)
|
||||
for (p.Y = min.Y; p.Y <= max.Y; p.Y++)
|
||||
for (p.X = min.X; p.X <= max.X; p.X++) {
|
||||
bool is_position_valid;
|
||||
MapNode n = map->getNode(p, &is_position_valid);
|
||||
|
||||
if (is_position_valid && n.getContent() != CONTENT_IGNORE) {
|
||||
// Object collides into walkable nodes
|
||||
|
||||
any_position_valid = true;
|
||||
const ContentFeatures &f = nodedef->get(n);
|
||||
|
||||
if (!f.walkable)
|
||||
continue;
|
||||
|
||||
// Negative bouncy may have a meaning, but we need +value here.
|
||||
int n_bouncy_value = abs(itemgroup_get(f.groups, "bouncy"));
|
||||
|
||||
u8 neighbors = n.getNeighbors(p, map);
|
||||
|
||||
nodeboxes.clear();
|
||||
n.getCollisionBoxes(nodedef, &nodeboxes, neighbors);
|
||||
|
||||
// Calculate float position only once
|
||||
v3f posf = intToFloat(p, BS);
|
||||
for (auto box : nodeboxes) {
|
||||
box.MinEdge += posf;
|
||||
box.MaxEdge += posf;
|
||||
cinfo.emplace_back(false, n_bouncy_value, p, box);
|
||||
}
|
||||
} else {
|
||||
// Collide with unloaded nodes (position invalid) and loaded
|
||||
// CONTENT_IGNORE nodes (position valid)
|
||||
aabb3f box = getNodeBox(p, BS);
|
||||
cinfo.emplace_back(true, 0, p, box);
|
||||
}
|
||||
}
|
||||
return any_position_valid;
|
||||
}
|
||||
|
||||
static void add_object_boxes(Environment *env,
|
||||
const aabb3f &box_0, f32 dtime,
|
||||
const v3f pos_f, const v3f speed_f, ActiveObject *self,
|
||||
std::vector<NearbyCollisionInfo> &cinfo)
|
||||
{
|
||||
auto process_object = [&] (ActiveObject *object) {
|
||||
if (object && object->collideWithObjects()) {
|
||||
aabb3f box;
|
||||
if (object->getCollisionBox(&box))
|
||||
cinfo.emplace_back(object, 0, box);
|
||||
}
|
||||
};
|
||||
|
||||
// Calculate distance by speed, add own extent and 1.5m of tolerance
|
||||
const f32 distance = speed_f.getLength() * dtime +
|
||||
box_0.getExtent().getLength() + 1.5f * BS;
|
||||
|
||||
#ifndef SERVER
|
||||
ClientEnvironment *c_env = dynamic_cast<ClientEnvironment*>(env);
|
||||
if (c_env) {
|
||||
std::vector<DistanceSortedActiveObject> clientobjects;
|
||||
c_env->getActiveObjects(pos_f, distance, clientobjects);
|
||||
|
||||
for (auto &clientobject : clientobjects) {
|
||||
// Do collide with everything but itself and the parent CAO
|
||||
if (!self || (self != clientobject.obj &&
|
||||
self != clientobject.obj->getParent())) {
|
||||
process_object(clientobject.obj);
|
||||
}
|
||||
}
|
||||
|
||||
// add collision with local player
|
||||
LocalPlayer *lplayer = c_env->getLocalPlayer();
|
||||
if (lplayer->getParent() == nullptr) {
|
||||
aabb3f lplayer_collisionbox = lplayer->getCollisionbox();
|
||||
v3f lplayer_pos = lplayer->getPosition();
|
||||
lplayer_collisionbox.MinEdge += lplayer_pos;
|
||||
lplayer_collisionbox.MaxEdge += lplayer_pos;
|
||||
auto *obj = (ActiveObject*) lplayer->getCAO();
|
||||
cinfo.emplace_back(obj, 0, lplayer_collisionbox);
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
ServerEnvironment *s_env = dynamic_cast<ServerEnvironment*>(env);
|
||||
if (s_env) {
|
||||
// search for objects which are not us, or we are not its parent.
|
||||
// we directly process the object in this callback to avoid useless
|
||||
// looping afterwards.
|
||||
auto include_obj_cb = [self, &process_object] (ServerActiveObject *obj) {
|
||||
if (!obj->isGone() &&
|
||||
(!self || (self != obj && self != obj->getParent()))) {
|
||||
process_object(obj);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
// nothing is put into this vector
|
||||
std::vector<ServerActiveObject*> s_objects;
|
||||
s_env->getObjectsInsideRadius(s_objects, pos_f, distance, include_obj_cb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define PROFILER_NAME(text) (dynamic_cast<ServerEnvironment*>(env) ? ("Server: " text) : ("Client: " text))
|
||||
|
||||
collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef,
|
||||
f32 pos_max_d, const aabb3f &box_0,
|
||||
f32 stepheight, f32 dtime,
|
||||
v3f *pos_f, v3f *speed_f,
|
||||
v3f accel_f, ActiveObject *self,
|
||||
bool collideWithObjects)
|
||||
bool collide_with_objects)
|
||||
{
|
||||
#define PROFILER_NAME(text) (s_env ? ("Server: " text) : ("Client: " text))
|
||||
static bool time_notification_done = false;
|
||||
Map *map = &env->getMap();
|
||||
ServerEnvironment *s_env = dynamic_cast<ServerEnvironment*>(env);
|
||||
|
||||
ScopeProfiler sp(g_profiler, PROFILER_NAME("collisionMoveSimple()"), SPT_AVG, PRECISION_MICRO);
|
||||
|
||||
|
@ -280,134 +386,36 @@ collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef,
|
|||
cinfo.clear();
|
||||
|
||||
{
|
||||
v3f minpos_f(
|
||||
MYMIN(pos_f->X, newpos_f.X),
|
||||
MYMIN(pos_f->Y, newpos_f.Y) + 0.01f * BS, // bias rounding, player often at +/-n.5
|
||||
MYMIN(pos_f->Z, newpos_f.Z)
|
||||
);
|
||||
v3f maxpos_f(
|
||||
MYMAX(pos_f->X, newpos_f.X),
|
||||
MYMAX(pos_f->Y, newpos_f.Y),
|
||||
MYMAX(pos_f->Z, newpos_f.Z)
|
||||
);
|
||||
v3s16 min = floatToInt(minpos_f + box_0.MinEdge, BS) - v3s16(1, 1, 1);
|
||||
v3s16 max = floatToInt(maxpos_f + box_0.MaxEdge, BS) + v3s16(1, 1, 1);
|
||||
v3f minpos_f(
|
||||
MYMIN(pos_f->X, newpos_f.X),
|
||||
MYMIN(pos_f->Y, newpos_f.Y) + 0.01f * BS, // bias rounding, player often at +/-n.5
|
||||
MYMIN(pos_f->Z, newpos_f.Z)
|
||||
);
|
||||
v3f maxpos_f(
|
||||
MYMAX(pos_f->X, newpos_f.X),
|
||||
MYMAX(pos_f->Y, newpos_f.Y),
|
||||
MYMAX(pos_f->Z, newpos_f.Z)
|
||||
);
|
||||
v3s16 min = floatToInt(minpos_f + box_0.MinEdge, BS) - v3s16(1, 1, 1);
|
||||
v3s16 max = floatToInt(maxpos_f + box_0.MaxEdge, BS) + v3s16(1, 1, 1);
|
||||
|
||||
const auto *nodedef = gamedef->getNodeDefManager();
|
||||
bool any_position_valid = false;
|
||||
bool any_position_valid = add_area_node_boxes(min, max, gamedef, env, cinfo);
|
||||
|
||||
thread_local std::vector<aabb3f> nodeboxes;
|
||||
|
||||
v3s16 p;
|
||||
for (p.Z = min.Z; p.Z <= max.Z; p.Z++)
|
||||
for (p.Y = min.Y; p.Y <= max.Y; p.Y++)
|
||||
for (p.X = min.X; p.X <= max.X; p.X++) {
|
||||
bool is_position_valid;
|
||||
MapNode n = map->getNode(p, &is_position_valid);
|
||||
|
||||
if (is_position_valid && n.getContent() != CONTENT_IGNORE) {
|
||||
// Object collides into walkable nodes
|
||||
|
||||
any_position_valid = true;
|
||||
const ContentFeatures &f = nodedef->get(n);
|
||||
|
||||
if (!f.walkable)
|
||||
continue;
|
||||
|
||||
// Negative bouncy may have a meaning, but we need +value here.
|
||||
int n_bouncy_value = abs(itemgroup_get(f.groups, "bouncy"));
|
||||
|
||||
u8 neighbors = n.getNeighbors(p, map);
|
||||
|
||||
nodeboxes.clear();
|
||||
n.getCollisionBoxes(nodedef, &nodeboxes, neighbors);
|
||||
|
||||
// Calculate float position only once
|
||||
v3f posf = intToFloat(p, BS);
|
||||
for (auto box : nodeboxes) {
|
||||
box.MinEdge += posf;
|
||||
box.MaxEdge += posf;
|
||||
cinfo.emplace_back(false, n_bouncy_value, p, box);
|
||||
}
|
||||
} else {
|
||||
// Collide with unloaded nodes (position invalid) and loaded
|
||||
// CONTENT_IGNORE nodes (position valid)
|
||||
aabb3f box = getNodeBox(p, BS);
|
||||
cinfo.emplace_back(true, 0, p, box);
|
||||
// Do not move if world has not loaded yet, since custom node boxes
|
||||
// are not available for collision detection.
|
||||
// This also intentionally occurs in the case of the object being positioned
|
||||
// solely on loaded CONTENT_IGNORE nodes, no matter where they come from.
|
||||
if (!any_position_valid) {
|
||||
*speed_f = v3f(0, 0, 0);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// Do not move if world has not loaded yet, since custom node boxes
|
||||
// are not available for collision detection.
|
||||
// This also intentionally occurs in the case of the object being positioned
|
||||
// solely on loaded CONTENT_IGNORE nodes, no matter where they come from.
|
||||
if (!any_position_valid) {
|
||||
*speed_f = v3f(0, 0, 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
Collect object boxes in movement range
|
||||
*/
|
||||
|
||||
auto process_object = [] (ActiveObject *object) {
|
||||
if (object && object->collideWithObjects()) {
|
||||
aabb3f box;
|
||||
if (object->getCollisionBox(&box))
|
||||
cinfo.emplace_back(object, 0, box);
|
||||
}
|
||||
};
|
||||
|
||||
if (collideWithObjects) {
|
||||
// Calculate distance by speed, add own extent and 1.5m of tolerance
|
||||
const f32 distance = speed_f->getLength() * dtime +
|
||||
box_0.getExtent().getLength() + 1.5f * BS;
|
||||
|
||||
#ifndef SERVER
|
||||
ClientEnvironment *c_env = dynamic_cast<ClientEnvironment*>(env);
|
||||
if (c_env) {
|
||||
std::vector<DistanceSortedActiveObject> clientobjects;
|
||||
c_env->getActiveObjects(*pos_f, distance, clientobjects);
|
||||
|
||||
for (auto &clientobject : clientobjects) {
|
||||
// Do collide with everything but itself and the parent CAO
|
||||
if (!self || (self != clientobject.obj &&
|
||||
self != clientobject.obj->getParent())) {
|
||||
process_object(clientobject.obj);
|
||||
}
|
||||
}
|
||||
|
||||
// add collision with local player
|
||||
LocalPlayer *lplayer = c_env->getLocalPlayer();
|
||||
if (lplayer->getParent() == nullptr) {
|
||||
aabb3f lplayer_collisionbox = lplayer->getCollisionbox();
|
||||
v3f lplayer_pos = lplayer->getPosition();
|
||||
lplayer_collisionbox.MinEdge += lplayer_pos;
|
||||
lplayer_collisionbox.MaxEdge += lplayer_pos;
|
||||
auto *obj = (ActiveObject*) lplayer->getCAO();
|
||||
cinfo.emplace_back(obj, 0, lplayer_collisionbox);
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
if (s_env) {
|
||||
// search for objects which are not us, or we are not its parent.
|
||||
// we directly process the object in this callback to avoid useless
|
||||
// looping afterwards.
|
||||
auto include_obj_cb = [self, &process_object] (ServerActiveObject *obj) {
|
||||
if (!obj->isGone() &&
|
||||
(!self || (self != obj && self != obj->getParent()))) {
|
||||
process_object(obj);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
// nothing is put into this vector
|
||||
std::vector<ServerActiveObject*> s_objects;
|
||||
s_env->getObjectsInsideRadius(s_objects, *pos_f, distance, include_obj_cb);
|
||||
}
|
||||
if (collide_with_objects) {
|
||||
add_object_boxes(env, box_0, dtime, *pos_f, *speed_f, self, cinfo);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -587,3 +595,44 @@ collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef,
|
|||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool collision_check_intersection(Environment *env, IGameDef *gamedef,
|
||||
const aabb3f &box_0, const v3f &pos_f, ActiveObject *self,
|
||||
bool collide_with_objects)
|
||||
{
|
||||
ScopeProfiler sp(g_profiler, PROFILER_NAME("collision_check_intersection()"), SPT_AVG, PRECISION_MICRO);
|
||||
|
||||
std::vector<NearbyCollisionInfo> cinfo;
|
||||
{
|
||||
v3s16 min = floatToInt(pos_f + box_0.MinEdge, BS) - v3s16(1, 1, 1);
|
||||
v3s16 max = floatToInt(pos_f + box_0.MaxEdge, BS) + v3s16(1, 1, 1);
|
||||
|
||||
bool any_position_valid = add_area_node_boxes(min, max, gamedef, env, cinfo);
|
||||
|
||||
if (!any_position_valid) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (collide_with_objects) {
|
||||
v3f speed;
|
||||
add_object_boxes(env, box_0, 0, pos_f, speed, self, cinfo);
|
||||
}
|
||||
|
||||
/*
|
||||
Collision detection
|
||||
*/
|
||||
aabb3f checkbox = box_0;
|
||||
checkbox.MinEdge += pos_f;
|
||||
checkbox.MaxEdge += pos_f;
|
||||
|
||||
/*
|
||||
Go through every node and object box
|
||||
*/
|
||||
for (const NearbyCollisionInfo &box_info : cinfo) {
|
||||
if (box_info.box.intersectsWithBox(checkbox))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -71,7 +71,12 @@ collisionMoveResult collisionMoveSimple(Environment *env,IGameDef *gamedef,
|
|||
f32 stepheight, f32 dtime,
|
||||
v3f *pos_f, v3f *speed_f,
|
||||
v3f accel_f, ActiveObject *self=NULL,
|
||||
bool collideWithObjects=true);
|
||||
bool collide_with_objects=true);
|
||||
|
||||
// check if box is in collision on actual position
|
||||
bool collision_check_intersection(Environment *env, IGameDef *gamedef,
|
||||
const aabb3f &box_0, const v3f &pos_f, ActiveObject *self = nullptr,
|
||||
bool collide_with_objects = true);
|
||||
|
||||
// Helper function:
|
||||
// Checks for collision of a moving aabbox with a static aabbox
|
||||
|
|
|
@ -26,35 +26,17 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
|
||||
ContentType getContentType(const std::string &path)
|
||||
{
|
||||
std::ifstream modpack_is((path + DIR_DELIM + "modpack.txt").c_str());
|
||||
if (modpack_is.good()) {
|
||||
modpack_is.close();
|
||||
if (fs::IsFile(path + DIR_DELIM "modpack.txt") || fs::IsFile(path + DIR_DELIM "modpack.conf"))
|
||||
return ContentType::MODPACK;
|
||||
}
|
||||
|
||||
std::ifstream modpack2_is((path + DIR_DELIM + "modpack.conf").c_str());
|
||||
if (modpack2_is.good()) {
|
||||
modpack2_is.close();
|
||||
return ContentType::MODPACK;
|
||||
}
|
||||
|
||||
std::ifstream init_is((path + DIR_DELIM + "init.lua").c_str());
|
||||
if (init_is.good()) {
|
||||
init_is.close();
|
||||
if (fs::IsFile(path + DIR_DELIM "init.lua"))
|
||||
return ContentType::MOD;
|
||||
}
|
||||
|
||||
std::ifstream game_is((path + DIR_DELIM + "game.conf").c_str());
|
||||
if (game_is.good()) {
|
||||
game_is.close();
|
||||
if (fs::IsFile(path + DIR_DELIM "game.conf"))
|
||||
return ContentType::GAME;
|
||||
}
|
||||
|
||||
std::ifstream txp_is((path + DIR_DELIM + "texture_pack.conf").c_str());
|
||||
if (txp_is.good()) {
|
||||
txp_is.close();
|
||||
if (fs::IsFile(path + DIR_DELIM "texture_pack.conf"))
|
||||
return ContentType::TXP;
|
||||
}
|
||||
|
||||
return ContentType::UNKNOWN;
|
||||
}
|
||||
|
@ -66,19 +48,19 @@ void parseContentInfo(ContentSpec &spec)
|
|||
switch (getContentType(spec.path)) {
|
||||
case ContentType::MOD:
|
||||
spec.type = "mod";
|
||||
conf_path = spec.path + DIR_DELIM + "mod.conf";
|
||||
conf_path = spec.path + DIR_DELIM "mod.conf";
|
||||
break;
|
||||
case ContentType::MODPACK:
|
||||
spec.type = "modpack";
|
||||
conf_path = spec.path + DIR_DELIM + "modpack.conf";
|
||||
conf_path = spec.path + DIR_DELIM "modpack.conf";
|
||||
break;
|
||||
case ContentType::GAME:
|
||||
spec.type = "game";
|
||||
conf_path = spec.path + DIR_DELIM + "game.conf";
|
||||
conf_path = spec.path + DIR_DELIM "game.conf";
|
||||
break;
|
||||
case ContentType::TXP:
|
||||
spec.type = "txp";
|
||||
conf_path = spec.path + DIR_DELIM + "texture_pack.conf";
|
||||
conf_path = spec.path + DIR_DELIM "texture_pack.conf";
|
||||
break;
|
||||
default:
|
||||
spec.type = "unknown";
|
||||
|
@ -124,8 +106,6 @@ void parseContentInfo(ContentSpec &spec)
|
|||
spec.textdomain = spec.name;
|
||||
|
||||
if (spec.desc.empty()) {
|
||||
std::ifstream is((spec.path + DIR_DELIM + "description.txt").c_str());
|
||||
spec.desc = std::string((std::istreambuf_iterator<char>(is)),
|
||||
std::istreambuf_iterator<char>());
|
||||
fs::ReadFile(spec.path + DIR_DELIM + "description.txt", spec.desc);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
#include "filesys.h"
|
||||
#include "gettext.h"
|
||||
#include "exceptions.h"
|
||||
|
||||
#include "util/numeric.h"
|
||||
#include <optional>
|
||||
#include <algorithm>
|
||||
|
||||
std::string ModConfiguration::getUnsatisfiedModsError() const
|
||||
{
|
||||
|
@ -115,6 +117,9 @@ void ModConfiguration::addGameMods(const SubgameSpec &gamespec)
|
|||
std::string game_virtual_path;
|
||||
game_virtual_path.append("games/").append(gamespec.id).append("/mods");
|
||||
addModsInPath(gamespec.gamemods_path, game_virtual_path);
|
||||
|
||||
m_first_mod = gamespec.first_mod;
|
||||
m_last_mod = gamespec.last_mod;
|
||||
}
|
||||
|
||||
void ModConfiguration::addModsFromConfig(
|
||||
|
@ -220,31 +225,77 @@ void ModConfiguration::checkConflictsAndDeps()
|
|||
|
||||
void ModConfiguration::resolveDependencies()
|
||||
{
|
||||
// Step 1: Compile a list of the mod names we're working with
|
||||
// Compile a list of the mod names we're working with
|
||||
std::set<std::string> modnames;
|
||||
for (const ModSpec &mod : m_unsatisfied_mods) {
|
||||
modnames.insert(mod.name);
|
||||
std::optional<ModSpec> first_mod_spec, last_mod_spec;
|
||||
for (ModSpec &mod : m_unsatisfied_mods) {
|
||||
if (mod.name == m_first_mod) {
|
||||
first_mod_spec = mod;
|
||||
} else if (mod.name == m_last_mod) {
|
||||
last_mod_spec = mod;
|
||||
} else {
|
||||
modnames.insert(mod.name);
|
||||
}
|
||||
}
|
||||
|
||||
// Step 2: get dependencies (including optional dependencies)
|
||||
// Optionally shuffle unsatisfied mods so non declared depends get found by their devs
|
||||
if (g_settings->getBool("random_mod_load_order")) {
|
||||
MyRandGenerator rg;
|
||||
std::shuffle(m_unsatisfied_mods.begin(),
|
||||
m_unsatisfied_mods.end(),
|
||||
rg
|
||||
);
|
||||
}
|
||||
|
||||
// Check for presence of first and last mod
|
||||
if (!m_first_mod.empty() && !first_mod_spec.has_value())
|
||||
throw ModError("The mod specified as first by the game was not found.");
|
||||
if (!m_last_mod.empty() && !last_mod_spec.has_value())
|
||||
throw ModError("The mod specified as last by the game was not found.");
|
||||
|
||||
// Check and add first mod
|
||||
if (first_mod_spec.has_value()) {
|
||||
// Dependencies are not allowed for first mod
|
||||
if (!first_mod_spec->depends.empty() || !first_mod_spec->optdepends.empty())
|
||||
throw ModError("Mod specified by first_mod cannot have dependencies");
|
||||
|
||||
m_sorted_mods.push_back(*first_mod_spec);
|
||||
}
|
||||
|
||||
// Get dependencies (including optional dependencies)
|
||||
// of each mod, split mods into satisfied and unsatisfied
|
||||
std::vector<ModSpec> satisfied;
|
||||
std::list<ModSpec> unsatisfied;
|
||||
for (ModSpec mod : m_unsatisfied_mods) {
|
||||
if (mod.name == m_first_mod || mod.name == m_last_mod)
|
||||
continue; // skip, handled separately
|
||||
|
||||
mod.unsatisfied_depends = mod.depends;
|
||||
// check which optional dependencies actually exist
|
||||
for (const std::string &optdep : mod.optdepends) {
|
||||
if (modnames.count(optdep) != 0)
|
||||
mod.unsatisfied_depends.insert(optdep);
|
||||
}
|
||||
mod.unsatisfied_depends.erase(m_first_mod); // first is already satisfied
|
||||
|
||||
if (last_mod_spec.has_value() && mod.unsatisfied_depends.count(last_mod_spec->name) != 0) {
|
||||
throw ModError("Impossible to depend on the mod specified by last_mod");
|
||||
}
|
||||
// if a mod has no depends it is initially satisfied
|
||||
if (mod.unsatisfied_depends.empty())
|
||||
if (mod.unsatisfied_depends.empty()) {
|
||||
satisfied.push_back(mod);
|
||||
else
|
||||
} else {
|
||||
unsatisfied.push_back(mod);
|
||||
}
|
||||
}
|
||||
|
||||
// Step 3: mods without unmet dependencies can be appended to
|
||||
// All dependencies of the last mod are initially unsatisfied
|
||||
if (last_mod_spec.has_value()) {
|
||||
last_mod_spec->unsatisfied_depends = last_mod_spec->depends;
|
||||
last_mod_spec->unsatisfied_depends.erase(m_first_mod);
|
||||
}
|
||||
|
||||
// Mods without unmet dependencies can be appended to
|
||||
// the sorted list.
|
||||
while (!satisfied.empty()) {
|
||||
ModSpec mod = satisfied.back();
|
||||
|
@ -260,8 +311,18 @@ void ModConfiguration::resolveDependencies()
|
|||
++it;
|
||||
}
|
||||
}
|
||||
if (last_mod_spec.has_value())
|
||||
last_mod_spec->unsatisfied_depends.erase(mod.name);
|
||||
}
|
||||
|
||||
// Step 4: write back list of unsatisfied mods
|
||||
// Write back list of unsatisfied mods
|
||||
m_unsatisfied_mods.assign(unsatisfied.begin(), unsatisfied.end());
|
||||
|
||||
// Check and add last mod
|
||||
if (last_mod_spec.has_value()) {
|
||||
if (last_mod_spec->unsatisfied_depends.empty())
|
||||
m_sorted_mods.push_back(*last_mod_spec);
|
||||
else
|
||||
m_unsatisfied_mods.push_back(*last_mod_spec);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -87,6 +87,9 @@ public:
|
|||
void checkConflictsAndDeps();
|
||||
|
||||
private:
|
||||
std::string m_first_mod; // "" <=> no mod
|
||||
std::string m_last_mod; // "" <=> no mod
|
||||
|
||||
std::vector<ModSpec> m_sorted_mods;
|
||||
|
||||
/**
|
||||
|
|
|
@ -94,6 +94,50 @@ std::string getSubgamePathEnv()
|
|||
return "";
|
||||
}
|
||||
|
||||
static SubgameSpec getSubgameSpec(const std::string &game_id,
|
||||
const std::string &game_path,
|
||||
const std::unordered_map<std::string, std::string> &mods_paths,
|
||||
const std::string &menuicon_path)
|
||||
{
|
||||
const auto gamemods_path = game_path + DIR_DELIM + "mods";
|
||||
// Get meta
|
||||
const std::string conf_path = game_path + DIR_DELIM + "game.conf";
|
||||
Settings conf;
|
||||
conf.readConfigFile(conf_path.c_str());
|
||||
|
||||
std::string game_title;
|
||||
if (conf.exists("title"))
|
||||
game_title = conf.get("title");
|
||||
else if (conf.exists("name"))
|
||||
game_title = conf.get("name");
|
||||
else
|
||||
game_title = game_id;
|
||||
|
||||
std::string game_author;
|
||||
if (conf.exists("author"))
|
||||
game_author = conf.get("author");
|
||||
|
||||
int game_release = 0;
|
||||
if (conf.exists("release"))
|
||||
game_release = conf.getS32("release");
|
||||
|
||||
std::string first_mod;
|
||||
if (conf.exists("first_mod"))
|
||||
first_mod = conf.get("first_mod");
|
||||
|
||||
std::string last_mod;
|
||||
if (conf.exists("last_mod"))
|
||||
last_mod = conf.get("last_mod");
|
||||
|
||||
SubgameSpec spec(game_id, game_path, gamemods_path, mods_paths, game_title,
|
||||
menuicon_path, game_author, game_release, first_mod, last_mod);
|
||||
|
||||
if (conf.exists("name") && !conf.exists("title"))
|
||||
spec.deprecation_msgs.push_back("\"name\" setting in game.conf is deprecated, please use \"title\" instead");
|
||||
|
||||
return spec;
|
||||
}
|
||||
|
||||
SubgameSpec findSubgame(const std::string &id)
|
||||
{
|
||||
if (id.empty())
|
||||
|
@ -137,8 +181,6 @@ SubgameSpec findSubgame(const std::string &id)
|
|||
if (game_path.empty())
|
||||
return SubgameSpec();
|
||||
|
||||
std::string gamemod_path = game_path + DIR_DELIM + "mods";
|
||||
|
||||
// Find mod directories
|
||||
std::unordered_map<std::string, std::string> mods_paths;
|
||||
mods_paths["mods"] = user + DIR_DELIM + "mods";
|
||||
|
@ -149,40 +191,13 @@ SubgameSpec findSubgame(const std::string &id)
|
|||
mods_paths[fs::AbsolutePath(mod_path)] = mod_path;
|
||||
}
|
||||
|
||||
// Get meta
|
||||
std::string conf_path = game_path + DIR_DELIM + "game.conf";
|
||||
Settings conf;
|
||||
conf.readConfigFile(conf_path.c_str());
|
||||
|
||||
std::string game_title;
|
||||
if (conf.exists("title"))
|
||||
game_title = conf.get("title");
|
||||
else if (conf.exists("name"))
|
||||
game_title = conf.get("name");
|
||||
else
|
||||
game_title = id;
|
||||
|
||||
std::string game_author;
|
||||
if (conf.exists("author"))
|
||||
game_author = conf.get("author");
|
||||
|
||||
int game_release = 0;
|
||||
if (conf.exists("release"))
|
||||
game_release = conf.getS32("release");
|
||||
|
||||
std::string menuicon_path;
|
||||
#ifndef SERVER
|
||||
menuicon_path = getImagePath(
|
||||
game_path + DIR_DELIM + "menu" + DIR_DELIM + "icon.png");
|
||||
#endif
|
||||
|
||||
SubgameSpec spec(id, game_path, gamemod_path, mods_paths, game_title,
|
||||
menuicon_path, game_author, game_release);
|
||||
|
||||
if (conf.exists("name") && !conf.exists("title"))
|
||||
spec.deprecation_msgs.push_back("\"name\" setting in game.conf is deprecated, please use \"title\" instead");
|
||||
|
||||
return spec;
|
||||
return getSubgameSpec(id, game_path, mods_paths, menuicon_path);
|
||||
}
|
||||
|
||||
SubgameSpec findWorldSubgame(const std::string &world_path)
|
||||
|
@ -190,25 +205,8 @@ SubgameSpec findWorldSubgame(const std::string &world_path)
|
|||
std::string world_gameid = getWorldGameId(world_path, true);
|
||||
// See if world contains an embedded game; if so, use it.
|
||||
std::string world_gamepath = world_path + DIR_DELIM + "game";
|
||||
if (fs::PathExists(world_gamepath)) {
|
||||
SubgameSpec gamespec;
|
||||
gamespec.id = world_gameid;
|
||||
gamespec.path = world_gamepath;
|
||||
gamespec.gamemods_path = world_gamepath + DIR_DELIM + "mods";
|
||||
|
||||
Settings conf;
|
||||
std::string conf_path = world_gamepath + DIR_DELIM + "game.conf";
|
||||
conf.readConfigFile(conf_path.c_str());
|
||||
|
||||
if (conf.exists("title"))
|
||||
gamespec.title = conf.get("title");
|
||||
else if (conf.exists("name"))
|
||||
gamespec.title = conf.get("name");
|
||||
else
|
||||
gamespec.title = world_gameid;
|
||||
|
||||
return gamespec;
|
||||
}
|
||||
if (fs::PathExists(world_gamepath))
|
||||
return getSubgameSpec(world_gameid, world_gamepath, {}, "");
|
||||
return findSubgame(world_gameid);
|
||||
}
|
||||
|
||||
|
|
|
@ -32,6 +32,8 @@ struct SubgameSpec
|
|||
std::string title;
|
||||
std::string author;
|
||||
int release;
|
||||
std::string first_mod; // "" <=> no mod
|
||||
std::string last_mod; // "" <=> no mod
|
||||
std::string path;
|
||||
std::string gamemods_path;
|
||||
|
||||
|
@ -49,10 +51,16 @@ struct SubgameSpec
|
|||
const std::unordered_map<std::string, std::string> &addon_mods_paths = {},
|
||||
const std::string &title = "",
|
||||
const std::string &menuicon_path = "",
|
||||
const std::string &author = "", int release = 0) :
|
||||
const std::string &author = "", int release = 0,
|
||||
const std::string &first_mod = "",
|
||||
const std::string &last_mod = "") :
|
||||
id(id),
|
||||
title(title), author(author), release(release), path(path),
|
||||
gamemods_path(gamemods_path), addon_mods_paths(addon_mods_paths),
|
||||
title(title), author(author), release(release),
|
||||
first_mod(first_mod),
|
||||
last_mod(last_mod),
|
||||
path(path),
|
||||
gamemods_path(gamemods_path),
|
||||
addon_mods_paths(addon_mods_paths),
|
||||
menuicon_path(menuicon_path)
|
||||
{
|
||||
}
|
||||
|
|
|
@ -165,11 +165,9 @@ void PlayerDatabaseFiles::savePlayer(RemotePlayer *player)
|
|||
}
|
||||
|
||||
// Open and deserialize file to check player name
|
||||
std::ifstream is(path.c_str(), std::ios_base::binary);
|
||||
if (!is.good()) {
|
||||
errorstream << "Failed to open " << path << std::endl;
|
||||
auto is = open_ifstream(path.c_str(), true);
|
||||
if (!is.good())
|
||||
return;
|
||||
}
|
||||
|
||||
deSerialize(&testplayer, is, path, NULL);
|
||||
is.close();
|
||||
|
@ -205,7 +203,7 @@ bool PlayerDatabaseFiles::removePlayer(const std::string &name)
|
|||
RemotePlayer temp_player("", NULL);
|
||||
for (u32 i = 0; i < PLAYER_FILE_ALTERNATE_TRIES; i++) {
|
||||
// Open file and deserialize
|
||||
std::ifstream is(path.c_str(), std::ios_base::binary);
|
||||
auto is = open_ifstream(path.c_str(), false);
|
||||
if (!is.good())
|
||||
continue;
|
||||
|
||||
|
@ -231,7 +229,7 @@ bool PlayerDatabaseFiles::loadPlayer(RemotePlayer *player, PlayerSAO *sao)
|
|||
const std::string player_to_load = player->getName();
|
||||
for (u32 i = 0; i < PLAYER_FILE_ALTERNATE_TRIES; i++) {
|
||||
// Open file and deserialize
|
||||
std::ifstream is(path.c_str(), std::ios_base::binary);
|
||||
auto is = open_ifstream(path.c_str(), false);
|
||||
if (!is.good())
|
||||
continue;
|
||||
|
||||
|
@ -260,7 +258,7 @@ void PlayerDatabaseFiles::listPlayers(std::vector<std::string> &res)
|
|||
|
||||
const std::string &filename = it->name;
|
||||
std::string full_path = m_savedir + DIR_DELIM + filename;
|
||||
std::ifstream is(full_path.c_str(), std::ios_base::binary);
|
||||
auto is = open_ifstream(full_path.c_str(), true);
|
||||
if (!is.good())
|
||||
continue;
|
||||
|
||||
|
@ -332,7 +330,7 @@ void AuthDatabaseFiles::reload()
|
|||
bool AuthDatabaseFiles::readAuthFile()
|
||||
{
|
||||
std::string path = m_savedir + DIR_DELIM + "auth.txt";
|
||||
std::ifstream file(path, std::ios::binary);
|
||||
auto file = open_ifstream(path.c_str(), false);
|
||||
if (!file.good()) {
|
||||
return false;
|
||||
}
|
||||
|
@ -528,8 +526,7 @@ Json::Value *ModStorageDatabaseFiles::getOrCreateJson(const std::string &modname
|
|||
|
||||
std::string path = m_storage_dir + DIR_DELIM + modname;
|
||||
if (fs::PathExists(path)) {
|
||||
std::ifstream is(path.c_str(), std::ios_base::binary);
|
||||
|
||||
auto is = open_ifstream(path.c_str(), true);
|
||||
Json::CharReaderBuilder builder;
|
||||
builder.settings_["collectComments"] = false;
|
||||
std::string errs;
|
||||
|
|
|
@ -32,6 +32,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
#include <hiredis.h>
|
||||
#include <cassert>
|
||||
|
||||
#if VERSION_MAJOR > 5 || VERSION_MINOR > 9
|
||||
#define DEPRECATION_PERIOD_OVER
|
||||
#endif
|
||||
|
||||
Database_Redis::Database_Redis(Settings &conf)
|
||||
{
|
||||
|
@ -66,6 +69,14 @@ Database_Redis::Database_Redis(Settings &conf)
|
|||
}
|
||||
freeReplyObject(reply);
|
||||
}
|
||||
|
||||
warningstream << "/!\\ You are using the deprecated redis backend. "
|
||||
#ifdef DEPRECATION_PERIOD_OVER
|
||||
<< "This backend is only still supported for migrations. /!\\\n"
|
||||
#else
|
||||
<< "This backend will become read-only in the next release. /!\\\n"
|
||||
#endif
|
||||
<< "Please migrate to SQLite3 or PostgreSQL instead." << std::endl;
|
||||
}
|
||||
|
||||
Database_Redis::~Database_Redis()
|
||||
|
@ -73,16 +84,22 @@ Database_Redis::~Database_Redis()
|
|||
redisFree(ctx);
|
||||
}
|
||||
|
||||
void Database_Redis::beginSave() {
|
||||
void Database_Redis::beginSave()
|
||||
{
|
||||
#ifdef DEPRECATION_PERIOD_OVER
|
||||
throw DatabaseException("Redis backend is read-only, see deprecation notice.");
|
||||
#else
|
||||
redisReply *reply = static_cast<redisReply *>(redisCommand(ctx, "MULTI"));
|
||||
if (!reply) {
|
||||
throw DatabaseException(std::string(
|
||||
"Redis command 'MULTI' failed: ") + ctx->errstr);
|
||||
}
|
||||
freeReplyObject(reply);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Database_Redis::endSave() {
|
||||
void Database_Redis::endSave()
|
||||
{
|
||||
redisReply *reply = static_cast<redisReply *>(redisCommand(ctx, "EXEC"));
|
||||
if (!reply) {
|
||||
throw DatabaseException(std::string(
|
||||
|
|
|
@ -172,17 +172,17 @@ void set_default_settings()
|
|||
settings->setDefault("keymap_toggle_block_bounds", "");
|
||||
settings->setDefault("keymap_toggle_hud", "KEY_F1");
|
||||
settings->setDefault("keymap_toggle_chat", "KEY_F2");
|
||||
#ifndef NDEBUG
|
||||
settings->setDefault("keymap_toggle_fog", "KEY_F3");
|
||||
#ifndef NDEBUG
|
||||
settings->setDefault("keymap_toggle_update_camera", "KEY_F4");
|
||||
#else
|
||||
settings->setDefault("keymap_toggle_fog", "");
|
||||
settings->setDefault("keymap_toggle_update_camera", "");
|
||||
#endif
|
||||
settings->setDefault("keymap_toggle_debug", "KEY_F5");
|
||||
settings->setDefault("keymap_toggle_profiler", "KEY_F6");
|
||||
settings->setDefault("keymap_camera_mode", "KEY_KEY_C");
|
||||
settings->setDefault("keymap_screenshot", "KEY_F12");
|
||||
settings->setDefault("keymap_fullscreen", "KEY_F11");
|
||||
settings->setDefault("keymap_increase_viewing_range_min", "+");
|
||||
settings->setDefault("keymap_decrease_viewing_range_min", "-");
|
||||
settings->setDefault("keymap_slot1", "KEY_KEY_1");
|
||||
|
@ -283,7 +283,7 @@ void set_default_settings()
|
|||
settings->setDefault("enable_3d_clouds", "true");
|
||||
settings->setDefault("cloud_radius", "12");
|
||||
settings->setDefault("menu_clouds", "true");
|
||||
settings->setDefault("opaque_water", "false");
|
||||
settings->setDefault("translucent_liquids", "true");
|
||||
settings->setDefault("console_height", "0.6");
|
||||
settings->setDefault("console_color", "(0,0,0)");
|
||||
settings->setDefault("console_alpha", "200");
|
||||
|
@ -423,6 +423,11 @@ void set_default_settings()
|
|||
// Server
|
||||
settings->setDefault("disable_escape_sequences", "false");
|
||||
settings->setDefault("strip_color_codes", "false");
|
||||
#ifndef NDEBUG
|
||||
settings->setDefault("random_mod_load_order", "true");
|
||||
#else
|
||||
settings->setDefault("random_mod_load_order", "false");
|
||||
#endif
|
||||
#if USE_PROMETHEUS
|
||||
settings->setDefault("prometheus_listener_address", "127.0.0.1:30000");
|
||||
#endif
|
||||
|
|
|
@ -25,6 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
#include <cstring>
|
||||
#include <cerrno>
|
||||
#include <fstream>
|
||||
#include <atomic>
|
||||
#include "log.h"
|
||||
#include "config.h"
|
||||
#include "porting.h"
|
||||
|
@ -41,6 +42,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
#endif
|
||||
#endif
|
||||
|
||||
// Error from last OS call as string
|
||||
#ifdef _WIN32
|
||||
#define LAST_OS_ERROR() porting::ConvertError(GetLastError())
|
||||
#else
|
||||
#define LAST_OS_ERROR() strerror(errno)
|
||||
#endif
|
||||
|
||||
namespace fs
|
||||
{
|
||||
|
||||
|
@ -847,42 +855,48 @@ const char *GetFilenameFromPath(const char *path)
|
|||
return filename ? filename + 1 : path;
|
||||
}
|
||||
|
||||
// Note: this is not safe if two MT processes try this at the same time (FIXME?)
|
||||
bool safeWriteToFile(const std::string &path, std::string_view content)
|
||||
{
|
||||
std::string tmp_file = path + ".~mt";
|
||||
// Prevent two threads from writing to the same temporary file
|
||||
static std::atomic<u16> g_file_counter;
|
||||
const std::string tmp_file = path + ".~mt" + itos(g_file_counter.fetch_add(1));
|
||||
|
||||
// Write to a tmp file
|
||||
bool tmp_success = false;
|
||||
// Write data to a temporary file
|
||||
std::string write_error;
|
||||
|
||||
#ifdef _WIN32
|
||||
// We've observed behavior suggesting that the MSVC implementation of std::ofstream::flush doesn't
|
||||
// actually flush, so we use win32 APIs.
|
||||
HANDLE tmp_handle = CreateFile(
|
||||
tmp_file.c_str(), GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
if (tmp_handle == INVALID_HANDLE_VALUE) {
|
||||
HANDLE handle = CreateFile(tmp_file.c_str(), GENERIC_WRITE, 0, nullptr,
|
||||
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
if (handle == INVALID_HANDLE_VALUE) {
|
||||
errorstream << "Failed to open file: " << LAST_OS_ERROR() << std::endl;
|
||||
return false;
|
||||
}
|
||||
DWORD bytes_written;
|
||||
tmp_success = (WriteFile(tmp_handle, content.data(), content.size(), &bytes_written, nullptr) &&
|
||||
FlushFileBuffers(tmp_handle));
|
||||
CloseHandle(tmp_handle);
|
||||
if (!WriteFile(handle, content.data(), content.size(), &bytes_written, nullptr))
|
||||
write_error = LAST_OS_ERROR();
|
||||
else if (!FlushFileBuffers(handle))
|
||||
write_error = LAST_OS_ERROR();
|
||||
CloseHandle(handle);
|
||||
#else
|
||||
std::ofstream os(tmp_file.c_str(), std::ios::binary);
|
||||
if (!os.good()) {
|
||||
auto os = open_ofstream(tmp_file.c_str(), true);
|
||||
if (!os.good())
|
||||
return false;
|
||||
}
|
||||
os << content;
|
||||
os.flush();
|
||||
os << content << std::flush;
|
||||
os.close();
|
||||
tmp_success = !os.fail();
|
||||
if (os.fail())
|
||||
write_error = "iostream fail";
|
||||
#endif
|
||||
|
||||
if (!tmp_success) {
|
||||
if (!write_error.empty()) {
|
||||
errorstream << "Failed to write file: " << write_error << std::endl;
|
||||
remove(tmp_file.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
bool rename_success = false;
|
||||
std::string rename_error;
|
||||
|
||||
// Move the finished temporary file over the real file
|
||||
#ifdef _WIN32
|
||||
|
@ -890,22 +904,25 @@ bool safeWriteToFile(const std::string &path, std::string_view content)
|
|||
// to query the file. This can make the move file call below fail.
|
||||
// We retry up to 5 times, with a 1ms sleep between, before we consider the whole operation failed
|
||||
for (int attempt = 0; attempt < 5; attempt++) {
|
||||
rename_success = MoveFileEx(tmp_file.c_str(), path.c_str(),
|
||||
auto ok = MoveFileEx(tmp_file.c_str(), path.c_str(),
|
||||
MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH);
|
||||
if (rename_success)
|
||||
if (ok) {
|
||||
rename_error.clear();
|
||||
break;
|
||||
}
|
||||
rename_error = LAST_OS_ERROR();
|
||||
sleep_ms(1);
|
||||
}
|
||||
#else
|
||||
// On POSIX compliant systems rename() is specified to be able to swap the
|
||||
// file in place of the destination file, making this a truly error-proof
|
||||
// transaction.
|
||||
rename_success = rename(tmp_file.c_str(), path.c_str()) == 0;
|
||||
if (rename(tmp_file.c_str(), path.c_str()) != 0)
|
||||
rename_error = LAST_OS_ERROR();
|
||||
#endif
|
||||
if (!rename_success) {
|
||||
warningstream << "Failed to write to file: " << path.c_str() << std::endl;
|
||||
// Remove the temporary file because moving it over the target file
|
||||
// failed.
|
||||
|
||||
if (!rename_error.empty()) {
|
||||
errorstream << "Failed to overwrite \"" << path << "\": " << rename_error << std::endl;
|
||||
remove(tmp_file.c_str());
|
||||
return false;
|
||||
}
|
||||
|
@ -932,6 +949,8 @@ bool extractZipFile(io::IFileSystem *fs, const char *filename, const std::string
|
|||
}
|
||||
|
||||
irr_ptr<io::IFileArchive> opened_zip(zip_loader->createArchive(filename, false, false));
|
||||
if (!opened_zip)
|
||||
return false;
|
||||
const io::IFileList* files_in_zip = opened_zip->getFileList();
|
||||
|
||||
for (u32 i = 0; i < files_in_zip->getFileCount(); i++) {
|
||||
|
@ -947,7 +966,7 @@ bool extractZipFile(io::IFileSystem *fs, const char *filename, const std::string
|
|||
|
||||
irr_ptr<io::IReadFile> toread(opened_zip->createAndOpenFile(i));
|
||||
|
||||
std::ofstream os(fullpath.c_str(), std::ios::binary);
|
||||
auto os = open_ofstream(fullpath.c_str(), true);
|
||||
if (!os.good())
|
||||
return false;
|
||||
|
||||
|
@ -974,12 +993,11 @@ bool extractZipFile(io::IFileSystem *fs, const char *filename, const std::string
|
|||
}
|
||||
#endif
|
||||
|
||||
bool ReadFile(const std::string &path, std::string &out)
|
||||
bool ReadFile(const std::string &path, std::string &out, bool log_error)
|
||||
{
|
||||
std::ifstream is(path, std::ios::binary | std::ios::ate);
|
||||
if (!is.good()) {
|
||||
auto is = open_ifstream(path.c_str(), log_error, std::ios::ate);
|
||||
if (!is.good())
|
||||
return false;
|
||||
}
|
||||
|
||||
auto size = is.tellg();
|
||||
out.resize(size);
|
||||
|
@ -994,4 +1012,22 @@ bool Rename(const std::string &from, const std::string &to)
|
|||
return rename(from.c_str(), to.c_str()) == 0;
|
||||
}
|
||||
|
||||
bool OpenStream(std::filebuf &stream, const char *filename,
|
||||
std::ios::openmode mode, bool log_error, bool log_warn)
|
||||
{
|
||||
assert((mode & std::ios::in) || (mode & std::ios::out));
|
||||
assert(!stream.is_open());
|
||||
// C++ dropped the ball hard for file opening error handling, there's not even
|
||||
// an implementation-defined mechanism for returning any kind of error code or message.
|
||||
if (!stream.open(filename, mode)) {
|
||||
if (log_warn || log_error) {
|
||||
std::string msg = LAST_OS_ERROR();
|
||||
(log_error ? errorstream : warningstream)
|
||||
<< "Failed to open \"" << filename << "\": " << msg << std::endl;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace fs
|
||||
|
|
|
@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
#include <fstream>
|
||||
|
||||
#ifdef _WIN32
|
||||
#define DIR_DELIM "\\"
|
||||
|
@ -148,14 +149,74 @@ std::string AbsolutePath(const std::string &path);
|
|||
// delimiter is found.
|
||||
const char *GetFilenameFromPath(const char *path);
|
||||
|
||||
// Replace the content of a file on disk in a way that is safe from
|
||||
// corruption/truncation on a crash.
|
||||
// logs and returns false on error
|
||||
bool safeWriteToFile(const std::string &path, std::string_view content);
|
||||
|
||||
#ifndef SERVER
|
||||
bool extractZipFile(irr::io::IFileSystem *fs, const char *filename, const std::string &destination);
|
||||
#endif
|
||||
|
||||
bool ReadFile(const std::string &path, std::string &out);
|
||||
bool ReadFile(const std::string &path, std::string &out, bool log_error = false);
|
||||
|
||||
bool Rename(const std::string &from, const std::string &to);
|
||||
|
||||
/**
|
||||
* Open a file buffer with error handling, commonly used with `std::fstream.rdbuf()`.
|
||||
*
|
||||
* @param stream stream references, must not already be open
|
||||
* @param filename filename to open
|
||||
* @param mode mode bits (used as-is)
|
||||
* @param log_error log failure to errorstream?
|
||||
* @param log_warn log failure to warningstream?
|
||||
* @return true if success
|
||||
*/
|
||||
bool OpenStream(std::filebuf &stream, const char *filename,
|
||||
std::ios::openmode mode, bool log_error, bool log_warn);
|
||||
|
||||
} // namespace fs
|
||||
|
||||
// outside of namespace for convenience:
|
||||
|
||||
/**
|
||||
* Helper function to open an output file stream (= writing).
|
||||
*
|
||||
* For compatibility with fopen() binary mode and truncate are always set.
|
||||
* Use `fs::OpenStream` for more control.
|
||||
* @param name file name
|
||||
* @param log if true, failure to open the file will be logged to errorstream
|
||||
* @param mode additional mode bits (e.g. std::ios::app)
|
||||
* @return file stream, will be !good in case of error
|
||||
*/
|
||||
inline std::ofstream open_ofstream(const char *name, bool log,
|
||||
std::ios::openmode mode = std::ios::openmode())
|
||||
{
|
||||
mode |= std::ios::out | std::ios::binary;
|
||||
if (!(mode & std::ios::app))
|
||||
mode |= std::ios::trunc;
|
||||
std::ofstream ofs;
|
||||
if (!fs::OpenStream(*ofs.rdbuf(), name, mode, log, false))
|
||||
ofs.setstate(std::ios::failbit);
|
||||
return ofs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to open an input file stream (= reading).
|
||||
*
|
||||
* For compatibility with fopen() binary mode is always set.
|
||||
* Use `fs::OpenStream` for more control.
|
||||
* @param name file name
|
||||
* @param log if true, failure to open the file will be logged to errorstream
|
||||
* @param mode additional mode bits (e.g. std::ios::ate)
|
||||
* @return file stream, will be !good in case of error
|
||||
*/
|
||||
inline std::ifstream open_ifstream(const char *name, bool log,
|
||||
std::ios::openmode mode = std::ios::openmode())
|
||||
{
|
||||
mode |= std::ios::in | std::ios::binary;
|
||||
std::ifstream ifs;
|
||||
if (!fs::OpenStream(*ifs.rdbuf(), name, mode, log, false))
|
||||
ifs.setstate(std::ios::failbit);
|
||||
return ifs;
|
||||
}
|
||||
|
|
|
@ -147,18 +147,15 @@ static void MSVC_LocaleWorkaround(int argc, char* argv[])
|
|||
exit(0);
|
||||
// NOTREACHED
|
||||
} else {
|
||||
char buffer[1024];
|
||||
auto e = GetLastError();
|
||||
|
||||
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(),
|
||||
MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT), buffer,
|
||||
sizeof(buffer) - 1, NULL);
|
||||
|
||||
errorstream << "*******************************************************" << std::endl;
|
||||
errorstream << "CMD: " << app_name << std::endl;
|
||||
errorstream << "Failed to restart with current locale: " << std::endl;
|
||||
errorstream << buffer;
|
||||
errorstream << "Expect language to be broken!" << std::endl;
|
||||
errorstream << "*******************************************************" << std::endl;
|
||||
errorstream
|
||||
<< "*******************************************************" << '\n'
|
||||
<< "CMD: " << app_name << '\n'
|
||||
<< "Failed to restart with current locale: "
|
||||
<< porting::ConvertError(e) << '\n'
|
||||
<< "Expect language to be broken!" << '\n'
|
||||
<< "*******************************************************" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -193,6 +193,8 @@ GUIEngine::GUIEngine(JoystickController *joystick,
|
|||
|
||||
m_script = std::make_unique<MainMenuScripting>(this);
|
||||
|
||||
g_settings->registerChangedCallback("fullscreen", fullscreenChangedCallback, this);
|
||||
|
||||
try {
|
||||
m_script->setMainMenuData(&m_data->script_data);
|
||||
m_data->script_data.errormessage.clear();
|
||||
|
@ -319,7 +321,8 @@ void GUIEngine::run()
|
|||
g_settings->getU16("screen_w"),
|
||||
g_settings->getU16("screen_h")
|
||||
);
|
||||
const bool initial_window_maximized = g_settings->getBool("window_maximized");
|
||||
const bool initial_window_maximized = !g_settings->getBool("fullscreen") &&
|
||||
g_settings->getBool("window_maximized");
|
||||
|
||||
FpsControl fps_control;
|
||||
f32 dtime = 0.0f;
|
||||
|
@ -377,6 +380,8 @@ void GUIEngine::run()
|
|||
/******************************************************************************/
|
||||
GUIEngine::~GUIEngine()
|
||||
{
|
||||
g_settings->deregisterChangedCallback("fullscreen", fullscreenChangedCallback, this);
|
||||
|
||||
// deinitialize script first. gc destructors might depend on other stuff
|
||||
infostream << "GUIEngine: Deinitializing scripting" << std::endl;
|
||||
m_script.reset();
|
||||
|
@ -620,10 +625,9 @@ bool GUIEngine::setTexture(texture_layer layer, const std::string &texturepath,
|
|||
bool GUIEngine::downloadFile(const std::string &url, const std::string &target)
|
||||
{
|
||||
#if USE_CURL
|
||||
std::ofstream target_file(target.c_str(), std::ios::out | std::ios::binary);
|
||||
if (!target_file.good()) {
|
||||
auto target_file = open_ofstream(target.c_str(), true);
|
||||
if (!target_file.good())
|
||||
return false;
|
||||
}
|
||||
|
||||
HTTPFetchRequest fetch_request;
|
||||
HTTPFetchResult fetch_result;
|
||||
|
@ -667,3 +671,9 @@ void GUIEngine::updateTopLeftTextSize()
|
|||
m_irr_toplefttext = gui::StaticText::add(m_rendering_engine->get_gui_env(),
|
||||
m_toplefttext, rect, false, true, 0, -1);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
void GUIEngine::fullscreenChangedCallback(const std::string &name, void *data)
|
||||
{
|
||||
static_cast<GUIEngine*>(data)->getScriptIface()->handleMainMenuEvent("FullscreenChange");
|
||||
}
|
||||
|
|
|
@ -296,4 +296,6 @@ private:
|
|||
bool m_clouds_enabled = true;
|
||||
/** data used to draw clouds */
|
||||
clouddata m_cloud;
|
||||
|
||||
static void fullscreenChangedCallback(const std::string &name, void *data);
|
||||
};
|
||||
|
|
|
@ -4625,11 +4625,6 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
|
|||
if (!m_left_dragging)
|
||||
break;
|
||||
|
||||
// Abort left-dragging
|
||||
m_left_dragging = false;
|
||||
m_client->inhibit_inventory_revert = false;
|
||||
m_left_drag_stacks.clear();
|
||||
|
||||
// Both the selected item and the hovered item need to be checked
|
||||
// because we don't know exactly when the double-click happened
|
||||
ItemStack slct;
|
||||
|
@ -4658,6 +4653,12 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
|
|||
}
|
||||
|
||||
if (amount > 0) {
|
||||
if (m_left_dragging) {
|
||||
// Abort left-dragging
|
||||
m_left_dragging = false;
|
||||
m_client->inhibit_inventory_revert = false;
|
||||
m_left_drag_stacks.clear();
|
||||
}
|
||||
IMoveAction *a = new IMoveAction();
|
||||
a->count = amount;
|
||||
a->from_inv = s.inventoryloc;
|
||||
|
@ -4740,6 +4741,8 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
|
|||
list_selected->changeItem(m_selected_item->i, stack_from);
|
||||
}
|
||||
|
||||
bool absorb_event = false;
|
||||
|
||||
// Possibly send inventory action to server
|
||||
if (move_amount > 0) {
|
||||
// Send IAction::Move
|
||||
|
@ -4882,6 +4885,10 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
|
|||
a->from_i = m_selected_item->i;
|
||||
m_invmgr->inventoryAction(a);
|
||||
|
||||
// Formspecs usually close when you click outside them, we absorb
|
||||
// the event to prevent that. See GUIModalMenu::remapClickOutside.
|
||||
absorb_event = true;
|
||||
|
||||
} else if (craft_amount > 0) {
|
||||
assert(s.isValid());
|
||||
|
||||
|
@ -4911,6 +4918,9 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
|
|||
m_selected_dragging = false;
|
||||
}
|
||||
m_old_pointer = m_pointer;
|
||||
|
||||
if (absorb_event)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (event.EventType == EET_GUI_EVENT) {
|
||||
|
|
|
@ -107,13 +107,9 @@ void GUIKeyChangeMenu::regenerateGui(v2u32 screensize)
|
|||
removeAllChildren();
|
||||
key_used_text = nullptr;
|
||||
|
||||
const float s = m_gui_scale;
|
||||
DesiredRect = core::rect<s32>(
|
||||
screensize.X / 2 - 835 * s / 2,
|
||||
screensize.Y / 2 - 430 * s / 2,
|
||||
screensize.X / 2 + 835 * s / 2,
|
||||
screensize.Y / 2 + 430 * s / 2
|
||||
);
|
||||
ScalingInfo info = getScalingInfo(screensize, v2u32(835, 430));
|
||||
const float s = info.scale;
|
||||
DesiredRect = info.rect;
|
||||
recalculateAbsolutePosition(false);
|
||||
|
||||
v2s32 size = DesiredRect.getSize();
|
||||
|
|
|
@ -66,13 +66,9 @@ void GUIOpenURLMenu::regenerateGui(v2u32 screensize)
|
|||
/*
|
||||
Calculate new sizes and positions
|
||||
*/
|
||||
const float s = m_gui_scale;
|
||||
DesiredRect = core::rect<s32>(
|
||||
screensize.X / 2 - 580 * s / 2,
|
||||
screensize.Y / 2 - 250 * s / 2,
|
||||
screensize.X / 2 + 580 * s / 2,
|
||||
screensize.Y / 2 + 250 * s / 2
|
||||
);
|
||||
ScalingInfo info = getScalingInfo(screensize, v2u32(580, 250));
|
||||
const float s = info.scale;
|
||||
DesiredRect = info.rect;
|
||||
recalculateAbsolutePosition(false);
|
||||
|
||||
v2s32 size = DesiredRect.getSize();
|
||||
|
|
|
@ -62,14 +62,9 @@ void GUIPasswordChange::regenerateGui(v2u32 screensize)
|
|||
/*
|
||||
Calculate new sizes and positions
|
||||
*/
|
||||
const float s = m_gui_scale;
|
||||
|
||||
DesiredRect = core::rect<s32>(
|
||||
screensize.X / 2 - 580 * s / 2,
|
||||
screensize.Y / 2 - 300 * s / 2,
|
||||
screensize.X / 2 + 580 * s / 2,
|
||||
screensize.Y / 2 + 300 * s / 2
|
||||
);
|
||||
ScalingInfo info = getScalingInfo(screensize, v2u32(580, 300));
|
||||
const float s = info.scale;
|
||||
DesiredRect = info.rect;
|
||||
recalculateAbsolutePosition(false);
|
||||
|
||||
v2s32 size = DesiredRect.getSize();
|
||||
|
|
|
@ -274,10 +274,10 @@ void GUIScrollBar::setPosRaw(const s32 &pos)
|
|||
s32 thumb_min = 0;
|
||||
|
||||
if (is_horizontal) {
|
||||
thumb_min = RelativeRect.getHeight();
|
||||
thumb_min = std::min(RelativeRect.getHeight(), RelativeRect.getWidth() / 2);
|
||||
thumb_area = RelativeRect.getWidth() - border_size * 2;
|
||||
} else {
|
||||
thumb_min = RelativeRect.getWidth();
|
||||
thumb_min = std::min(RelativeRect.getWidth(), RelativeRect.getHeight() / 2);
|
||||
thumb_area = RelativeRect.getHeight() - border_size * 2;
|
||||
}
|
||||
|
||||
|
|
|
@ -60,7 +60,7 @@ GUITable::GUITable(gui::IGUIEnvironment *env,
|
|||
m_rowheight = MYMAX(m_rowheight, 1);
|
||||
}
|
||||
|
||||
const s32 s = skin->getSize(gui::EGDS_SCROLLBAR_SIZE) * 1.5f;
|
||||
const s32 s = skin->getSize(gui::EGDS_SCROLLBAR_SIZE);
|
||||
m_scrollbar = new GUIScrollBar(Environment, this, -1,
|
||||
core::rect<s32>(RelativeRect.getWidth() - s,
|
||||
0,
|
||||
|
@ -183,7 +183,6 @@ void GUITable::setTable(const TableOptions &options,
|
|||
}
|
||||
|
||||
// Handle table options
|
||||
video::SColor default_color(255, 255, 255, 255);
|
||||
s32 opendepth = 0;
|
||||
for (const Option &option : options) {
|
||||
const std::string &name = option.name;
|
||||
|
|
|
@ -54,13 +54,9 @@ void GUIVolumeChange::regenerateGui(v2u32 screensize)
|
|||
/*
|
||||
Calculate new sizes and positions
|
||||
*/
|
||||
const float s = m_gui_scale;
|
||||
DesiredRect = core::rect<s32>(
|
||||
screensize.X / 2 - 380 * s / 2,
|
||||
screensize.Y / 2 - 200 * s / 2,
|
||||
screensize.X / 2 + 380 * s / 2,
|
||||
screensize.Y / 2 + 200 * s / 2
|
||||
);
|
||||
ScalingInfo info = getScalingInfo(screensize, v2u32(380, 200));
|
||||
const float s = info.scale;
|
||||
DesiredRect = info.rect;
|
||||
recalculateAbsolutePosition(false);
|
||||
|
||||
v2s32 size = DesiredRect.getSize();
|
||||
|
|
|
@ -25,19 +25,39 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
#include "client/renderingengine.h"
|
||||
#include "modalMenu.h"
|
||||
#include "gettext.h"
|
||||
#include "gui/guiInventoryList.h"
|
||||
#include "porting.h"
|
||||
#include "settings.h"
|
||||
#include "touchscreengui.h"
|
||||
|
||||
PointerAction PointerAction::fromEvent(const SEvent &event) {
|
||||
switch (event.EventType) {
|
||||
case EET_MOUSE_INPUT_EVENT:
|
||||
return {v2s32(event.MouseInput.X, event.MouseInput.Y), porting::getTimeMs()};
|
||||
case EET_TOUCH_INPUT_EVENT:
|
||||
return {v2s32(event.TouchInput.X, event.TouchInput.Y), porting::getTimeMs()};
|
||||
default:
|
||||
FATAL_ERROR("SEvent given to PointerAction::fromEvent has wrong EventType");
|
||||
}
|
||||
}
|
||||
|
||||
bool PointerAction::isRelated(PointerAction previous) {
|
||||
u64 time_delta = porting::getDeltaMs(previous.time, time);
|
||||
v2s32 pos_delta = pos - previous.pos;
|
||||
f32 distance_sq = (f32)pos_delta.X * pos_delta.X + (f32)pos_delta.Y * pos_delta.Y;
|
||||
|
||||
return time_delta < 400 && distance_sq < (30.0f * 30.0f);
|
||||
}
|
||||
|
||||
GUIModalMenu::GUIModalMenu(gui::IGUIEnvironment* env, gui::IGUIElement* parent,
|
||||
s32 id, IMenuManager *menumgr, bool remap_dbl_click) :
|
||||
s32 id, IMenuManager *menumgr, bool remap_click_outside) :
|
||||
IGUIElement(gui::EGUIET_ELEMENT, env, parent, id,
|
||||
core::rect<s32>(0, 0, 100, 100)),
|
||||
#ifdef __ANDROID__
|
||||
m_jni_field_name(""),
|
||||
#endif
|
||||
m_menumgr(menumgr),
|
||||
m_remap_dbl_click(remap_dbl_click)
|
||||
m_remap_click_outside(remap_click_outside)
|
||||
{
|
||||
m_gui_scale = std::max(g_settings->getFloat("gui_scaling"), 0.5f);
|
||||
const float screen_dpi_scale = RenderingEngine::getDisplayDensity();
|
||||
|
@ -50,9 +70,6 @@ GUIModalMenu::GUIModalMenu(gui::IGUIEnvironment* env, gui::IGUIElement* parent,
|
|||
|
||||
setVisible(true);
|
||||
m_menumgr->createdMenu(this);
|
||||
|
||||
m_last_touch.time = 0;
|
||||
m_last_touch.pos = v2s32(0, 0);
|
||||
}
|
||||
|
||||
GUIModalMenu::~GUIModalMenu()
|
||||
|
@ -115,42 +132,53 @@ static bool isChild(gui::IGUIElement *tocheck, gui::IGUIElement *parent)
|
|||
return false;
|
||||
}
|
||||
|
||||
bool GUIModalMenu::remapDoubleClick(const SEvent &event)
|
||||
bool GUIModalMenu::remapClickOutside(const SEvent &event)
|
||||
{
|
||||
/* The following code is for capturing double-clicks of the mouse button
|
||||
* and translating the double-click into an EET_KEY_INPUT_EVENT event
|
||||
* -- which closes the form -- under some circumstances.
|
||||
*
|
||||
* There have been many github issues reporting this as a bug even though it
|
||||
* was an intended feature. For this reason, remapping the double-click as
|
||||
* an ESC must be explicitly set when creating this class via the
|
||||
* /p remap_dbl_click parameter of the constructor.
|
||||
*/
|
||||
|
||||
if (!m_remap_dbl_click)
|
||||
if (!m_remap_click_outside || event.EventType != EET_MOUSE_INPUT_EVENT ||
|
||||
(event.MouseInput.Event != EMIE_LMOUSE_PRESSED_DOWN &&
|
||||
event.MouseInput.Event != EMIE_LMOUSE_LEFT_UP))
|
||||
return false;
|
||||
|
||||
if (event.EventType != EET_MOUSE_INPUT_EVENT ||
|
||||
event.MouseInput.Event != EMIE_LMOUSE_DOUBLE_CLICK)
|
||||
return false;
|
||||
// The formspec must only be closed if both the EMIE_LMOUSE_PRESSED_DOWN and
|
||||
// the EMIE_LMOUSE_LEFT_UP event haven't been absorbed by something else.
|
||||
|
||||
PointerAction last = m_last_click_outside;
|
||||
m_last_click_outside = {}; // always reset
|
||||
PointerAction current = PointerAction::fromEvent(event);
|
||||
|
||||
// Only exit if the double-click happened outside the menu.
|
||||
gui::IGUIElement *hovered =
|
||||
Environment->getRootGUIElement()->getElementFromPoint(m_pointer);
|
||||
Environment->getRootGUIElement()->getElementFromPoint(current.pos);
|
||||
if (isChild(hovered, this))
|
||||
return false;
|
||||
|
||||
// Translate double-click to escape.
|
||||
SEvent translated{};
|
||||
translated.EventType = EET_KEY_INPUT_EVENT;
|
||||
translated.KeyInput.Key = KEY_ESCAPE;
|
||||
translated.KeyInput.Control = false;
|
||||
translated.KeyInput.Shift = false;
|
||||
translated.KeyInput.PressedDown = true;
|
||||
translated.KeyInput.Char = 0;
|
||||
OnEvent(translated);
|
||||
// Dropping items is also done by tapping outside the formspec. If an item
|
||||
// is selected, make sure it is dropped without closing the formspec.
|
||||
// We have to explicitly restrict this to GUIInventoryList because other
|
||||
// GUI elements like text fields like to absorb events for no reason.
|
||||
GUIInventoryList *focused = dynamic_cast<GUIInventoryList *>(Environment->getFocus());
|
||||
if (focused && focused->OnEvent(event))
|
||||
// Return true since the event was handled, even if it wasn't handled by us.
|
||||
return true;
|
||||
|
||||
return true;
|
||||
if (event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) {
|
||||
m_last_click_outside = current;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP &&
|
||||
current.isRelated(last)) {
|
||||
SEvent translated{};
|
||||
translated.EventType = EET_KEY_INPUT_EVENT;
|
||||
translated.KeyInput.Key = KEY_ESCAPE;
|
||||
translated.KeyInput.Control = false;
|
||||
translated.KeyInput.Shift = false;
|
||||
translated.KeyInput.PressedDown = true;
|
||||
translated.KeyInput.Char = 0;
|
||||
OnEvent(translated);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GUIModalMenu::simulateMouseEvent(ETOUCH_INPUT_EVENT touch_event, bool second_try)
|
||||
|
@ -319,20 +347,12 @@ bool GUIModalMenu::preprocessEvent(const SEvent &event)
|
|||
|
||||
// Detect double-taps and convert them into double-click events.
|
||||
if (event.TouchInput.Event == ETIE_PRESSED_DOWN) {
|
||||
u64 time_now = porting::getTimeMs();
|
||||
u64 time_delta = porting::getDeltaMs(m_last_touch.time, time_now);
|
||||
|
||||
v2s32 pos_delta = m_pointer - m_last_touch.pos;
|
||||
f32 distance_sq = (f32)pos_delta.X * pos_delta.X +
|
||||
(f32)pos_delta.Y * pos_delta.Y;
|
||||
|
||||
if (time_delta < 400 && distance_sq < (30 * 30)) {
|
||||
PointerAction current = PointerAction::fromEvent(event);
|
||||
if (current.isRelated(m_last_touch)) {
|
||||
// ETIE_COUNT is used for double-tap events.
|
||||
simulateMouseEvent(ETIE_COUNT);
|
||||
}
|
||||
|
||||
m_last_touch.time = time_now;
|
||||
m_last_touch.pos = m_pointer;
|
||||
m_last_touch = current;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
@ -361,7 +381,7 @@ bool GUIModalMenu::preprocessEvent(const SEvent &event)
|
|||
m_touch_hovered.reset();
|
||||
}
|
||||
|
||||
if (remapDoubleClick(event))
|
||||
if (remapClickOutside(event))
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -378,3 +398,17 @@ porting::AndroidDialogState GUIModalMenu::getAndroidUIInputState()
|
|||
return porting::getInputDialogState();
|
||||
}
|
||||
#endif
|
||||
|
||||
GUIModalMenu::ScalingInfo GUIModalMenu::getScalingInfo(v2u32 screensize, v2u32 base_size)
|
||||
{
|
||||
f32 scale = m_gui_scale;
|
||||
scale = std::min(scale, (f32)screensize.X / (f32)base_size.X);
|
||||
scale = std::min(scale, (f32)screensize.Y / (f32)base_size.Y);
|
||||
s32 w = base_size.X * scale, h = base_size.Y * scale;
|
||||
return {scale, core::rect<s32>(
|
||||
screensize.X / 2 - w / 2,
|
||||
screensize.Y / 2 - h / 2,
|
||||
screensize.X / 2 + w / 2,
|
||||
screensize.Y / 2 + h / 2
|
||||
)};
|
||||
}
|
||||
|
|
|
@ -31,6 +31,14 @@ enum class PointerType {
|
|||
Touch,
|
||||
};
|
||||
|
||||
struct PointerAction {
|
||||
v2s32 pos;
|
||||
u64 time; // ms
|
||||
|
||||
static PointerAction fromEvent(const SEvent &event);
|
||||
bool isRelated(PointerAction other);
|
||||
};
|
||||
|
||||
class GUIModalMenu;
|
||||
|
||||
class IMenuManager
|
||||
|
@ -86,6 +94,12 @@ protected:
|
|||
std::string m_jni_field_name;
|
||||
#endif
|
||||
|
||||
struct ScalingInfo {
|
||||
f32 scale;
|
||||
core::rect<s32> rect;
|
||||
};
|
||||
ScalingInfo getScalingInfo(v2u32 screensize, v2u32 base_size);
|
||||
|
||||
// This is set to true if the menu is currently processing a second-touch event.
|
||||
bool m_second_touch = false;
|
||||
// This is set to true if the menu is currently processing a mouse event
|
||||
|
@ -94,14 +108,14 @@ protected:
|
|||
|
||||
private:
|
||||
IMenuManager *m_menumgr;
|
||||
/* If true, remap a double-click (or double-tap) action to ESC. This is so
|
||||
* that, for example, Android users can double-tap to close a formspec.
|
||||
*
|
||||
* This value can (currently) only be set by the class constructor
|
||||
* and the default value for the setting is true.
|
||||
/* If true, remap a click outside the formspec to ESC. This is so that, for
|
||||
* example, touchscreen users can close formspecs.
|
||||
* The default for this setting is true. Currently, it's set to false for
|
||||
* the mainmenu to prevent Minetest from closing unexpectedly.
|
||||
*/
|
||||
bool m_remap_dbl_click;
|
||||
bool remapDoubleClick(const SEvent &event);
|
||||
bool m_remap_click_outside;
|
||||
bool remapClickOutside(const SEvent &event);
|
||||
PointerAction m_last_click_outside{};
|
||||
|
||||
// This might be necessary to expose to the implementation if it
|
||||
// wants to launch other menus
|
||||
|
@ -111,13 +125,11 @@ private:
|
|||
|
||||
irr_ptr<gui::IGUIElement> m_touch_hovered;
|
||||
|
||||
// Converts touches into clicks.
|
||||
bool simulateMouseEvent(ETOUCH_INPUT_EVENT touch_event, bool second_try=false);
|
||||
void enter(gui::IGUIElement *element);
|
||||
void leave();
|
||||
|
||||
// Used to detect double-taps and convert them into double-click events.
|
||||
struct {
|
||||
v2s32 pos;
|
||||
s64 time;
|
||||
} m_last_touch;
|
||||
PointerAction m_last_touch{};
|
||||
};
|
||||
|
|
|
@ -857,8 +857,10 @@ void TouchScreenGUI::setVisible(bool visible)
|
|||
if (!visible) {
|
||||
while (!m_pointer_pos.empty())
|
||||
handleReleaseEvent(m_pointer_pos.begin()->first);
|
||||
for (AutoHideButtonBar &bar : m_buttonbars)
|
||||
for (AutoHideButtonBar &bar : m_buttonbars) {
|
||||
bar.deactivate();
|
||||
bar.hide();
|
||||
}
|
||||
} else {
|
||||
for (AutoHideButtonBar &bar : m_buttonbars)
|
||||
bar.show();
|
||||
|
|
|
@ -94,7 +94,6 @@ void StaticText::draw()
|
|||
getTextWidth();
|
||||
}
|
||||
|
||||
irr::video::SColor previous_color(255, 255, 255, 255);
|
||||
for (const EnrichedString &str : BrokenText) {
|
||||
if (HAlign == EGUIA_LOWERRIGHT)
|
||||
{
|
||||
|
|
95
src/log.cpp
95
src/log.cpp
|
@ -28,15 +28,19 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
#include "exceptions.h"
|
||||
#include "util/numeric.h"
|
||||
#include "log.h"
|
||||
#include "filesys.h"
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#include <android/log.h>
|
||||
#endif
|
||||
|
||||
#if !defined(_WIN32)
|
||||
#include <unistd.h> // isatty
|
||||
#endif
|
||||
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
|
||||
class LevelTarget : public LogTarget {
|
||||
|
@ -104,21 +108,19 @@ thread_local LogStream dout_con(trace_target);
|
|||
|
||||
static unsigned int g_level_to_android[] = {
|
||||
ANDROID_LOG_INFO, // LL_NONE
|
||||
//ANDROID_LOG_FATAL,
|
||||
ANDROID_LOG_ERROR, // LL_ERROR
|
||||
ANDROID_LOG_WARN, // LL_WARNING
|
||||
ANDROID_LOG_WARN, // LL_ACTION
|
||||
//ANDROID_LOG_INFO,
|
||||
ANDROID_LOG_INFO, // LL_ACTION
|
||||
ANDROID_LOG_DEBUG, // LL_INFO
|
||||
ANDROID_LOG_VERBOSE, // LL_VERBOSE
|
||||
ANDROID_LOG_VERBOSE, // LL_TRACE
|
||||
};
|
||||
|
||||
void AndroidLogOutput::logRaw(LogLevel lev, const std::string &line) {
|
||||
void AndroidLogOutput::logRaw(LogLevel lev, const std::string &line)
|
||||
{
|
||||
static_assert(ARRLEN(g_level_to_android) == LL_MAX,
|
||||
"mismatch between android and internal loglevels");
|
||||
__android_log_print(g_level_to_android[lev],
|
||||
PROJECT_NAME_C, "%s", line.c_str());
|
||||
__android_log_write(g_level_to_android[lev], PROJECT_NAME_C, line.c_str());
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -156,9 +158,7 @@ void Logger::addOutput(ILogOutput *out)
|
|||
|
||||
void Logger::addOutput(ILogOutput *out, LogLevel lev)
|
||||
{
|
||||
MutexAutoLock lock(m_mutex);
|
||||
m_outputs[lev].push_back(out);
|
||||
m_has_outputs[lev] = true;
|
||||
addOutputMasked(out, LOGLEVEL_TO_MASKLEVEL(lev));
|
||||
}
|
||||
|
||||
void Logger::addOutputMasked(ILogOutput *out, LogLevelMask mask)
|
||||
|
@ -187,9 +187,7 @@ LogLevelMask Logger::removeOutput(ILogOutput *out)
|
|||
MutexAutoLock lock(m_mutex);
|
||||
LogLevelMask ret_mask = 0;
|
||||
for (size_t i = 0; i < LL_MAX; i++) {
|
||||
std::vector<ILogOutput *>::iterator it;
|
||||
|
||||
it = std::find(m_outputs[i].begin(), m_outputs[i].end(), out);
|
||||
auto it = std::find(m_outputs[i].begin(), m_outputs[i].end(), out);
|
||||
if (it != m_outputs[i].end()) {
|
||||
ret_mask |= LOGLEVEL_TO_MASKLEVEL(i);
|
||||
m_outputs[i].erase(it);
|
||||
|
@ -218,9 +216,9 @@ void Logger::deregisterThread()
|
|||
m_thread_names.erase(id);
|
||||
}
|
||||
|
||||
const std::string Logger::getLevelLabel(LogLevel lev)
|
||||
const char *Logger::getLevelLabel(LogLevel lev)
|
||||
{
|
||||
static const std::string names[] = {
|
||||
static const char *names[] = {
|
||||
"",
|
||||
"ERROR",
|
||||
"WARNING",
|
||||
|
@ -229,45 +227,50 @@ const std::string Logger::getLevelLabel(LogLevel lev)
|
|||
"VERBOSE",
|
||||
"TRACE",
|
||||
};
|
||||
assert(lev < LL_MAX && lev >= 0);
|
||||
static_assert(ARRLEN(names) == LL_MAX,
|
||||
"mismatch between loglevel names and enum");
|
||||
assert(lev < LL_MAX && lev >= 0);
|
||||
return names[lev];
|
||||
}
|
||||
|
||||
LogColor Logger::color_mode = LOG_COLOR_AUTO;
|
||||
|
||||
const std::string Logger::getThreadName()
|
||||
const std::string &Logger::getThreadName()
|
||||
{
|
||||
std::map<std::thread::id, std::string>::const_iterator it;
|
||||
|
||||
std::thread::id id = std::this_thread::get_id();
|
||||
it = m_thread_names.find(id);
|
||||
|
||||
auto it = m_thread_names.find(id);
|
||||
if (it != m_thread_names.end())
|
||||
return it->second;
|
||||
|
||||
std::ostringstream os;
|
||||
os << "#0x" << std::hex << id;
|
||||
return os.str();
|
||||
thread_local std::string fallback_name;
|
||||
if (fallback_name.empty()) {
|
||||
std::ostringstream os;
|
||||
os << "#0x" << std::hex << id;
|
||||
fallback_name = os.str();
|
||||
}
|
||||
return fallback_name;
|
||||
}
|
||||
|
||||
void Logger::log(LogLevel lev, const std::string &text)
|
||||
{
|
||||
if (m_silenced_levels[lev])
|
||||
if (isLevelSilenced(lev))
|
||||
return;
|
||||
|
||||
const std::string thread_name = getThreadName();
|
||||
const std::string label = getLevelLabel(lev);
|
||||
const std::string &thread_name = getThreadName();
|
||||
const char *label = getLevelLabel(lev);
|
||||
const std::string timestamp = getTimestamp();
|
||||
std::ostringstream os(std::ios_base::binary);
|
||||
os << timestamp << ": " << label << "[" << thread_name << "]: " << text;
|
||||
|
||||
logToOutputs(lev, os.str(), timestamp, thread_name, text);
|
||||
std::string line = timestamp;
|
||||
line.append(": ").append(label).append("[").append(thread_name)
|
||||
.append("]: ").append(text);
|
||||
|
||||
logToOutputs(lev, line, timestamp, thread_name, text);
|
||||
}
|
||||
|
||||
void Logger::logRaw(LogLevel lev, const std::string &text)
|
||||
{
|
||||
if (m_silenced_levels[lev])
|
||||
if (isLevelSilenced(lev))
|
||||
return;
|
||||
|
||||
logToOutputsRaw(lev, text);
|
||||
|
@ -299,28 +302,38 @@ void FileLogOutput::setFile(const std::string &filename, s64 file_size_max)
|
|||
bool is_too_large = false;
|
||||
if (file_size_max > 0) {
|
||||
std::ifstream ifile(filename, std::ios::binary | std::ios::ate);
|
||||
is_too_large = ifile.tellg() > file_size_max;
|
||||
ifile.close();
|
||||
if (ifile.good())
|
||||
is_too_large = ifile.tellg() > file_size_max;
|
||||
}
|
||||
|
||||
if (is_too_large) {
|
||||
std::string filename_secondary = filename + ".1";
|
||||
actionstream << "The log file grew too big; it is moved to " <<
|
||||
filename_secondary << std::endl;
|
||||
remove(filename_secondary.c_str());
|
||||
rename(filename.c_str(), filename_secondary.c_str());
|
||||
fs::DeleteSingleFileOrEmptyDirectory(filename_secondary);
|
||||
fs::Rename(filename, filename_secondary);
|
||||
}
|
||||
m_stream.open(filename, std::ios::app | std::ios::ate);
|
||||
|
||||
if (!m_stream.good())
|
||||
throw FileNotGoodException("Failed to open log file " +
|
||||
filename + ": " + strerror(errno));
|
||||
// Intentionally not using open_ofstream() to keep the text mode
|
||||
if (!fs::OpenStream(*m_stream.rdbuf(), filename.c_str(), std::ios::out | std::ios::app, true, false))
|
||||
throw FileNotGoodException("Failed to open log file");
|
||||
|
||||
m_stream << "\n\n"
|
||||
"-------------" << std::endl <<
|
||||
" Separator" << std::endl <<
|
||||
"-------------\n" <<
|
||||
" Separator\n" <<
|
||||
"-------------\n" << std::endl;
|
||||
}
|
||||
|
||||
StreamLogOutput::StreamLogOutput(std::ostream &stream) :
|
||||
m_stream(stream)
|
||||
{
|
||||
#if !defined(_WIN32)
|
||||
if (&stream == &std::cout)
|
||||
is_tty = isatty(STDOUT_FILENO);
|
||||
else if (&stream == &std::cerr)
|
||||
is_tty = isatty(STDERR_FILENO);
|
||||
#endif
|
||||
}
|
||||
|
||||
void StreamLogOutput::logRaw(LogLevel lev, const std::string &line)
|
||||
{
|
||||
bool colored_message = (Logger::color_mode == LOG_COLOR_ALWAYS) ||
|
||||
|
|
28
src/log.h
28
src/log.h
|
@ -26,9 +26,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
#include <fstream>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#if !defined(_WIN32) // POSIX
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include "threading/mutex_auto_lock.h"
|
||||
#include "util/basic_macros.h"
|
||||
#include "util/stream.h"
|
||||
|
@ -73,12 +70,16 @@ public:
|
|||
void logRaw(LogLevel lev, const std::string &text);
|
||||
|
||||
static LogLevel stringToLevel(const std::string &name);
|
||||
static const std::string getLevelLabel(LogLevel lev);
|
||||
static const char *getLevelLabel(LogLevel lev);
|
||||
|
||||
bool hasOutput(LogLevel level) {
|
||||
return m_has_outputs[level].load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
bool isLevelSilenced(LogLevel level) {
|
||||
return m_silenced_levels[level].load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
static LogColor color_mode;
|
||||
|
||||
private:
|
||||
|
@ -87,15 +88,11 @@ private:
|
|||
const std::string &time, const std::string &thread_name,
|
||||
const std::string &payload_text);
|
||||
|
||||
const std::string getThreadName();
|
||||
const std::string &getThreadName();
|
||||
|
||||
std::vector<ILogOutput *> m_outputs[LL_MAX];
|
||||
std::atomic<bool> m_has_outputs[LL_MAX];
|
||||
|
||||
// Should implement atomic loads and stores (even though it's only
|
||||
// written to when one thread has access currently).
|
||||
// Works on all known architectures (x86, ARM, MIPS).
|
||||
volatile bool m_silenced_levels[LL_MAX];
|
||||
std::atomic<bool> m_silenced_levels[LL_MAX];
|
||||
std::map<std::thread::id, std::string> m_thread_names;
|
||||
mutable std::mutex m_mutex;
|
||||
};
|
||||
|
@ -120,16 +117,7 @@ public:
|
|||
|
||||
class StreamLogOutput : public ICombinedLogOutput {
|
||||
public:
|
||||
StreamLogOutput(std::ostream &stream) :
|
||||
m_stream(stream)
|
||||
{
|
||||
#if !defined(_WIN32)
|
||||
if (&stream == &std::cout)
|
||||
is_tty = isatty(STDOUT_FILENO);
|
||||
else if (&stream == &std::cerr)
|
||||
is_tty = isatty(STDERR_FILENO);
|
||||
#endif
|
||||
}
|
||||
StreamLogOutput(std::ostream &stream);
|
||||
|
||||
void logRaw(LogLevel lev, const std::string &line);
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
#include "filesys.h"
|
||||
#include "version.h"
|
||||
#include "defaultsettings.h"
|
||||
#include "migratesettings.h"
|
||||
#include "gettext.h"
|
||||
#include "log.h"
|
||||
#include "util/quicktune.h"
|
||||
|
@ -426,7 +427,11 @@ static void print_version(std::ostream &os)
|
|||
os << PROJECT_NAME_C " " << g_version_hash
|
||||
<< " (" << porting::getPlatformName() << ")" << std::endl;
|
||||
#if USE_LUAJIT
|
||||
os << "Using " << LUAJIT_VERSION << std::endl;
|
||||
os << "Using " << LUAJIT_VERSION
|
||||
#ifdef OPENRESTY_LUAJIT
|
||||
<< " (OpenResty)"
|
||||
#endif
|
||||
<< std::endl;
|
||||
#else
|
||||
os << "Using " << LUA_RELEASE << std::endl;
|
||||
#endif
|
||||
|
@ -687,6 +692,8 @@ static bool init_common(const Settings &cmd_args, int argc, char *argv[])
|
|||
if (!read_config_file(cmd_args))
|
||||
return false;
|
||||
|
||||
migrate_settings();
|
||||
|
||||
init_log_streams(cmd_args);
|
||||
|
||||
// Initialize random seed
|
||||
|
|
|
@ -96,13 +96,9 @@ bool MapSettingsManager::setMapSettingNoiseParams(
|
|||
|
||||
bool MapSettingsManager::loadMapMeta()
|
||||
{
|
||||
std::ifstream is(m_map_meta_path.c_str(), std::ios_base::binary);
|
||||
|
||||
if (!is.good()) {
|
||||
errorstream << "loadMapMeta: could not open "
|
||||
<< m_map_meta_path << std::endl;
|
||||
auto is = open_ifstream(m_map_meta_path.c_str(), true);
|
||||
if (!is.good())
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_map_settings->parseConfigLines(is)) {
|
||||
errorstream << "loadMapMeta: Format error. '[end_of_params]' missing?" << std::endl;
|
||||
|
|
|
@ -85,6 +85,7 @@ MapBlock::~MapBlock()
|
|||
#endif
|
||||
|
||||
delete[] data;
|
||||
porting::TrackFreedMemory(sizeof(MapNode) * nodecount);
|
||||
}
|
||||
|
||||
bool MapBlock::onObjectsActivation()
|
||||
|
|
|
@ -167,7 +167,7 @@ public:
|
|||
inline void setLightingComplete(LightBank bank, u8 direction,
|
||||
bool is_complete)
|
||||
{
|
||||
assert(direction >= 0 && direction <= 5);
|
||||
assert(direction <= 5);
|
||||
if (bank == LIGHTBANK_NIGHT) {
|
||||
direction += 6;
|
||||
}
|
||||
|
@ -182,7 +182,7 @@ public:
|
|||
|
||||
inline bool isLightingComplete(LightBank bank, u8 direction)
|
||||
{
|
||||
assert(direction >= 0 && direction <= 5);
|
||||
assert(direction <= 5);
|
||||
if (bank == LIGHTBANK_NIGHT) {
|
||||
direction += 6;
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
#include "porting.h"
|
||||
#include "settings.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
|
|
@ -19,7 +19,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
*/
|
||||
|
||||
#include <fstream>
|
||||
#include <typeinfo>
|
||||
#include "mg_schematic.h"
|
||||
#include "server.h"
|
||||
#include "mapgen.h"
|
||||
|
@ -32,6 +31,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
#include "serialization.h"
|
||||
#include "filesys.h"
|
||||
#include "voxelalgorithms.h"
|
||||
#include "porting.h"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
@ -80,6 +80,8 @@ Schematic::~Schematic()
|
|||
{
|
||||
delete []schemdata;
|
||||
delete []slice_probs;
|
||||
u32 nodecount = size.X * size.Y * size.Z;
|
||||
porting::TrackFreedMemory(nodecount * sizeof(MapNode));
|
||||
}
|
||||
|
||||
ObjDef *Schematic::clone() const
|
||||
|
@ -485,12 +487,9 @@ bool Schematic::serializeToLua(std::ostream *os, bool use_comments,
|
|||
bool Schematic::loadSchematicFromFile(const std::string &filename,
|
||||
const NodeDefManager *ndef, StringMap *replace_names)
|
||||
{
|
||||
std::ifstream is(filename.c_str(), std::ios_base::binary);
|
||||
if (!is.good()) {
|
||||
errorstream << __FUNCTION__ << ": unable to open file '"
|
||||
<< filename << "'" << std::endl;
|
||||
auto is = open_ifstream(filename.c_str(), true);
|
||||
if (!is.good())
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_ndef)
|
||||
m_ndef = ndef;
|
||||
|
|
14
src/migratesettings.h
Normal file
14
src/migratesettings.h
Normal file
|
@ -0,0 +1,14 @@
|
|||
// Minetest
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
#include "settings.h"
|
||||
|
||||
void migrate_settings()
|
||||
{
|
||||
// Converts opaque_water to translucent_liquids
|
||||
if (g_settings->existsLocal("opaque_water")) {
|
||||
g_settings->set("translucent_liquids",
|
||||
g_settings->getBool("opaque_water") ? "false" : "true");
|
||||
g_settings->remove("opaque_water");
|
||||
}
|
||||
}
|
|
@ -1078,7 +1078,7 @@ bool UDPPeer::processReliableSendCommand(
|
|||
bool have_sequence_number = false;
|
||||
bool have_initial_sequence_number = false;
|
||||
std::queue<BufferedPacketPtr> toadd;
|
||||
volatile u16 initial_sequence_number = 0;
|
||||
u16 initial_sequence_number = 0;
|
||||
|
||||
for (SharedBuffer<u8> &original : originals) {
|
||||
u16 seqnum = chan.getOutgoingSequenceNumber(have_sequence_number);
|
||||
|
@ -1118,7 +1118,7 @@ bool UDPPeer::processReliableSendCommand(
|
|||
return true;
|
||||
}
|
||||
|
||||
volatile u16 packets_available = toadd.size();
|
||||
u16 packets_available = toadd.size();
|
||||
/* we didn't get a single sequence number no need to fill queue */
|
||||
if (!have_initial_sequence_number) {
|
||||
LOG(derr_con << m_connection->getDesc() << "Ran out of sequence numbers!" << std::endl);
|
||||
|
|
|
@ -455,7 +455,7 @@ public:
|
|||
void PutReliableSendCommand(ConnectionCommandPtr &c,
|
||||
unsigned int max_packet_size) override;
|
||||
|
||||
virtual const Address &getAddress() const {
|
||||
virtual const Address &getAddress() const override {
|
||||
return address;
|
||||
}
|
||||
|
||||
|
|
|
@ -283,7 +283,7 @@ void TileDef::deSerialize(std::istream &is, NodeDrawType drawtype, u16 protocol_
|
|||
void TextureSettings::readSettings()
|
||||
{
|
||||
connected_glass = g_settings->getBool("connected_glass");
|
||||
opaque_water = g_settings->getBool("opaque_water");
|
||||
translucent_liquids = g_settings->getBool("translucent_liquids");
|
||||
bool smooth_lighting = g_settings->getBool("smooth_lighting");
|
||||
enable_mesh_cache = g_settings->getBool("enable_mesh_cache");
|
||||
enable_minimap = g_settings->getBool("enable_minimap");
|
||||
|
@ -683,7 +683,7 @@ void ContentFeatures::deSerialize(std::istream &is, u16 protocol_version)
|
|||
if (is.eof())
|
||||
throw SerializationError("");
|
||||
post_effect_color_shaded = tmp;
|
||||
} catch(SerializationError &e) {};
|
||||
} catch (SerializationError &e) {};
|
||||
}
|
||||
|
||||
#ifndef SERVER
|
||||
|
@ -825,14 +825,14 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc
|
|||
solidness = 0;
|
||||
break;
|
||||
case NDT_LIQUID:
|
||||
if (tsettings.opaque_water)
|
||||
if (!tsettings.translucent_liquids)
|
||||
alpha = ALPHAMODE_OPAQUE;
|
||||
solidness = 1;
|
||||
is_liquid = true;
|
||||
break;
|
||||
case NDT_FLOWINGLIQUID:
|
||||
solidness = 0;
|
||||
if (tsettings.opaque_water)
|
||||
if (!tsettings.translucent_liquids)
|
||||
alpha = ALPHAMODE_OPAQUE;
|
||||
is_liquid = true;
|
||||
break;
|
||||
|
|
|
@ -184,7 +184,7 @@ public:
|
|||
WorldAlignMode world_aligned_mode;
|
||||
AutoScale autoscale_mode;
|
||||
int node_texture_size;
|
||||
bool opaque_water;
|
||||
bool translucent_liquids;
|
||||
bool connected_glass;
|
||||
bool enable_mesh_cache;
|
||||
bool enable_minimap;
|
||||
|
|
|
@ -63,7 +63,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
#include <FindDirectory.h>
|
||||
#endif
|
||||
|
||||
#include "config.h"
|
||||
#if HAVE_MALLOC_TRIM
|
||||
// glibc-only pretty much
|
||||
#include <malloc.h>
|
||||
#endif
|
||||
|
||||
#include "debug.h"
|
||||
#include "filesys.h"
|
||||
#include "log.h"
|
||||
|
@ -72,6 +76,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
#include <cstdarg>
|
||||
#include <cstdio>
|
||||
#include <signal.h>
|
||||
#include <atomic>
|
||||
|
||||
#if !defined(SERVER) && defined(_WIN32)
|
||||
// On Windows export some driver-specific variables to encourage Minetest to be
|
||||
|
@ -582,7 +587,7 @@ static void createCacheDirTag()
|
|||
if (fs::PathExists(path))
|
||||
return;
|
||||
fs::CreateAllDirs(path_cache);
|
||||
std::ofstream ofs(path, std::ios::out | std::ios::binary);
|
||||
auto ofs = open_ofstream(path.c_str(), false);
|
||||
if (!ofs.good())
|
||||
return;
|
||||
ofs << "Signature: 8a477f597d28d172789f06886806bc55\n"
|
||||
|
@ -800,6 +805,21 @@ std::string QuoteArgv(const std::string &arg)
|
|||
ret.push_back('"');
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string ConvertError(DWORD error_code)
|
||||
{
|
||||
wchar_t buffer[320];
|
||||
|
||||
auto r = FormatMessageW(
|
||||
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
nullptr, error_code, 0, buffer, ARRLEN(buffer) - 1, nullptr);
|
||||
if (!r)
|
||||
return std::to_string(error_code);
|
||||
|
||||
if (!buffer[0]) // should not happen normally
|
||||
return "?";
|
||||
return wide_to_utf8(buffer);
|
||||
}
|
||||
#endif
|
||||
|
||||
int mt_snprintf(char *buf, const size_t buf_size, const char *fmt, ...)
|
||||
|
@ -888,4 +908,37 @@ double perf_freq = get_perf_freq();
|
|||
|
||||
#endif
|
||||
|
||||
#if HAVE_MALLOC_TRIM
|
||||
|
||||
/*
|
||||
* On Linux/glibc we found that after deallocating bigger chunks of data (esp. MapBlocks)
|
||||
* the memory would not be given back to the OS and would stay at peak usage.
|
||||
* This appears to be a combination of unfortunate allocation order/fragmentation
|
||||
* and the fact that glibc does not call madvise(MADV_DONTNEED) on its own.
|
||||
* Some other allocators were also affected, jemalloc and musl libc were not.
|
||||
* read more: <https://forum.minetest.net/viewtopic.php?t=30509>
|
||||
*
|
||||
* As a workaround we track freed memory coarsely and call malloc_trim() once a
|
||||
* certain amount is reached.
|
||||
*/
|
||||
|
||||
static std::atomic<size_t> memory_freed;
|
||||
|
||||
constexpr size_t MEMORY_TRIM_THRESHOLD = 128 * 1024 * 1024;
|
||||
|
||||
void TrackFreedMemory(size_t amount)
|
||||
{
|
||||
constexpr auto MO = std::memory_order_relaxed;
|
||||
memory_freed.fetch_add(amount, MO);
|
||||
if (memory_freed.load(MO) >= MEMORY_TRIM_THRESHOLD) {
|
||||
// Synchronize call
|
||||
if (memory_freed.exchange(0, MO) < MEMORY_TRIM_THRESHOLD)
|
||||
return;
|
||||
// Leave some headroom for future allocations
|
||||
malloc_trim(1 * 1024 * 1024);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} //namespace porting
|
||||
|
|
|
@ -290,11 +290,26 @@ void osSpecificInit();
|
|||
// This attaches to the parents process console, or creates a new one if it doesnt exist.
|
||||
void attachOrCreateConsole();
|
||||
|
||||
/**
|
||||
* Call this after freeing bigger blocks of memory. Used on some platforms to
|
||||
* properly give memory back to the OS.
|
||||
* @param amount Number of bytes freed
|
||||
*/
|
||||
#if HAVE_MALLOC_TRIM
|
||||
void TrackFreedMemory(size_t amount);
|
||||
#else
|
||||
inline void TrackFreedMemory(size_t amount) { (void)amount; }
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
// Quotes an argument for use in a CreateProcess() commandline (not cmd.exe!!)
|
||||
std::string QuoteArgv(const std::string &arg);
|
||||
|
||||
// Convert an error code (e.g. from GetLastError()) into a string.
|
||||
std::string ConvertError(DWORD error_code);
|
||||
#endif
|
||||
|
||||
// snprintf wrapper
|
||||
int mt_snprintf(char *buf, const size_t buf_size, const char *fmt, ...);
|
||||
|
||||
/**
|
||||
|
|
103
src/precompiled_headers.txt
Normal file
103
src/precompiled_headers.txt
Normal file
|
@ -0,0 +1,103 @@
|
|||
|
||||
# stdlib
|
||||
# ------
|
||||
|
||||
# C stuff:
|
||||
<cassert>
|
||||
<cctype>
|
||||
<cerrno>
|
||||
<cfenv>
|
||||
<cfloat>
|
||||
<cinttypes>
|
||||
<ciso646>
|
||||
<climits>
|
||||
<clocale>
|
||||
<cmath>
|
||||
<csetjmp>
|
||||
<csignal>
|
||||
<cstdarg>
|
||||
<cstdbool>
|
||||
<cstddef>
|
||||
<cstdint>
|
||||
<cstdio>
|
||||
<cstdlib>
|
||||
<cstring>
|
||||
<ctgmath>
|
||||
<ctime>
|
||||
<cuchar>
|
||||
<cwchar>
|
||||
<cwctype>
|
||||
|
||||
# Containers:
|
||||
<array>
|
||||
<deque>
|
||||
<forward_list>
|
||||
<list>
|
||||
<map>
|
||||
<queue>
|
||||
<set>
|
||||
<stack>
|
||||
<unordered_map>
|
||||
<unordered_set>
|
||||
<vector>
|
||||
|
||||
# Input/Output:
|
||||
<fstream>
|
||||
<iomanip>
|
||||
<ios>
|
||||
<iosfwd>
|
||||
<iostream>
|
||||
<istream>
|
||||
<ostream>
|
||||
<sstream>
|
||||
<streambuf>
|
||||
|
||||
# Multi-threading:
|
||||
<atomic>
|
||||
<condition_variable>
|
||||
<future>
|
||||
<mutex>
|
||||
<shared_mutex>
|
||||
<thread>
|
||||
|
||||
# Other:
|
||||
<algorithm>
|
||||
<any>
|
||||
<bitset>
|
||||
<charconv>
|
||||
<chrono>
|
||||
<codecvt>
|
||||
<complex>
|
||||
<exception>
|
||||
<execution>
|
||||
<functional>
|
||||
<initializer_list>
|
||||
<iterator>
|
||||
<limits>
|
||||
<locale>
|
||||
<memory>
|
||||
<memory_resource>
|
||||
<new>
|
||||
<numeric>
|
||||
<optional>
|
||||
<random>
|
||||
<ratio>
|
||||
<regex>
|
||||
<stdexcept>
|
||||
<string>
|
||||
<string_view>
|
||||
<system_error>
|
||||
<tuple>
|
||||
<typeindex>
|
||||
<typeinfo>
|
||||
<type_traits>
|
||||
<utility>
|
||||
<valarray>
|
||||
<variant>
|
||||
|
||||
|
||||
# libs
|
||||
# ----
|
||||
|
||||
# jsoncpp
|
||||
<json/json.h>
|
|
@ -58,6 +58,7 @@ extern "C" {
|
|||
*/
|
||||
static void read_v3_aux(lua_State *L, int index)
|
||||
{
|
||||
CHECK_POS_TAB(index);
|
||||
lua_pushvalue(L, index);
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_READ_VECTOR);
|
||||
lua_insert(L, -2);
|
||||
|
|
|
@ -298,7 +298,7 @@ int LuaAreaStore::l_from_file(lua_State *L)
|
|||
const char *filename = luaL_checkstring(L, 2);
|
||||
CHECK_SECURE_PATH(L, filename, false);
|
||||
|
||||
std::ifstream is(filename, std::ios::binary);
|
||||
auto is = open_ifstream(filename, true);
|
||||
return deserialization_helper(L, o->as, is);
|
||||
}
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
#include "version.h"
|
||||
#include "util/hex.h"
|
||||
#include "util/sha1.h"
|
||||
#include "util/sha256.h"
|
||||
#include "my_sha256.h"
|
||||
#include "util/png.h"
|
||||
#include <cstdio>
|
||||
|
||||
|
@ -573,7 +573,7 @@ int ModApiUtil::l_sha256(lua_State *L)
|
|||
|
||||
auto data = readParam<std::string_view>(L, 1);
|
||||
bool hex = !lua_isboolean(L, 2) || !readParam<bool>(L, 2);
|
||||
|
||||
|
||||
std::string data_sha256;
|
||||
data_sha256.resize(SHA256_DIGEST_LENGTH);
|
||||
SHA256(reinterpret_cast<const unsigned char*>(data.data()), data.size(),
|
||||
|
|
|
@ -2547,9 +2547,7 @@ bool Server::addMediaFile(const std::string &filename,
|
|||
|
||||
// Read data
|
||||
std::string filedata;
|
||||
if (!fs::ReadFile(filepath, filedata)) {
|
||||
errorstream << "Server::addMediaFile(): Failed to open \""
|
||||
<< filename << "\" for reading" << std::endl;
|
||||
if (!fs::ReadFile(filepath, filedata, true)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -2717,9 +2715,7 @@ void Server::sendRequestedMedia(session_t peer_id,
|
|||
|
||||
// Read data
|
||||
std::string data;
|
||||
if (!fs::ReadFile(m.path, data)) {
|
||||
errorstream << "Server::sendRequestedMedia(): Failed to read \""
|
||||
<< name << "\"" << std::endl;
|
||||
if (!fs::ReadFile(m.path, data, true)) {
|
||||
continue;
|
||||
}
|
||||
file_size_bunch_total += data.size();
|
||||
|
@ -3620,7 +3616,7 @@ namespace {
|
|||
auto filepath = fs::CreateTempFile();
|
||||
if (filepath.empty())
|
||||
return "";
|
||||
std::ofstream os(filepath, std::ios::binary);
|
||||
auto os = open_ofstream(filepath.c_str(), true);
|
||||
if (!os.good())
|
||||
return "";
|
||||
os << content;
|
||||
|
@ -4202,7 +4198,7 @@ Translations *Server::getTranslationLanguage(const std::string &lang_code)
|
|||
for (const auto &i : m_media) {
|
||||
if (str_ends_with(i.first, suffix)) {
|
||||
std::string data;
|
||||
if (fs::ReadFile(i.second.path, data)) {
|
||||
if (fs::ReadFile(i.second.path, data, true)) {
|
||||
translations->loadTranslation(data);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,9 +48,8 @@ void BanManager::load()
|
|||
{
|
||||
MutexAutoLock lock(m_mutex);
|
||||
infostream<<"BanManager: loading from "<<m_banfilepath<<std::endl;
|
||||
std::ifstream is(m_banfilepath.c_str(), std::ios::binary);
|
||||
auto is = open_ifstream(m_banfilepath.c_str(), false);
|
||||
if (!is.good()) {
|
||||
infostream<<"BanManager: failed loading from "<<m_banfilepath<<std::endl;
|
||||
throw SerializationError("BanManager::load(): Couldn't open file");
|
||||
}
|
||||
|
||||
|
|
|
@ -392,11 +392,8 @@ bool Settings::updateConfigFile(const char *filename)
|
|||
if (!was_modified)
|
||||
return true;
|
||||
|
||||
if (!fs::safeWriteToFile(filename, os.str())) {
|
||||
errorstream << "Error writing configuration file: \""
|
||||
<< filename << "\"" << std::endl;
|
||||
if (!fs::safeWriteToFile(filename, os.str()))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -426,7 +426,7 @@ void TerminalChatConsole::step(int ch)
|
|||
printw("[ESC] Toggle ESC mode |"
|
||||
" [CTRL+C] Shut down |"
|
||||
" (L) in-, (l) decrease loglevel %s",
|
||||
Logger::getLevelLabel((LogLevel) m_log_level).c_str());
|
||||
Logger::getLevelLabel((LogLevel) m_log_level));
|
||||
}
|
||||
|
||||
refresh();
|
||||
|
|
|
@ -20,6 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
#include "texture_override.h"
|
||||
|
||||
#include "log.h"
|
||||
#include "filesys.h"
|
||||
#include "util/string.h"
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
|
@ -48,7 +49,7 @@ static const std::map<std::string, OverrideTarget> override_LUT = {
|
|||
|
||||
TextureOverrideSource::TextureOverrideSource(const std::string &filepath)
|
||||
{
|
||||
std::ifstream infile(filepath.c_str());
|
||||
auto infile = open_ifstream(filepath.c_str(), false);
|
||||
std::string line;
|
||||
int line_index = 0;
|
||||
while (std::getline(infile, line)) {
|
||||
|
|
|
@ -17,6 +17,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "catch.h"
|
||||
|
||||
#include "test.h"
|
||||
|
||||
#include "nodedef.h"
|
||||
|
@ -25,6 +27,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
#include "modchannels.h"
|
||||
#include "util/numeric.h"
|
||||
#include "porting.h"
|
||||
#include "debug.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "catch.h"
|
||||
|
||||
content_t t_CONTENT_STONE;
|
||||
content_t t_CONTENT_GRASS;
|
||||
|
@ -233,6 +240,21 @@ bool run_tests()
|
|||
num_total_tests_run += testmod->num_tests_run;
|
||||
}
|
||||
|
||||
rawstream << "Catch test results: " << std::endl;
|
||||
Catch::Session session{};
|
||||
auto config = session.configData();
|
||||
config.skipBenchmarks = true;
|
||||
config.allowZeroTests = true;
|
||||
session.useConfigData(config);
|
||||
auto exit_code = session.run();
|
||||
// We count all the Catch tests as one test for Minetest's own logging
|
||||
// because we don't have a way to tell how many individual tests Catch ran.
|
||||
++num_total_tests_run;
|
||||
if (exit_code != 0) {
|
||||
++num_modules_failed;
|
||||
++num_total_tests_failed;
|
||||
}
|
||||
|
||||
u64 tdiff = porting::getTimeMs() - t1;
|
||||
|
||||
g_logger.setLevelSilenced(LL_ERROR, false);
|
||||
|
@ -268,8 +290,11 @@ bool run_tests(const std::string &module_name)
|
|||
|
||||
auto testmod = findTestModule(module_name);
|
||||
if (!testmod) {
|
||||
errorstream << "Test module not found: " << module_name << std::endl;
|
||||
return 1;
|
||||
rawstream << "Did not find module, searching Catch tests: " << module_name << std::endl;
|
||||
Catch::Session session;
|
||||
session.configData().testsOrTags.push_back(module_name);
|
||||
auto catch_test_failures = session.run();
|
||||
return catch_test_failures == 0;
|
||||
}
|
||||
|
||||
g_logger.setLevelSilenced(LL_ERROR, true);
|
||||
|
@ -348,11 +373,14 @@ void TestBase::runTest(const char *name, std::function<void()> &&test)
|
|||
rawstream << " at " << e.file << ":" << e.line << std::endl;
|
||||
rawstream << "[FAIL] ";
|
||||
num_tests_failed++;
|
||||
} catch (std::exception &e) {
|
||||
}
|
||||
#if CATCH_UNHANDLED_EXCEPTIONS == 1
|
||||
catch (std::exception &e) {
|
||||
rawstream << "Caught unhandled exception: " << e.what() << std::endl;
|
||||
rawstream << "[FAIL] ";
|
||||
num_tests_failed++;
|
||||
}
|
||||
#endif
|
||||
num_tests_run++;
|
||||
u64 tdiff = porting::getTimeMs() - t1;
|
||||
rawstream << name << " - " << tdiff << "ms" << std::endl;
|
||||
|
|
|
@ -80,13 +80,13 @@ void TestBan::testCreate()
|
|||
BanManager bm(m_testbm);
|
||||
}
|
||||
|
||||
UASSERT(std::ifstream(m_testbm, std::ios::binary).is_open());
|
||||
UASSERT(fs::IsFile(m_testbm));
|
||||
|
||||
// test manual save
|
||||
{
|
||||
BanManager bm(m_testbm2);
|
||||
bm.save();
|
||||
UASSERT(std::ifstream(m_testbm2, std::ios::binary).is_open());
|
||||
UASSERT(fs::IsFile(m_testbm2));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -17,11 +17,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "client/activeobjectmgr.h"
|
||||
#include <algorithm>
|
||||
#include "test.h"
|
||||
|
||||
#include "profiler.h"
|
||||
#include "client/activeobjectmgr.h"
|
||||
|
||||
#include "catch.h"
|
||||
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
|
||||
class TestClientActiveObject : public ClientActiveObject
|
||||
{
|
||||
|
@ -58,9 +61,6 @@ public:
|
|||
|
||||
void runTests(IGameDef *gamedef);
|
||||
|
||||
void testFreeID();
|
||||
void testRegisterObject();
|
||||
void testRemoveObject();
|
||||
void testGetActiveSelectableObjects();
|
||||
};
|
||||
|
||||
|
@ -68,75 +68,74 @@ static TestClientActiveObjectMgr g_test_instance;
|
|||
|
||||
void TestClientActiveObjectMgr::runTests(IGameDef *gamedef)
|
||||
{
|
||||
TEST(testFreeID);
|
||||
TEST(testRegisterObject)
|
||||
TEST(testRemoveObject)
|
||||
TEST(testGetActiveSelectableObjects)
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void TestClientActiveObjectMgr::testFreeID()
|
||||
static TestClientActiveObject* register_default_test_object(
|
||||
client::ActiveObjectMgr &caomgr) {
|
||||
auto test_obj = std::make_unique<TestClientActiveObject>();
|
||||
auto result = test_obj.get();
|
||||
REQUIRE(caomgr.registerObject(std::move(test_obj)));
|
||||
return result;
|
||||
}
|
||||
|
||||
TEST_CASE("test client active object manager")
|
||||
{
|
||||
client::ActiveObjectMgr caomgr;
|
||||
std::vector<u16> aoids;
|
||||
auto tcao1 = register_default_test_object(caomgr);
|
||||
|
||||
u16 aoid = caomgr.getFreeId();
|
||||
// Ensure it's not the same id
|
||||
UASSERT(caomgr.getFreeId() != aoid);
|
||||
|
||||
aoids.push_back(aoid);
|
||||
|
||||
// Register basic objects, ensure we never found
|
||||
for (u8 i = 0; i < UINT8_MAX; i++) {
|
||||
// Register an object
|
||||
auto tcao_u = std::make_unique<TestClientActiveObject>();
|
||||
auto tcao = tcao_u.get();
|
||||
caomgr.registerObject(std::move(tcao_u));
|
||||
aoids.push_back(tcao->getId());
|
||||
|
||||
// Ensure next id is not in registered list
|
||||
UASSERT(std::find(aoids.begin(), aoids.end(), caomgr.getFreeId()) ==
|
||||
aoids.end());
|
||||
SECTION("When we register many client objects, "
|
||||
"then all the assigned IDs should be unique.")
|
||||
{
|
||||
// This should be enough rounds to be pretty confident
|
||||
// there are no duplicates.
|
||||
u16 n = 255;
|
||||
std::unordered_set<u16> ids;
|
||||
ids.insert(tcao1->getId());
|
||||
for (u16 i = 0; i < n; ++i) {
|
||||
auto other_tcao = register_default_test_object(caomgr);
|
||||
ids.insert(other_tcao->getId());
|
||||
}
|
||||
// n added objects & tcao1
|
||||
CHECK(n + 1 == static_cast<u16>(ids.size()));
|
||||
}
|
||||
|
||||
caomgr.clear();
|
||||
}
|
||||
SECTION("two registered objects")
|
||||
{
|
||||
auto tcao2 = register_default_test_object(caomgr);
|
||||
auto tcao2_id = tcao2->getId();
|
||||
|
||||
void TestClientActiveObjectMgr::testRegisterObject()
|
||||
{
|
||||
client::ActiveObjectMgr caomgr;
|
||||
auto tcao_u = std::make_unique<TestClientActiveObject>();
|
||||
auto tcao = tcao_u.get();
|
||||
UASSERT(caomgr.registerObject(std::move(tcao_u)));
|
||||
auto obj1 = caomgr.getActiveObject(tcao1->getId());
|
||||
REQUIRE(obj1 != nullptr);
|
||||
|
||||
u16 id = tcao->getId();
|
||||
auto obj2 = caomgr.getActiveObject(tcao2_id);
|
||||
REQUIRE(obj2 != nullptr);
|
||||
|
||||
auto tcaoToCompare = caomgr.getActiveObject(id);
|
||||
UASSERT(tcaoToCompare->getId() == id);
|
||||
UASSERT(tcaoToCompare == tcao);
|
||||
SECTION("When we query an object by its ID, "
|
||||
"then we should get back an object with that ID.")
|
||||
{
|
||||
CHECK(obj1->getId() == tcao1->getId());
|
||||
CHECK(obj2->getId() == tcao2->getId());
|
||||
}
|
||||
|
||||
tcao_u = std::make_unique<TestClientActiveObject>();
|
||||
tcao = tcao_u.get();
|
||||
UASSERT(caomgr.registerObject(std::move(tcao_u)));
|
||||
UASSERT(caomgr.getActiveObject(tcao->getId()) == tcao);
|
||||
UASSERT(caomgr.getActiveObject(tcao->getId()) != tcaoToCompare);
|
||||
SECTION("When we register and query for an object, "
|
||||
"its memory location should not have changed.")
|
||||
{
|
||||
CHECK(obj1 == tcao1);
|
||||
CHECK(obj2 == tcao2);
|
||||
}
|
||||
}
|
||||
|
||||
caomgr.clear();
|
||||
}
|
||||
|
||||
void TestClientActiveObjectMgr::testRemoveObject()
|
||||
{
|
||||
client::ActiveObjectMgr caomgr;
|
||||
auto tcao_u = std::make_unique<TestClientActiveObject>();
|
||||
auto tcao = tcao_u.get();
|
||||
UASSERT(caomgr.registerObject(std::move(tcao_u)));
|
||||
|
||||
u16 id = tcao->getId();
|
||||
UASSERT(caomgr.getActiveObject(id) != nullptr)
|
||||
|
||||
caomgr.removeObject(tcao->getId());
|
||||
UASSERT(caomgr.getActiveObject(id) == nullptr)
|
||||
SECTION("Given an object has been removed, "
|
||||
"when we query for it, "
|
||||
"then we should get nullptr.")
|
||||
{
|
||||
auto id = tcao1->getId();
|
||||
caomgr.removeObject(tcao1->getId()); // may invalidate tcao1
|
||||
CHECK(caomgr.getActiveObject(id) == nullptr);
|
||||
}
|
||||
|
||||
caomgr.clear();
|
||||
}
|
||||
|
|
|
@ -41,6 +41,7 @@ public:
|
|||
void testRemoveRelativePathComponent();
|
||||
void testSafeWriteToFile();
|
||||
void testCopyFileContents();
|
||||
void testNonExist();
|
||||
};
|
||||
|
||||
static TestFileSys g_test_instance;
|
||||
|
@ -54,6 +55,7 @@ void TestFileSys::runTests(IGameDef *gamedef)
|
|||
TEST(testRemoveRelativePathComponent);
|
||||
TEST(testSafeWriteToFile);
|
||||
TEST(testCopyFileContents);
|
||||
TEST(testNonExist);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -311,3 +313,27 @@ void TestFileSys::testCopyFileContents()
|
|||
UASSERT(fs::ReadFile(file2, contents_actual));
|
||||
UASSERTEQ(auto, contents_actual, test_data);
|
||||
}
|
||||
|
||||
void TestFileSys::testNonExist()
|
||||
{
|
||||
const auto path = getTestTempFile();
|
||||
fs::DeleteSingleFileOrEmptyDirectory(path);
|
||||
|
||||
UASSERT(!fs::IsFile(path));
|
||||
UASSERT(!fs::IsDir(path));
|
||||
UASSERT(!fs::IsExecutable(path));
|
||||
|
||||
std::string s;
|
||||
UASSERT(!fs::ReadFile(path, s));
|
||||
UASSERT(s.empty());
|
||||
|
||||
UASSERT(!fs::Rename(path, getTestTempFile()));
|
||||
|
||||
std::filebuf buf;
|
||||
// with logging enabled to test that code path
|
||||
UASSERT(!fs::OpenStream(buf, path.c_str(), std::ios::in, false, true));
|
||||
UASSERT(!buf.is_open());
|
||||
|
||||
auto ifs = open_ifstream(path.c_str(), false);
|
||||
UASSERT(!ifs.good());
|
||||
}
|
||||
|
|
|
@ -121,7 +121,8 @@ void TestServerModManager::testGetMods()
|
|||
{
|
||||
ServerModManager sm(m_worlddir);
|
||||
const auto &mods = sm.getMods();
|
||||
UASSERTEQ(bool, mods.empty(), false);
|
||||
// `ls ./games/devtest/mods | wc -l` + 1 (test mod)
|
||||
UASSERTEQ(std::size_t, mods.size(), 31 + 1);
|
||||
|
||||
// Ensure we found basenodes mod (part of devtest)
|
||||
// and test_mod (for testing MINETEST_MOD_PATH).
|
||||
|
@ -139,6 +140,9 @@ void TestServerModManager::testGetMods()
|
|||
|
||||
UASSERTEQ(bool, default_found, true);
|
||||
UASSERTEQ(bool, test_mod_found, true);
|
||||
|
||||
UASSERT(mods.front().name == "first_mod");
|
||||
UASSERT(mods.back().name == "last_mod");
|
||||
}
|
||||
|
||||
void TestServerModManager::testGetModspec()
|
||||
|
|
|
@ -161,7 +161,7 @@ void TestThreading::testAtomicSemaphoreThread()
|
|||
|
||||
|
||||
|
||||
static volatile bool g_tls_broken;
|
||||
static std::atomic<bool> g_tls_broken;
|
||||
|
||||
class TLSTestThread : public Thread {
|
||||
public:
|
||||
|
@ -226,7 +226,7 @@ private:
|
|||
*/
|
||||
void TestThreading::testTLS()
|
||||
{
|
||||
static const int num_threads = 10;
|
||||
constexpr int num_threads = 10;
|
||||
|
||||
for (int j = 0; j < num_threads; j++) {
|
||||
g_tls_broken = false;
|
||||
|
|
|
@ -13,7 +13,6 @@ set(UTIL_SRCS
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/quicktune.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/serialize.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/sha1.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/sha256.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/string.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/srp.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/timetaker.cpp
|
||||
|
|
|
@ -1,580 +0,0 @@
|
|||
/* libcrypto/sha/sha256.c */
|
||||
/* ====================================================================
|
||||
* Copyright (c) 1998-2011 The OpenSSL Project. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* 3. All advertising materials mentioning features or use of this
|
||||
* software must display the following acknowledgment:
|
||||
* "This product includes software developed by the OpenSSL Project
|
||||
* for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
|
||||
*
|
||||
* 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
|
||||
* endorse or promote products derived from this software without
|
||||
* prior written permission. For written permission, please contact
|
||||
* openssl-core@openssl.org.
|
||||
*
|
||||
* 5. Products derived from this software may not be called "OpenSSL"
|
||||
* nor may "OpenSSL" appear in their names without prior written
|
||||
* permission of the OpenSSL Project.
|
||||
*
|
||||
* 6. Redistributions of any form whatsoever must retain the following
|
||||
* acknowledgment:
|
||||
* "This product includes software developed by the OpenSSL Project
|
||||
* for use in the OpenSSL Toolkit (http://www.openssl.org/)"
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
|
||||
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR
|
||||
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
* ====================================================================
|
||||
*
|
||||
* This product includes cryptographic software written by Eric Young
|
||||
* (eay@cryptsoft.com). This product includes software written by Tim
|
||||
* Hudson (tjh@cryptsoft.com).
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "util/sha256.h"
|
||||
|
||||
#if defined(_MSC_VER) && !defined(__clang__) && !defined(__attribute__)
|
||||
#define __attribute__(a)
|
||||
#endif
|
||||
|
||||
/* pull HAVE_ENDIAN_H from Minetest */
|
||||
#include "config.h"
|
||||
#if !HAVE_ENDIAN_H
|
||||
#undef HAVE_ENDIAN_H
|
||||
#endif
|
||||
|
||||
/** endian.h **/
|
||||
/*
|
||||
* Public domain
|
||||
* endian.h compatibility shim
|
||||
*/
|
||||
|
||||
#if defined(_WIN32)
|
||||
|
||||
#define LITTLE_ENDIAN 1234
|
||||
#define BIG_ENDIAN 4321
|
||||
#define PDP_ENDIAN 3412
|
||||
|
||||
/*
|
||||
* Use GCC and Visual Studio compiler defines to determine endian.
|
||||
*/
|
||||
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
#define BYTE_ORDER LITTLE_ENDIAN
|
||||
#else
|
||||
#define BYTE_ORDER BIG_ENDIAN
|
||||
#endif
|
||||
|
||||
#elif defined(HAVE_ENDIAN_H)
|
||||
#include <endian.h>
|
||||
|
||||
#elif defined(__MACH__) && defined(__APPLE__)
|
||||
#include <machine/endian.h>
|
||||
|
||||
#elif defined(__sun) || defined(_AIX) || defined(__hpux)
|
||||
#include <arpa/nameser_compat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#elif defined(__sgi)
|
||||
#include <standards.h>
|
||||
#include <sys/endian.h>
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef __STRICT_ALIGNMENT
|
||||
#define __STRICT_ALIGNMENT
|
||||
#if defined(__i386) || defined(__i386__) || defined(__x86_64) || \
|
||||
defined(__x86_64__) || defined(__s390__) || defined(__s390x__) || \
|
||||
defined(__aarch64__) || \
|
||||
((defined(__arm__) || defined(__arm)) && __ARM_ARCH >= 6)
|
||||
#undef __STRICT_ALIGNMENT
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__) && !defined(HAVE_ENDIAN_H)
|
||||
#include <libkern/OSByteOrder.h>
|
||||
#define be16toh(x) OSSwapBigToHostInt16((x))
|
||||
#define htobe16(x) OSSwapHostToBigInt16((x))
|
||||
#define le32toh(x) OSSwapLittleToHostInt32((x))
|
||||
#define be32toh(x) OSSwapBigToHostInt32((x))
|
||||
#define htole32(x) OSSwapHostToLittleInt32(x)
|
||||
#define htobe32(x) OSSwapHostToBigInt32(x)
|
||||
#endif /* __APPLE__ && !HAVE_ENDIAN_H */
|
||||
|
||||
#if defined(_WIN32) && !defined(HAVE_ENDIAN_H)
|
||||
#include <winsock2.h>
|
||||
|
||||
#define be16toh(x) ntohs((x))
|
||||
#define htobe16(x) htons((x))
|
||||
#define le32toh(x) (x)
|
||||
#define be32toh(x) ntohl((x))
|
||||
#define htole32(x) (x)
|
||||
#define htobe32(x) ntohl((x))
|
||||
#endif /* _WIN32 && !HAVE_ENDIAN_H */
|
||||
|
||||
#ifdef __linux__
|
||||
#if !defined(betoh16)
|
||||
#define betoh16(x) be16toh(x)
|
||||
#endif
|
||||
#if !defined(betoh32)
|
||||
#define betoh32(x) be32toh(x)
|
||||
#endif
|
||||
#endif /* __linux__ */
|
||||
|
||||
#if defined(__FreeBSD__)
|
||||
#if !defined(HAVE_ENDIAN_H)
|
||||
#include <sys/endian.h>
|
||||
#endif
|
||||
#if !defined(betoh16)
|
||||
#define betoh16(x) be16toh(x)
|
||||
#endif
|
||||
#if !defined(betoh32)
|
||||
#define betoh32(x) be32toh(x)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(__NetBSD__)
|
||||
#if !defined(betoh16)
|
||||
#define betoh16(x) be16toh(x)
|
||||
#endif
|
||||
#if !defined(betoh32)
|
||||
#define betoh32(x) be32toh(x)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(__sun)
|
||||
#include <sys/byteorder.h>
|
||||
#define be16toh(x) BE_16(x)
|
||||
#define htobe16(x) BE_16(x)
|
||||
#define le32toh(x) LE_32(x)
|
||||
#define be32toh(x) BE_32(x)
|
||||
#define htole32(x) LE_32(x)
|
||||
#define htobe32(x) BE_32(x)
|
||||
#endif
|
||||
/** **/
|
||||
|
||||
/** libcrypto/crypto_internal.h **/
|
||||
#define CTASSERT(x) \
|
||||
extern char _ctassert[(x) ? 1 : -1] __attribute__((__unused__))
|
||||
|
||||
static inline uint32_t
|
||||
crypto_load_be32toh(const uint8_t *src)
|
||||
{
|
||||
uint32_t v;
|
||||
|
||||
memcpy(&v, src, sizeof(v));
|
||||
|
||||
return be32toh(v);
|
||||
}
|
||||
|
||||
static inline void
|
||||
crypto_store_htobe32(uint8_t *dst, uint32_t v)
|
||||
{
|
||||
v = htobe32(v);
|
||||
memcpy(dst, &v, sizeof(v));
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
crypto_ror_u32(uint32_t v, size_t shift)
|
||||
{
|
||||
return (v << (32 - shift)) | (v >> shift);
|
||||
}
|
||||
/** **/
|
||||
|
||||
/** libcrypto/hidden/crypto_namespace.h **/
|
||||
# define LCRYPTO_UNUSED(x)
|
||||
# define LCRYPTO_USED(x)
|
||||
# define LCRYPTO_ALIAS1(pre,x)
|
||||
# define LCRYPTO_ALIAS(x)
|
||||
/** **/
|
||||
|
||||
/* Ensure that SHA_LONG and uint32_t are equivalent. */
|
||||
CTASSERT(sizeof(SHA_LONG) == sizeof(uint32_t));
|
||||
|
||||
static void sha256_block_data_order(SHA256_CTX *ctx, const void *_in, size_t num);
|
||||
|
||||
static const SHA_LONG K256[64] = {
|
||||
0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL,
|
||||
0x3956c25bUL, 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL,
|
||||
0xd807aa98UL, 0x12835b01UL, 0x243185beUL, 0x550c7dc3UL,
|
||||
0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, 0xc19bf174UL,
|
||||
0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL,
|
||||
0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL,
|
||||
0x983e5152UL, 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL,
|
||||
0xc6e00bf3UL, 0xd5a79147UL, 0x06ca6351UL, 0x14292967UL,
|
||||
0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, 0x53380d13UL,
|
||||
0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL,
|
||||
0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL,
|
||||
0xd192e819UL, 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL,
|
||||
0x19a4c116UL, 0x1e376c08UL, 0x2748774cUL, 0x34b0bcb5UL,
|
||||
0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL, 0x682e6ff3UL,
|
||||
0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL,
|
||||
0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL,
|
||||
};
|
||||
|
||||
static inline SHA_LONG
|
||||
Sigma0(SHA_LONG x)
|
||||
{
|
||||
return crypto_ror_u32(x, 2) ^ crypto_ror_u32(x, 13) ^
|
||||
crypto_ror_u32(x, 22);
|
||||
}
|
||||
|
||||
static inline SHA_LONG
|
||||
Sigma1(SHA_LONG x)
|
||||
{
|
||||
return crypto_ror_u32(x, 6) ^ crypto_ror_u32(x, 11) ^
|
||||
crypto_ror_u32(x, 25);
|
||||
}
|
||||
|
||||
static inline SHA_LONG
|
||||
sigma0(SHA_LONG x)
|
||||
{
|
||||
return crypto_ror_u32(x, 7) ^ crypto_ror_u32(x, 18) ^ (x >> 3);
|
||||
}
|
||||
|
||||
static inline SHA_LONG
|
||||
sigma1(SHA_LONG x)
|
||||
{
|
||||
return crypto_ror_u32(x, 17) ^ crypto_ror_u32(x, 19) ^ (x >> 10);
|
||||
}
|
||||
|
||||
static inline SHA_LONG
|
||||
Ch(SHA_LONG x, SHA_LONG y, SHA_LONG z)
|
||||
{
|
||||
return (x & y) ^ (~x & z);
|
||||
}
|
||||
|
||||
static inline SHA_LONG
|
||||
Maj(SHA_LONG x, SHA_LONG y, SHA_LONG z)
|
||||
{
|
||||
return (x & y) ^ (x & z) ^ (y & z);
|
||||
}
|
||||
|
||||
static inline void
|
||||
sha256_msg_schedule_update(SHA_LONG *W0, SHA_LONG W1,
|
||||
SHA_LONG W9, SHA_LONG W14)
|
||||
{
|
||||
*W0 = sigma1(W14) + W9 + sigma0(W1) + *W0;
|
||||
}
|
||||
|
||||
static inline void
|
||||
sha256_round(SHA_LONG *a, SHA_LONG *b, SHA_LONG *c, SHA_LONG *d,
|
||||
SHA_LONG *e, SHA_LONG *f, SHA_LONG *g, SHA_LONG *h,
|
||||
SHA_LONG Kt, SHA_LONG Wt)
|
||||
{
|
||||
SHA_LONG T1, T2;
|
||||
|
||||
T1 = *h + Sigma1(*e) + Ch(*e, *f, *g) + Kt + Wt;
|
||||
T2 = Sigma0(*a) + Maj(*a, *b, *c);
|
||||
|
||||
*h = *g;
|
||||
*g = *f;
|
||||
*f = *e;
|
||||
*e = *d + T1;
|
||||
*d = *c;
|
||||
*c = *b;
|
||||
*b = *a;
|
||||
*a = T1 + T2;
|
||||
}
|
||||
|
||||
static void
|
||||
sha256_block_data_order(SHA256_CTX *ctx, const void *_in, size_t num)
|
||||
{
|
||||
const uint8_t *in = _in;
|
||||
const SHA_LONG *in32;
|
||||
SHA_LONG a, b, c, d, e, f, g, h;
|
||||
SHA_LONG X[16];
|
||||
int i;
|
||||
|
||||
while (num--) {
|
||||
a = ctx->h[0];
|
||||
b = ctx->h[1];
|
||||
c = ctx->h[2];
|
||||
d = ctx->h[3];
|
||||
e = ctx->h[4];
|
||||
f = ctx->h[5];
|
||||
g = ctx->h[6];
|
||||
h = ctx->h[7];
|
||||
|
||||
if ((size_t)in % 4 == 0) {
|
||||
/* Input is 32 bit aligned. */
|
||||
in32 = (const SHA_LONG *)in;
|
||||
X[0] = be32toh(in32[0]);
|
||||
X[1] = be32toh(in32[1]);
|
||||
X[2] = be32toh(in32[2]);
|
||||
X[3] = be32toh(in32[3]);
|
||||
X[4] = be32toh(in32[4]);
|
||||
X[5] = be32toh(in32[5]);
|
||||
X[6] = be32toh(in32[6]);
|
||||
X[7] = be32toh(in32[7]);
|
||||
X[8] = be32toh(in32[8]);
|
||||
X[9] = be32toh(in32[9]);
|
||||
X[10] = be32toh(in32[10]);
|
||||
X[11] = be32toh(in32[11]);
|
||||
X[12] = be32toh(in32[12]);
|
||||
X[13] = be32toh(in32[13]);
|
||||
X[14] = be32toh(in32[14]);
|
||||
X[15] = be32toh(in32[15]);
|
||||
} else {
|
||||
/* Input is not 32 bit aligned. */
|
||||
X[0] = crypto_load_be32toh(&in[0 * 4]);
|
||||
X[1] = crypto_load_be32toh(&in[1 * 4]);
|
||||
X[2] = crypto_load_be32toh(&in[2 * 4]);
|
||||
X[3] = crypto_load_be32toh(&in[3 * 4]);
|
||||
X[4] = crypto_load_be32toh(&in[4 * 4]);
|
||||
X[5] = crypto_load_be32toh(&in[5 * 4]);
|
||||
X[6] = crypto_load_be32toh(&in[6 * 4]);
|
||||
X[7] = crypto_load_be32toh(&in[7 * 4]);
|
||||
X[8] = crypto_load_be32toh(&in[8 * 4]);
|
||||
X[9] = crypto_load_be32toh(&in[9 * 4]);
|
||||
X[10] = crypto_load_be32toh(&in[10 * 4]);
|
||||
X[11] = crypto_load_be32toh(&in[11 * 4]);
|
||||
X[12] = crypto_load_be32toh(&in[12 * 4]);
|
||||
X[13] = crypto_load_be32toh(&in[13 * 4]);
|
||||
X[14] = crypto_load_be32toh(&in[14 * 4]);
|
||||
X[15] = crypto_load_be32toh(&in[15 * 4]);
|
||||
}
|
||||
in += SHA256_CBLOCK;
|
||||
|
||||
sha256_round(&a, &b, &c, &d, &e, &f, &g, &h, K256[0], X[0]);
|
||||
sha256_round(&a, &b, &c, &d, &e, &f, &g, &h, K256[1], X[1]);
|
||||
sha256_round(&a, &b, &c, &d, &e, &f, &g, &h, K256[2], X[2]);
|
||||
sha256_round(&a, &b, &c, &d, &e, &f, &g, &h, K256[3], X[3]);
|
||||
sha256_round(&a, &b, &c, &d, &e, &f, &g, &h, K256[4], X[4]);
|
||||
sha256_round(&a, &b, &c, &d, &e, &f, &g, &h, K256[5], X[5]);
|
||||
sha256_round(&a, &b, &c, &d, &e, &f, &g, &h, K256[6], X[6]);
|
||||
sha256_round(&a, &b, &c, &d, &e, &f, &g, &h, K256[7], X[7]);
|
||||
sha256_round(&a, &b, &c, &d, &e, &f, &g, &h, K256[8], X[8]);
|
||||
sha256_round(&a, &b, &c, &d, &e, &f, &g, &h, K256[9], X[9]);
|
||||
sha256_round(&a, &b, &c, &d, &e, &f, &g, &h, K256[10], X[10]);
|
||||
sha256_round(&a, &b, &c, &d, &e, &f, &g, &h, K256[11], X[11]);
|
||||
sha256_round(&a, &b, &c, &d, &e, &f, &g, &h, K256[12], X[12]);
|
||||
sha256_round(&a, &b, &c, &d, &e, &f, &g, &h, K256[13], X[13]);
|
||||
sha256_round(&a, &b, &c, &d, &e, &f, &g, &h, K256[14], X[14]);
|
||||
sha256_round(&a, &b, &c, &d, &e, &f, &g, &h, K256[15], X[15]);
|
||||
|
||||
for (i = 16; i < 64; i += 16) {
|
||||
sha256_msg_schedule_update(&X[0], X[1], X[9], X[14]);
|
||||
sha256_msg_schedule_update(&X[1], X[2], X[10], X[15]);
|
||||
sha256_msg_schedule_update(&X[2], X[3], X[11], X[0]);
|
||||
sha256_msg_schedule_update(&X[3], X[4], X[12], X[1]);
|
||||
sha256_msg_schedule_update(&X[4], X[5], X[13], X[2]);
|
||||
sha256_msg_schedule_update(&X[5], X[6], X[14], X[3]);
|
||||
sha256_msg_schedule_update(&X[6], X[7], X[15], X[4]);
|
||||
sha256_msg_schedule_update(&X[7], X[8], X[0], X[5]);
|
||||
sha256_msg_schedule_update(&X[8], X[9], X[1], X[6]);
|
||||
sha256_msg_schedule_update(&X[9], X[10], X[2], X[7]);
|
||||
sha256_msg_schedule_update(&X[10], X[11], X[3], X[8]);
|
||||
sha256_msg_schedule_update(&X[11], X[12], X[4], X[9]);
|
||||
sha256_msg_schedule_update(&X[12], X[13], X[5], X[10]);
|
||||
sha256_msg_schedule_update(&X[13], X[14], X[6], X[11]);
|
||||
sha256_msg_schedule_update(&X[14], X[15], X[7], X[12]);
|
||||
sha256_msg_schedule_update(&X[15], X[0], X[8], X[13]);
|
||||
|
||||
sha256_round(&a, &b, &c, &d, &e, &f, &g, &h, K256[i + 0], X[0]);
|
||||
sha256_round(&a, &b, &c, &d, &e, &f, &g, &h, K256[i + 1], X[1]);
|
||||
sha256_round(&a, &b, &c, &d, &e, &f, &g, &h, K256[i + 2], X[2]);
|
||||
sha256_round(&a, &b, &c, &d, &e, &f, &g, &h, K256[i + 3], X[3]);
|
||||
sha256_round(&a, &b, &c, &d, &e, &f, &g, &h, K256[i + 4], X[4]);
|
||||
sha256_round(&a, &b, &c, &d, &e, &f, &g, &h, K256[i + 5], X[5]);
|
||||
sha256_round(&a, &b, &c, &d, &e, &f, &g, &h, K256[i + 6], X[6]);
|
||||
sha256_round(&a, &b, &c, &d, &e, &f, &g, &h, K256[i + 7], X[7]);
|
||||
sha256_round(&a, &b, &c, &d, &e, &f, &g, &h, K256[i + 8], X[8]);
|
||||
sha256_round(&a, &b, &c, &d, &e, &f, &g, &h, K256[i + 9], X[9]);
|
||||
sha256_round(&a, &b, &c, &d, &e, &f, &g, &h, K256[i + 10], X[10]);
|
||||
sha256_round(&a, &b, &c, &d, &e, &f, &g, &h, K256[i + 11], X[11]);
|
||||
sha256_round(&a, &b, &c, &d, &e, &f, &g, &h, K256[i + 12], X[12]);
|
||||
sha256_round(&a, &b, &c, &d, &e, &f, &g, &h, K256[i + 13], X[13]);
|
||||
sha256_round(&a, &b, &c, &d, &e, &f, &g, &h, K256[i + 14], X[14]);
|
||||
sha256_round(&a, &b, &c, &d, &e, &f, &g, &h, K256[i + 15], X[15]);
|
||||
}
|
||||
|
||||
ctx->h[0] += a;
|
||||
ctx->h[1] += b;
|
||||
ctx->h[2] += c;
|
||||
ctx->h[3] += d;
|
||||
ctx->h[4] += e;
|
||||
ctx->h[5] += f;
|
||||
ctx->h[6] += g;
|
||||
ctx->h[7] += h;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
SHA256_Init(SHA256_CTX *c)
|
||||
{
|
||||
memset(c, 0, sizeof(*c));
|
||||
|
||||
c->h[0] = 0x6a09e667UL;
|
||||
c->h[1] = 0xbb67ae85UL;
|
||||
c->h[2] = 0x3c6ef372UL;
|
||||
c->h[3] = 0xa54ff53aUL;
|
||||
c->h[4] = 0x510e527fUL;
|
||||
c->h[5] = 0x9b05688cUL;
|
||||
c->h[6] = 0x1f83d9abUL;
|
||||
c->h[7] = 0x5be0cd19UL;
|
||||
|
||||
c->md_len = SHA256_DIGEST_LENGTH;
|
||||
|
||||
return 1;
|
||||
}
|
||||
LCRYPTO_ALIAS(SHA256_Init);
|
||||
|
||||
int
|
||||
SHA256_Update(SHA256_CTX *c, const void *data_, size_t len)
|
||||
{
|
||||
const unsigned char *data = data_;
|
||||
unsigned char *p;
|
||||
SHA_LONG l;
|
||||
size_t n;
|
||||
|
||||
if (len == 0)
|
||||
return 1;
|
||||
|
||||
l = (c->Nl + (((SHA_LONG)len) << 3)) & 0xffffffffUL;
|
||||
/* 95-05-24 eay Fixed a bug with the overflow handling, thanks to
|
||||
* Wei Dai <weidai@eskimo.com> for pointing it out. */
|
||||
if (l < c->Nl) /* overflow */
|
||||
c->Nh++;
|
||||
c->Nh += (SHA_LONG)(len >> 29); /* might cause compiler warning on 16-bit */
|
||||
c->Nl = l;
|
||||
|
||||
n = c->num;
|
||||
if (n != 0) {
|
||||
p = (unsigned char *)c->data;
|
||||
|
||||
if (len >= SHA_CBLOCK || len + n >= SHA_CBLOCK) {
|
||||
memcpy(p + n, data, SHA_CBLOCK - n);
|
||||
sha256_block_data_order(c, p, 1);
|
||||
n = SHA_CBLOCK - n;
|
||||
data += n;
|
||||
len -= n;
|
||||
c->num = 0;
|
||||
memset(p, 0, SHA_CBLOCK); /* keep it zeroed */
|
||||
} else {
|
||||
memcpy(p + n, data, len);
|
||||
c->num += (unsigned int)len;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
n = len/SHA_CBLOCK;
|
||||
if (n > 0) {
|
||||
sha256_block_data_order(c, data, n);
|
||||
n *= SHA_CBLOCK;
|
||||
data += n;
|
||||
len -= n;
|
||||
}
|
||||
|
||||
if (len != 0) {
|
||||
p = (unsigned char *)c->data;
|
||||
c->num = (unsigned int)len;
|
||||
memcpy(p, data, len);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
LCRYPTO_ALIAS(SHA256_Update);
|
||||
|
||||
void
|
||||
SHA256_Transform(SHA256_CTX *c, const unsigned char *data)
|
||||
{
|
||||
sha256_block_data_order(c, data, 1);
|
||||
}
|
||||
LCRYPTO_ALIAS(SHA256_Transform);
|
||||
|
||||
int
|
||||
SHA256_Final(unsigned char *md, SHA256_CTX *c)
|
||||
{
|
||||
unsigned char *p = (unsigned char *)c->data;
|
||||
size_t n = c->num;
|
||||
unsigned int nn;
|
||||
|
||||
p[n] = 0x80; /* there is always room for one */
|
||||
n++;
|
||||
|
||||
if (n > (SHA_CBLOCK - 8)) {
|
||||
memset(p + n, 0, SHA_CBLOCK - n);
|
||||
n = 0;
|
||||
sha256_block_data_order(c, p, 1);
|
||||
}
|
||||
|
||||
memset(p + n, 0, SHA_CBLOCK - 8 - n);
|
||||
c->data[SHA_LBLOCK - 2] = htobe32(c->Nh);
|
||||
c->data[SHA_LBLOCK - 1] = htobe32(c->Nl);
|
||||
|
||||
sha256_block_data_order(c, p, 1);
|
||||
c->num = 0;
|
||||
memset(p, 0, SHA_CBLOCK);
|
||||
|
||||
/*
|
||||
* Note that FIPS180-2 discusses "Truncation of the Hash Function Output."
|
||||
* default: case below covers for it. It's not clear however if it's
|
||||
* permitted to truncate to amount of bytes not divisible by 4. I bet not,
|
||||
* but if it is, then default: case shall be extended. For reference.
|
||||
* Idea behind separate cases for pre-defined lengths is to let the
|
||||
* compiler decide if it's appropriate to unroll small loops.
|
||||
*/
|
||||
switch (c->md_len) {
|
||||
case SHA256_DIGEST_LENGTH:
|
||||
for (nn = 0; nn < SHA256_DIGEST_LENGTH / 4; nn++) {
|
||||
crypto_store_htobe32(md, c->h[nn]);
|
||||
md += 4;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if (c->md_len > SHA256_DIGEST_LENGTH)
|
||||
return 0;
|
||||
for (nn = 0; nn < c->md_len / 4; nn++) {
|
||||
crypto_store_htobe32(md, c->h[nn]);
|
||||
md += 4;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
LCRYPTO_ALIAS(SHA256_Final);
|
||||
|
||||
unsigned char *
|
||||
SHA256(const unsigned char *d, size_t n, unsigned char *md)
|
||||
{
|
||||
SHA256_CTX c;
|
||||
static unsigned char m[SHA256_DIGEST_LENGTH];
|
||||
|
||||
if (md == NULL)
|
||||
md = m;
|
||||
|
||||
SHA256_Init(&c);
|
||||
SHA256_Update(&c, d, n);
|
||||
SHA256_Final(md, &c);
|
||||
|
||||
memset(&c, 0, sizeof(c));
|
||||
|
||||
return (md);
|
||||
}
|
||||
LCRYPTO_ALIAS(SHA256);
|
|
@ -1,103 +0,0 @@
|
|||
/* libcrypto/sha/sha.h */
|
||||
/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* This package is an SSL implementation written
|
||||
* by Eric Young (eay@cryptsoft.com).
|
||||
* The implementation was written so as to conform with Netscapes SSL.
|
||||
*
|
||||
* This library is free for commercial and non-commercial use as long as
|
||||
* the following conditions are aheared to. The following conditions
|
||||
* apply to all code found in this distribution, be it the RC4, RSA,
|
||||
* lhash, DES, etc., code; not just the SSL code. The SSL documentation
|
||||
* included with this distribution is covered by the same copyright terms
|
||||
* except that the holder is Tim Hudson (tjh@cryptsoft.com).
|
||||
*
|
||||
* Copyright remains Eric Young's, and as such any Copyright notices in
|
||||
* the code are not to be removed.
|
||||
* If this package is used in a product, Eric Young should be given attribution
|
||||
* as the author of the parts of the library used.
|
||||
* This can be in the form of a textual message at program startup or
|
||||
* in documentation (online or textual) provided with the package.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* "This product includes cryptographic software written by
|
||||
* Eric Young (eay@cryptsoft.com)"
|
||||
* The word 'cryptographic' can be left out if the rouines from the library
|
||||
* being used are not cryptographic related :-).
|
||||
* 4. If you include any Windows specific code (or a derivative thereof) from
|
||||
* the apps directory (application code) you must include an acknowledgement:
|
||||
* "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* The licence and distribution terms for any publically available version or
|
||||
* derivative of this code cannot be changed. i.e. this code cannot simply be
|
||||
* copied and put under another distribution licence
|
||||
* [including the GNU Public Licence.]
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#ifndef HEADER_SHA_H
|
||||
#define HEADER_SHA_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
* ! SHA_LONG has to be at least 32 bits wide. !
|
||||
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
*/
|
||||
|
||||
#define SHA_LONG unsigned int
|
||||
|
||||
#define SHA_LBLOCK 16
|
||||
#define SHA_CBLOCK (SHA_LBLOCK*4) /* SHA treats input data as a
|
||||
* contiguous array of 32 bit
|
||||
* wide big-endian values. */
|
||||
|
||||
#define SHA256_CBLOCK (SHA_LBLOCK*4) /* SHA-256 treats input data as a
|
||||
* contiguous array of 32 bit
|
||||
* wide big-endian values. */
|
||||
#define SHA256_DIGEST_LENGTH 32
|
||||
|
||||
typedef struct SHA256state_st {
|
||||
SHA_LONG h[8];
|
||||
SHA_LONG Nl, Nh;
|
||||
SHA_LONG data[SHA_LBLOCK];
|
||||
unsigned int num, md_len;
|
||||
} SHA256_CTX;
|
||||
|
||||
int SHA256_Init(SHA256_CTX *c);
|
||||
int SHA256_Update(SHA256_CTX *c, const void *data, size_t len);
|
||||
int SHA256_Final(unsigned char *md, SHA256_CTX *c);
|
||||
unsigned char *SHA256(const unsigned char *d, size_t n, unsigned char *md);
|
||||
void SHA256_Transform(SHA256_CTX *c, const unsigned char *data);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -50,7 +50,7 @@
|
|||
#include <mini-gmp.h>
|
||||
#endif
|
||||
|
||||
#include "util/sha256.h"
|
||||
#include "my_sha256.h"
|
||||
|
||||
#include "srp.h"
|
||||
//#define CSRP_USE_SHA1
|
||||
|
|
|
@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
#include "nodedef.h"
|
||||
#include "util/directiontables.h"
|
||||
#include "util/timetaker.h"
|
||||
#include "porting.h"
|
||||
#include <cstring> // memcpy, memset
|
||||
|
||||
/*
|
||||
|
@ -40,12 +41,15 @@ VoxelManipulator::~VoxelManipulator()
|
|||
|
||||
void VoxelManipulator::clear()
|
||||
{
|
||||
// Reset area to volume=0
|
||||
m_area = VoxelArea();
|
||||
// Reset area to empty volume
|
||||
VoxelArea old;
|
||||
std::swap(m_area, old);
|
||||
delete[] m_data;
|
||||
m_data = nullptr;
|
||||
delete[] m_flags;
|
||||
m_flags = nullptr;
|
||||
|
||||
porting::TrackFreedMemory((sizeof(*m_data) + sizeof(*m_flags)) * old.getVolume());
|
||||
}
|
||||
|
||||
void VoxelManipulator::print(std::ostream &o, const NodeDefManager *ndef,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue