diff options
Diffstat (limited to 'src')
92 files changed, 1151 insertions, 1536 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7bcf8d6c7..8a6eabccc 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -63,14 +63,13 @@ if(ENABLE_GETTEXT) if(GETTEXTLIB_FOUND) if(WIN32) message(STATUS "GetText library: ${GETTEXT_LIBRARY}") - message(STATUS "GetText DLL: ${GETTEXT_DLL}") - message(STATUS "GetText iconv DLL: ${GETTEXT_ICONV_DLL}") + message(STATUS "GetText DLL(s): ${GETTEXT_DLL}") endif() set(USE_GETTEXT TRUE) message(STATUS "GetText enabled; locales found: ${GETTEXT_AVAILABLE_LOCALES}") endif(GETTEXTLIB_FOUND) else() - mark_as_advanced(GETTEXT_ICONV_DLL GETTEXT_INCLUDE_DIR GETTEXT_LIBRARY GETTEXT_MSGFMT) + mark_as_advanced(GETTEXT_INCLUDE_DIR GETTEXT_LIBRARY GETTEXT_MSGFMT) message(STATUS "GetText disabled.") endif() @@ -268,8 +267,10 @@ if(WIN32) if(ENABLE_SOUND) set(OPENAL_DLL "" CACHE FILEPATH "Path to OpenAL32.dll for installation (optional)") set(OGG_DLL "" CACHE FILEPATH "Path to libogg.dll for installation (optional)") - set(VORBIS_DLL "" CACHE FILEPATH "Path to libvorbis.dll for installation (optional)") - set(VORBISFILE_DLL "" CACHE FILEPATH "Path to libvorbisfile.dll for installation (optional)") + set(VORBIS_DLL "" CACHE FILEPATH "Path to Vorbis DLLs for installation (optional)") + endif() + if(USE_GETTEXT) + set(GETTEXT_DLL "" CACHE FILEPATH "Path to Intl/Iconv DLLs for installation (optional)") endif() if(USE_LUAJIT) set(LUA_DLL "" CACHE FILEPATH "Path to luajit-5.1.dll for installation (optional)") @@ -295,7 +296,6 @@ else() endif(NOT HAIKU AND NOT APPLE) find_package(JPEG REQUIRED) - find_package(BZip2 REQUIRED) find_package(PNG REQUIRED) if(APPLE) find_library(CARBON_LIB Carbon REQUIRED) @@ -713,7 +713,7 @@ if(MSVC) # Flags that cannot be shared between cl and clang-cl # https://clang.llvm.org/docs/UsersManual.html#clang-cl - if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fuse-ld=lld") # Disable pragma-pack warning @@ -731,7 +731,7 @@ else() else() set(RELEASE_WARNING_FLAGS "") endif() - if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + if(CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?Clang") set(WARNING_FLAGS "${WARNING_FLAGS} -Wsign-compare") endif() @@ -768,7 +768,7 @@ else() else() set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${MATH_FLAGS}") endif() - endif(CMAKE_SYSTEM_NAME MATCHES "(Darwin|BSD|DragonFly)") + endif() set(CMAKE_CXX_FLAGS_SEMIDEBUG "-g -O1 -Wall ${WARNING_FLAGS} ${OTHER_FLAGS}") set(CMAKE_CXX_FLAGS_DEBUG "-g -O0 -Wall ${WARNING_FLAGS} ${OTHER_FLAGS}") @@ -805,7 +805,7 @@ if(WIN32) FILES_MATCHING PATTERN "*.dll") else() # Use the old-style way to install dll's - if(USE_SOUND) + if(BUILD_CLIENT AND USE_SOUND) if(OPENAL_DLL) install(FILES ${OPENAL_DLL} DESTINATION ${BINDIR}) endif() @@ -815,9 +815,6 @@ if(WIN32) if(VORBIS_DLL) install(FILES ${VORBIS_DLL} DESTINATION ${BINDIR}) endif() - if(VORBISFILE_DLL) - install(FILES ${VORBISFILE_DLL} DESTINATION ${BINDIR}) - endif() endif() if(CURL_DLL) install(FILES ${CURL_DLL} DESTINATION ${BINDIR}) @@ -825,7 +822,7 @@ if(WIN32) if(ZLIB_DLL) install(FILES ${ZLIB_DLL} DESTINATION ${BINDIR}) endif() - if(FREETYPE_DLL) + if(BUILD_CLIENT AND FREETYPE_DLL) install(FILES ${FREETYPE_DLL} DESTINATION ${BINDIR}) endif() if(SQLITE3_DLL) @@ -837,6 +834,12 @@ if(WIN32) if(LUA_DLL) install(FILES ${LUA_DLL} DESTINATION ${BINDIR}) endif() + if(BUILD_CLIENT AND IRRLICHT_DLL) + install(FILES ${IRRLICHT_DLL} DESTINATION ${BINDIR}) + endif() + if(BUILD_CLIENT AND USE_GETTEXT AND GETTEXT_DLL) + install(FILES ${GETTEXT_DLL} DESTINATION ${BINDIR}) + endif() endif() endif() @@ -864,6 +867,7 @@ if(BUILD_CLIENT) endforeach() endif() + # Install necessary fonts depending on configuration if(USE_FREETYPE) install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../fonts" DESTINATION "${SHAREDIR}" FILES_MATCHING PATTERN "*.ttf" PATTERN "*.txt") @@ -871,22 +875,6 @@ if(BUILD_CLIENT) install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../fonts" DESTINATION "${SHAREDIR}" FILES_MATCHING PATTERN "*.png" PATTERN "*.xml") endif() - - if(WIN32) - if(NOT VCPKG_APPLOCAL_DEPS) - if(DEFINED IRRLICHT_DLL) - install(FILES ${IRRLICHT_DLL} DESTINATION ${BINDIR}) - endif() - if(USE_GETTEXT) - if(DEFINED GETTEXT_DLL) - install(FILES ${GETTEXT_DLL} DESTINATION ${BINDIR}) - endif() - if(DEFINED GETTEXT_ICONV_DLL) - install(FILES ${GETTEXT_ICONV_DLL} DESTINATION ${BINDIR}) - endif() - endif() - endif() - endif() endif(BUILD_CLIENT) if(BUILD_SERVER) diff --git a/src/activeobjectmgr.h b/src/activeobjectmgr.h index 95e7d3344..aa0538e60 100644 --- a/src/activeobjectmgr.h +++ b/src/activeobjectmgr.h @@ -25,7 +25,8 @@ with this program; if not, write to the Free Software Foundation, Inc., class TestClientActiveObjectMgr; class TestServerActiveObjectMgr; -template <typename T> class ActiveObjectMgr +template <typename T> +class ActiveObjectMgr { friend class ::TestClientActiveObjectMgr; friend class ::TestServerActiveObjectMgr; diff --git a/src/client/camera.cpp b/src/client/camera.cpp index 9a08254b4..5158d0dd1 100644 --- a/src/client/camera.cpp +++ b/src/client/camera.cpp @@ -79,6 +79,7 @@ Camera::Camera(MapDrawControl &draw_control, Client *client): m_cache_fov = std::fmax(g_settings->getFloat("fov"), 45.0f); m_arm_inertia = g_settings->getBool("arm_inertia"); m_nametags.clear(); + m_show_nametag_backgrounds = g_settings->getBool("show_nametag_backgrounds"); } Camera::~Camera() @@ -663,7 +664,7 @@ void Camera::wield(const ItemStack &item) void Camera::drawWieldedTool(irr::core::matrix4* translation) { // Clear Z buffer so that the wielded tool stays in front of world geometry - m_wieldmgr->getVideoDriver()->clearZBuffer(); + m_wieldmgr->getVideoDriver()->clearBuffers(video::ECBF_DEPTH); // Draw the wielded node (in a separate scene manager) scene::ICameraSceneNode* cam = m_wieldmgr->getActiveCamera(); @@ -696,18 +697,14 @@ void Camera::drawNametags() v2u32 screensize = driver->getScreenSize(); for (const Nametag *nametag : m_nametags) { - if (nametag->nametag_color.getAlpha() == 0) { - // Enforce hiding nametag, - // because if freetype is enabled, a grey - // shadow can remain. - continue; - } - v3f pos = nametag->parent_node->getAbsolutePosition() + nametag->nametag_pos * BS; + // Nametags are hidden in GenericCAO::updateNametag() + + v3f pos = nametag->parent_node->getAbsolutePosition() + nametag->pos * BS; f32 transformed_pos[4] = { pos.X, pos.Y, pos.Z, 1.0f }; trans.multiplyWith1x4Matrix(transformed_pos); if (transformed_pos[3] > 0) { std::wstring nametag_colorless = - unescape_translate(utf8_to_wide(nametag->nametag_text)); + unescape_translate(utf8_to_wide(nametag->text)); core::dimension2d<u32> textsize = font->getDimension( nametag_colorless.c_str()); f32 zDiv = transformed_pos[3] == 0.0f ? 1.0f : @@ -720,26 +717,22 @@ void Camera::drawNametags() core::rect<s32> size(0, 0, textsize.Width, textsize.Height); core::rect<s32> bg_size(-2, 0, textsize.Width+2, textsize.Height); - video::SColor textColor = nametag->nametag_color; - - bool darkBackground = textColor.getLuminance() > 186; - video::SColor backgroundColor = darkBackground - ? video::SColor(50, 50, 50, 50) - : video::SColor(50, 255, 255, 255); - driver->draw2DRectangle(backgroundColor, bg_size + screen_pos); + auto bgcolor = nametag->getBgColor(m_show_nametag_backgrounds); + if (bgcolor.getAlpha() != 0) + driver->draw2DRectangle(bgcolor, bg_size + screen_pos); font->draw( - translate_string(utf8_to_wide(nametag->nametag_text)).c_str(), - size + screen_pos, textColor); + translate_string(utf8_to_wide(nametag->text)).c_str(), + size + screen_pos, nametag->textcolor); } } } Nametag *Camera::addNametag(scene::ISceneNode *parent_node, - const std::string &nametag_text, video::SColor nametag_color, - const v3f &pos) + const std::string &text, video::SColor textcolor, + Optional<video::SColor> bgcolor, const v3f &pos) { - Nametag *nametag = new Nametag(parent_node, nametag_text, nametag_color, pos); + Nametag *nametag = new Nametag(parent_node, text, textcolor, bgcolor, pos); m_nametags.push_back(nametag); return nametag; } diff --git a/src/client/camera.h b/src/client/camera.h index 16a1961be..6fd8d9aa7 100644 --- a/src/client/camera.h +++ b/src/client/camera.h @@ -25,27 +25,47 @@ with this program; if not, write to the Free Software Foundation, Inc., #include <ICameraSceneNode.h> #include <ISceneNode.h> #include <list> +#include "util/Optional.h" class LocalPlayer; struct MapDrawControl; class Client; class WieldMeshSceneNode; -struct Nametag { +struct Nametag +{ + scene::ISceneNode *parent_node; + std::string text; + video::SColor textcolor; + Optional<video::SColor> bgcolor; + v3f pos; + Nametag(scene::ISceneNode *a_parent_node, - const std::string &a_nametag_text, - const video::SColor &a_nametag_color, - const v3f &a_nametag_pos): + const std::string &text, + const video::SColor &textcolor, + const Optional<video::SColor> &bgcolor, + const v3f &pos): parent_node(a_parent_node), - nametag_text(a_nametag_text), - nametag_color(a_nametag_color), - nametag_pos(a_nametag_pos) + text(text), + textcolor(textcolor), + bgcolor(bgcolor), + pos(pos) { } - scene::ISceneNode *parent_node; - std::string nametag_text; - video::SColor nametag_color; - v3f nametag_pos; + + video::SColor getBgColor(bool use_fallback) const + { + if (bgcolor) + return bgcolor.value(); + else if (!use_fallback) + return video::SColor(0, 0, 0, 0); + else if (textcolor.getLuminance() > 186) + // Dark background for light text + return video::SColor(50, 50, 50, 50); + else + // Light background for dark text + return video::SColor(50, 255, 255, 255); + } }; enum CameraMode {CAMERA_MODE_FIRST, CAMERA_MODE_THIRD, CAMERA_MODE_THIRD_FRONT}; @@ -164,8 +184,8 @@ public: } Nametag *addNametag(scene::ISceneNode *parent_node, - const std::string &nametag_text, video::SColor nametag_color, - const v3f &pos); + const std::string &text, video::SColor textcolor, + Optional<video::SColor> bgcolor, const v3f &pos); void removeNametag(Nametag *nametag); @@ -245,4 +265,5 @@ private: bool m_arm_inertia; std::list<Nametag *> m_nametags; + bool m_show_nametag_backgrounds; }; diff --git a/src/client/client.cpp b/src/client/client.cpp index ef4a3cdfc..0486bc0a9 100644 --- a/src/client/client.cpp +++ b/src/client/client.cpp @@ -663,12 +663,15 @@ bool Client::loadMedia(const std::string &data, const std::string &filename, io::IFileSystem *irrfs = RenderingEngine::get_filesystem(); video::IVideoDriver *vdrv = RenderingEngine::get_video_driver(); +#if IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR > 8 + io::IReadFile *rfile = irrfs->createMemoryReadFile( + data.c_str(), data.size(), "_tempreadfile"); +#else // Silly irrlicht's const-incorrectness Buffer<char> data_rw(data.c_str(), data.size()); - - // Create an irrlicht memory file io::IReadFile *rfile = irrfs->createMemoryReadFile( *data_rw, data_rw.getSize(), "_tempreadfile"); +#endif FATAL_ERROR_IF(!rfile, "Could not create irrlicht memory file."); @@ -1914,13 +1917,20 @@ scene::IAnimatedMesh* Client::getMesh(const std::string &filename, bool cache) // Create the mesh, remove it from cache and return it // This allows unique vertex colors and other properties for each instance +#if IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR > 8 + io::IReadFile *rfile = RenderingEngine::get_filesystem()->createMemoryReadFile( + data.c_str(), data.size(), filename.c_str()); +#else Buffer<char> data_rw(data.c_str(), data.size()); // Const-incorrect Irrlicht - io::IReadFile *rfile = RenderingEngine::get_filesystem()->createMemoryReadFile( + io::IReadFile *rfile = RenderingEngine::get_filesystem()->createMemoryReadFile( *data_rw, data_rw.getSize(), filename.c_str()); +#endif FATAL_ERROR_IF(!rfile, "Could not create/open RAM file"); scene::IAnimatedMesh *mesh = RenderingEngine::get_scene_manager()->getMesh(rfile); rfile->drop(); + if (!mesh) + return nullptr; mesh->grab(); if (!cache) RenderingEngine::get_mesh_cache()->removeMesh(mesh); diff --git a/src/client/client.h b/src/client/client.h index 25a1b97ba..da7f8e8a1 100644 --- a/src/client/client.h +++ b/src/client/client.h @@ -412,12 +412,7 @@ public: inline bool checkCSMRestrictionFlag(CSMRestrictionFlags flag) const { - return m_csm_restriction_flags & flag; - } - - inline std::unordered_map<u32, u32> &getHUDTranslationMap() - { - return m_hud_server_to_client; + return m_csm_restriction_flags & 0; } bool joinModChannel(const std::string &channel) override; @@ -556,9 +551,6 @@ private: // Relation of client id to object id std::unordered_map<int, u16> m_sounds_to_objects; - // Map server hud ids to client hud ids - std::unordered_map<u32, u32> m_hud_server_to_client; - // Privileges std::unordered_set<std::string> m_privileges; diff --git a/src/client/clientevent.h b/src/client/clientevent.h index 9bd31efce..2215aecbd 100644 --- a/src/client/clientevent.h +++ b/src/client/clientevent.h @@ -52,6 +52,31 @@ enum ClientEventType : u8 CLIENTEVENT_MAX, }; +struct ClientEventHudAdd +{ + u32 server_id; + u8 type; + v2f pos, scale; + std::string name; + std::string text, text2; + u32 number, item, dir; + v2f align, offset; + v3f world_pos; + v2s32 size; + s16 z_index; +}; + +struct ClientEventHudChange +{ + u32 id; + HudElementStat stat; + v2f v2fdata; + std::string sdata; + u32 data; + v3f v3fdata; + v2s32 v2s32data; +}; + struct ClientEvent { ClientEventType type; @@ -93,38 +118,12 @@ struct ClientEvent { u32 id; } delete_particlespawner; - struct - { - u32 server_id; - u8 type; - v2f *pos; - std::string *name; - v2f *scale; - std::string *text; - u32 number; - u32 item; - u32 dir; - v2f *align; - v2f *offset; - v3f *world_pos; - v2s32 *size; - s16 z_index; - std::string *text2; - } hudadd; + ClientEventHudAdd *hudadd; struct { u32 id; } hudrm; - struct - { - u32 id; - HudElementStat stat; - v2f *v2fdata; - std::string *sdata; - u32 data; - v3f *v3fdata; - v2s32 *v2s32data; - } hudchange; + ClientEventHudChange *hudchange; SkyboxParams *set_sky; struct { diff --git a/src/client/clientmap.cpp b/src/client/clientmap.cpp index b9e0cc2ce..be8343009 100644 --- a/src/client/clientmap.cpp +++ b/src/client/clientmap.cpp @@ -165,6 +165,9 @@ void ClientMap::updateDrawList() v3s16 p_blocks_max; getBlocksInViewRange(cam_pos_nodes, &p_blocks_min, &p_blocks_max); + // Read the vision range, unless unlimited range is enabled. + float range = m_control.range_all ? 1e7 : m_control.wanted_range; + // Number of blocks currently loaded by the client u32 blocks_loaded = 0; // Number of blocks with mesh in rendering range @@ -182,6 +185,7 @@ void ClientMap::updateDrawList() occlusion_culling_enabled = false; } + // Uncomment to debug occluded blocks in the wireframe mode // TODO: Include this as a flag for an extended debugging setting //if (occlusion_culling_enabled && m_control.show_wireframe) @@ -218,32 +222,34 @@ void ClientMap::updateDrawList() continue; } - float range = 100000 * BS; - if (!m_control.range_all) - range = m_control.wanted_range * BS; + v3s16 block_coord = block->getPos(); + v3s16 block_position = block->getPosRelative() + MAP_BLOCKSIZE / 2; - float d = 0.0; - if (!isBlockInSight(block->getPos(), camera_position, - camera_direction, camera_fov, range, &d)) - continue; + // First, perform a simple distance check, with a padding of one extra block. + if (!m_control.range_all && + block_position.getDistanceFrom(cam_pos_nodes) > range + MAP_BLOCKSIZE) + continue; // Out of range, skip. + // Keep the block alive as long as it is in range. + block->resetUsageTimer(); blocks_in_range_with_mesh++; - /* - Occlusion culling - */ + // Frustum culling + float d = 0.0; + if (!isBlockInSight(block_coord, camera_position, + camera_direction, camera_fov, range * BS, &d)) + continue; + + // Occlusion culling if ((!m_control.range_all && d > m_control.wanted_range * BS) || (occlusion_culling_enabled && isBlockOccluded(block, cam_pos_nodes))) { blocks_occlusion_culled++; continue; } - // This block is in range. Reset usage timer. - block->resetUsageTimer(); - // Add to set block->refGrab(); - m_drawlist[block->getPos()] = block; + m_drawlist[block_coord] = block; sector_blocks_drawn++; } // foreach sectorblocks @@ -282,8 +288,6 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) const u32 daynight_ratio = m_client->getEnv().getDayNightRatio(); const v3f camera_position = m_camera_position; - const v3f camera_direction = m_camera_direction; - const f32 camera_fov = m_camera_fov; /* Get all blocks and draw all visible ones @@ -310,11 +314,10 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) if (!block->mesh) continue; - float d = 0.0; - if (!isBlockInSight(block->getPos(), camera_position, - camera_direction, camera_fov, 100000 * BS, &d)) - continue; - + v3f block_pos_r = intToFloat(block->getPosRelative() + MAP_BLOCKSIZE / 2, BS); + float d = camera_position.getDistanceFrom(block_pos_r); + d = MYMAX(0,d - BLOCK_MAX_RADIUS); + // Mesh animation if (pass == scene::ESNRP_SOLID) { //MutexAutoLock lock(block->mesh_mutex); diff --git a/src/client/content_cao.cpp b/src/client/content_cao.cpp index c65977b44..97ae9afc4 100644 --- a/src/client/content_cao.cpp +++ b/src/client/content_cao.cpp @@ -934,7 +934,7 @@ void GenericCAO::updateNametag() if (m_is_local_player) // No nametag for local player return; - if (m_prop.nametag.empty()) { + if (m_prop.nametag.empty() || m_prop.nametag_color.getAlpha() == 0) { // Delete nametag if (m_nametag) { m_client->getCamera()->removeNametag(m_nametag); @@ -952,12 +952,14 @@ void GenericCAO::updateNametag() if (!m_nametag) { // Add nametag m_nametag = m_client->getCamera()->addNametag(node, - m_prop.nametag, m_prop.nametag_color, pos); + m_prop.nametag, m_prop.nametag_color, + m_prop.nametag_bgcolor, pos); } else { // Update nametag - m_nametag->nametag_text = m_prop.nametag; - m_nametag->nametag_color = m_prop.nametag_color; - m_nametag->nametag_pos = pos; + m_nametag->text = m_prop.nametag; + m_nametag->textcolor = m_prop.nametag_color; + m_nametag->bgcolor = m_prop.nametag_bgcolor; + m_nametag->pos = pos; } } diff --git a/src/client/game.cpp b/src/client/game.cpp index 3c58fb46f..31c782c51 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -68,6 +68,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/pointedthing.h" #include "util/quicktune_shortcutter.h" #include "irrlicht_changes/static_text.h" +#include "irr_ptr.h" #include "version.h" #include "script/scripting_client.h" #include "hud.h" @@ -425,6 +426,7 @@ class GameGlobalShaderConstantSetter : public IShaderConstantSetter CachedPixelShaderSetting<float, 3> m_camera_offset_pixel; CachedPixelShaderSetting<float, 3> m_camera_offset_vertex; CachedPixelShaderSetting<SamplerLayer_t> m_base_texture; + CachedPixelShaderSetting<SamplerLayer_t> m_normal_texture; Client *m_client; public: @@ -458,6 +460,7 @@ public: m_camera_offset_pixel("cameraOffset"), m_camera_offset_vertex("cameraOffset"), m_base_texture("baseTexture"), + m_normal_texture("normalTexture"), m_client(client) { g_settings->registerChangedCallback("enable_fog", settingsCallback, this); @@ -545,8 +548,9 @@ public: m_camera_offset_pixel.set(camera_offset_array, services); m_camera_offset_vertex.set(camera_offset_array, services); - SamplerLayer_t base_tex = 0; + SamplerLayer_t base_tex = 0, normal_tex = 1; m_base_texture.set(&base_tex, services); + m_normal_texture.set(&normal_tex, services); } }; @@ -647,6 +651,8 @@ struct ClientEventHandler THE GAME ****************************************************************************/ +using PausedNodesList = std::vector<std::pair<irr_ptr<scene::IAnimatedMeshSceneNode>, float>>; + /* This is not intended to be a public class. If a public class becomes * desirable then it may be better to create another 'wrapper' class that * hides most of the stuff in this class (nothing in this class is required @@ -796,6 +802,9 @@ private: void showDeathFormspec(); void showPauseMenu(); + void pauseAnimation(); + void resumeAnimation(); + // ClientEvent handlers void handleClientEvent_None(ClientEvent *event, CameraOrientation *cam); void handleClientEvent_PlayerDamage(ClientEvent *event, CameraOrientation *cam); @@ -823,6 +832,8 @@ private: const NodeMetadata *meta); static const ClientEventHandler clientEventHandler[CLIENTEVENT_MAX]; + f32 getSensitivityScaleFactor() const; + InputHandler *input = nullptr; Client *client = nullptr; @@ -856,6 +867,9 @@ private: Hud *hud = nullptr; Minimap *mapper = nullptr; + // Map server hud ids to client hud ids + std::unordered_map<u32, u32> m_hud_server_to_client; + GameRunData runData; Flags m_flags; @@ -870,6 +884,7 @@ private: std::string *error_message; bool *reconnect_requested; scene::ISceneNode *skybox; + PausedNodesList paused_animated_nodes; bool simple_singleplayer_mode; /* End 'cache' */ @@ -2331,7 +2346,6 @@ void Game::checkZoomEnabled() m_game_ui->showTranslatedStatusText("Zoom currently disabled by game or mod"); } - void Game::updateCameraDirection(CameraOrientation *cam, float dtime) { if ((device->isWindowActive() && device->isWindowFocused() @@ -2367,6 +2381,18 @@ void Game::updateCameraDirection(CameraOrientation *cam, float dtime) } } +// Get the factor to multiply with sensitivity to get the same mouse/joystick +// responsiveness independently of FOV. +f32 Game::getSensitivityScaleFactor() const +{ + f32 fov_y = client->getCamera()->getFovY(); + + // Multiply by a constant such that it becomes 1.0 at 72 degree FOV and + // 16:9 aspect ratio to minimize disruption of existing sensitivity + // settings. + return tan(fov_y / 2.0f) * 1.3763818698f; +} + void Game::updateCameraOrientation(CameraOrientation *cam, float dtime) { #ifdef HAVE_TOUCHSCREENGUI @@ -2382,8 +2408,9 @@ void Game::updateCameraOrientation(CameraOrientation *cam, float dtime) dist.Y = -dist.Y; } - cam->camera_yaw -= dist.X * m_cache_mouse_sensitivity; - cam->camera_pitch += dist.Y * m_cache_mouse_sensitivity; + f32 sens_scale = getSensitivityScaleFactor(); + cam->camera_yaw -= dist.X * m_cache_mouse_sensitivity * sens_scale; + cam->camera_pitch += dist.Y * m_cache_mouse_sensitivity * sens_scale; if (dist.X != 0 || dist.Y != 0) input->setMousePos(center.X, center.Y); @@ -2392,7 +2419,8 @@ void Game::updateCameraOrientation(CameraOrientation *cam, float dtime) #endif if (m_cache_enable_joysticks) { - f32 c = m_cache_joystick_frustum_sensitivity * (1.f / 32767.f) * dtime; + f32 sens_scale = getSensitivityScaleFactor(); + f32 c = m_cache_joystick_frustum_sensitivity * (1.f / 32767.f) * dtime * sens_scale; cam->camera_yaw -= input->joystick.getAxisWithoutDead(JA_FRUSTUM_HORIZONTAL) * c; cam->camera_pitch += input->joystick.getAxisWithoutDead(JA_FRUSTUM_VERTICAL) * c; } @@ -2415,7 +2443,7 @@ void Game::updatePlayerControl(const CameraOrientation &cam) input->isKeyDown(KeyType::LEFT), input->isKeyDown(KeyType::RIGHT), isKeyDown(KeyType::JUMP), - isKeyDown(KeyType::SPECIAL1), + isKeyDown(KeyType::AUX1), isKeyDown(KeyType::SNEAK), isKeyDown(KeyType::ZOOM), isKeyDown(KeyType::DIG), @@ -2432,7 +2460,7 @@ void Game::updatePlayerControl(const CameraOrientation &cam) ( (u32)(isKeyDown(KeyType::LEFT) & 0x1) << 2) | ( (u32)(isKeyDown(KeyType::RIGHT) & 0x1) << 3) | ( (u32)(isKeyDown(KeyType::JUMP) & 0x1) << 4) | - ( (u32)(isKeyDown(KeyType::SPECIAL1) & 0x1) << 5) | + ( (u32)(isKeyDown(KeyType::AUX1) & 0x1) << 5) | ( (u32)(isKeyDown(KeyType::SNEAK) & 0x1) << 6) | ( (u32)(isKeyDown(KeyType::DIG) & 0x1) << 7) | ( (u32)(isKeyDown(KeyType::PLACE) & 0x1) << 8) | @@ -2481,6 +2509,9 @@ inline void Game::step(f32 *dtime) if (can_be_and_is_paused) { // This is for a singleplayer server *dtime = 0; // No time passes } else { + if (simple_singleplayer_mode && !paused_animated_nodes.empty()) + resumeAnimation(); + if (server) server->step(*dtime); @@ -2488,6 +2519,33 @@ inline void Game::step(f32 *dtime) } } +static void pauseNodeAnimation(PausedNodesList &paused, scene::ISceneNode *node) { + if (!node) + return; + for (auto &&child: node->getChildren()) + pauseNodeAnimation(paused, child); + if (node->getType() != scene::ESNT_ANIMATED_MESH) + return; + auto animated_node = static_cast<scene::IAnimatedMeshSceneNode *>(node); + float speed = animated_node->getAnimationSpeed(); + if (!speed) + return; + paused.push_back({grab(animated_node), speed}); + animated_node->setAnimationSpeed(0.0f); +} + +void Game::pauseAnimation() +{ + pauseNodeAnimation(paused_animated_nodes, smgr->getRootSceneNode()); +} + +void Game::resumeAnimation() +{ + for (auto &&pair: paused_animated_nodes) + pair.first->setAnimationSpeed(pair.second); + paused_animated_nodes.clear(); +} + const ClientEventHandler Game::clientEventHandler[CLIENTEVENT_MAX] = { {&Game::handleClientEvent_None}, {&Game::handleClientEvent_PlayerDamage}, @@ -2602,132 +2660,100 @@ void Game::handleClientEvent_HandleParticleEvent(ClientEvent *event, void Game::handleClientEvent_HudAdd(ClientEvent *event, CameraOrientation *cam) { LocalPlayer *player = client->getEnv().getLocalPlayer(); - auto &hud_server_to_client = client->getHUDTranslationMap(); - u32 server_id = event->hudadd.server_id; + u32 server_id = event->hudadd->server_id; // ignore if we already have a HUD with that ID - auto i = hud_server_to_client.find(server_id); - if (i != hud_server_to_client.end()) { - delete event->hudadd.pos; - delete event->hudadd.name; - delete event->hudadd.scale; - delete event->hudadd.text; - delete event->hudadd.align; - delete event->hudadd.offset; - delete event->hudadd.world_pos; - delete event->hudadd.size; - delete event->hudadd.text2; + auto i = m_hud_server_to_client.find(server_id); + if (i != m_hud_server_to_client.end()) { + delete event->hudadd; return; } HudElement *e = new HudElement; - e->type = (HudElementType)event->hudadd.type; - e->pos = *event->hudadd.pos; - e->name = *event->hudadd.name; - e->scale = *event->hudadd.scale; - e->text = *event->hudadd.text; - e->number = event->hudadd.number; - e->item = event->hudadd.item; - e->dir = event->hudadd.dir; - e->align = *event->hudadd.align; - e->offset = *event->hudadd.offset; - e->world_pos = *event->hudadd.world_pos; - e->size = *event->hudadd.size; - e->z_index = event->hudadd.z_index; - e->text2 = *event->hudadd.text2; - hud_server_to_client[server_id] = player->addHud(e); - - delete event->hudadd.pos; - delete event->hudadd.name; - delete event->hudadd.scale; - delete event->hudadd.text; - delete event->hudadd.align; - delete event->hudadd.offset; - delete event->hudadd.world_pos; - delete event->hudadd.size; - delete event->hudadd.text2; + e->type = static_cast<HudElementType>(event->hudadd->type); + e->pos = event->hudadd->pos; + e->name = event->hudadd->name; + e->scale = event->hudadd->scale; + e->text = event->hudadd->text; + e->number = event->hudadd->number; + e->item = event->hudadd->item; + e->dir = event->hudadd->dir; + e->align = event->hudadd->align; + e->offset = event->hudadd->offset; + e->world_pos = event->hudadd->world_pos; + e->size = event->hudadd->size; + e->z_index = event->hudadd->z_index; + e->text2 = event->hudadd->text2; + m_hud_server_to_client[server_id] = player->addHud(e); + + delete event->hudadd; } void Game::handleClientEvent_HudRemove(ClientEvent *event, CameraOrientation *cam) { LocalPlayer *player = client->getEnv().getLocalPlayer(); - HudElement *e = player->removeHud(event->hudrm.id); - delete e; + + auto i = m_hud_server_to_client.find(event->hudrm.id); + if (i != m_hud_server_to_client.end()) { + HudElement *e = player->removeHud(i->second); + delete e; + m_hud_server_to_client.erase(i); + } + } void Game::handleClientEvent_HudChange(ClientEvent *event, CameraOrientation *cam) { LocalPlayer *player = client->getEnv().getLocalPlayer(); - u32 id = event->hudchange.id; - HudElement *e = player->getHud(id); + HudElement *e = nullptr; - if (e == NULL) { - delete event->hudchange.v3fdata; - delete event->hudchange.v2fdata; - delete event->hudchange.sdata; - delete event->hudchange.v2s32data; + auto i = m_hud_server_to_client.find(event->hudchange->id); + if (i != m_hud_server_to_client.end()) { + e = player->getHud(i->second); + } + + if (e == nullptr) { + delete event->hudchange; return; } - switch (event->hudchange.stat) { - case HUD_STAT_POS: - e->pos = *event->hudchange.v2fdata; - break; +#define CASE_SET(statval, prop, dataprop) \ + case statval: \ + e->prop = event->hudchange->dataprop; \ + break - case HUD_STAT_NAME: - e->name = *event->hudchange.sdata; - break; + switch (event->hudchange->stat) { + CASE_SET(HUD_STAT_POS, pos, v2fdata); - case HUD_STAT_SCALE: - e->scale = *event->hudchange.v2fdata; - break; + CASE_SET(HUD_STAT_NAME, name, sdata); - case HUD_STAT_TEXT: - e->text = *event->hudchange.sdata; - break; + CASE_SET(HUD_STAT_SCALE, scale, v2fdata); - case HUD_STAT_NUMBER: - e->number = event->hudchange.data; - break; + CASE_SET(HUD_STAT_TEXT, text, sdata); - case HUD_STAT_ITEM: - e->item = event->hudchange.data; - break; + CASE_SET(HUD_STAT_NUMBER, number, data); - case HUD_STAT_DIR: - e->dir = event->hudchange.data; - break; + CASE_SET(HUD_STAT_ITEM, item, data); - case HUD_STAT_ALIGN: - e->align = *event->hudchange.v2fdata; - break; + CASE_SET(HUD_STAT_DIR, dir, data); - case HUD_STAT_OFFSET: - e->offset = *event->hudchange.v2fdata; - break; + CASE_SET(HUD_STAT_ALIGN, align, v2fdata); - case HUD_STAT_WORLD_POS: - e->world_pos = *event->hudchange.v3fdata; - break; + CASE_SET(HUD_STAT_OFFSET, offset, v2fdata); - case HUD_STAT_SIZE: - e->size = *event->hudchange.v2s32data; - break; + CASE_SET(HUD_STAT_WORLD_POS, world_pos, v3fdata); - case HUD_STAT_Z_INDEX: - e->z_index = event->hudchange.data; - break; + CASE_SET(HUD_STAT_SIZE, size, v2s32data); - case HUD_STAT_TEXT2: - e->text2 = *event->hudchange.sdata; - break; + CASE_SET(HUD_STAT_Z_INDEX, z_index, data); + + CASE_SET(HUD_STAT_TEXT2, text2, sdata); } - delete event->hudchange.v3fdata; - delete event->hudchange.v2fdata; - delete event->hudchange.sdata; - delete event->hudchange.v2s32data; +#undef CASE_SET + + delete event->hudchange; } void Game::handleClientEvent_SetSky(ClientEvent *event, CameraOrientation *cam) @@ -3279,7 +3305,8 @@ bool Game::nodePlacement(const ItemDefinition &selected_def, const ItemStack &selected_item, const v3s16 &nodepos, const v3s16 &neighbourpos, const PointedThing &pointed, const NodeMetadata *meta) { - std::string prediction = selected_def.node_placement_prediction; + const auto &prediction = selected_def.node_placement_prediction; + const NodeDefManager *nodedef = client->ndef(); ClientMap &map = client->getEnv().getClientMap(); MapNode node; @@ -3349,8 +3376,7 @@ bool Game::nodePlacement(const ItemDefinition &selected_def, if (!found) { errorstream << "Node placement prediction failed for " - << selected_def.name << " (places " - << prediction + << selected_def.name << " (places " << prediction << ") - Name not known" << std::endl; // Handle this as if prediction was empty // Report to server @@ -3361,9 +3387,14 @@ bool Game::nodePlacement(const ItemDefinition &selected_def, const ContentFeatures &predicted_f = nodedef->get(id); // Predict param2 for facedir and wallmounted nodes + // Compare core.item_place_node() for what the server does u8 param2 = 0; - if (predicted_f.param_type_2 == CPT2_WALLMOUNTED || + const u8 place_param2 = selected_def.place_param2; + + if (place_param2) { + param2 = place_param2; + } else if (predicted_f.param_type_2 == CPT2_WALLMOUNTED || predicted_f.param_type_2 == CPT2_COLORED_WALLMOUNTED) { v3s16 dir = nodepos - neighbourpos; @@ -3374,9 +3405,7 @@ bool Game::nodePlacement(const ItemDefinition &selected_def, } else { param2 = dir.Z < 0 ? 5 : 4; } - } - - if (predicted_f.param_type_2 == CPT2_FACEDIR || + } else if (predicted_f.param_type_2 == CPT2_FACEDIR || predicted_f.param_type_2 == CPT2_COLORED_FACEDIR) { v3s16 dir = nodepos - floatToInt(client->getEnv().getLocalPlayer()->getPosition(), BS); @@ -3387,11 +3416,9 @@ bool Game::nodePlacement(const ItemDefinition &selected_def, } } - assert(param2 <= 5); - - //Check attachment if node is in group attached_node - if (((ItemGroupList) predicted_f.groups)["attached_node"] != 0) { - static v3s16 wallmounted_dirs[8] = { + // Check attachment if node is in group attached_node + if (itemgroup_get(predicted_f.groups, "attached_node") != 0) { + const static v3s16 wallmounted_dirs[8] = { v3s16(0, 1, 0), v3s16(0, -1, 0), v3s16(1, 0, 0), @@ -3416,11 +3443,11 @@ bool Game::nodePlacement(const ItemDefinition &selected_def, } // Apply color - if ((predicted_f.param_type_2 == CPT2_COLOR + if (!place_param2 && (predicted_f.param_type_2 == CPT2_COLOR || predicted_f.param_type_2 == CPT2_COLORED_FACEDIR || predicted_f.param_type_2 == CPT2_COLORED_WALLMOUNTED)) { - const std::string &indexstr = selected_item.metadata.getString( - "palette_index", 0); + const auto &indexstr = selected_item.metadata. + getString("palette_index", 0); if (!indexstr.empty()) { s32 index = mystoi(indexstr); if (predicted_f.param_type_2 == CPT2_COLOR) { @@ -3460,11 +3487,10 @@ bool Game::nodePlacement(const ItemDefinition &selected_def, soundmaker->m_player_rightpunch_sound = selected_def.sound_place_failed; return false; } - } catch (InvalidPositionException &e) { + } catch (const InvalidPositionException &e) { errorstream << "Node placement prediction failed for " << selected_def.name << " (places " - << prediction - << ") - Position not loaded" << std::endl; + << prediction << ") - Position not loaded" << std::endl; soundmaker->m_player_rightpunch_sound = selected_def.sound_place_failed; return false; } @@ -4218,6 +4244,9 @@ void Game::showPauseMenu() fs_src, txt_dst, client->getFormspecPrepend(), sound); formspec->setFocus("btn_continue"); formspec->doPause = true; + + if (simple_singleplayer_mode) + pauseAnimation(); } /****************************************************************************/ diff --git a/src/client/guiscalingfilter.cpp b/src/client/guiscalingfilter.cpp index 406c096e6..8c565a52f 100644 --- a/src/client/guiscalingfilter.cpp +++ b/src/client/guiscalingfilter.cpp @@ -129,7 +129,7 @@ video::ITexture *guiScalingResizeCached(video::IVideoDriver *driver, #endif // Convert the scaled image back into a texture. - scaled = driver->addTexture(scalename, destimg, NULL); + scaled = driver->addTexture(scalename, destimg); destimg->drop(); g_txrCache[scalename] = scaled; diff --git a/src/client/hud.cpp b/src/client/hud.cpp index 46736b325..74c1828e3 100644 --- a/src/client/hud.cpp +++ b/src/client/hud.cpp @@ -950,7 +950,7 @@ void drawItemStack( if (imesh && imesh->mesh) { scene::IMesh *mesh = imesh->mesh; - driver->clearZBuffer(); + driver->clearBuffers(video::ECBF_DEPTH); s32 delta = 0; if (rotation_kind < IT_ROT_NONE) { MeshTimeInfo &ti = rotation_time_infos[rotation_kind]; diff --git a/src/client/inputhandler.cpp b/src/client/inputhandler.cpp index 608a405a8..b7e70fa6c 100644 --- a/src/client/inputhandler.cpp +++ b/src/client/inputhandler.cpp @@ -35,7 +35,7 @@ void KeyCache::populate() key[KeyType::LEFT] = getKeySetting("keymap_left"); key[KeyType::RIGHT] = getKeySetting("keymap_right"); key[KeyType::JUMP] = getKeySetting("keymap_jump"); - key[KeyType::SPECIAL1] = getKeySetting("keymap_special1"); + key[KeyType::AUX1] = getKeySetting("keymap_aux1"); key[KeyType::SNEAK] = getKeySetting("keymap_sneak"); key[KeyType::DIG] = getKeySetting("keymap_dig"); key[KeyType::PLACE] = getKeySetting("keymap_place"); @@ -113,17 +113,12 @@ bool MyEventReceiver::OnEvent(const SEvent &event) if (event.EventType == irr::EET_KEY_INPUT_EVENT) { const KeyPress &keyCode = event.KeyInput; if (keysListenedFor[keyCode]) { - // If the key is being held down then the OS may - // send a continuous stream of keydown events. - // In this case, we don't want to let this - // stream reach the application as it will cause - // certain actions to repeat constantly. if (event.KeyInput.PressedDown) { - if (!IsKeyDown(keyCode)) { - keyWasDown.set(keyCode); + if (!IsKeyDown(keyCode)) keyWasPressed.set(keyCode); - } + keyIsDown.set(keyCode); + keyWasDown.set(keyCode); } else { if (IsKeyDown(keyCode)) keyWasReleased.set(keyCode); @@ -224,7 +219,7 @@ void RandomInputHandler::step(float dtime) { static RandomInputHandlerSimData rnd_data[] = { { "keymap_jump", 0.0f, 40 }, - { "keymap_special1", 0.0f, 40 }, + { "keymap_aux1", 0.0f, 40 }, { "keymap_forward", 0.0f, 40 }, { "keymap_left", 0.0f, 40 }, { "keymap_dig", 0.0f, 30 }, diff --git a/src/client/inputhandler.h b/src/client/inputhandler.h index 7487bbdc7..1fb4cf0ec 100644 --- a/src/client/inputhandler.h +++ b/src/client/inputhandler.h @@ -201,7 +201,7 @@ private: // The current state of keys KeyList keyIsDown; - // Whether a key was down + // Like keyIsDown but only reset when that key is read KeyList keyWasDown; // Whether a key has just been pressed diff --git a/src/client/joystick_controller.cpp b/src/client/joystick_controller.cpp index f61ae4ae6..919db5315 100644 --- a/src/client/joystick_controller.cpp +++ b/src/client/joystick_controller.cpp @@ -79,7 +79,7 @@ JoystickLayout create_default_layout() // Accessible without any modifier pressed JLO_B_PB(KeyType::JUMP, bm | 1 << 0, 1 << 0); - JLO_B_PB(KeyType::SPECIAL1, bm | 1 << 1, 1 << 1); + JLO_B_PB(KeyType::AUX1, bm | 1 << 1, 1 << 1); // Accessible with start button not pressed, but four pressed // TODO find usage for button 0 @@ -126,11 +126,11 @@ JoystickLayout create_xbox_layout() // 4 Buttons JLO_B_PB(KeyType::JUMP, 1 << 0, 1 << 0); // A/green JLO_B_PB(KeyType::ESC, 1 << 1, 1 << 1); // B/red - JLO_B_PB(KeyType::SPECIAL1, 1 << 2, 1 << 2); // X/blue + JLO_B_PB(KeyType::AUX1, 1 << 2, 1 << 2); // X/blue JLO_B_PB(KeyType::INVENTORY, 1 << 3, 1 << 3); // Y/yellow // Analog Sticks - JLO_B_PB(KeyType::SPECIAL1, 1 << 11, 1 << 11); // left + JLO_B_PB(KeyType::AUX1, 1 << 11, 1 << 11); // left JLO_B_PB(KeyType::SNEAK, 1 << 12, 1 << 12); // right // Triggers diff --git a/src/client/keys.h b/src/client/keys.h index 60a7a3c45..9f90da6b8 100644 --- a/src/client/keys.h +++ b/src/client/keys.h @@ -32,7 +32,7 @@ public: LEFT, RIGHT, JUMP, - SPECIAL1, + AUX1, SNEAK, AUTOFORWARD, DIG, diff --git a/src/client/mapblock_mesh.cpp b/src/client/mapblock_mesh.cpp index d78a86b2d..167e1e3ec 100644 --- a/src/client/mapblock_mesh.cpp +++ b/src/client/mapblock_mesh.cpp @@ -404,7 +404,7 @@ static void getNodeVertexDirs(const v3s16 &dir, v3s16 *vertex_dirs) static void getNodeTextureCoords(v3f base, const v3f &scale, const v3s16 &dir, float *u, float *v) { - if (dir.X > 0 || dir.Y > 0 || dir.Z < 0) + if (dir.X > 0 || dir.Y != 0 || dir.Z < 0) base -= scale; if (dir == v3s16(0,0,1)) { *u = -base.X - 1; @@ -422,8 +422,8 @@ static void getNodeTextureCoords(v3f base, const v3f &scale, const v3s16 &dir, f *u = base.X + 1; *v = -base.Z - 2; } else if (dir == v3s16(0,-1,0)) { - *u = base.X; - *v = base.Z; + *u = base.X + 1; + *v = base.Z + 1; } } diff --git a/src/client/minimap.cpp b/src/client/minimap.cpp index c6ccf86e8..dd810ee0a 100644 --- a/src/client/minimap.cpp +++ b/src/client/minimap.cpp @@ -304,7 +304,7 @@ void Minimap::setModeIndex(size_t index) data->mode = m_modes[index]; m_current_mode_index = index; } else { - data->mode = MinimapModeDef{MINIMAP_TYPE_OFF, N_("Minimap hidden"), 0, 0, ""}; + data->mode = MinimapModeDef{MINIMAP_TYPE_OFF, gettext("Minimap hidden"), 0, 0, ""}; m_current_mode_index = 0; } @@ -330,25 +330,26 @@ void Minimap::addMode(MinimapModeDef mode) if (mode.label == "") { switch (mode.type) { case MINIMAP_TYPE_OFF: - mode.label = N_("Minimap hidden"); + mode.label = gettext("Minimap hidden"); break; case MINIMAP_TYPE_SURFACE: - mode.label = N_("Minimap in surface mode, Zoom x%d"); + mode.label = gettext("Minimap in surface mode, Zoom x%d"); if (mode.map_size > 0) zoom = 256 / mode.map_size; break; case MINIMAP_TYPE_RADAR: - mode.label = N_("Minimap in radar mode, Zoom x%d"); + mode.label = gettext("Minimap in radar mode, Zoom x%d"); if (mode.map_size > 0) zoom = 512 / mode.map_size; break; case MINIMAP_TYPE_TEXTURE: - mode.label = N_("Minimap in texture mode"); + mode.label = gettext("Minimap in texture mode"); break; default: break; } } + // else: Custom labels need mod-provided client-side translation if (zoom >= 0) { char label_buf[1024]; diff --git a/src/client/render/anaglyph.cpp b/src/client/render/anaglyph.cpp index 9ba4464a2..153e77400 100644 --- a/src/client/render/anaglyph.cpp +++ b/src/client/render/anaglyph.cpp @@ -40,7 +40,7 @@ void RenderingCoreAnaglyph::setupMaterial(int color_mask) void RenderingCoreAnaglyph::useEye(bool right) { RenderingCoreStereo::useEye(right); - driver->clearZBuffer(); + driver->clearBuffers(video::ECBF_DEPTH); setupMaterial(right ? video::ECP_GREEN | video::ECP_BLUE : video::ECP_RED); } diff --git a/src/client/render/interlaced.cpp b/src/client/render/interlaced.cpp index ce8e92f21..3f79a8eb5 100644 --- a/src/client/render/interlaced.cpp +++ b/src/client/render/interlaced.cpp @@ -35,7 +35,11 @@ void RenderingCoreInterlaced::initMaterial() IShaderSource *s = client->getShaderSource(); mat.UseMipMaps = false; mat.ZBuffer = false; +#if IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR > 8 + mat.ZWriteEnable = video::EZW_OFF; +#else mat.ZWriteEnable = false; +#endif u32 shader = s->getShader("3d_interlaced_merge", TILE_MATERIAL_BASIC); mat.MaterialType = s->getShaderInfo(shader).material; for (int k = 0; k < 3; ++k) { diff --git a/src/client/renderingengine.cpp b/src/client/renderingengine.cpp index 99ff8c1ee..4f59bbae3 100644 --- a/src/client/renderingengine.cpp +++ b/src/client/renderingengine.cpp @@ -118,6 +118,8 @@ RenderingEngine::RenderingEngine(IEventReceiver *receiver) } SIrrlichtCreationParameters params = SIrrlichtCreationParameters(); + if (g_logger.getTraceEnabled()) + params.LoggingLevel = irr::ELL_DEBUG; params.DriverType = driverType; params.WindowSize = core::dimension2d<u32>(screen_w, screen_h); params.Bits = bits; @@ -325,9 +327,11 @@ static bool getWindowHandle(irr::video::IVideoDriver *driver, HWND &hWnd) const video::SExposedVideoData exposedData = driver->getExposedVideoData(); switch (driver->getDriverType()) { +#if IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 9 case video::EDT_DIRECT3D8: hWnd = reinterpret_cast<HWND>(exposedData.D3D8.HWnd); break; +#endif case video::EDT_DIRECT3D9: hWnd = reinterpret_cast<HWND>(exposedData.D3D9.HWnd); break; diff --git a/src/client/shader.h b/src/client/shader.h index 38ab76704..49a563115 100644 --- a/src/client/shader.h +++ b/src/client/shader.h @@ -96,9 +96,10 @@ public: if (has_been_set && std::equal(m_sent, m_sent + count, value)) return; if (is_pixel) - services->setPixelShaderConstant(m_name, value, count); + services->setPixelShaderConstant(services->getPixelShaderConstantID(m_name), value, count); else - services->setVertexShaderConstant(m_name, value, count); + services->setVertexShaderConstant(services->getVertexShaderConstantID(m_name), value, count); + std::copy(value, value + count, m_sent); has_been_set = true; } diff --git a/src/client/sky.cpp b/src/client/sky.cpp index 3a40321dd..caf695e7a 100644 --- a/src/client/sky.cpp +++ b/src/client/sky.cpp @@ -39,12 +39,13 @@ static video::SMaterial baseMaterial() { video::SMaterial mat; mat.Lighting = false; -#if ENABLE_GLES +#if IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR > 8 mat.ZBuffer = video::ECFN_DISABLED; + mat.ZWriteEnable = video::EZW_OFF; #else + mat.ZWriteEnable = false; mat.ZBuffer = video::ECFN_NEVER; #endif - mat.ZWriteEnable = false; mat.AntiAliasing = 0; mat.TextureLayer[0].TextureWrapU = video::ETC_CLAMP_TO_EDGE; mat.TextureLayer[0].TextureWrapV = video::ETC_CLAMP_TO_EDGE; diff --git a/src/client/sound_openal.cpp b/src/client/sound_openal.cpp index f4e61f93e..8dceeede6 100644 --- a/src/client/sound_openal.cpp +++ b/src/client/sound_openal.cpp @@ -671,8 +671,8 @@ public: alSourcei(sound->source_id, AL_SOURCE_RELATIVE, false); alSource3f(sound->source_id, AL_POSITION, pos.X, pos.Y, pos.Z); - alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0); - alSourcef(sound->source_id, AL_REFERENCE_DISTANCE, 30.0); + alSource3f(sound->source_id, AL_VELOCITY, 0.0f, 0.0f, 0.0f); + alSourcef(sound->source_id, AL_REFERENCE_DISTANCE, 10.0f); } bool updateSoundGain(int id, float gain) diff --git a/src/client/tile.cpp b/src/client/tile.cpp index aad956ada..7e3901247 100644 --- a/src/client/tile.cpp +++ b/src/client/tile.cpp @@ -837,17 +837,16 @@ static video::IImage *createInventoryCubeImage( image = scaled; } sanity_check(image->getPitch() == 4 * size); - return reinterpret_cast<u32 *>(image->lock()); + return reinterpret_cast<u32 *>(image->getData()); }; auto free_image = [] (video::IImage *image) -> void { - image->unlock(); image->drop(); }; video::IImage *result = driver->createImage(video::ECF_A8R8G8B8, {cube_size, cube_size}); sanity_check(result->getPitch() == 4 * cube_size); result->fill(video::SColor(0x00000000u)); - u32 *target = reinterpret_cast<u32 *>(result->lock()); + u32 *target = reinterpret_cast<u32 *>(result->getData()); // Draws single cube face // `shade_factor` is face brightness, in range [0.0, 1.0] @@ -906,7 +905,6 @@ static video::IImage *createInventoryCubeImage( {0, 5}, {1, 5}, }); - result->unlock(); return result; } @@ -2224,9 +2222,14 @@ video::SColor TextureSource::getTextureAverageColor(const std::string &name) video::IVideoDriver *driver = RenderingEngine::get_video_driver(); video::SColor c(0, 0, 0, 0); video::ITexture *texture = getTexture(name); + if (!texture) + return c; video::IImage *image = driver->createImage(texture, core::position2d<s32>(0, 0), texture->getOriginalSize()); + if (!image) + return c; + u32 total = 0; u32 tR = 0; u32 tG = 0; diff --git a/src/client/wieldmesh.cpp b/src/client/wieldmesh.cpp index ad583210a..387eb17c3 100644 --- a/src/client/wieldmesh.cpp +++ b/src/client/wieldmesh.cpp @@ -294,7 +294,7 @@ void WieldMeshSceneNode::setExtruded(const std::string &imagename, } material.setFlag(video::EMF_ANISOTROPIC_FILTER, m_anisotropic_filter); // mipmaps cause "thin black line" artifacts -#if (IRRLICHT_VERSION_MAJOR >= 1 && IRRLICHT_VERSION_MINOR >= 8) || IRRLICHT_VERSION_MAJOR >= 2 +#if (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR >= 8) || IRRLICHT_VERSION_MAJOR >= 2 material.setFlag(video::EMF_USE_MIP_MAPS, false); #endif if (m_enable_shaders) { @@ -303,23 +303,26 @@ void WieldMeshSceneNode::setExtruded(const std::string &imagename, } } -scene::SMesh *createSpecialNodeMesh(Client *client, content_t id, std::vector<ItemPartColor> *colors, const ContentFeatures &f) +static scene::SMesh *createSpecialNodeMesh(Client *client, MapNode n, + std::vector<ItemPartColor> *colors, const ContentFeatures &f) { MeshMakeData mesh_make_data(client, false); MeshCollector collector; mesh_make_data.setSmoothLighting(false); MapblockMeshGenerator gen(&mesh_make_data, &collector); - u8 param2 = 0; - if (f.param_type_2 == CPT2_WALLMOUNTED || + + if (n.getParam2()) { + // keep it + } else if (f.param_type_2 == CPT2_WALLMOUNTED || f.param_type_2 == CPT2_COLORED_WALLMOUNTED) { if (f.drawtype == NDT_TORCHLIKE) - param2 = 1; + n.setParam2(1); else if (f.drawtype == NDT_SIGNLIKE || f.drawtype == NDT_NODEBOX || f.drawtype == NDT_MESH) - param2 = 4; + n.setParam2(4); } - gen.renderSingle(id, param2); + gen.renderSingle(n.getContent(), n.getParam2()); colors->clear(); scene::SMesh *mesh = new scene::SMesh(); @@ -413,9 +416,12 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client, bool che case NDT_LIQUID: setCube(f, def.wield_scale); break; - default: + default: { // Render non-trivial drawtypes like the actual node - mesh = createSpecialNodeMesh(client, id, &m_colors, f); + MapNode n(id); + n.setParam2(def.place_param2); + + mesh = createSpecialNodeMesh(client, n, &m_colors, f); changeToMesh(mesh); mesh->drop(); m_meshnode->setScale( @@ -423,6 +429,7 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client, bool che / (BS * f.visual_scale)); break; } + } u32 material_count = m_meshnode->getMaterialCount(); for (u32 i = 0; i < material_count; ++i) { @@ -585,12 +592,16 @@ void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result) result->buffer_colors.emplace_back(l0.has_color, l0.color); break; } - default: + default: { // Render non-trivial drawtypes like the actual node - mesh = createSpecialNodeMesh(client, id, &result->buffer_colors, f); + MapNode n(id); + n.setParam2(def.place_param2); + + mesh = createSpecialNodeMesh(client, n, &result->buffer_colors, f); scaleMesh(mesh, v3f(0.12, 0.12, 0.12)); break; } + } u32 mc = mesh->getMeshBufferCount(); for (u32 i = 0; i < mc; ++i) { diff --git a/src/content/mods.cpp b/src/content/mods.cpp index 95ab0290a..434004b29 100644 --- a/src/content/mods.cpp +++ b/src/content/mods.cpp @@ -28,6 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "settings.h" #include "porting.h" #include "convert_json.h" +#include "script/common/c_internal.h" bool parseDependsString(std::string &dep, std::unordered_set<char> &symbols) { @@ -44,20 +45,24 @@ bool parseDependsString(std::string &dep, std::unordered_set<char> &symbols) return !dep.empty(); } -void parseModContents(ModSpec &spec) +static void log_mod_deprecation(const ModSpec &spec, const std::string &warning) { - // NOTE: this function works in mutual recursion with getModsInPath - Settings info; - info.readConfigFile((spec.path + DIR_DELIM + "mod.conf").c_str()); - - if (info.exists("name")) - spec.name = info.get("name"); + auto handling_mode = get_deprecated_handling_mode(); + if (handling_mode != DeprecatedHandlingMode::Ignore) { + std::ostringstream os; + os << warning << " (" << spec.name << " at " << spec.path << ")" << std::endl; - if (info.exists("author")) - spec.author = info.get("author"); + if (handling_mode == DeprecatedHandlingMode::Error) { + throw ModError(os.str()); + } else { + warningstream << os.str(); + } + } +} - if (info.exists("release")) - spec.release = info.getS32("release"); +void parseModContents(ModSpec &spec) +{ + // NOTE: this function works in mutual recursion with getModsInPath spec.depends.clear(); spec.optdepends.clear(); @@ -78,6 +83,20 @@ void parseModContents(ModSpec &spec) spec.modpack_content = getModsInPath(spec.path, true); } else { + Settings info; + info.readConfigFile((spec.path + DIR_DELIM + "mod.conf").c_str()); + + if (info.exists("name")) + spec.name = info.get("name"); + else + log_mod_deprecation(spec, "Mods not having a mod.conf file with the name is deprecated."); + + if (info.exists("author")) + spec.author = info.get("author"); + + if (info.exists("release")) + spec.release = info.getS32("release"); + // Attempt to load dependencies from mod.conf bool mod_conf_has_depends = false; if (info.exists("depends")) { @@ -109,6 +128,10 @@ void parseModContents(ModSpec &spec) std::vector<std::string> dependencies; std::ifstream is((spec.path + DIR_DELIM + "depends.txt").c_str()); + + if (is.good()) + log_mod_deprecation(spec, "depends.txt is deprecated, please use mod.conf instead."); + while (is.good()) { std::string dep; std::getline(is, dep); @@ -127,14 +150,10 @@ void parseModContents(ModSpec &spec) } } - if (info.exists("description")) { + if (info.exists("description")) spec.desc = info.get("description"); - } else { - std::ifstream is((spec.path + DIR_DELIM + "description.txt") - .c_str()); - spec.desc = std::string((std::istreambuf_iterator<char>(is)), - std::istreambuf_iterator<char>()); - } + else if (fs::ReadFile(spec.path + DIR_DELIM + "description.txt", spec.desc)) + log_mod_deprecation(spec, "description.txt is deprecated, please use mod.conf instead."); } } diff --git a/src/convert_json.cpp b/src/convert_json.cpp index c774aa002..e9ff1e56c 100644 --- a/src/convert_json.cpp +++ b/src/convert_json.cpp @@ -68,12 +68,17 @@ Json::Value fetchJsonValue(const std::string &url, return root; } -std::string fastWriteJson(const Json::Value &value) +void fastWriteJson(const Json::Value &value, std::ostream &to) { - std::ostringstream oss; Json::StreamWriterBuilder builder; builder["indentation"] = ""; std::unique_ptr<Json::StreamWriter> writer(builder.newStreamWriter()); - writer->write(value, &oss); + writer->write(value, &to); +} + +std::string fastWriteJson(const Json::Value &value) +{ + std::ostringstream oss; + fastWriteJson(value, oss); return oss.str(); } diff --git a/src/convert_json.h b/src/convert_json.h index d8825acdc..2c094a946 100644 --- a/src/convert_json.h +++ b/src/convert_json.h @@ -20,8 +20,11 @@ with this program; if not, write to the Free Software Foundation, Inc., #pragma once #include <json/json.h> +#include <ostream> Json::Value fetchJsonValue(const std::string &url, std::vector<std::string> *extra_headers); +void fastWriteJson(const Json::Value &value, std::ostream &to); + std::string fastWriteJson(const Json::Value &value); diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index 132e31d2b..d5bd56bc9 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -80,7 +80,7 @@ void set_default_settings() settings->setDefault("keymap_drop", "KEY_KEY_Q"); settings->setDefault("keymap_zoom", "KEY_KEY_Z"); settings->setDefault("keymap_inventory", "KEY_KEY_I"); - settings->setDefault("keymap_special1", "KEY_KEY_E"); + settings->setDefault("keymap_aux1", "KEY_KEY_E"); settings->setDefault("keymap_chat", "KEY_KEY_T"); settings->setDefault("keymap_cmd", "/"); settings->setDefault("keymap_cmd_local", "."); @@ -240,6 +240,7 @@ void set_default_settings() #endif settings->setDefault("enable_particles", "true"); settings->setDefault("arm_inertia", "true"); + settings->setDefault("show_nametag_backgrounds", "true"); settings->setDefault("enable_minimap", "true"); settings->setDefault("minimap_shape_round", "true"); @@ -466,7 +467,7 @@ void set_default_settings() settings->setDefault("touchtarget", "true"); settings->setDefault("touchscreen_threshold","20"); settings->setDefault("fixed_virtual_joystick", "false"); - settings->setDefault("virtual_joystick_triggers_aux", "false"); + settings->setDefault("virtual_joystick_triggers_aux1", "false"); settings->setDefault("smooth_lighting", "false"); settings->setDefault("max_simultaneous_block_sends_per_client", "10"); settings->setDefault("emergequeue_limit_diskonly", "16"); diff --git a/src/emerge.cpp b/src/emerge.cpp index e0dc5628e..32e7d9f24 100644 --- a/src/emerge.cpp +++ b/src/emerge.cpp @@ -113,13 +113,15 @@ EmergeParams::~EmergeParams() { infostream << "EmergeParams: destroying " << this << std::endl; // Delete everything that was cloned on creation of EmergeParams + delete biomegen; delete biomemgr; delete oremgr; delete decomgr; delete schemmgr; } -EmergeParams::EmergeParams(EmergeManager *parent, const BiomeManager *biomemgr, +EmergeParams::EmergeParams(EmergeManager *parent, const BiomeGen *biomegen, + const BiomeManager *biomemgr, const OreManager *oremgr, const DecorationManager *decomgr, const SchematicManager *schemmgr) : ndef(parent->ndef), @@ -129,6 +131,7 @@ EmergeParams::EmergeParams(EmergeManager *parent, const BiomeManager *biomemgr, biomemgr(biomemgr->clone()), oremgr(oremgr->clone()), decomgr(decomgr->clone()), schemmgr(schemmgr->clone()) { + this->biomegen = biomegen->clone(this->biomemgr); } //// @@ -143,6 +146,10 @@ EmergeManager::EmergeManager(Server *server) this->decomgr = new DecorationManager(server); this->schemmgr = new SchematicManager(server); + // initialized later + this->mgparams = nullptr; + this->biomegen = nullptr; + // Note that accesses to this variable are not synchronized. // This is because the *only* thread ever starting or stopping // EmergeThreads should be the ServerThread. @@ -240,9 +247,12 @@ void EmergeManager::initMapgens(MapgenParams *params) mgparams = params; + v3s16 csize = v3s16(1, 1, 1) * (params->chunksize * MAP_BLOCKSIZE); + biomegen = biomemgr->createBiomeGen(BIOMEGEN_ORIGINAL, params->bparams, csize); + for (u32 i = 0; i != m_threads.size(); i++) { - EmergeParams *p = new EmergeParams( - this, biomemgr, oremgr, decomgr, schemmgr); + EmergeParams *p = new EmergeParams(this, biomegen, + biomemgr, oremgr, decomgr, schemmgr); infostream << "EmergeManager: Created params " << p << " for thread " << i << std::endl; m_mapgens.push_back(Mapgen::createMapgen(params->mgtype, params, p)); diff --git a/src/emerge.h b/src/emerge.h index da845e243..aac3e7dd3 100644 --- a/src/emerge.h +++ b/src/emerge.h @@ -99,13 +99,15 @@ public: u32 gen_notify_on; const std::set<u32> *gen_notify_on_deco_ids; // shared + BiomeGen *biomegen; BiomeManager *biomemgr; OreManager *oremgr; DecorationManager *decomgr; SchematicManager *schemmgr; private: - EmergeParams(EmergeManager *parent, const BiomeManager *biomemgr, + EmergeParams(EmergeManager *parent, const BiomeGen *biomegen, + const BiomeManager *biomemgr, const OreManager *oremgr, const DecorationManager *decomgr, const SchematicManager *schemmgr); }; @@ -140,6 +142,8 @@ public: ~EmergeManager(); DISABLE_CLASS_COPY(EmergeManager); + const BiomeGen *getBiomeGen() const { return biomegen; } + // no usage restrictions const BiomeManager *getBiomeManager() const { return biomemgr; } const OreManager *getOreManager() const { return oremgr; } @@ -196,6 +200,7 @@ private: // Managers of various map generation-related components // Note that each Mapgen gets a copy(!) of these to work with + BiomeGen *biomegen; BiomeManager *biomemgr; OreManager *oremgr; DecorationManager *decomgr; diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index fdd36914a..5552cebea 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -23,7 +23,6 @@ set(gui_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/guiTable.cpp ${CMAKE_CURRENT_SOURCE_DIR}/guiHyperText.cpp ${CMAKE_CURRENT_SOURCE_DIR}/guiVolumeChange.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/intlGUIEditBox.cpp ${CMAKE_CURRENT_SOURCE_DIR}/modalMenu.cpp ${CMAKE_CURRENT_SOURCE_DIR}/profilergraph.cpp PARENT_SCOPE diff --git a/src/gui/guiButton.cpp b/src/gui/guiButton.cpp index b98e5de82..d6dbddf54 100644 --- a/src/gui/guiButton.cpp +++ b/src/gui/guiButton.cpp @@ -506,6 +506,13 @@ video::SColor GUIButton::getOverrideColor() const return OverrideColor;
}
+#if IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR > 8
+video::SColor GUIButton::getActiveColor() const
+{
+ return video::SColor(0,0,0,0); // unused?
+}
+#endif
+
void GUIButton::enableOverrideColor(bool enable)
{
OverrideColorEnabled = enable;
diff --git a/src/gui/guiButton.h b/src/gui/guiButton.h index 4e1b04aac..834405f51 100644 --- a/src/gui/guiButton.h +++ b/src/gui/guiButton.h @@ -69,6 +69,12 @@ using namespace irr; class ISimpleTextureSource;
+#if (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR <= 8)
+#define OVERRIDE_19
+#else
+#define OVERRIDE_19 override
+#endif
+
class GUIButton : public gui::IGUIButton
{
public:
@@ -97,22 +103,27 @@ public: virtual gui::IGUIFont* getActiveFont() const override;
//! Sets another color for the button text.
- virtual void setOverrideColor(video::SColor color);
+ virtual void setOverrideColor(video::SColor color) OVERRIDE_19;
//! Gets the override color
- virtual video::SColor getOverrideColor(void) const;
+ virtual video::SColor getOverrideColor(void) const OVERRIDE_19;
+
+ #if IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR > 8
+ //! Gets the currently used text color
+ virtual video::SColor getActiveColor() const override;
+ #endif
//! Sets if the button text should use the override color or the color in the gui skin.
- virtual void enableOverrideColor(bool enable);
+ virtual void enableOverrideColor(bool enable) OVERRIDE_19;
//! Checks if an override color is enabled
- virtual bool isOverrideColorEnabled(void) const;
+ virtual bool isOverrideColorEnabled(void) const OVERRIDE_19;
// PATCH
//! Sets an image which should be displayed on the button when it is in the given state.
virtual void setImage(gui::EGUI_BUTTON_IMAGE_STATE state,
video::ITexture* image=nullptr,
- const core::rect<s32>& sourceRect=core::rect<s32>(0,0,0,0));
+ const core::rect<s32>& sourceRect=core::rect<s32>(0,0,0,0)) OVERRIDE_19;
//! Sets an image which should be displayed on the button when it is in normal state.
virtual void setImage(video::ITexture* image=nullptr) override;
@@ -141,7 +152,7 @@ public: */
virtual void setSprite(gui::EGUI_BUTTON_STATE state, s32 index,
video::SColor color=video::SColor(255,255,255,255),
- bool loop=false, bool scale=false);
+ bool loop=false, bool scale=false) OVERRIDE_19;
#if (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR <= 8)
void setSprite(gui::EGUI_BUTTON_STATE state, s32 index, video::SColor color, bool loop) override {
@@ -150,16 +161,16 @@ public: #endif
//! Get the sprite-index for the given state or -1 when no sprite is set
- virtual s32 getSpriteIndex(gui::EGUI_BUTTON_STATE state) const;
+ virtual s32 getSpriteIndex(gui::EGUI_BUTTON_STATE state) const OVERRIDE_19;
//! Get the sprite color for the given state. Color is only used when a sprite is set.
- virtual video::SColor getSpriteColor(gui::EGUI_BUTTON_STATE state) const;
+ virtual video::SColor getSpriteColor(gui::EGUI_BUTTON_STATE state) const OVERRIDE_19;
//! Returns if the sprite in the given state does loop
- virtual bool getSpriteLoop(gui::EGUI_BUTTON_STATE state) const;
+ virtual bool getSpriteLoop(gui::EGUI_BUTTON_STATE state) const OVERRIDE_19;
//! Returns if the sprite in the given state is scaled
- virtual bool getSpriteScale(gui::EGUI_BUTTON_STATE state) const;
+ virtual bool getSpriteScale(gui::EGUI_BUTTON_STATE state) const OVERRIDE_19;
//! Sets if the button should behave like a push button. Which means it
//! can be in two states: Normal or Pressed. With a click on the button,
@@ -199,13 +210,13 @@ public: virtual bool isScalingImage() const override;
//! Get if the shift key was pressed in last EGET_BUTTON_CLICKED event
- virtual bool getClickShiftState() const
+ virtual bool getClickShiftState() const OVERRIDE_19
{
return ClickShiftState;
}
//! Get if the control key was pressed in last EGET_BUTTON_CLICKED event
- virtual bool getClickControlState() const
+ virtual bool getClickControlState() const OVERRIDE_19
{
return ClickControlState;
}
diff --git a/src/gui/guiChatConsole.cpp b/src/gui/guiChatConsole.cpp index 6330d95d4..fd92cf298 100644 --- a/src/gui/guiChatConsole.cpp +++ b/src/gui/guiChatConsole.cpp @@ -635,13 +635,7 @@ bool GUIChatConsole::OnEvent(const SEvent& event) prompt.nickCompletion(names, backwards); return true; } else if (!iswcntrl(event.KeyInput.Char) && !event.KeyInput.Control) { - #if defined(__linux__) && (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 9) - wchar_t wc = L'_'; - mbtowc( &wc, (char *) &event.KeyInput.Char, sizeof(event.KeyInput.Char) ); - prompt.input(wc); - #else - prompt.input(event.KeyInput.Char); - #endif + prompt.input(event.KeyInput.Char); return true; } } diff --git a/src/gui/guiConfirmRegistration.cpp b/src/gui/guiConfirmRegistration.cpp index 4a798c39b..4ca9a64ed 100644 --- a/src/gui/guiConfirmRegistration.cpp +++ b/src/gui/guiConfirmRegistration.cpp @@ -25,7 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include <IGUIButton.h> #include <IGUIStaticText.h> #include <IGUIFont.h> -#include "intlGUIEditBox.h" +#include "guiEditBoxWithScrollbar.h" #include "porting.h" #include "gettext.h" @@ -109,10 +109,9 @@ void GUIConfirmRegistration::regenerateGui(v2u32 screensize) porting::mt_snprintf(info_text_buf, sizeof(info_text_buf), info_text_template.c_str(), m_playername.c_str()); - wchar_t *info_text_buf_wide = utf8_to_wide_c(info_text_buf); - gui::IGUIEditBox *e = new gui::intlGUIEditBox(info_text_buf_wide, true, - Environment, this, ID_intotext, rect2, false, true); - delete[] info_text_buf_wide; + std::wstring info_text_w = utf8_to_wide(info_text_buf); + gui::IGUIEditBox *e = new GUIEditBoxWithScrollBar(info_text_w.c_str(), + true, Environment, this, ID_intotext, rect2, false, true); e->drop(); e->setMultiLine(true); e->setWordWrap(true); diff --git a/src/gui/guiEditBox.cpp b/src/gui/guiEditBox.cpp index 79979dbc3..cd5a0868d 100644 --- a/src/gui/guiEditBox.cpp +++ b/src/gui/guiEditBox.cpp @@ -208,31 +208,10 @@ bool GUIEditBox::OnEvent(const SEvent &event) } } break; - case EET_KEY_INPUT_EVENT: { -#if (defined(__linux__) || defined(__FreeBSD__)) || defined(__DragonFly__) - // ################################################################ - // ValkaTR: - // This part is the difference from the original intlGUIEditBox - // It converts UTF-8 character into a UCS-2 (wchar_t) - wchar_t wc = L'_'; - mbtowc(&wc, (char *)&event.KeyInput.Char, - sizeof(event.KeyInput.Char)); - - // printf( "char: %lc (%u) \r\n", wc, wc ); - - SEvent irrevent(event); - irrevent.KeyInput.Char = wc; - // ################################################################ - - if (processKey(irrevent)) - return true; -#else + case EET_KEY_INPUT_EVENT: if (processKey(event)) return true; -#endif // defined(linux) - break; - } case EET_MOUSE_INPUT_EVENT: if (processMouse(event)) return true; diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp index 5aa6dc9ae..fd35f2d84 100644 --- a/src/gui/guiFormSpecMenu.cpp +++ b/src/gui/guiFormSpecMenu.cpp @@ -65,7 +65,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "guiInventoryList.h" #include "guiItemImage.h" #include "guiScrollContainer.h" -#include "intlGUIEditBox.h" #include "guiHyperText.h" #include "guiScene.h" @@ -1547,21 +1546,13 @@ void GUIFormSpecMenu::createTextField(parserData *data, FieldSpec &spec, } gui::IGUIEditBox *e = nullptr; - static constexpr bool use_intl_edit_box = USE_FREETYPE && - IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 9; - - if (use_intl_edit_box && g_settings->getBool("freetype")) { - e = new gui::intlGUIEditBox(spec.fdefault.c_str(), true, Environment, - data->current_parent, spec.fid, rect, is_editable, is_multiline); - } else { - if (is_multiline) { - e = new GUIEditBoxWithScrollBar(spec.fdefault.c_str(), true, Environment, - data->current_parent, spec.fid, rect, is_editable, true); - } else if (is_editable) { - e = Environment->addEditBox(spec.fdefault.c_str(), rect, true, - data->current_parent, spec.fid); - e->grab(); - } + if (is_multiline) { + e = new GUIEditBoxWithScrollBar(spec.fdefault.c_str(), true, Environment, + data->current_parent, spec.fid, rect, is_editable, true); + } else if (is_editable) { + e = Environment->addEditBox(spec.fdefault.c_str(), rect, true, + data->current_parent, spec.fid); + e->grab(); } auto style = getDefaultStyleForElement(is_multiline ? "textarea" : "field", spec.fname); @@ -2746,7 +2737,7 @@ void GUIFormSpecMenu::parseModel(parserData *data, const std::string &element) { std::vector<std::string> parts = split(element, ';'); - if (parts.size() < 5 || (parts.size() > 9 && + if (parts.size() < 5 || (parts.size() > 10 && m_formspec_version <= FORMSPEC_API_VERSION)) { errorstream << "Invalid model element (" << parts.size() << "): '" << element << "'" << std::endl; @@ -2754,8 +2745,8 @@ void GUIFormSpecMenu::parseModel(parserData *data, const std::string &element) } // Avoid length checks by resizing - if (parts.size() < 9) - parts.resize(9); + if (parts.size() < 10) + parts.resize(10); std::vector<std::string> v_pos = split(parts[0], ','); std::vector<std::string> v_geom = split(parts[1], ','); @@ -2766,6 +2757,7 @@ void GUIFormSpecMenu::parseModel(parserData *data, const std::string &element) bool inf_rotation = is_yes(parts[6]); bool mousectrl = is_yes(parts[7]) || parts[7].empty(); // default true std::vector<std::string> frame_loop = split(parts[8], ','); + std::string speed = unescape_string(parts[9]); MY_CHECKPOS("model", 0); MY_CHECKGEOM("model", 1); @@ -2825,6 +2817,7 @@ void GUIFormSpecMenu::parseModel(parserData *data, const std::string &element) } e->setFrameLoop(frame_loop_begin, frame_loop_end); + e->setAnimationSpeed(stof(speed)); auto style = getStyleForElement("model", spec.fname); e->setStyles(style); diff --git a/src/gui/guiKeyChangeMenu.cpp b/src/gui/guiKeyChangeMenu.cpp index 4dcb47779..84678b629 100644 --- a/src/gui/guiKeyChangeMenu.cpp +++ b/src/gui/guiKeyChangeMenu.cpp @@ -46,7 +46,7 @@ enum GUI_ID_KEY_BACKWARD_BUTTON, GUI_ID_KEY_LEFT_BUTTON, GUI_ID_KEY_RIGHT_BUTTON, - GUI_ID_KEY_USE_BUTTON, + GUI_ID_KEY_AUX1_BUTTON, GUI_ID_KEY_FLY_BUTTON, GUI_ID_KEY_FAST_BUTTON, GUI_ID_KEY_JUMP_BUTTON, @@ -177,7 +177,7 @@ void GUIKeyChangeMenu::regenerateGui(v2u32 screensize) { core::rect<s32> rect(0, 0, option_w, 30 * s); rect += topleft + v2s32(option_x, option_y); - const wchar_t *text = wgettext("\"Special\" = climb down"); + const wchar_t *text = wgettext("\"Aux1\" = climb down"); Environment->addCheckBox(g_settings->getBool("aux1_descends"), rect, this, GUI_ID_CB_AUX1_DESCENDS, text); delete[] text; @@ -416,7 +416,7 @@ void GUIKeyChangeMenu::init_keys() this->add_key(GUI_ID_KEY_BACKWARD_BUTTON, wgettext("Backward"), "keymap_backward"); this->add_key(GUI_ID_KEY_LEFT_BUTTON, wgettext("Left"), "keymap_left"); this->add_key(GUI_ID_KEY_RIGHT_BUTTON, wgettext("Right"), "keymap_right"); - this->add_key(GUI_ID_KEY_USE_BUTTON, wgettext("Special"), "keymap_special1"); + this->add_key(GUI_ID_KEY_AUX1_BUTTON, wgettext("Aux1"), "keymap_aux1"); this->add_key(GUI_ID_KEY_JUMP_BUTTON, wgettext("Jump"), "keymap_jump"); this->add_key(GUI_ID_KEY_SNEAK_BUTTON, wgettext("Sneak"), "keymap_sneak"); this->add_key(GUI_ID_KEY_DROP_BUTTON, wgettext("Drop"), "keymap_drop"); diff --git a/src/gui/guiScene.cpp b/src/gui/guiScene.cpp index 5f4c50b91..f0cfbec5e 100644 --- a/src/gui/guiScene.cpp +++ b/src/gui/guiScene.cpp @@ -34,9 +34,6 @@ GUIScene::GUIScene(gui::IGUIEnvironment *env, scene::ISceneManager *smgr, m_cam = m_smgr->addCameraSceneNode(0, v3f(0.f, 0.f, -100.f), v3f(0.f)); m_cam->setFOV(30.f * core::DEGTORAD); - scene::ILightSceneNode *light = m_smgr->addLightSceneNode(m_cam); - light->setRadius(1000.f); - m_smgr->getParameters()->setAttribute(scene::ALLOW_ZWRITE_ON_TRANSPARENT, true); } @@ -60,6 +57,7 @@ scene::IAnimatedMeshSceneNode *GUIScene::setMesh(scene::IAnimatedMesh *mesh) m_mesh = m_smgr->addAnimatedMeshSceneNode(mesh); m_mesh->setPosition(-m_mesh->getBoundingBox().getCenter()); m_mesh->animateJoints(); + return m_mesh; } @@ -73,10 +71,13 @@ void GUIScene::setTexture(u32 idx, video::ITexture *texture) material.setFlag(video::EMF_FOG_ENABLE, true); material.setFlag(video::EMF_BILINEAR_FILTER, false); material.setFlag(video::EMF_BACK_FACE_CULLING, false); + material.setFlag(video::EMF_ZWRITE_ENABLE, true); } void GUIScene::draw() { + m_driver->clearBuffers(video::ECBF_DEPTH); + // Control rotation speed based on time u64 new_time = porting::getTimeMs(); u64 dtime_ms = 0; @@ -161,6 +162,14 @@ void GUIScene::setFrameLoop(s32 begin, s32 end) m_mesh->setFrameLoop(begin, end); } +/** + * Sets the animation speed (FPS) for the mesh + */ +void GUIScene::setAnimationSpeed(f32 speed) +{ + m_mesh->setAnimationSpeed(speed); +} + /* Camera control functions */ inline void GUIScene::calcOptimalDistance() diff --git a/src/gui/guiScene.h b/src/gui/guiScene.h index 08eb7f350..0f5f3a891 100644 --- a/src/gui/guiScene.h +++ b/src/gui/guiScene.h @@ -37,6 +37,7 @@ public: void setTexture(u32 idx, video::ITexture *texture); void setBackgroundColor(const video::SColor &color) noexcept { m_bgcolor = color; }; void setFrameLoop(s32 begin, s32 end); + void setAnimationSpeed(f32 speed); void enableMouseControl(bool enable) noexcept { m_mouse_ctrl = enable; }; void setRotation(v2f rot) noexcept { m_custom_rot = rot; }; void enableContinuousRotation(bool enable) noexcept { m_inf_rot = enable; }; diff --git a/src/gui/intlGUIEditBox.cpp b/src/gui/intlGUIEditBox.cpp deleted file mode 100644 index 0f09ea746..000000000 --- a/src/gui/intlGUIEditBox.cpp +++ /dev/null @@ -1,626 +0,0 @@ -// 11.11.2011 11:11 ValkaTR -// -// This is a copy of intlGUIEditBox from the irrlicht, but with a -// fix in the OnEvent function, which doesn't allowed input of -// other keyboard layouts than latin-1 -// -// Characters like: ä ö ü õ ы й ю я ъ № € ° ... -// -// This fix is only needed for linux, because of a bug -// in the CIrrDeviceLinux.cpp:1014-1015 of the irrlicht -// -// Also locale in the programm should not be changed to -// a "C", "POSIX" or whatever, it should be set to "", -// or XLookupString will return nothing for the international -// characters. -// -// From the "man setlocale": -// -// On startup of the main program, the portable "C" locale -// is selected as default. A program may be made -// portable to all locales by calling: -// -// setlocale(LC_ALL, ""); -// -// after program initialization.... -// - -// Copyright (C) 2002-2013 Nikolaus Gebhardt -// This file is part of the "Irrlicht Engine". -// For conditions of distribution and use, see copyright notice in irrlicht.h - -#include <util/numeric.h> -#include "intlGUIEditBox.h" - -#include "IGUISkin.h" -#include "IGUIEnvironment.h" -#include "IGUIFont.h" -#include "IVideoDriver.h" -//#include "irrlicht/os.cpp" -#include "porting.h" -//#include "Keycodes.h" -#include "log.h" - -/* - todo: - optional scrollbars - ctrl+left/right to select word - double click/ctrl click: word select + drag to select whole words, triple click to select line - optional? dragging selected text - numerical -*/ - -namespace irr -{ -namespace gui -{ - -//! constructor -intlGUIEditBox::intlGUIEditBox(const wchar_t* text, bool border, - IGUIEnvironment* environment, IGUIElement* parent, s32 id, - const core::rect<s32>& rectangle, bool writable, bool has_vscrollbar) - : GUIEditBox(environment, parent, id, rectangle, border, writable) -{ - #ifdef _DEBUG - setDebugName("intlintlGUIEditBox"); - #endif - - Text = text; - - if (Environment) - m_operator = Environment->getOSOperator(); - - if (m_operator) - m_operator->grab(); - - // this element can be tabbed to - setTabStop(true); - setTabOrder(-1); - - IGUISkin *skin = 0; - if (Environment) - skin = Environment->getSkin(); - if (m_border && skin) - { - m_frame_rect.UpperLeftCorner.X += skin->getSize(EGDS_TEXT_DISTANCE_X)+1; - m_frame_rect.UpperLeftCorner.Y += skin->getSize(EGDS_TEXT_DISTANCE_Y)+1; - m_frame_rect.LowerRightCorner.X -= skin->getSize(EGDS_TEXT_DISTANCE_X)+1; - m_frame_rect.LowerRightCorner.Y -= skin->getSize(EGDS_TEXT_DISTANCE_Y)+1; - } - - if (skin && has_vscrollbar) { - m_scrollbar_width = skin->getSize(gui::EGDS_SCROLLBAR_SIZE); - - if (m_scrollbar_width > 0) { - createVScrollBar(); - } - } - - breakText(); - - calculateScrollPos(); - setWritable(writable); -} - -//! Sets whether to draw the background -void intlGUIEditBox::setDrawBackground(bool draw) -{ -} - -void intlGUIEditBox::updateAbsolutePosition() -{ - core::rect<s32> oldAbsoluteRect(AbsoluteRect); - IGUIElement::updateAbsolutePosition(); - if ( oldAbsoluteRect != AbsoluteRect ) - { - breakText(); - } -} - - -//! draws the element and its children -void intlGUIEditBox::draw() -{ - if (!IsVisible) - return; - - const bool focus = Environment->hasFocus(this); - - IGUISkin* skin = Environment->getSkin(); - if (!skin) - return; - - m_frame_rect = AbsoluteRect; - - // draw the border - - if (m_border) - { - if (m_writable) { - skin->draw3DSunkenPane(this, skin->getColor(EGDC_WINDOW), - false, true, m_frame_rect, &AbsoluteClippingRect); - } - - m_frame_rect.UpperLeftCorner.X += skin->getSize(EGDS_TEXT_DISTANCE_X)+1; - m_frame_rect.UpperLeftCorner.Y += skin->getSize(EGDS_TEXT_DISTANCE_Y)+1; - m_frame_rect.LowerRightCorner.X -= skin->getSize(EGDS_TEXT_DISTANCE_X)+1; - m_frame_rect.LowerRightCorner.Y -= skin->getSize(EGDS_TEXT_DISTANCE_Y)+1; - } - - updateVScrollBar(); - core::rect<s32> localClipRect = m_frame_rect; - localClipRect.clipAgainst(AbsoluteClippingRect); - - // draw the text - - IGUIFont* font = m_override_font; - if (!m_override_font) - font = skin->getFont(); - - s32 cursorLine = 0; - s32 charcursorpos = 0; - - if (font) - { - if (m_last_break_font != font) - { - breakText(); - } - - // calculate cursor pos - - core::stringw *txtLine = &Text; - s32 startPos = 0; - - core::stringw s, s2; - - // get mark position - const bool ml = (!m_passwordbox && (m_word_wrap || m_multiline)); - const s32 realmbgn = m_mark_begin < m_mark_end ? m_mark_begin : m_mark_end; - const s32 realmend = m_mark_begin < m_mark_end ? m_mark_end : m_mark_begin; - const s32 hlineStart = ml ? getLineFromPos(realmbgn) : 0; - const s32 hlineCount = ml ? getLineFromPos(realmend) - hlineStart + 1 : 1; - const s32 lineCount = ml ? m_broken_text.size() : 1; - - // Save the override color information. - // Then, alter it if the edit box is disabled. - const bool prevOver = m_override_color_enabled; - const video::SColor prevColor = m_override_color; - - if (!Text.empty()) { - if (!IsEnabled && !m_override_color_enabled) - { - m_override_color_enabled = true; - m_override_color = skin->getColor(EGDC_GRAY_TEXT); - } - - for (s32 i=0; i < lineCount; ++i) - { - setTextRect(i); - - // clipping test - don't draw anything outside the visible area - core::rect<s32> c = localClipRect; - c.clipAgainst(m_current_text_rect); - if (!c.isValid()) - continue; - - // get current line - if (m_passwordbox) - { - if (m_broken_text.size() != 1) - { - m_broken_text.clear(); - m_broken_text.emplace_back(); - } - if (m_broken_text[0].size() != Text.size()) - { - m_broken_text[0] = Text; - for (u32 q = 0; q < Text.size(); ++q) - { - m_broken_text[0] [q] = m_passwordchar; - } - } - txtLine = &m_broken_text[0]; - startPos = 0; - } - else - { - txtLine = ml ? &m_broken_text[i] : &Text; - startPos = ml ? m_broken_text_positions[i] : 0; - } - - - // draw normal text - font->draw(txtLine->c_str(), m_current_text_rect, - m_override_color_enabled ? m_override_color : skin->getColor(EGDC_BUTTON_TEXT), - false, true, &localClipRect); - - // draw mark and marked text - if (focus && m_mark_begin != m_mark_end && i >= hlineStart && i < hlineStart + hlineCount) - { - - s32 mbegin = 0, mend = 0; - s32 lineStartPos = 0, lineEndPos = txtLine->size(); - - if (i == hlineStart) - { - // highlight start is on this line - s = txtLine->subString(0, realmbgn - startPos); - mbegin = font->getDimension(s.c_str()).Width; - - // deal with kerning - mbegin += font->getKerningWidth( - &((*txtLine)[realmbgn - startPos]), - realmbgn - startPos > 0 ? &((*txtLine)[realmbgn - startPos - 1]) : 0); - - lineStartPos = realmbgn - startPos; - } - if (i == hlineStart + hlineCount - 1) - { - // highlight end is on this line - s2 = txtLine->subString(0, realmend - startPos); - mend = font->getDimension(s2.c_str()).Width; - lineEndPos = (s32)s2.size(); - } - else - mend = font->getDimension(txtLine->c_str()).Width; - - m_current_text_rect.UpperLeftCorner.X += mbegin; - m_current_text_rect.LowerRightCorner.X = m_current_text_rect.UpperLeftCorner.X + mend - mbegin; - - // draw mark - skin->draw2DRectangle(this, skin->getColor(EGDC_HIGH_LIGHT), m_current_text_rect, &localClipRect); - - // draw marked text - s = txtLine->subString(lineStartPos, lineEndPos - lineStartPos); - - if (!s.empty()) - font->draw(s.c_str(), m_current_text_rect, - m_override_color_enabled ? m_override_color : skin->getColor(EGDC_HIGH_LIGHT_TEXT), - false, true, &localClipRect); - - } - } - - // Return the override color information to its previous settings. - m_override_color_enabled = prevOver; - m_override_color = prevColor; - } - - // draw cursor - - if (m_word_wrap || m_multiline) - { - cursorLine = getLineFromPos(m_cursor_pos); - txtLine = &m_broken_text[cursorLine]; - startPos = m_broken_text_positions[cursorLine]; - } - s = txtLine->subString(0,m_cursor_pos-startPos); - charcursorpos = font->getDimension(s.c_str()).Width + - font->getKerningWidth(L"_", m_cursor_pos-startPos > 0 ? &((*txtLine)[m_cursor_pos-startPos-1]) : 0); - - if (m_writable) { - if (focus && (porting::getTimeMs() - m_blink_start_time) % 700 < 350) { - setTextRect(cursorLine); - m_current_text_rect.UpperLeftCorner.X += charcursorpos; - - font->draw(L"_", m_current_text_rect, - m_override_color_enabled ? m_override_color : skin->getColor(EGDC_BUTTON_TEXT), - false, true, &localClipRect); - } - } - } - - // draw children - IGUIElement::draw(); -} - - -s32 intlGUIEditBox::getCursorPos(s32 x, s32 y) -{ - IGUIFont* font = getActiveFont(); - - const u32 lineCount = (m_word_wrap || m_multiline) ? m_broken_text.size() : 1; - - core::stringw *txtLine = NULL; - s32 startPos = 0; - u32 curr_line_idx = 0; - x += 3; - - for (; curr_line_idx < lineCount; ++curr_line_idx) { - setTextRect(curr_line_idx); - if (curr_line_idx == 0 && y < m_current_text_rect.UpperLeftCorner.Y) - y = m_current_text_rect.UpperLeftCorner.Y; - if (curr_line_idx == lineCount - 1 && y > m_current_text_rect.LowerRightCorner.Y) - y = m_current_text_rect.LowerRightCorner.Y; - - // is it inside this region? - if (y >= m_current_text_rect.UpperLeftCorner.Y && y <= m_current_text_rect.LowerRightCorner.Y) { - // we've found the clicked line - txtLine = (m_word_wrap || m_multiline) ? &m_broken_text[curr_line_idx] : &Text; - startPos = (m_word_wrap || m_multiline) ? m_broken_text_positions[curr_line_idx] : 0; - break; - } - } - - if (x < m_current_text_rect.UpperLeftCorner.X) - x = m_current_text_rect.UpperLeftCorner.X; - else if (x > m_current_text_rect.LowerRightCorner.X) - x = m_current_text_rect.LowerRightCorner.X; - - s32 idx = font->getCharacterFromPos(txtLine->c_str(), x - m_current_text_rect.UpperLeftCorner.X); - // Special handling for last line, if we are on limits, add 1 extra shift because idx - // will be the last char, not null char of the wstring - if (curr_line_idx == lineCount - 1 && x == m_current_text_rect.LowerRightCorner.X) - idx++; - - return rangelim(idx + startPos, 0, S32_MAX); -} - - -//! Breaks the single text line. -void intlGUIEditBox::breakText() -{ - IGUISkin* skin = Environment->getSkin(); - - if ((!m_word_wrap && !m_multiline) || !skin) - return; - - m_broken_text.clear(); // need to reallocate :/ - m_broken_text_positions.clear(); - - IGUIFont* font = m_override_font; - if (!m_override_font) - font = skin->getFont(); - - if (!font) - return; - - m_last_break_font = font; - - core::stringw line; - core::stringw word; - core::stringw whitespace; - s32 lastLineStart = 0; - s32 size = Text.size(); - s32 length = 0; - s32 elWidth = RelativeRect.getWidth() - m_scrollbar_width - 10; - wchar_t c; - - for (s32 i=0; i<size; ++i) - { - c = Text[i]; - bool lineBreak = false; - - if (c == L'\r') // Mac or Windows breaks - { - lineBreak = true; - c = ' '; - if (Text[i+1] == L'\n') // Windows breaks - { - Text.erase(i+1); - --size; - } - } - else if (c == L'\n') // Unix breaks - { - lineBreak = true; - c = ' '; - } - - // don't break if we're not a multi-line edit box - if (!m_multiline) - lineBreak = false; - - if (c == L' ' || c == 0 || i == (size-1)) - { - if (!word.empty()) { - // here comes the next whitespace, look if - // we can break the last word to the next line. - s32 whitelgth = font->getDimension(whitespace.c_str()).Width; - s32 worldlgth = font->getDimension(word.c_str()).Width; - - if (m_word_wrap && length + worldlgth + whitelgth > elWidth) - { - // break to next line - length = worldlgth; - m_broken_text.push_back(line); - m_broken_text_positions.push_back(lastLineStart); - lastLineStart = i - (s32)word.size(); - line = word; - } - else - { - // add word to line - line += whitespace; - line += word; - length += whitelgth + worldlgth; - } - - word = L""; - whitespace = L""; - } - - whitespace += c; - - // compute line break - if (lineBreak) - { - line += whitespace; - line += word; - m_broken_text.push_back(line); - m_broken_text_positions.push_back(lastLineStart); - lastLineStart = i+1; - line = L""; - word = L""; - whitespace = L""; - length = 0; - } - } - else - { - // yippee this is a word.. - word += c; - } - } - - line += whitespace; - line += word; - m_broken_text.push_back(line); - m_broken_text_positions.push_back(lastLineStart); -} - - -void intlGUIEditBox::setTextRect(s32 line) -{ - core::dimension2du d; - - IGUISkin* skin = Environment->getSkin(); - if (!skin) - return; - - IGUIFont* font = m_override_font ? m_override_font : skin->getFont(); - - if (!font) - return; - - // get text dimension - const u32 lineCount = (m_word_wrap || m_multiline) ? m_broken_text.size() : 1; - if (m_word_wrap || m_multiline) - { - d = font->getDimension(m_broken_text[line].c_str()); - } - else - { - d = font->getDimension(Text.c_str()); - d.Height = AbsoluteRect.getHeight(); - } - d.Height += font->getKerningHeight(); - - // justification - switch (m_halign) - { - case EGUIA_CENTER: - // align to h centre - m_current_text_rect.UpperLeftCorner.X = (m_frame_rect.getWidth()/2) - (d.Width/2); - m_current_text_rect.LowerRightCorner.X = (m_frame_rect.getWidth()/2) + (d.Width/2); - break; - case EGUIA_LOWERRIGHT: - // align to right edge - m_current_text_rect.UpperLeftCorner.X = m_frame_rect.getWidth() - d.Width; - m_current_text_rect.LowerRightCorner.X = m_frame_rect.getWidth(); - break; - default: - // align to left edge - m_current_text_rect.UpperLeftCorner.X = 0; - m_current_text_rect.LowerRightCorner.X = d.Width; - - } - - switch (m_valign) - { - case EGUIA_CENTER: - // align to v centre - m_current_text_rect.UpperLeftCorner.Y = - (m_frame_rect.getHeight()/2) - (lineCount*d.Height)/2 + d.Height*line; - break; - case EGUIA_LOWERRIGHT: - // align to bottom edge - m_current_text_rect.UpperLeftCorner.Y = - m_frame_rect.getHeight() - lineCount*d.Height + d.Height*line; - break; - default: - // align to top edge - m_current_text_rect.UpperLeftCorner.Y = d.Height*line; - break; - } - - m_current_text_rect.UpperLeftCorner.X -= m_hscroll_pos; - m_current_text_rect.LowerRightCorner.X -= m_hscroll_pos; - m_current_text_rect.UpperLeftCorner.Y -= m_vscroll_pos; - m_current_text_rect.LowerRightCorner.Y = m_current_text_rect.UpperLeftCorner.Y + d.Height; - - m_current_text_rect += m_frame_rect.UpperLeftCorner; - -} - -void intlGUIEditBox::calculateScrollPos() -{ - if (!m_autoscroll) - return; - - // calculate horizontal scroll position - s32 cursLine = getLineFromPos(m_cursor_pos); - setTextRect(cursLine); - - // don't do horizontal scrolling when wordwrap is enabled. - if (!m_word_wrap) - { - // get cursor position - IGUISkin* skin = Environment->getSkin(); - if (!skin) - return; - IGUIFont* font = m_override_font ? m_override_font : skin->getFont(); - if (!font) - return; - - core::stringw *txtLine = m_multiline ? &m_broken_text[cursLine] : &Text; - s32 cPos = m_multiline ? m_cursor_pos - m_broken_text_positions[cursLine] : m_cursor_pos; - - s32 cStart = m_current_text_rect.UpperLeftCorner.X + m_hscroll_pos + - font->getDimension(txtLine->subString(0, cPos).c_str()).Width; - - s32 cEnd = cStart + font->getDimension(L"_ ").Width; - - if (m_frame_rect.LowerRightCorner.X < cEnd) - m_hscroll_pos = cEnd - m_frame_rect.LowerRightCorner.X; - else if (m_frame_rect.UpperLeftCorner.X > cStart) - m_hscroll_pos = cStart - m_frame_rect.UpperLeftCorner.X; - else - m_hscroll_pos = 0; - - // todo: adjust scrollbar - } - - if (!m_word_wrap && !m_multiline) - return; - - // vertical scroll position - if (m_frame_rect.LowerRightCorner.Y < m_current_text_rect.LowerRightCorner.Y) - m_vscroll_pos += m_current_text_rect.LowerRightCorner.Y - m_frame_rect.LowerRightCorner.Y; // scrolling downwards - else if (m_frame_rect.UpperLeftCorner.Y > m_current_text_rect.UpperLeftCorner.Y) - m_vscroll_pos += m_current_text_rect.UpperLeftCorner.Y - m_frame_rect.UpperLeftCorner.Y; // scrolling upwards - - // todo: adjust scrollbar - if (m_vscrollbar) - m_vscrollbar->setPos(m_vscroll_pos); -} - - -//! Create a vertical scrollbar -void intlGUIEditBox::createVScrollBar() -{ - s32 fontHeight = 1; - - if (m_override_font) { - fontHeight = m_override_font->getDimension(L"").Height; - } else { - if (IGUISkin* skin = Environment->getSkin()) { - if (IGUIFont* font = skin->getFont()) { - fontHeight = font->getDimension(L"").Height; - } - } - } - - irr::core::rect<s32> scrollbarrect = m_frame_rect; - scrollbarrect.UpperLeftCorner.X += m_frame_rect.getWidth() - m_scrollbar_width; - m_vscrollbar = new GUIScrollBar(Environment, getParent(), -1, - scrollbarrect, false, true); - - m_vscrollbar->setVisible(false); - m_vscrollbar->setSmallStep(3 * fontHeight); - m_vscrollbar->setLargeStep(10 * fontHeight); -} - -} // end namespace gui -} // end namespace irr diff --git a/src/gui/intlGUIEditBox.h b/src/gui/intlGUIEditBox.h deleted file mode 100644 index 007fe1c93..000000000 --- a/src/gui/intlGUIEditBox.h +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright (C) 2002-2013 Nikolaus Gebhardt -// This file is part of the "Irrlicht Engine". -// For conditions of distribution and use, see copyright notice in irrlicht.h - -#pragma once - -#include "IrrCompileConfig.h" -//#ifdef _IRR_COMPILE_WITH_GUI_ - -#include "guiEditBox.h" -#include "irrArray.h" -#include "IOSOperator.h" - -namespace irr -{ -namespace gui -{ - class intlGUIEditBox : public GUIEditBox - { - public: - - //! constructor - intlGUIEditBox(const wchar_t* text, bool border, IGUIEnvironment* environment, - IGUIElement* parent, s32 id, const core::rect<s32>& rectangle, - bool writable = true, bool has_vscrollbar = false); - - //! destructor - virtual ~intlGUIEditBox() {} - - //! Sets whether to draw the background - virtual void setDrawBackground(bool draw); - - virtual bool isDrawBackgroundEnabled() const { return true; } - - //! draws the element and its children - virtual void draw(); - - //! Updates the absolute position, splits text if required - virtual void updateAbsolutePosition(); - - virtual void setCursorChar(const wchar_t cursorChar) {} - - virtual wchar_t getCursorChar() const { return L'|'; } - - virtual void setCursorBlinkTime(u32 timeMs) {} - - virtual u32 getCursorBlinkTime() const { return 500; } - - protected: - //! Breaks the single text line. - virtual void breakText(); - //! sets the area of the given line - virtual void setTextRect(s32 line); - - //! calculates the current scroll position - void calculateScrollPos(); - - s32 getCursorPos(s32 x, s32 y); - - //! Create a vertical scrollbar - void createVScrollBar(); - }; - - -} // end namespace gui -} // end namespace irr - -//#endif // _IRR_COMPILE_WITH_GUI_ diff --git a/src/gui/touchscreengui.cpp b/src/gui/touchscreengui.cpp index e1a971462..78b18c2d9 100644 --- a/src/gui/touchscreengui.cpp +++ b/src/gui/touchscreengui.cpp @@ -40,7 +40,7 @@ const char **button_imagenames = (const char *[]) { "jump_btn.png", "down.png", "zoom.png", - "aux_btn.png" + "aux1_btn.png" }; const char **joystick_imagenames = (const char *[]) { @@ -80,8 +80,8 @@ static irr::EKEY_CODE id2keycode(touch_gui_button_id id) case zoom_id: key = "zoom"; break; - case special1_id: - key = "special1"; + case aux1_id: + key = "aux1"; break; case fly_id: key = "freemove"; @@ -425,7 +425,7 @@ TouchScreenGUI::TouchScreenGUI(IrrlichtDevice *device, IEventReceiver *receiver) m_touchscreen_threshold = g_settings->getU16("touchscreen_threshold"); m_fixed_joystick = g_settings->getBool("fixed_virtual_joystick"); - m_joystick_triggers_special1 = g_settings->getBool("virtual_joystick_triggers_aux"); + m_joystick_triggers_aux1 = g_settings->getBool("virtual_joystick_triggers_aux1"); m_screensize = m_device->getVideoDriver()->getScreenSize(); button_size = MYMIN(m_screensize.Y / 4.5f, porting::getDisplayDensity() * @@ -521,9 +521,9 @@ void TouchScreenGUI::init(ISimpleTextureSource *tsrc) m_screensize.Y - (3 * button_size)), L"z", false); - // init special1/aux button - if (!m_joystick_triggers_special1) - initButton(special1_id, + // init aux1 button + if (!m_joystick_triggers_aux1) + initButton(aux1_id, rect<s32>(m_screensize.X - (1.25 * button_size), m_screensize.Y - (2.5 * button_size), m_screensize.X - (0.25 * button_size), @@ -923,7 +923,7 @@ void TouchScreenGUI::translateEvent(const SEvent &event) } if (distance > button_size) { - m_joystick_status[j_special1] = true; + m_joystick_status[j_aux1] = true; // move joystick "button" s32 ndx = button_size * dx / distance - button_size / 2.0f; s32 ndy = button_size * dy / distance - button_size / 2.0f; @@ -1039,7 +1039,7 @@ bool TouchScreenGUI::doubleTapDetection() void TouchScreenGUI::applyJoystickStatus() { for (unsigned int i = 0; i < 5; i++) { - if (i == 4 && !m_joystick_triggers_special1) + if (i == 4 && !m_joystick_triggers_aux1) continue; SEvent translated{}; diff --git a/src/gui/touchscreengui.h b/src/gui/touchscreengui.h index 0349624fa..ad5abae87 100644 --- a/src/gui/touchscreengui.h +++ b/src/gui/touchscreengui.h @@ -39,7 +39,7 @@ typedef enum jump_id = 0, crunch_id, zoom_id, - special1_id, + aux1_id, after_last_element_id, settings_starter_id, rare_controls_starter_id, @@ -69,7 +69,7 @@ typedef enum j_backward, j_left, j_right, - j_special1 + j_aux1 } touch_gui_joystick_move_id; typedef enum @@ -217,7 +217,7 @@ private: // forward, backward, left, right touch_gui_button_id m_joystick_names[5] = { - forward_id, backward_id, left_id, right_id, special1_id}; + forward_id, backward_id, left_id, right_id, aux1_id}; bool m_joystick_status[5] = {false, false, false, false, false}; /* @@ -237,7 +237,7 @@ private: int m_joystick_id = -1; bool m_joystick_has_really_moved = false; bool m_fixed_joystick = false; - bool m_joystick_triggers_special1 = false; + bool m_joystick_triggers_aux1 = false; button_info *m_joystick_btn_off = nullptr; button_info *m_joystick_btn_bg = nullptr; button_info *m_joystick_btn_center = nullptr; diff --git a/src/inventorymanager.cpp b/src/inventorymanager.cpp index 635bd2e4b..1e81c1dbc 100644 --- a/src/inventorymanager.cpp +++ b/src/inventorymanager.cpp @@ -301,6 +301,7 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame if (!list_to->getItem(dest_i).empty()) { to_i = dest_i; apply(mgr, player, gamedef); + assert(move_count <= count); count -= move_count; } } @@ -339,7 +340,7 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame */ ItemStack src_item = list_from->getItem(from_i); - if (count > 0) + if (count > 0 && count < src_item.count) src_item.count = count; if (src_item.empty()) return; @@ -352,10 +353,12 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame bool allow_swap = !list_to->itemFits(to_i, src_item, &restitem) && restitem.count == src_item.count && !caused_by_move_somewhere; + move_count = src_item.count - restitem.count; // Shift-click: Cannot fill this stack, proceed with next slot - if (caused_by_move_somewhere && restitem.count == src_item.count) + if (caused_by_move_somewhere && move_count == 0) { return; + } if (allow_swap) { // Swap will affect the entire stack if it can performed. @@ -384,9 +387,16 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame src_can_take_count = dst_can_put_count = 0; } else { // Take from one inventory, put into another + int src_item_count = src_item.count; + if (caused_by_move_somewhere) + // When moving somewhere: temporarily use the actual movable stack + // size to ensure correct callback execution. + src_item.count = move_count; dst_can_put_count = allowPut(src_item, player); src_can_take_count = allowTake(src_item, player); - + if (caused_by_move_somewhere) + // Reset source item count + src_item.count = src_item_count; bool swap_expected = allow_swap; allow_swap = allow_swap && (src_can_take_count == -1 || src_can_take_count >= src_item.count) @@ -416,12 +426,17 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame count = src_can_take_count; if (dst_can_put_count != -1 && count > dst_can_put_count) count = dst_can_put_count; + /* Limit according to source item count */ if (count > list_from->getItem(from_i).count) count = list_from->getItem(from_i).count; /* If no items will be moved, don't go further */ if (count == 0) { + if (caused_by_move_somewhere) + // Set move count to zero, as no items have been moved + move_count = 0; + // Undo client prediction. See 'clientApply' if (from_inv.type == InventoryLocation::PLAYER) list_from->setModified(); @@ -438,6 +453,7 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame <<" list=\""<<to_list<<"\"" <<" i="<<to_i <<std::endl; + return; } @@ -455,6 +471,8 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame bool did_swap = false; move_count = list_from->moveItem(from_i, list_to, to_i, count, allow_swap, &did_swap); + if (caused_by_move_somewhere) + count = old_count; assert(allow_swap == did_swap); // If source is infinite, reset it's stack @@ -503,8 +521,7 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame << std::endl; // If we are inside the move somewhere loop, we don't need to report - // anything if nothing happened (perhaps we don't need to report - // anything for caused_by_move_somewhere == true, but this way its safer) + // anything if nothing happened if (caused_by_move_somewhere && move_count == 0) return; @@ -558,7 +575,15 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame } mgr->setInventoryModified(from_inv); } else { + int src_item_count = src_item.count; + if (caused_by_move_somewhere) + // When moving somewhere: temporarily use the actual movable stack + // size to ensure correct callback execution. + src_item.count = move_count; onPutAndOnTake(src_item, player); + if (caused_by_move_somewhere) + // Reset source item count + src_item.count = src_item_count; if (did_swap) { // Item is now placed in source list src_item = list_from->getItem(from_i); diff --git a/src/irrlicht_changes/CGUITTFont.cpp b/src/irrlicht_changes/CGUITTFont.cpp index bd4e700de..960b2320a 100644 --- a/src/irrlicht_changes/CGUITTFont.cpp +++ b/src/irrlicht_changes/CGUITTFont.cpp @@ -103,7 +103,7 @@ video::IImage* SGUITTGlyph::createGlyphImage(const FT_Bitmap& bits, video::IVide // Load the monochrome data in. const u32 image_pitch = image->getPitch() / sizeof(u16); - u16* image_data = (u16*)image->lock(); + u16* image_data = (u16*)image->getData(); u8* glyph_data = bits.buffer; for (s32 y = 0; y < (s32)bits.rows; ++y) @@ -119,7 +119,6 @@ video::IImage* SGUITTGlyph::createGlyphImage(const FT_Bitmap& bits, video::IVide } image_data += image_pitch; } - image->unlock(); break; } @@ -133,7 +132,7 @@ video::IImage* SGUITTGlyph::createGlyphImage(const FT_Bitmap& bits, video::IVide // Load the grayscale data in. const float gray_count = static_cast<float>(bits.num_grays); const u32 image_pitch = image->getPitch() / sizeof(u32); - u32* image_data = (u32*)image->lock(); + u32* image_data = (u32*)image->getData(); u8* glyph_data = bits.buffer; for (s32 y = 0; y < (s32)bits.rows; ++y) { @@ -145,7 +144,6 @@ video::IImage* SGUITTGlyph::createGlyphImage(const FT_Bitmap& bits, video::IVide } glyph_data += bits.pitch; } - image->unlock(); break; } default: diff --git a/src/irrlicht_changes/static_text.cpp b/src/irrlicht_changes/static_text.cpp index bf61cd64e..a8cc33352 100644 --- a/src/irrlicht_changes/static_text.cpp +++ b/src/irrlicht_changes/static_text.cpp @@ -255,6 +255,12 @@ video::SColor StaticText::getOverrideColor() const return ColoredText.getDefaultColor(); } +#if IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR > 8 +video::SColor StaticText::getActiveColor() const +{ + return getOverrideColor(); +} +#endif //! Sets if the static text should use the overide color or the //! color in the gui skin. diff --git a/src/irrlicht_changes/static_text.h b/src/irrlicht_changes/static_text.h index 1f111ea56..786129d57 100644 --- a/src/irrlicht_changes/static_text.h +++ b/src/irrlicht_changes/static_text.h @@ -140,6 +140,11 @@ namespace gui virtual video::SColor getOverrideColor() const; #endif + #if IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR > 8 + //! Gets the currently used text color + virtual video::SColor getActiveColor() const; + #endif + //! Sets if the static text should use the overide color or the //! color in the gui skin. virtual void enableOverrideColor(bool enable); diff --git a/src/itemdef.cpp b/src/itemdef.cpp index 5fb1e4c47..d79d6b263 100644 --- a/src/itemdef.cpp +++ b/src/itemdef.cpp @@ -71,13 +71,11 @@ ItemDefinition& ItemDefinition::operator=(const ItemDefinition &def) stack_max = def.stack_max; usable = def.usable; liquids_pointable = def.liquids_pointable; - if(def.tool_capabilities) - { - tool_capabilities = new ToolCapabilities( - *def.tool_capabilities); - } + if (def.tool_capabilities) + tool_capabilities = new ToolCapabilities(*def.tool_capabilities); groups = def.groups; node_placement_prediction = def.node_placement_prediction; + place_param2 = def.place_param2; sound_place = def.sound_place; sound_place_failed = def.sound_place_failed; range = def.range; @@ -120,8 +118,8 @@ void ItemDefinition::reset() sound_place = SimpleSoundSpec(); sound_place_failed = SimpleSoundSpec(); range = -1; - node_placement_prediction = ""; + place_param2 = 0; } void ItemDefinition::serialize(std::ostream &os, u16 protocol_version) const @@ -166,6 +164,8 @@ void ItemDefinition::serialize(std::ostream &os, u16 protocol_version) const os << serializeString16(wield_overlay); os << serializeString16(short_description); + + os << place_param2; } void ItemDefinition::deSerialize(std::istream &is) @@ -219,6 +219,8 @@ void ItemDefinition::deSerialize(std::istream &is) // block to not need to increase the version. try { short_description = deSerializeString16(is); + + place_param2 = readU8(is); // 0 if missing } catch(SerializationError &e) {}; } diff --git a/src/itemdef.h b/src/itemdef.h index ebf0d3527..3e302840f 100644 --- a/src/itemdef.h +++ b/src/itemdef.h @@ -86,6 +86,7 @@ struct ItemDefinition // Server will update the precise end result a moment later. // "" = no prediction std::string node_placement_prediction; + u8 place_param2; /* Some helpful methods diff --git a/src/mapgen/mapgen.cpp b/src/mapgen/mapgen.cpp index e0dfd2d71..7984ff609 100644 --- a/src/mapgen/mapgen.cpp +++ b/src/mapgen/mapgen.cpp @@ -595,7 +595,8 @@ MapgenBasic::MapgenBasic(int mapgenid, MapgenParams *params, EmergeParams *emerg this->heightmap = new s16[csize.X * csize.Z]; //// Initialize biome generator - biomegen = m_bmgr->createBiomeGen(BIOMEGEN_ORIGINAL, params->bparams, csize); + biomegen = emerge->biomegen; + biomegen->assertChunkSize(csize); biomemap = biomegen->biomemap; //// Look up some commonly used content @@ -621,7 +622,6 @@ MapgenBasic::MapgenBasic(int mapgenid, MapgenParams *params, EmergeParams *emerg MapgenBasic::~MapgenBasic() { - delete biomegen; delete []heightmap; delete m_emerge; // destroying EmergeParams is our responsibility diff --git a/src/mapgen/mapgen_valleys.cpp b/src/mapgen/mapgen_valleys.cpp index c4234857e..80a99b1f0 100644 --- a/src/mapgen/mapgen_valleys.cpp +++ b/src/mapgen/mapgen_valleys.cpp @@ -57,7 +57,8 @@ FlagDesc flagdesc_mapgen_valleys[] = { MapgenValleys::MapgenValleys(MapgenValleysParams *params, EmergeParams *emerge) : MapgenBasic(MAPGEN_VALLEYS, params, emerge) { - // NOTE: MapgenValleys has a hard dependency on BiomeGenOriginal + FATAL_ERROR_IF(biomegen->getType() != BIOMEGEN_ORIGINAL, + "MapgenValleys has a hard dependency on BiomeGenOriginal"); m_bgen = (BiomeGenOriginal *)biomegen; spflags = params->spflags; diff --git a/src/mapgen/mg_biome.cpp b/src/mapgen/mg_biome.cpp index 610c38594..f08cc190f 100644 --- a/src/mapgen/mg_biome.cpp +++ b/src/mapgen/mg_biome.cpp @@ -101,71 +101,6 @@ BiomeManager *BiomeManager::clone() const return mgr; } - -// For BiomeGen type 'BiomeGenOriginal' -float BiomeManager::getHeatAtPosOriginal(v3s16 pos, NoiseParams &np_heat, - NoiseParams &np_heat_blend, u64 seed) const -{ - return - NoisePerlin2D(&np_heat, pos.X, pos.Z, seed) + - NoisePerlin2D(&np_heat_blend, pos.X, pos.Z, seed); -} - - -// For BiomeGen type 'BiomeGenOriginal' -float BiomeManager::getHumidityAtPosOriginal(v3s16 pos, NoiseParams &np_humidity, - NoiseParams &np_humidity_blend, u64 seed) const -{ - return - NoisePerlin2D(&np_humidity, pos.X, pos.Z, seed) + - NoisePerlin2D(&np_humidity_blend, pos.X, pos.Z, seed); -} - - -// For BiomeGen type 'BiomeGenOriginal' -const Biome *BiomeManager::getBiomeFromNoiseOriginal(float heat, - float humidity, v3s16 pos) const -{ - Biome *biome_closest = nullptr; - Biome *biome_closest_blend = nullptr; - float dist_min = FLT_MAX; - float dist_min_blend = FLT_MAX; - - for (size_t i = 1; i < getNumObjects(); i++) { - Biome *b = (Biome *)getRaw(i); - if (!b || - pos.Y < b->min_pos.Y || pos.Y > b->max_pos.Y + b->vertical_blend || - pos.X < b->min_pos.X || pos.X > b->max_pos.X || - pos.Z < b->min_pos.Z || pos.Z > b->max_pos.Z) - continue; - - float d_heat = heat - b->heat_point; - float d_humidity = humidity - b->humidity_point; - float dist = (d_heat * d_heat) + (d_humidity * d_humidity); - - if (pos.Y <= b->max_pos.Y) { // Within y limits of biome b - if (dist < dist_min) { - dist_min = dist; - biome_closest = b; - } - } else if (dist < dist_min_blend) { // Blend area above biome b - dist_min_blend = dist; - biome_closest_blend = b; - } - } - - const u64 seed = pos.Y + (heat + humidity) * 0.9f; - PcgRandom rng(seed); - - if (biome_closest_blend && dist_min_blend <= dist_min && - rng.range(0, biome_closest_blend->vertical_blend) >= - pos.Y - biome_closest_blend->max_pos.Y) - return biome_closest_blend; - - return (biome_closest) ? biome_closest : (Biome *)getRaw(BIOME_NONE); -} - - //////////////////////////////////////////////////////////////////////////////// void BiomeParamsOriginal::readParams(const Settings *settings) @@ -189,7 +124,7 @@ void BiomeParamsOriginal::writeParams(Settings *settings) const //////////////////////////////////////////////////////////////////////////////// BiomeGenOriginal::BiomeGenOriginal(BiomeManager *biomemgr, - BiomeParamsOriginal *params, v3s16 chunksize) + const BiomeParamsOriginal *params, v3s16 chunksize) { m_bmgr = biomemgr; m_params = params; @@ -224,17 +159,26 @@ BiomeGenOriginal::~BiomeGenOriginal() delete noise_humidity_blend; } -// Only usable in a mapgen thread -Biome *BiomeGenOriginal::calcBiomeAtPoint(v3s16 pos) const +BiomeGen *BiomeGenOriginal::clone(BiomeManager *biomemgr) const +{ + return new BiomeGenOriginal(biomemgr, m_params, m_csize); +} + +float BiomeGenOriginal::calcHeatAtPoint(v3s16 pos) const { - float heat = - NoisePerlin2D(&m_params->np_heat, pos.X, pos.Z, m_params->seed) + + return NoisePerlin2D(&m_params->np_heat, pos.X, pos.Z, m_params->seed) + NoisePerlin2D(&m_params->np_heat_blend, pos.X, pos.Z, m_params->seed); - float humidity = - NoisePerlin2D(&m_params->np_humidity, pos.X, pos.Z, m_params->seed) + +} + +float BiomeGenOriginal::calcHumidityAtPoint(v3s16 pos) const +{ + return NoisePerlin2D(&m_params->np_humidity, pos.X, pos.Z, m_params->seed) + NoisePerlin2D(&m_params->np_humidity_blend, pos.X, pos.Z, m_params->seed); +} - return calcBiomeFromNoise(heat, humidity, pos); +Biome *BiomeGenOriginal::calcBiomeAtPoint(v3s16 pos) const +{ + return calcBiomeFromNoise(calcHeatAtPoint(pos), calcHumidityAtPoint(pos), pos); } diff --git a/src/mapgen/mg_biome.h b/src/mapgen/mg_biome.h index be4cfea4d..c85afc3a0 100644 --- a/src/mapgen/mg_biome.h +++ b/src/mapgen/mg_biome.h @@ -97,6 +97,15 @@ public: virtual BiomeGenType getType() const = 0; + // Clone this BiomeGen and set a the new BiomeManager to be used by the copy + virtual BiomeGen *clone(BiomeManager *biomemgr) const = 0; + + // Check that the internal chunk size is what the mapgen expects, just to be sure. + inline void assertChunkSize(v3s16 expect) const + { + FATAL_ERROR_IF(m_csize != expect, "Chunk size mismatches"); + } + // Calculates the biome at the exact position provided. This function can // be called at any time, but may be less efficient than the latter methods, // depending on implementation. @@ -158,12 +167,18 @@ struct BiomeParamsOriginal : public BiomeParams { class BiomeGenOriginal : public BiomeGen { public: BiomeGenOriginal(BiomeManager *biomemgr, - BiomeParamsOriginal *params, v3s16 chunksize); + const BiomeParamsOriginal *params, v3s16 chunksize); virtual ~BiomeGenOriginal(); BiomeGenType getType() const { return BIOMEGEN_ORIGINAL; } + BiomeGen *clone(BiomeManager *biomemgr) const; + + // Slower, meant for Script API use + float calcHeatAtPoint(v3s16 pos) const; + float calcHumidityAtPoint(v3s16 pos) const; Biome *calcBiomeAtPoint(v3s16 pos) const; + void calcBiomeNoise(v3s16 pmin); biome_t *getBiomes(s16 *heightmap, v3s16 pmin); @@ -176,7 +191,7 @@ public: float *humidmap; private: - BiomeParamsOriginal *m_params; + const BiomeParamsOriginal *m_params; Noise *noise_heat; Noise *noise_humidity; @@ -229,14 +244,6 @@ public: virtual void clear(); - // For BiomeGen type 'BiomeGenOriginal' - float getHeatAtPosOriginal(v3s16 pos, NoiseParams &np_heat, - NoiseParams &np_heat_blend, u64 seed) const; - float getHumidityAtPosOriginal(v3s16 pos, NoiseParams &np_humidity, - NoiseParams &np_humidity_blend, u64 seed) const; - const Biome *getBiomeFromNoiseOriginal(float heat, float humidity, - v3s16 pos) const; - private: BiomeManager() {}; diff --git a/src/mapgen/mg_ore.h b/src/mapgen/mg_ore.h index a58fa9bfe..a757fa6d0 100644 --- a/src/mapgen/mg_ore.h +++ b/src/mapgen/mg_ore.h @@ -85,7 +85,7 @@ class OreScatter : public Ore { public: OreScatter() : Ore(false) {} - ObjDef *clone() const; + ObjDef *clone() const override; void generate(MMVManip *vm, int mapseed, u32 blockseed, v3s16 nmin, v3s16 nmax, biome_t *biomemap) override; @@ -95,7 +95,7 @@ class OreSheet : public Ore { public: OreSheet() : Ore(true) {} - ObjDef *clone() const; + ObjDef *clone() const override; u16 column_height_min; u16 column_height_max; @@ -107,7 +107,7 @@ public: class OrePuff : public Ore { public: - ObjDef *clone() const; + ObjDef *clone() const override; NoiseParams np_puff_top; NoiseParams np_puff_bottom; @@ -123,7 +123,7 @@ public: class OreBlob : public Ore { public: - ObjDef *clone() const; + ObjDef *clone() const override; OreBlob() : Ore(true) {} void generate(MMVManip *vm, int mapseed, u32 blockseed, @@ -132,7 +132,7 @@ public: class OreVein : public Ore { public: - ObjDef *clone() const; + ObjDef *clone() const override; float random_factor; Noise *noise2 = nullptr; @@ -147,7 +147,7 @@ public: class OreStratum : public Ore { public: - ObjDef *clone() const; + ObjDef *clone() const override; NoiseParams np_stratum_thickness; Noise *noise_stratum_thickness = nullptr; diff --git a/src/mapgen/mg_schematic.cpp b/src/mapgen/mg_schematic.cpp index e70e97e48..653bad4fe 100644 --- a/src/mapgen/mg_schematic.cpp +++ b/src/mapgen/mg_schematic.cpp @@ -76,10 +76,6 @@ void SchematicManager::clear() /////////////////////////////////////////////////////////////////////////////// -Schematic::Schematic() -= default; - - Schematic::~Schematic() { delete []schemdata; @@ -108,13 +104,19 @@ ObjDef *Schematic::clone() const void Schematic::resolveNodeNames() { + c_nodes.clear(); getIdsFromNrBacklog(&c_nodes, true, CONTENT_AIR); size_t bufsize = size.X * size.Y * size.Z; for (size_t i = 0; i != bufsize; i++) { content_t c_original = schemdata[i].getContent(); - content_t c_new = c_nodes[c_original]; - schemdata[i].setContent(c_new); + if (c_original >= c_nodes.size()) { + errorstream << "Corrupt schematic. name=\"" << name + << "\" at index " << i << std::endl; + c_original = 0; + } + // Unfold condensed ID layout to content_t + schemdata[i].setContent(c_nodes[c_original]); } } @@ -279,8 +281,7 @@ void Schematic::placeOnMap(ServerMap *map, v3s16 p, u32 flags, } -bool Schematic::deserializeFromMts(std::istream *is, - std::vector<std::string> *names) +bool Schematic::deserializeFromMts(std::istream *is) { std::istream &ss = *is; content_t cignore = CONTENT_IGNORE; @@ -312,6 +313,8 @@ bool Schematic::deserializeFromMts(std::istream *is, slice_probs[y] = (version >= 3) ? readU8(ss) : MTSCHEM_PROB_ALWAYS_OLD; //// Read node names + NodeResolver::reset(); + u16 nidmapcount = readU16(ss); for (int i = 0; i != nidmapcount; i++) { std::string name = deSerializeString16(ss); @@ -324,9 +327,12 @@ bool Schematic::deserializeFromMts(std::istream *is, have_cignore = true; } - names->push_back(name); + m_nodenames.push_back(name); } + // Prepare for node resolver + m_nnlistsizes.push_back(m_nodenames.size()); + //// Read node data size_t nodecount = size.X * size.Y * size.Z; @@ -358,9 +364,11 @@ bool Schematic::deserializeFromMts(std::istream *is, } -bool Schematic::serializeToMts(std::ostream *os, - const std::vector<std::string> &names) const +bool Schematic::serializeToMts(std::ostream *os) const { + // Nodes must not be resolved (-> condensed) + // checking here is not possible because "schemdata" might be temporary. + std::ostream &ss = *os; writeU32(ss, MTSCHEM_FILE_SIGNATURE); // signature @@ -370,9 +378,10 @@ bool Schematic::serializeToMts(std::ostream *os, for (int y = 0; y != size.Y; y++) // Y slice probabilities writeU8(ss, slice_probs[y]); - writeU16(ss, names.size()); // name count - for (size_t i = 0; i != names.size(); i++) - ss << serializeString16(names[i]); // node names + writeU16(ss, m_nodenames.size()); // name count + for (size_t i = 0; i != m_nodenames.size(); i++) { + ss << serializeString16(m_nodenames[i]); // node names + } // compressed bulk node data MapNode::serializeBulk(ss, SER_FMT_VER_HIGHEST_WRITE, @@ -382,8 +391,7 @@ bool Schematic::serializeToMts(std::ostream *os, } -bool Schematic::serializeToLua(std::ostream *os, - const std::vector<std::string> &names, bool use_comments, +bool Schematic::serializeToLua(std::ostream *os, bool use_comments, u32 indent_spaces) const { std::ostream &ss = *os; @@ -392,6 +400,9 @@ bool Schematic::serializeToLua(std::ostream *os, if (indent_spaces > 0) indent.assign(indent_spaces, ' '); + bool resolve_done = isResolveDone(); + FATAL_ERROR_IF(resolve_done && !m_ndef, "serializeToLua: NodeDefManager is required"); + //// Write header { ss << "schematic = {" << std::endl; @@ -436,9 +447,22 @@ bool Schematic::serializeToLua(std::ostream *os, u8 probability = schemdata[i].param1 & MTSCHEM_PROB_MASK; bool force_place = schemdata[i].param1 & MTSCHEM_FORCE_PLACE; - ss << indent << indent << "{" - << "name=\"" << names[schemdata[i].getContent()] - << "\", prob=" << (u16)probability * 2 + // After node resolving: real content_t, lookup using NodeDefManager + // Prior node resolving: condensed ID, lookup using m_nodenames + content_t c = schemdata[i].getContent(); + + ss << indent << indent << "{" << "name=\""; + + if (!resolve_done) { + // Prior node resolving (eg. direct schematic load) + FATAL_ERROR_IF(c >= m_nodenames.size(), "Invalid node list"); + ss << m_nodenames[c]; + } else { + // After node resolving (eg. biome decoration) + ss << m_ndef->get(c).name; + } + + ss << "\", prob=" << (u16)probability * 2 << ", param2=" << (u16)schemdata[i].param2; if (force_place) @@ -467,25 +491,24 @@ bool Schematic::loadSchematicFromFile(const std::string &filename, return false; } - size_t origsize = m_nodenames.size(); - if (!deserializeFromMts(&is, &m_nodenames)) - return false; + if (!m_ndef) + m_ndef = ndef; - m_nnlistsizes.push_back(m_nodenames.size() - origsize); + if (!deserializeFromMts(&is)) + return false; name = filename; if (replace_names) { - for (size_t i = origsize; i < m_nodenames.size(); i++) { - std::string &node_name = m_nodenames[i]; + for (std::string &node_name : m_nodenames) { StringMap::iterator it = replace_names->find(node_name); if (it != replace_names->end()) node_name = it->second; } } - if (ndef) - ndef->pendNodeResolve(this); + if (m_ndef) + m_ndef->pendNodeResolve(this); return true; } @@ -494,33 +517,26 @@ bool Schematic::loadSchematicFromFile(const std::string &filename, bool Schematic::saveSchematicToFile(const std::string &filename, const NodeDefManager *ndef) { - MapNode *orig_schemdata = schemdata; - std::vector<std::string> ndef_nodenames; - std::vector<std::string> *names; + Schematic *schem = this; - if (m_resolve_done && ndef == NULL) - ndef = m_ndef; + bool needs_condense = isResolveDone(); - if (ndef) { - names = &ndef_nodenames; + if (!m_ndef) + m_ndef = ndef; - u32 volume = size.X * size.Y * size.Z; - schemdata = new MapNode[volume]; - for (u32 i = 0; i != volume; i++) - schemdata[i] = orig_schemdata[i]; + if (needs_condense) { + if (!m_ndef) + return false; - generate_nodelist_and_update_ids(schemdata, volume, names, ndef); - } else { // otherwise, use the names we have on hand in the list - names = &m_nodenames; + schem = (Schematic *)this->clone(); + schem->condenseContentIds(); } std::ostringstream os(std::ios_base::binary); - bool status = serializeToMts(&os, *names); + bool status = schem->serializeToMts(&os); - if (ndef) { - delete []schemdata; - schemdata = orig_schemdata; - } + if (needs_condense) + delete schem; if (!status) return false; @@ -556,6 +572,10 @@ bool Schematic::getSchematicFromMap(Map *map, v3s16 p1, v3s16 p2) } delete vm; + + // Reset and mark as complete + NodeResolver::reset(true); + return true; } @@ -584,26 +604,29 @@ void Schematic::applyProbabilities(v3s16 p0, } -void generate_nodelist_and_update_ids(MapNode *nodes, size_t nodecount, - std::vector<std::string> *usednodes, const NodeDefManager *ndef) +void Schematic::condenseContentIds() { std::unordered_map<content_t, content_t> nodeidmap; content_t numids = 0; + // Reset node resolve fields + NodeResolver::reset(); + + size_t nodecount = size.X * size.Y * size.Z; for (size_t i = 0; i != nodecount; i++) { content_t id; - content_t c = nodes[i].getContent(); + content_t c = schemdata[i].getContent(); - std::unordered_map<content_t, content_t>::const_iterator it = nodeidmap.find(c); + auto it = nodeidmap.find(c); if (it == nodeidmap.end()) { id = numids; numids++; - usednodes->push_back(ndef->get(c).name); - nodeidmap.insert(std::make_pair(c, id)); + m_nodenames.push_back(m_ndef->get(c).name); + nodeidmap.emplace(std::make_pair(c, id)); } else { id = it->second; } - nodes[i].setContent(id); + schemdata[i].setContent(id); } } diff --git a/src/mapgen/mg_schematic.h b/src/mapgen/mg_schematic.h index 6b31251b6..5f64ea280 100644 --- a/src/mapgen/mg_schematic.h +++ b/src/mapgen/mg_schematic.h @@ -92,7 +92,7 @@ enum SchematicFormatType { class Schematic : public ObjDef, public NodeResolver { public: - Schematic(); + Schematic() = default; virtual ~Schematic(); ObjDef *clone() const; @@ -105,11 +105,9 @@ public: const NodeDefManager *ndef); bool getSchematicFromMap(Map *map, v3s16 p1, v3s16 p2); - bool deserializeFromMts(std::istream *is, std::vector<std::string> *names); - bool serializeToMts(std::ostream *os, - const std::vector<std::string> &names) const; - bool serializeToLua(std::ostream *os, const std::vector<std::string> &names, - bool use_comments, u32 indent_spaces) const; + bool deserializeFromMts(std::istream *is); + bool serializeToMts(std::ostream *os) const; + bool serializeToLua(std::ostream *os, bool use_comments, u32 indent_spaces) const; void blitToVManip(MMVManip *vm, v3s16 p, Rotation rot, bool force_place); bool placeOnVManip(MMVManip *vm, v3s16 p, u32 flags, Rotation rot, bool force_place); @@ -124,6 +122,10 @@ public: v3s16 size; MapNode *schemdata = nullptr; u8 *slice_probs = nullptr; + +private: + // Counterpart to the node resolver: Condense content_t to a sequential "m_nodenames" list + void condenseContentIds(); }; class SchematicManager : public ObjDefManager { @@ -151,5 +153,3 @@ private: Server *m_server; }; -void generate_nodelist_and_update_ids(MapNode *nodes, size_t nodecount, - std::vector<std::string> *usednodes, const NodeDefManager *ndef); diff --git a/src/network/clientpackethandler.cpp b/src/network/clientpackethandler.cpp index 65db02300..c8a160732 100644 --- a/src/network/clientpackethandler.cpp +++ b/src/network/clientpackethandler.cpp @@ -1041,9 +1041,6 @@ void Client::handleCommand_DeleteParticleSpawner(NetworkPacket* pkt) void Client::handleCommand_HudAdd(NetworkPacket* pkt) { - std::string datastring(pkt->getString(0), pkt->getSize()); - std::istringstream is(datastring, std::ios_base::binary); - u32 server_id; u8 type; v2f pos; @@ -1070,22 +1067,23 @@ void Client::handleCommand_HudAdd(NetworkPacket* pkt) } catch(PacketError &e) {}; ClientEvent *event = new ClientEvent(); - event->type = CE_HUDADD; - event->hudadd.server_id = server_id; - event->hudadd.type = type; - event->hudadd.pos = new v2f(pos); - event->hudadd.name = new std::string(name); - event->hudadd.scale = new v2f(scale); - event->hudadd.text = new std::string(text); - event->hudadd.number = number; - event->hudadd.item = item; - event->hudadd.dir = dir; - event->hudadd.align = new v2f(align); - event->hudadd.offset = new v2f(offset); - event->hudadd.world_pos = new v3f(world_pos); - event->hudadd.size = new v2s32(size); - event->hudadd.z_index = z_index; - event->hudadd.text2 = new std::string(text2); + event->type = CE_HUDADD; + event->hudadd = new ClientEventHudAdd(); + event->hudadd->server_id = server_id; + event->hudadd->type = type; + event->hudadd->pos = pos; + event->hudadd->name = name; + event->hudadd->scale = scale; + event->hudadd->text = text; + event->hudadd->number = number; + event->hudadd->item = item; + event->hudadd->dir = dir; + event->hudadd->align = align; + event->hudadd->offset = offset; + event->hudadd->world_pos = world_pos; + event->hudadd->size = size; + event->hudadd->z_index = z_index; + event->hudadd->text2 = text2; m_client_event_queue.push(event); } @@ -1095,16 +1093,10 @@ void Client::handleCommand_HudRemove(NetworkPacket* pkt) *pkt >> server_id; - auto i = m_hud_server_to_client.find(server_id); - if (i != m_hud_server_to_client.end()) { - int client_id = i->second; - m_hud_server_to_client.erase(i); - - ClientEvent *event = new ClientEvent(); - event->type = CE_HUDRM; - event->hudrm.id = client_id; - m_client_event_queue.push(event); - } + ClientEvent *event = new ClientEvent(); + event->type = CE_HUDRM; + event->hudrm.id = server_id; + m_client_event_queue.push(event); } void Client::handleCommand_HudChange(NetworkPacket* pkt) @@ -1131,19 +1123,17 @@ void Client::handleCommand_HudChange(NetworkPacket* pkt) else *pkt >> intdata; - std::unordered_map<u32, u32>::const_iterator i = m_hud_server_to_client.find(server_id); - if (i != m_hud_server_to_client.end()) { - ClientEvent *event = new ClientEvent(); - event->type = CE_HUDCHANGE; - event->hudchange.id = i->second; - event->hudchange.stat = (HudElementStat)stat; - event->hudchange.v2fdata = new v2f(v2fdata); - event->hudchange.v3fdata = new v3f(v3fdata); - event->hudchange.sdata = new std::string(sdata); - event->hudchange.data = intdata; - event->hudchange.v2s32data = new v2s32(v2s32data); - m_client_event_queue.push(event); - } + ClientEvent *event = new ClientEvent(); + event->type = CE_HUDCHANGE; + event->hudchange = new ClientEventHudChange(); + event->hudchange->id = server_id; + event->hudchange->stat = static_cast<HudElementStat>(stat); + event->hudchange->v2fdata = v2fdata; + event->hudchange->v3fdata = v3fdata; + event->hudchange->sdata = sdata; + event->hudchange->data = intdata; + event->hudchange->v2s32data = v2s32data; + m_client_event_queue.push(event); } void Client::handleCommand_HudSetFlags(NetworkPacket* pkt) diff --git a/src/network/serverpackethandler.cpp b/src/network/serverpackethandler.cpp index 270b8e01f..5b378a083 100644 --- a/src/network/serverpackethandler.cpp +++ b/src/network/serverpackethandler.cpp @@ -174,6 +174,16 @@ void Server::handleCommand_Init(NetworkPacket* pkt) return; } + RemotePlayer *player = m_env->getPlayer(playername); + + // If player is already connected, cancel + if (player && player->getPeerId() != PEER_ID_INEXISTENT) { + actionstream << "Server: Player with name \"" << playername << + "\" tried to connect, but player with same name is already connected" << std::endl; + DenyAccess(peer_id, SERVER_ACCESSDENIED_ALREADY_CONNECTED); + return; + } + m_clients.setPlayerName(peer_id, playername); //TODO (later) case insensitivity @@ -488,8 +498,12 @@ void Server::process_PlayerPos(RemotePlayer *player, PlayerSAO *playersao, pitch = modulo360f(pitch); yaw = wrapDegrees_0_360(yaw); - playersao->setBasePosition(position); - player->setSpeed(speed); + if (!playersao->isAttached()) { + // Only update player positions when moving freely + // to not interfere with attachment handling + playersao->setBasePosition(position); + player->setSpeed(speed); + } playersao->setLookPitch(pitch); playersao->setPlayerYaw(yaw); playersao->setFov(fov); @@ -622,21 +636,36 @@ void Server::handleCommand_InventoryAction(NetworkPacket* pkt) const bool player_has_interact = checkPriv(player->getName(), "interact"); - auto check_inv_access = [player, player_has_interact] ( + auto check_inv_access = [player, player_has_interact, this] ( const InventoryLocation &loc) -> bool { - if (loc.type == InventoryLocation::CURRENT_PLAYER) - return false; // Only used internally on the client, never sent - if (loc.type == InventoryLocation::PLAYER) { - // Allow access to own inventory in all cases - return loc.name == player->getName(); - } - if (!player_has_interact) { + // Players without interact may modify their own inventory + if (!player_has_interact && loc.type != InventoryLocation::PLAYER) { infostream << "Cannot modify foreign inventory: " << "No interact privilege" << std::endl; return false; } - return true; + + switch (loc.type) { + case InventoryLocation::CURRENT_PLAYER: + // Only used internally on the client, never sent + return false; + case InventoryLocation::PLAYER: + // Allow access to own inventory in all cases + return loc.name == player->getName(); + case InventoryLocation::NODEMETA: + { + // Check for out-of-range interaction + v3f node_pos = intToFloat(loc.p, BS); + v3f player_pos = player->getPlayerSAO()->getEyePosition(); + f32 d = player_pos.getDistanceFrom(node_pos); + return checkInteractDistance(player, d, "inventory"); + } + case InventoryLocation::DETACHED: + return getInventoryMgr()->checkDetachedInventoryAccess(loc, player->getName()); + default: + return false; + } }; /* @@ -656,18 +685,6 @@ void Server::handleCommand_InventoryAction(NetworkPacket* pkt) !check_inv_access(ma->to_inv)) return; - InventoryLocation *remote = ma->from_inv.type == InventoryLocation::PLAYER ? - &ma->to_inv : &ma->from_inv; - - // Check for out-of-range interaction - if (remote->type == InventoryLocation::NODEMETA) { - v3f node_pos = intToFloat(remote->p, BS); - v3f player_pos = player->getPlayerSAO()->getEyePosition(); - f32 d = player_pos.getDistanceFrom(node_pos); - if (!checkInteractDistance(player, d, "inventory")) - return; - } - /* Disable moving items out of craftpreview */ diff --git a/src/nodedef.cpp b/src/nodedef.cpp index 57d4c008f..8a1f6203b 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -1675,8 +1675,7 @@ bool NodeDefManager::nodeboxConnects(MapNode from, MapNode to, NodeResolver::NodeResolver() { - m_nodenames.reserve(16); - m_nnlistsizes.reserve(4); + reset(); } @@ -1779,3 +1778,16 @@ bool NodeResolver::getIdsFromNrBacklog(std::vector<content_t> *result_out, return success; } + +void NodeResolver::reset(bool resolve_done) +{ + m_nodenames.clear(); + m_nodenames_idx = 0; + m_nnlistsizes.clear(); + m_nnlistsizes_idx = 0; + + m_resolve_done = resolve_done; + + m_nodenames.reserve(16); + m_nnlistsizes.reserve(4); +} diff --git a/src/nodedef.h b/src/nodedef.h index 6fc20518d..3e77624eb 100644 --- a/src/nodedef.h +++ b/src/nodedef.h @@ -44,6 +44,9 @@ class ITextureSource; class IShaderSource; class IGameDef; class NodeResolver; +#if BUILD_UNITTESTS +class TestSchematic; +#endif enum ContentParamType { @@ -789,10 +792,13 @@ private: NodeDefManager *createNodeDefManager(); +// NodeResolver: Queue for node names which are then translated +// to content_t after the NodeDefManager was initialized class NodeResolver { public: NodeResolver(); virtual ~NodeResolver(); + // Callback which is run as soon NodeDefManager is ready virtual void resolveNodeNames() = 0; // required because this class is used as mixin for ObjDef @@ -804,12 +810,31 @@ public: bool getIdsFromNrBacklog(std::vector<content_t> *result_out, bool all_required = false, content_t c_fallback = CONTENT_IGNORE); - void nodeResolveInternal(); + inline bool isResolveDone() const { return m_resolve_done; } + void reset(bool resolve_done = false); - u32 m_nodenames_idx = 0; - u32 m_nnlistsizes_idx = 0; + // Vector containing all node names in the resolve "queue" std::vector<std::string> m_nodenames; + // Specifies the "set size" of node names which are to be processed + // this is used for getIdsFromNrBacklog + // TODO: replace or remove std::vector<size_t> m_nnlistsizes; + +protected: + friend class NodeDefManager; // m_ndef + const NodeDefManager *m_ndef = nullptr; + // Index of the next "m_nodenames" entry to resolve + u32 m_nodenames_idx = 0; + +private: +#if BUILD_UNITTESTS + // Unittest requires access to m_resolve_done + friend class TestSchematic; +#endif + void nodeResolveInternal(); + + // Index of the next "m_nnlistsizes" entry to process + u32 m_nnlistsizes_idx = 0; bool m_resolve_done = false; }; diff --git a/src/noise.cpp b/src/noise.cpp index e16564b05..a10efa3c4 100644 --- a/src/noise.cpp +++ b/src/noise.cpp @@ -369,7 +369,7 @@ float contour(float v) ///////////////////////// [ New noise ] //////////////////////////// -float NoisePerlin2D(NoiseParams *np, float x, float y, s32 seed) +float NoisePerlin2D(const NoiseParams *np, float x, float y, s32 seed) { float a = 0; float f = 1.0; @@ -395,7 +395,7 @@ float NoisePerlin2D(NoiseParams *np, float x, float y, s32 seed) } -float NoisePerlin3D(NoiseParams *np, float x, float y, float z, s32 seed) +float NoisePerlin3D(const NoiseParams *np, float x, float y, float z, s32 seed) { float a = 0; float f = 1.0; @@ -422,7 +422,7 @@ float NoisePerlin3D(NoiseParams *np, float x, float y, float z, s32 seed) } -Noise::Noise(NoiseParams *np_, s32 seed, u32 sx, u32 sy, u32 sz) +Noise::Noise(const NoiseParams *np_, s32 seed, u32 sx, u32 sy, u32 sz) { np = *np_; this->seed = seed; diff --git a/src/noise.h b/src/noise.h index 613879890..854781731 100644 --- a/src/noise.h +++ b/src/noise.h @@ -146,7 +146,7 @@ public: float *persist_buf = nullptr; float *result = nullptr; - Noise(NoiseParams *np, s32 seed, u32 sx, u32 sy, u32 sz=1); + Noise(const NoiseParams *np, s32 seed, u32 sx, u32 sy, u32 sz=1); ~Noise(); void setSize(u32 sx, u32 sy, u32 sz=1); @@ -192,8 +192,8 @@ private: }; -float NoisePerlin2D(NoiseParams *np, float x, float y, s32 seed); -float NoisePerlin3D(NoiseParams *np, float x, float y, float z, s32 seed); +float NoisePerlin2D(const NoiseParams *np, float x, float y, s32 seed); +float NoisePerlin3D(const NoiseParams *np, float x, float y, float z, s32 seed); inline float NoisePerlin2D_PO(NoiseParams *np, float x, float xoff, float y, float yoff, s32 seed) diff --git a/src/object_properties.cpp b/src/object_properties.cpp index f31773060..2eebc27d6 100644 --- a/src/object_properties.cpp +++ b/src/object_properties.cpp @@ -24,6 +24,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/basic_macros.h" #include <sstream> +static const video::SColor NULL_BGCOLOR{0, 1, 1, 1}; + ObjectProperties::ObjectProperties() { textures.emplace_back("unknown_object.png"); @@ -62,6 +64,13 @@ std::string ObjectProperties::dump() os << ", nametag=" << nametag; os << ", nametag_color=" << "\"" << nametag_color.getAlpha() << "," << nametag_color.getRed() << "," << nametag_color.getGreen() << "," << nametag_color.getBlue() << "\" "; + + if (nametag_bgcolor) + os << ", nametag_bgcolor=" << "\"" << nametag_color.getAlpha() << "," << nametag_color.getRed() + << "," << nametag_color.getGreen() << "," << nametag_color.getBlue() << "\" "; + else + os << ", nametag_bgcolor=null "; + os << ", selectionbox=" << PP(selectionbox.MinEdge) << "," << PP(selectionbox.MaxEdge); os << ", pointable=" << pointable; os << ", static_save=" << static_save; @@ -121,6 +130,13 @@ void ObjectProperties::serialize(std::ostream &os) const writeU8(os, shaded); writeU8(os, show_on_minimap); + if (!nametag_bgcolor) + writeARGB8(os, NULL_BGCOLOR); + else if (nametag_bgcolor.value().getAlpha() == 0) + writeARGB8(os, video::SColor(0, 0, 0, 0)); + else + writeARGB8(os, nametag_bgcolor.value()); + // Add stuff only at the bottom. // Never remove anything, because we don't want new versions of this } @@ -182,5 +198,11 @@ void ObjectProperties::deSerialize(std::istream &is) if (is.eof()) return; show_on_minimap = tmp; + + auto bgcolor = readARGB8(is); + if (bgcolor != NULL_BGCOLOR) + nametag_bgcolor = bgcolor; + else + nametag_bgcolor = nullopt; } catch (SerializationError &e) {} } diff --git a/src/object_properties.h b/src/object_properties.h index adb483527..db28eebfd 100644 --- a/src/object_properties.h +++ b/src/object_properties.h @@ -24,6 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include <iostream> #include <map> #include <vector> +#include "util/Optional.h" struct ObjectProperties { @@ -53,6 +54,7 @@ struct ObjectProperties s8 glow = 0; std::string nametag = ""; video::SColor nametag_color = video::SColor(255, 255, 255, 255); + Optional<video::SColor> nametag_bgcolor = nullopt; f32 automatic_face_movement_max_rotation_per_sec = -1.0f; std::string infotext; //! For dropped items, this contains item information. diff --git a/src/porting.h b/src/porting.h index e4ebe36fd..93932e1d9 100644 --- a/src/porting.h +++ b/src/porting.h @@ -234,21 +234,21 @@ inline u64 getTimeMs() { struct timespec ts; os_get_clock(&ts); - return ts.tv_sec * 1000 + ts.tv_nsec / 1000000; + return ((u64) ts.tv_sec) * 1000LL + ((u64) ts.tv_nsec) / 1000000LL; } inline u64 getTimeUs() { struct timespec ts; os_get_clock(&ts); - return ts.tv_sec * 1000000 + ts.tv_nsec / 1000; + return ((u64) ts.tv_sec) * 1000000LL + ((u64) ts.tv_nsec) / 1000LL; } inline u64 getTimeNs() { struct timespec ts; os_get_clock(&ts); - return ts.tv_sec * 1000000000 + ts.tv_nsec; + return ((u64) ts.tv_sec) * 1000000000LL + ((u64) ts.tv_nsec); } #endif diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index ecab7baa1..eca0c89d1 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -119,6 +119,8 @@ void read_item_definition(lua_State* L, int index, // "" = no prediction getstringfield(L, index, "node_placement_prediction", def.node_placement_prediction); + + getintfield(L, index, "place_param2", def.place_param2); } /******************************************************************************/ @@ -140,8 +142,10 @@ void push_item_definition_full(lua_State *L, const ItemDefinition &i) lua_setfield(L, -2, "name"); lua_pushstring(L, i.description.c_str()); lua_setfield(L, -2, "description"); - lua_pushstring(L, i.short_description.c_str()); - lua_setfield(L, -2, "short_description"); + if (!i.short_description.empty()) { + lua_pushstring(L, i.short_description.c_str()); + lua_setfield(L, -2, "short_description"); + } lua_pushstring(L, type.c_str()); lua_setfield(L, -2, "type"); lua_pushstring(L, i.inventory_image.c_str()); @@ -310,6 +314,17 @@ void read_object_properties(lua_State *L, int index, prop->nametag_color = color; } lua_pop(L, 1); + lua_getfield(L, -1, "nametag_bgcolor"); + if (!lua_isnil(L, -1)) { + if (lua_toboolean(L, -1)) { + video::SColor color; + if (read_color(L, -1, &color)) + prop->nametag_bgcolor = color; + } else { + prop->nametag_bgcolor = nullopt; + } + } + lua_pop(L, 1); lua_getfield(L, -1, "automatic_face_movement_max_rotation_per_sec"); if (lua_isnumber(L, -1)) { @@ -401,6 +416,13 @@ void push_object_properties(lua_State *L, ObjectProperties *prop) lua_setfield(L, -2, "nametag"); push_ARGB8(L, prop->nametag_color); lua_setfield(L, -2, "nametag_color"); + if (prop->nametag_bgcolor) { + push_ARGB8(L, prop->nametag_bgcolor.value()); + lua_setfield(L, -2, "nametag_bgcolor"); + } else { + lua_pushboolean(L, false); + lua_setfield(L, -2, "nametag_bgcolor"); + } lua_pushnumber(L, prop->automatic_face_movement_max_rotation_per_sec); lua_setfield(L, -2, "automatic_face_movement_max_rotation_per_sec"); lua_pushlstring(L, prop->infotext.c_str(), prop->infotext.size()); diff --git a/src/script/common/helper.cpp b/src/script/common/helper.cpp index 488144790..fbf24e1b7 100644 --- a/src/script/common/helper.cpp +++ b/src/script/common/helper.cpp @@ -50,22 +50,26 @@ bool LuaHelper::isNaN(lua_State *L, int idx) /* * Read template functions */ -template <> bool LuaHelper::readParam(lua_State *L, int index) +template <> +bool LuaHelper::readParam(lua_State *L, int index) { return lua_toboolean(L, index) != 0; } -template <> s16 LuaHelper::readParam(lua_State *L, int index) +template <> +s16 LuaHelper::readParam(lua_State *L, int index) { return lua_tonumber(L, index); } -template <> int LuaHelper::readParam(lua_State *L, int index) +template <> +int LuaHelper::readParam(lua_State *L, int index) { return luaL_checkint(L, index); } -template <> float LuaHelper::readParam(lua_State *L, int index) +template <> +float LuaHelper::readParam(lua_State *L, int index) { if (isNaN(L, index)) throw LuaError("NaN value is not allowed."); @@ -73,7 +77,8 @@ template <> float LuaHelper::readParam(lua_State *L, int index) return (float)luaL_checknumber(L, index); } -template <> v2s16 LuaHelper::readParam(lua_State *L, int index) +template <> +v2s16 LuaHelper::readParam(lua_State *L, int index) { v2s16 p; CHECK_POS_TAB(index); @@ -88,7 +93,8 @@ template <> v2s16 LuaHelper::readParam(lua_State *L, int index) return p; } -template <> v2f LuaHelper::readParam(lua_State *L, int index) +template <> +v2f LuaHelper::readParam(lua_State *L, int index) { v2f p; CHECK_POS_TAB(index); @@ -103,7 +109,8 @@ template <> v2f LuaHelper::readParam(lua_State *L, int index) return p; } -template <> v3f LuaHelper::readParam(lua_State *L, int index) +template <> +v3f LuaHelper::readParam(lua_State *L, int index) { v3f p; CHECK_POS_TAB(index); @@ -122,7 +129,8 @@ template <> v3f LuaHelper::readParam(lua_State *L, int index) return p; } -template <> std::string LuaHelper::readParam(lua_State *L, int index) +template <> +std::string LuaHelper::readParam(lua_State *L, int index) { size_t length; std::string result; diff --git a/src/script/common/helper.h b/src/script/common/helper.h index 7a794dc9b..6491e73cf 100644 --- a/src/script/common/helper.h +++ b/src/script/common/helper.h @@ -38,7 +38,8 @@ protected: * @param index Lua Index to read * @return read value from Lua */ - template <typename T> static T readParam(lua_State *L, int index); + template <typename T> + static T readParam(lua_State *L, int index); /** * Read a value using a template type T from Lua State L and index diff --git a/src/script/lua_api/l_mapgen.cpp b/src/script/lua_api/l_mapgen.cpp index 12a497b1e..eb3d49a5e 100644 --- a/src/script/lua_api/l_mapgen.cpp +++ b/src/script/lua_api/l_mapgen.cpp @@ -482,9 +482,7 @@ int ModApiMapgen::l_get_biome_id(lua_State *L) { NO_MAP_LOCK_REQUIRED; - const char *biome_str = lua_tostring(L, 1); - if (!biome_str) - return 0; + const char *biome_str = luaL_checkstring(L, 1); const BiomeManager *bmgr = getServer(L)->getEmergeManager()->getBiomeManager(); if (!bmgr) @@ -527,30 +525,12 @@ int ModApiMapgen::l_get_heat(lua_State *L) v3s16 pos = read_v3s16(L, 1); - NoiseParams np_heat; - NoiseParams np_heat_blend; - - MapSettingsManager *settingsmgr = - getServer(L)->getEmergeManager()->map_settings_mgr; + const BiomeGen *biomegen = getServer(L)->getEmergeManager()->getBiomeGen(); - if (!settingsmgr->getMapSettingNoiseParams("mg_biome_np_heat", - &np_heat) || - !settingsmgr->getMapSettingNoiseParams("mg_biome_np_heat_blend", - &np_heat_blend)) - return 0; - - std::string value; - if (!settingsmgr->getMapSetting("seed", &value)) - return 0; - std::istringstream ss(value); - u64 seed; - ss >> seed; - - const BiomeManager *bmgr = getServer(L)->getEmergeManager()->getBiomeManager(); - if (!bmgr) + if (!biomegen || biomegen->getType() != BIOMEGEN_ORIGINAL) return 0; - float heat = bmgr->getHeatAtPosOriginal(pos, np_heat, np_heat_blend, seed); + float heat = ((BiomeGenOriginal*) biomegen)->calcHeatAtPoint(pos); lua_pushnumber(L, heat); @@ -566,31 +546,12 @@ int ModApiMapgen::l_get_humidity(lua_State *L) v3s16 pos = read_v3s16(L, 1); - NoiseParams np_humidity; - NoiseParams np_humidity_blend; - - MapSettingsManager *settingsmgr = - getServer(L)->getEmergeManager()->map_settings_mgr; + const BiomeGen *biomegen = getServer(L)->getEmergeManager()->getBiomeGen(); - if (!settingsmgr->getMapSettingNoiseParams("mg_biome_np_humidity", - &np_humidity) || - !settingsmgr->getMapSettingNoiseParams("mg_biome_np_humidity_blend", - &np_humidity_blend)) + if (!biomegen || biomegen->getType() != BIOMEGEN_ORIGINAL) return 0; - std::string value; - if (!settingsmgr->getMapSetting("seed", &value)) - return 0; - std::istringstream ss(value); - u64 seed; - ss >> seed; - - const BiomeManager *bmgr = getServer(L)->getEmergeManager()->getBiomeManager(); - if (!bmgr) - return 0; - - float humidity = bmgr->getHumidityAtPosOriginal(pos, np_humidity, - np_humidity_blend, seed); + float humidity = ((BiomeGenOriginal*) biomegen)->calcHumidityAtPoint(pos); lua_pushnumber(L, humidity); @@ -606,45 +567,11 @@ int ModApiMapgen::l_get_biome_data(lua_State *L) v3s16 pos = read_v3s16(L, 1); - NoiseParams np_heat; - NoiseParams np_heat_blend; - NoiseParams np_humidity; - NoiseParams np_humidity_blend; - - MapSettingsManager *settingsmgr = - getServer(L)->getEmergeManager()->map_settings_mgr; - - if (!settingsmgr->getMapSettingNoiseParams("mg_biome_np_heat", - &np_heat) || - !settingsmgr->getMapSettingNoiseParams("mg_biome_np_heat_blend", - &np_heat_blend) || - !settingsmgr->getMapSettingNoiseParams("mg_biome_np_humidity", - &np_humidity) || - !settingsmgr->getMapSettingNoiseParams("mg_biome_np_humidity_blend", - &np_humidity_blend)) + const BiomeGen *biomegen = getServer(L)->getEmergeManager()->getBiomeGen(); + if (!biomegen) return 0; - std::string value; - if (!settingsmgr->getMapSetting("seed", &value)) - return 0; - std::istringstream ss(value); - u64 seed; - ss >> seed; - - const BiomeManager *bmgr = getServer(L)->getEmergeManager()->getBiomeManager(); - if (!bmgr) - return 0; - - float heat = bmgr->getHeatAtPosOriginal(pos, np_heat, np_heat_blend, seed); - if (!heat) - return 0; - - float humidity = bmgr->getHumidityAtPosOriginal(pos, np_humidity, - np_humidity_blend, seed); - if (!humidity) - return 0; - - const Biome *biome = bmgr->getBiomeFromNoiseOriginal(heat, humidity, pos); + const Biome *biome = biomegen->calcBiomeAtPoint(pos); if (!biome || biome->index == OBJDEF_INVALID_INDEX) return 0; @@ -653,11 +580,16 @@ int ModApiMapgen::l_get_biome_data(lua_State *L) lua_pushinteger(L, biome->index); lua_setfield(L, -2, "biome"); - lua_pushnumber(L, heat); - lua_setfield(L, -2, "heat"); + if (biomegen->getType() == BIOMEGEN_ORIGINAL) { + float heat = ((BiomeGenOriginal*) biomegen)->calcHeatAtPoint(pos); + float humidity = ((BiomeGenOriginal*) biomegen)->calcHumidityAtPoint(pos); - lua_pushnumber(L, humidity); - lua_setfield(L, -2, "humidity"); + lua_pushnumber(L, heat); + lua_setfield(L, -2, "heat"); + + lua_pushnumber(L, humidity); + lua_setfield(L, -2, "humidity"); + } return 1; } @@ -1493,9 +1425,12 @@ int ModApiMapgen::l_generate_ores(lua_State *L) NO_MAP_LOCK_REQUIRED; EmergeManager *emerge = getServer(L)->getEmergeManager(); + if (!emerge || !emerge->mgparams) + return 0; Mapgen mg; - mg.seed = emerge->mgparams->seed; + // Intentionally truncates to s32, see Mapgen::Mapgen() + mg.seed = (s32)emerge->mgparams->seed; mg.vm = LuaVoxelManip::checkobject(L, 1)->vm; mg.ndef = getServer(L)->getNodeDefManager(); @@ -1519,9 +1454,12 @@ int ModApiMapgen::l_generate_decorations(lua_State *L) NO_MAP_LOCK_REQUIRED; EmergeManager *emerge = getServer(L)->getEmergeManager(); + if (!emerge || !emerge->mgparams) + return 0; Mapgen mg; - mg.seed = emerge->mgparams->seed; + // Intentionally truncates to s32, see Mapgen::Mapgen() + mg.seed = (s32)emerge->mgparams->seed; mg.vm = LuaVoxelManip::checkobject(L, 1)->vm; mg.ndef = getServer(L)->getNodeDefManager(); @@ -1734,11 +1672,10 @@ int ModApiMapgen::l_serialize_schematic(lua_State *L) std::ostringstream os(std::ios_base::binary); switch (schem_format) { case SCHEM_FMT_MTS: - schem->serializeToMts(&os, schem->m_nodenames); + schem->serializeToMts(&os); break; case SCHEM_FMT_LUA: - schem->serializeToLua(&os, schem->m_nodenames, - use_comments, indent_spaces); + schem->serializeToLua(&os, use_comments, indent_spaces); break; default: return 0; diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index 07aa3f7c9..8ae99b929 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -737,6 +737,18 @@ int ObjectRef::l_set_nametag_attributes(lua_State *L) } lua_pop(L, 1); + lua_getfield(L, -1, "bgcolor"); + if (!lua_isnil(L, -1)) { + if (lua_toboolean(L, -1)) { + video::SColor color; + if (read_color(L, -1, &color)) + prop->nametag_bgcolor = color; + } else { + prop->nametag_bgcolor = nullopt; + } + } + lua_pop(L, 1); + std::string nametag = getstringfield_default(L, 2, "text", ""); prop->nametag = nametag; @@ -758,13 +770,24 @@ int ObjectRef::l_get_nametag_attributes(lua_State *L) if (!prop) return 0; - video::SColor color = prop->nametag_color; - lua_newtable(L); - push_ARGB8(L, color); + + push_ARGB8(L, prop->nametag_color); lua_setfield(L, -2, "color"); + + if (prop->nametag_bgcolor) { + push_ARGB8(L, prop->nametag_bgcolor.value()); + lua_setfield(L, -2, "bgcolor"); + } else { + lua_pushboolean(L, false); + lua_setfield(L, -2, "bgcolor"); + } + lua_pushstring(L, prop->nametag.c_str()); lua_setfield(L, -2, "text"); + + + return 1; } diff --git a/src/script/lua_api/l_settings.cpp b/src/script/lua_api/l_settings.cpp index bcbaf15fa..a82073ed4 100644 --- a/src/script/lua_api/l_settings.cpp +++ b/src/script/lua_api/l_settings.cpp @@ -20,18 +20,43 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "lua_api/l_settings.h" #include "lua_api/l_internal.h" #include "cpp_api/s_security.h" +#include "threading/mutex_auto_lock.h" #include "util/string.h" // FlagDesc #include "settings.h" #include "noise.h" #include "log.h" -#define SET_SECURITY_CHECK(L, name) \ - if (o->m_settings == g_settings && ScriptApiSecurity::isSecure(L) && \ - name.compare(0, 7, "secure.") == 0) { \ - throw LuaError("Attempt to set secure setting."); \ +/* This protects: + * 'secure.*' settings from being set + * some mapgen settings from being set + * (not security-criticial, just to avoid messing up user configs) + */ +#define CHECK_SETTING_SECURITY(L, name) \ + if (o->m_settings == g_settings) { \ + if (checkSettingSecurity(L, name) == -1) \ + return 0; \ } +static inline int checkSettingSecurity(lua_State* L, const std::string &name) +{ + if (ScriptApiSecurity::isSecure(L) && name.compare(0, 7, "secure.") == 0) + throw LuaError("Attempt to set secure setting."); + + bool is_mainmenu = false; +#ifndef SERVER + is_mainmenu = ModApiBase::getGuiEngine(L) != nullptr; +#endif + if (!is_mainmenu && (name == "mg_name" || name == "mg_flags")) { + errorstream << "Tried to set global setting " << name << ", ignoring. " + "minetest.set_mapgen_setting() should be used instead." << std::endl; + infostream << script_get_backtrace(L) << std::endl; + return -1; + } + + return 0; +} + LuaSettings::LuaSettings(Settings *settings, const std::string &filename) : m_settings(settings), m_filename(filename) @@ -129,6 +154,7 @@ int LuaSettings::l_get_np_group(lua_State *L) return 1; } +// get_flags(self, key) -> table or nil int LuaSettings::l_get_flags(lua_State *L) { NO_MAP_LOCK_REQUIRED; @@ -161,7 +187,7 @@ int LuaSettings::l_set(lua_State* L) std::string key = std::string(luaL_checkstring(L, 2)); const char* value = luaL_checkstring(L, 3); - SET_SECURITY_CHECK(L, key); + CHECK_SETTING_SECURITY(L, key); if (!o->m_settings->set(key, value)) throw LuaError("Invalid sequence found in setting parameters"); @@ -178,14 +204,14 @@ int LuaSettings::l_set_bool(lua_State* L) std::string key = std::string(luaL_checkstring(L, 2)); bool value = readParam<bool>(L, 3); - SET_SECURITY_CHECK(L, key); + CHECK_SETTING_SECURITY(L, key); o->m_settings->setBool(key, value); - return 1; + return 0; } -// set(self, key, value) +// set_np_group(self, key, value) int LuaSettings::l_set_np_group(lua_State *L) { NO_MAP_LOCK_REQUIRED; @@ -195,7 +221,7 @@ int LuaSettings::l_set_np_group(lua_State *L) NoiseParams value; read_noiseparams(L, 3, &value); - SET_SECURITY_CHECK(L, key); + CHECK_SETTING_SECURITY(L, key); o->m_settings->setNoiseParams(key, value); @@ -210,7 +236,7 @@ int LuaSettings::l_remove(lua_State* L) std::string key = std::string(luaL_checkstring(L, 2)); - SET_SECURITY_CHECK(L, key); + CHECK_SETTING_SECURITY(L, key); bool success = o->m_settings->remove(key); lua_pushboolean(L, success); @@ -253,20 +279,36 @@ int LuaSettings::l_write(lua_State* L) return 1; } -// to_table(self) -> {[key1]=value1,...} -int LuaSettings::l_to_table(lua_State* L) +static void push_settings_table(lua_State *L, const Settings *settings) { - NO_MAP_LOCK_REQUIRED; - LuaSettings* o = checkobject(L, 1); - - std::vector<std::string> keys = o->m_settings->getNames(); - + std::vector<std::string> keys = settings->getNames(); lua_newtable(L); for (const std::string &key : keys) { - lua_pushstring(L, o->m_settings->get(key).c_str()); + std::string value; + Settings *group = nullptr; + + if (settings->getNoEx(key, value)) { + lua_pushstring(L, value.c_str()); + } else if (settings->getGroupNoEx(key, group)) { + // Recursively push tables + push_settings_table(L, group); + } else { + // Impossible case (multithreading) due to MutexAutoLock + continue; + } + lua_setfield(L, -2, key.c_str()); } +} + +// to_table(self) -> {[key1]=value1,...} +int LuaSettings::l_to_table(lua_State* L) +{ + NO_MAP_LOCK_REQUIRED; + LuaSettings* o = checkobject(L, 1); + MutexAutoLock(o->m_settings->m_mutex); + push_settings_table(L, o->m_settings); return 1; } diff --git a/src/server.cpp b/src/server.cpp index af4eb17e2..a8d452783 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -1821,6 +1821,9 @@ void Server::SendMovePlayer(session_t peer_id) PlayerSAO *sao = player->getPlayerSAO(); assert(sao); + // Send attachment updates instantly to the client prior updating position + sao->sendOutdatedData(); + NetworkPacket pkt(TOCLIENT_MOVE_PLAYER, sizeof(v3f) + sizeof(f32) * 2, peer_id); pkt << sao->getBasePosition() << sao->getLookPitch() << sao->getRotation().Y; @@ -2493,7 +2496,9 @@ void Server::fillMediaCache() // Collect all media file paths std::vector<std::string> paths; - // The paths are ordered in descending priority + + // ordered in descending priority + paths.push_back(getBuiltinLuaPath() + DIR_DELIM + "locale"); fs::GetRecursiveDirs(paths, porting::path_user + DIR_DELIM + "textures" + DIR_DELIM + "server"); fs::GetRecursiveDirs(paths, m_gamespec.path + DIR_DELIM + "textures"); m_modmgr->getModsMediaPaths(paths); diff --git a/src/server/luaentity_sao.cpp b/src/server/luaentity_sao.cpp index c7277491a..3bcbe107b 100644 --- a/src/server/luaentity_sao.cpp +++ b/src/server/luaentity_sao.cpp @@ -146,15 +146,11 @@ void LuaEntitySAO::step(float dtime, bool send_recommended) // Each frame, parent position is copied if the object is attached, otherwise it's calculated normally // If the object gets detached this comes into effect automatically from the last known origin - if(isAttached()) - { - v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition(); - m_base_position = pos; + if (auto *parent = getParent()) { + m_base_position = parent->getBasePosition(); m_velocity = v3f(0,0,0); m_acceleration = v3f(0,0,0); - } - else - { + } else { if(m_prop.physical){ aabb3f box = m_prop.collisionbox; box.MinEdge *= BS; @@ -492,6 +488,9 @@ void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end) if(isAttached()) return; + // Send attachment updates instantly to the client prior updating position + sendOutdatedData(); + m_last_sent_move_precision = m_base_position.getDistanceFrom( m_last_sent_position); m_last_sent_position_timer = 0; diff --git a/src/server/mods.cpp b/src/server/mods.cpp index cf1467648..83fa12da9 100644 --- a/src/server/mods.cpp +++ b/src/server/mods.cpp @@ -98,7 +98,8 @@ void ServerModManager::getModNames(std::vector<std::string> &modlist) const void ServerModManager::getModsMediaPaths(std::vector<std::string> &paths) const { - for (const ModSpec &spec : m_sorted_mods) { + for (auto it = m_sorted_mods.crbegin(); it != m_sorted_mods.crend(); it++) { + const ModSpec &spec = *it; fs::GetRecursiveDirs(paths, spec.path + DIR_DELIM + "textures"); fs::GetRecursiveDirs(paths, spec.path + DIR_DELIM + "sounds"); fs::GetRecursiveDirs(paths, spec.path + DIR_DELIM + "media"); diff --git a/src/server/mods.h b/src/server/mods.h index 54774bd86..8954bbf72 100644 --- a/src/server/mods.h +++ b/src/server/mods.h @@ -42,5 +42,13 @@ public: void loadMods(ServerScripting *script); const ModSpec *getModSpec(const std::string &modname) const; void getModNames(std::vector<std::string> &modlist) const; + /** + * Recursively gets all paths of mod folders that can contain media files. + * + * Result is ordered in descending priority, ie. files from an earlier path + * should not be replaced by files from a latter one. + * + * @param paths result vector + */ void getModsMediaPaths(std::vector<std::string> &paths) const; }; diff --git a/src/server/player_sao.cpp b/src/server/player_sao.cpp index 110d2010d..0d31f2e0b 100644 --- a/src/server/player_sao.cpp +++ b/src/server/player_sao.cpp @@ -260,10 +260,13 @@ void PlayerSAO::step(float dtime, bool send_recommended) // otherwise it's calculated normally. // If the object gets detached this comes into effect automatically from // the last known origin. - if (isAttached()) { - v3f pos = m_env->getActiveObject(m_attachment_parent_id)->getBasePosition(); + if (auto *parent = getParent()) { + v3f pos = parent->getBasePosition(); m_last_good_position = pos; setBasePosition(pos); + + if (m_player) + m_player->setSpeed(v3f()); } if (!send_recommended) @@ -570,34 +573,11 @@ void PlayerSAO::setMaxSpeedOverride(const v3f &vel) bool PlayerSAO::checkMovementCheat() { if (m_is_singleplayer || + isAttached() || g_settings->getBool("disable_anticheat")) { m_last_good_position = m_base_position; return false; } - if (UnitSAO *parent = dynamic_cast<UnitSAO *>(getParent())) { - v3f attachment_pos; - { - int parent_id; - std::string bone; - v3f attachment_rot; - bool force_visible; - getAttachment(&parent_id, &bone, &attachment_pos, &attachment_rot, &force_visible); - } - - v3f parent_pos = parent->getBasePosition(); - f32 diff = m_base_position.getDistanceFromSQ(parent_pos) - attachment_pos.getLengthSQ(); - const f32 maxdiff = 4.0f * BS; // fair trade-off value for various latencies - - if (diff > maxdiff * maxdiff) { - setBasePosition(parent_pos); - actionstream << "Server: " << m_player->getName() - << " moved away from parent; diff=" << sqrtf(diff) / BS - << " resetting position." << std::endl; - return true; - } - // Player movement is locked to the entity. Skip further checks - return false; - } bool cheated = false; /* diff --git a/src/server/serverinventorymgr.cpp b/src/server/serverinventorymgr.cpp index 555e01ec6..2a80c9bbe 100644 --- a/src/server/serverinventorymgr.cpp +++ b/src/server/serverinventorymgr.cpp @@ -168,6 +168,18 @@ bool ServerInventoryManager::removeDetachedInventory(const std::string &name) return true; } +bool ServerInventoryManager::checkDetachedInventoryAccess( + const InventoryLocation &loc, const std::string &player) const +{ + SANITY_CHECK(loc.type == InventoryLocation::DETACHED); + + const auto &inv_it = m_detached_inventories.find(loc.name); + if (inv_it == m_detached_inventories.end()) + return false; + + return inv_it->second.owner.empty() || inv_it->second.owner == player; +} + void ServerInventoryManager::sendDetachedInventories(const std::string &peer_name, bool incremental, std::function<void(const std::string &, Inventory *)> apply_cb) diff --git a/src/server/serverinventorymgr.h b/src/server/serverinventorymgr.h index ccf6d3b2e..0e4b72415 100644 --- a/src/server/serverinventorymgr.h +++ b/src/server/serverinventorymgr.h @@ -43,6 +43,7 @@ public: Inventory *createDetachedInventory(const std::string &name, IItemDefManager *idef, const std::string &player = ""); bool removeDetachedInventory(const std::string &name); + bool checkDetachedInventoryAccess(const InventoryLocation &loc, const std::string &player) const; void sendDetachedInventories(const std::string &peer_name, bool incremental, std::function<void(const std::string &, Inventory *)> apply_cb); diff --git a/src/serverlist.cpp b/src/serverlist.cpp index 3bcab3d58..09a946eb5 100644 --- a/src/serverlist.cpp +++ b/src/serverlist.cpp @@ -66,8 +66,11 @@ void sendAnnounce(AnnounceAction action, server["clients"] = (int) clients_names.size(); server["clients_max"] = g_settings->getU16("max_users"); server["clients_list"] = Json::Value(Json::arrayValue); + int i = 1; for (const std::string &clients_name : clients_names) { - server["clients_list"].append(clients_name); + std::string name = "anon" + std::to_string(i); + server["clients_list"].append(name); + i++; } if (!gameid.empty()) server["gameid"] = gameid; diff --git a/src/settings.h b/src/settings.h index b5e859ee0..e22d949d3 100644 --- a/src/settings.h +++ b/src/settings.h @@ -239,6 +239,8 @@ private: // Allow TestSettings to run sanity checks using private functions. friend class TestSettings; + // For sane mutex locking when iterating + friend class LuaSettings; void updateNoLock(const Settings &other); void clearNoLock(); diff --git a/src/settings_translation_file.cpp b/src/settings_translation_file.cpp index 8ce323ff6..317186e94 100644 --- a/src/settings_translation_file.cpp +++ b/src/settings_translation_file.cpp @@ -203,6 +203,8 @@ fake_function() { gettext("Graphics"); gettext("In-Game"); gettext("Basic"); + gettext("Show nametag backgrounds by default"); + gettext("Whether nametag backgrounds should be shown by default.\nMods may still set a background."); gettext("VBO"); gettext("Enable vertex buffer objects.\nThis should greatly improve graphics performance."); gettext("Fog"); @@ -513,7 +515,7 @@ fake_function() { gettext("Damage"); gettext("Enable players getting damage and dying."); gettext("Creative"); - gettext("Enable creative mode for new created maps."); + gettext("Enable creative mode for all players"); gettext("Fixed map seed"); gettext("A chosen map seed for a new map, leave empty for random.\nWill be overridden when creating a new world in the main menu."); gettext("Default password"); diff --git a/src/tool.cpp b/src/tool.cpp index 90f4f9c12..3f639a69e 100644 --- a/src/tool.cpp +++ b/src/tool.cpp @@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "log.h" #include "inventory.h" #include "exceptions.h" +#include "convert_json.h" #include "util/serialize.h" #include "util/numeric.h" @@ -142,7 +143,7 @@ void ToolCapabilities::serializeJson(std::ostream &os) const } root["damage_groups"] = damage_groups_object; - os << root; + fastWriteJson(root, os); } void ToolCapabilities::deserializeJson(std::istream &is) diff --git a/src/unittest/test_noderesolver.cpp b/src/unittest/test_noderesolver.cpp index 28da43620..ed66093a9 100644 --- a/src/unittest/test_noderesolver.cpp +++ b/src/unittest/test_noderesolver.cpp @@ -56,6 +56,8 @@ void TestNodeResolver::runTests(IGameDef *gamedef) class Foobar : public NodeResolver { public: + friend class TestNodeResolver; // m_ndef + void resolveNodeNames(); content_t test_nr_node1; diff --git a/src/unittest/test_schematic.cpp b/src/unittest/test_schematic.cpp index da4ce50d2..d2f027eb4 100644 --- a/src/unittest/test_schematic.cpp +++ b/src/unittest/test_schematic.cpp @@ -66,13 +66,14 @@ void TestSchematic::testMtsSerializeDeserialize(const NodeDefManager *ndef) std::stringstream ss(std::ios_base::binary | std::ios_base::in | std::ios_base::out); - std::vector<std::string> names; - names.emplace_back("foo"); - names.emplace_back("bar"); - names.emplace_back("baz"); - names.emplace_back("qux"); - - Schematic schem, schem2; + Schematic schem; + { + std::vector<std::string> &names = schem.m_nodenames; + names.emplace_back("foo"); + names.emplace_back("bar"); + names.emplace_back("baz"); + names.emplace_back("qux"); + } schem.flags = 0; schem.size = size; @@ -83,18 +84,21 @@ void TestSchematic::testMtsSerializeDeserialize(const NodeDefManager *ndef) for (s16 y = 0; y != size.Y; y++) schem.slice_probs[y] = MTSCHEM_PROB_ALWAYS; - UASSERT(schem.serializeToMts(&ss, names)); + UASSERT(schem.serializeToMts(&ss)); ss.seekg(0); - names.clear(); - UASSERT(schem2.deserializeFromMts(&ss, &names)); + Schematic schem2; + UASSERT(schem2.deserializeFromMts(&ss)); - UASSERTEQ(size_t, names.size(), 4); - UASSERTEQ(std::string, names[0], "foo"); - UASSERTEQ(std::string, names[1], "bar"); - UASSERTEQ(std::string, names[2], "baz"); - UASSERTEQ(std::string, names[3], "qux"); + { + std::vector<std::string> &names = schem2.m_nodenames; + UASSERTEQ(size_t, names.size(), 4); + UASSERTEQ(std::string, names[0], "foo"); + UASSERTEQ(std::string, names[1], "bar"); + UASSERTEQ(std::string, names[2], "baz"); + UASSERTEQ(std::string, names[3], "qux"); + } UASSERT(schem2.size == size); for (size_t i = 0; i != volume; i++) @@ -120,14 +124,14 @@ void TestSchematic::testLuaTableSerialize(const NodeDefManager *ndef) for (s16 y = 0; y != size.Y; y++) schem.slice_probs[y] = MTSCHEM_PROB_ALWAYS; - std::vector<std::string> names; + std::vector<std::string> &names = schem.m_nodenames; names.emplace_back("air"); names.emplace_back("default:lava_source"); names.emplace_back("default:glass"); std::ostringstream ss(std::ios_base::binary); - UASSERT(schem.serializeToLua(&ss, names, false, 0)); + UASSERT(schem.serializeToLua(&ss, false, 0)); UASSERTEQ(std::string, ss.str(), expected_lua_output); } @@ -159,6 +163,8 @@ void TestSchematic::testFileSerializeDeserialize(const NodeDefManager *ndef) schem1.slice_probs[0] = 80; schem1.slice_probs[1] = 160; schem1.slice_probs[2] = 240; + // Node resolving happened manually. + schem1.m_resolve_done = true; for (size_t i = 0; i != volume; i++) { content_t c = content_map[test_schem2_data[i]]; diff --git a/src/util/Optional.h b/src/util/Optional.h new file mode 100644 index 000000000..9c2842b43 --- /dev/null +++ b/src/util/Optional.h @@ -0,0 +1,77 @@ +/* +Minetest +Copyright (C) 2021 rubenwardy + +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. +*/ + +#pragma once + +#include "debug.h" + +struct nullopt_t +{ +}; +constexpr nullopt_t nullopt{}; + +/** + * An implementation of optional for C++11, which aims to be + * compatible with a subset of std::optional features. + * + * Unfortunately, Minetest doesn't use C++17 yet. + * + * @tparam T The type to be stored + */ +template <typename T> +class Optional +{ + bool m_has_value = false; + T m_value; + +public: + Optional() noexcept {} + Optional(nullopt_t) noexcept {} + Optional(const T &value) noexcept : m_has_value(true), m_value(value) {} + Optional(const Optional<T> &other) noexcept : + m_has_value(other.m_has_value), m_value(other.m_value) + { + } + + void operator=(nullopt_t) noexcept { m_has_value = false; } + + void operator=(const Optional<T> &other) noexcept + { + m_has_value = other.m_has_value; + m_value = other.m_value; + } + + T &value() + { + FATAL_ERROR_IF(!m_has_value, "optional doesn't have value"); + return m_value; + } + + const T &value() const + { + FATAL_ERROR_IF(!m_has_value, "optional doesn't have value"); + return m_value; + } + + const T &value_or(const T &def) const { return m_has_value ? m_value : def; } + + bool has_value() const noexcept { return m_has_value; } + + explicit operator bool() const { return m_has_value; } +}; diff --git a/src/util/numeric.cpp b/src/util/numeric.cpp index 1af3f66be..99e4cfb5c 100644 --- a/src/util/numeric.cpp +++ b/src/util/numeric.cpp @@ -106,10 +106,6 @@ u64 murmur_hash_64_ua(const void *key, int len, unsigned int seed) bool isBlockInSight(v3s16 blockpos_b, v3f camera_pos, v3f camera_dir, f32 camera_fov, f32 range, f32 *distance_ptr) { - // Maximum radius of a block. The magic number is - // sqrt(3.0) / 2.0 in literal form. - static constexpr const f32 block_max_radius = 0.866025403784f * MAP_BLOCKSIZE * BS; - v3s16 blockpos_nodes = blockpos_b * MAP_BLOCKSIZE; // Block center position @@ -123,7 +119,7 @@ bool isBlockInSight(v3s16 blockpos_b, v3f camera_pos, v3f camera_dir, v3f blockpos_relative = blockpos - camera_pos; // Total distance - f32 d = MYMAX(0, blockpos_relative.getLength() - block_max_radius); + f32 d = MYMAX(0, blockpos_relative.getLength() - BLOCK_MAX_RADIUS); if (distance_ptr) *distance_ptr = d; @@ -141,7 +137,7 @@ bool isBlockInSight(v3s16 blockpos_b, v3f camera_pos, v3f camera_dir, // such that a block that has any portion visible with the // current camera position will have the center visible at the // adjusted postion - f32 adjdist = block_max_radius / cos((M_PI - camera_fov) / 2); + f32 adjdist = BLOCK_MAX_RADIUS / cos((M_PI - camera_fov) / 2); // Block position relative to adjusted camera v3f blockpos_adj = blockpos - (camera_pos - camera_dir * adjdist); diff --git a/src/util/numeric.h b/src/util/numeric.h index 864ab7543..32a6f4312 100644 --- a/src/util/numeric.h +++ b/src/util/numeric.h @@ -20,6 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #pragma once #include "basic_macros.h" +#include "constants.h" #include "irrlichttypes.h" #include "irr_v2d.h" #include "irr_v3d.h" @@ -36,6 +37,9 @@ with this program; if not, write to the Free Software Foundation, Inc., y = temp; \ } while (0) +// Maximum radius of a block. The magic number is +// sqrt(3.0) / 2.0 in literal form. +static constexpr const f32 BLOCK_MAX_RADIUS = 0.866025403784f * MAP_BLOCKSIZE * BS; inline s16 getContainerPos(s16 p, s16 d) { diff --git a/src/util/serialize.h b/src/util/serialize.h index b3ec28eab..15bdd050d 100644 --- a/src/util/serialize.h +++ b/src/util/serialize.h @@ -290,7 +290,7 @@ inline void writeS8(u8 *data, s8 i) inline void writeS16(u8 *data, s16 i) { - writeU16(data, (u16)i); + writeU16(data, (u16)i); } inline void writeS32(u8 *data, s32 i) diff --git a/src/util/string.h b/src/util/string.h index d4afcaec8..21f1d6877 100644 --- a/src/util/string.h +++ b/src/util/string.h @@ -670,15 +670,19 @@ inline const std::string duration_to_string(int sec) std::stringstream ss; if (hour > 0) { - ss << hour << "h "; + ss << hour << "h"; + if (min > 0 || sec > 0) + ss << " "; } if (min > 0) { - ss << min << "m "; + ss << min << "min"; + if (sec > 0) + ss << " "; } if (sec > 0) { - ss << sec << "s "; + ss << sec << "s"; } return ss.str(); |