1
0
Fork 0
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:
Gefüllte Taubenbrust 2024-06-11 13:59:14 +02:00
commit cd6e304cfa
172 changed files with 27747 additions and 18949 deletions

View file

@ -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)

View file

@ -31,7 +31,6 @@ class TestServerActiveObjectMgr;
template <typename T>
class ActiveObjectMgr
{
friend class ::TestClientActiveObjectMgr;
friend class ::TestServerActiveObjectMgr;
public:

View file

@ -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)
{

View file

@ -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"

View file

@ -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"

View file

@ -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>

View file

@ -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

View file

@ -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>

View file

@ -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
View 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
View 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();
}

View file

@ -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) {

View file

@ -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++;
}

View file

@ -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);
}
}

View file

@ -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);

View file

@ -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)

View file

@ -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)

View file

@ -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;

View file

@ -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;

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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;

View file

@ -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;

View file

@ -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);
}

View file

@ -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;

View file

@ -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;
}

View file

@ -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

View file

@ -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;
}

View file

@ -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
);
}

View file

@ -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;
}

View file

@ -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

View file

@ -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;
}

View file

@ -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

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -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;
/**

View file

@ -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);
}

View file

@ -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)
{
}

View file

@ -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;

View file

@ -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(

View file

@ -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

View file

@ -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

View file

@ -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;
}

View file

@ -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;
}
}

View file

@ -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");
}

View file

@ -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);
};

View file

@ -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) {

View file

@ -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();

View file

@ -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();

View file

@ -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();

View file

@ -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;
}

View file

@ -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;

View file

@ -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();

View file

@ -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
)};
}

View file

@ -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{};
};

View file

@ -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();

View file

@ -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)
{

View file

@ -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) ||

View file

@ -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);

View file

@ -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

View file

@ -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;

View file

@ -85,6 +85,7 @@ MapBlock::~MapBlock()
#endif
delete[] data;
porting::TrackFreedMemory(sizeof(MapNode) * nodecount);
}
bool MapBlock::onObjectsActivation()

View file

@ -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;
}

View file

@ -28,6 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "porting.h"
#include "settings.h"
#include <algorithm>
///////////////////////////////////////////////////////////////////////////////

View file

@ -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
View 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");
}
}

View file

@ -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);

View file

@ -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;
}

View file

@ -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;

View file

@ -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;

View file

@ -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

View file

@ -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
View 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>

View file

@ -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);

View file

@ -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);
}

View file

@ -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(),

View file

@ -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);
}
}

View file

@ -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");
}

View 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;
}

View file

@ -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();

View file

@ -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)) {

View file

@ -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;

View file

@ -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));
}
}

View file

@ -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();
}

View file

@ -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());
}

View file

@ -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()

View file

@ -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;

View file

@ -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

View file

@ -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);

View file

@ -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

View file

@ -50,7 +50,7 @@
#include <mini-gmp.h>
#endif
#include "util/sha256.h"
#include "my_sha256.h"
#include "srp.h"
//#define CSRP_USE_SHA1

View file

@ -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,