diff options
author | Elias Fleckenstein <eliasfleckenstein@web.de> | 2020-11-28 13:48:33 +0100 |
---|---|---|
committer | Elias Fleckenstein <eliasfleckenstein@web.de> | 2020-11-28 13:48:33 +0100 |
commit | eb6aca8b4a67ef55108231e71ff29a18a29bf5ae (patch) | |
tree | f891914d25cae2cdaa24392381436a287340651e /src | |
parent | 8de51dae97aa2fe6ea02e4cf437bfe2b2a38eb06 (diff) | |
parent | f1d72d212a0661588be27003069abf4bd8092e55 (diff) | |
download | dragonfireclient-eb6aca8b4a67ef55108231e71ff29a18a29bf5ae.tar.xz |
Merged Minetest
Diffstat (limited to 'src')
211 files changed, 5045 insertions, 4647 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3d6d1b0ea..b8ce69f1d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -351,6 +351,10 @@ else() if (ICONV_LIBRARY) set(PLATFORM_LIBS ${PLATFORM_LIBS} ${ICONV_LIBRARY}) endif() + if (HAIKU) + set(PLATFORM_LIBS ${PLATFORM_LIBS} intl network) + endif() + endif() check_include_files(endian.h HAVE_ENDIAN_H) @@ -673,10 +677,11 @@ endif(BUILD_SERVER) # Blacklisted locales that don't work. # see issue #4638 set(GETTEXT_BLACKLISTED_LOCALES - be + ar he - ko ky + ms_Arab + th ) option(APPLY_LOCALE_BLACKLIST "Use a blacklist to avoid broken locales" TRUE) @@ -697,21 +702,14 @@ include(CheckCSourceCompiles) if(MSVC) # Visual Studio - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /D WIN32_LEAN_AND_MEAN /MP") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /D WIN32_LEAN_AND_MEAN") # EHa enables SEH exceptions (used for catching segfaults) - set(CMAKE_CXX_FLAGS_RELEASE "/EHa /Ox /GL /FD /MD /GS- /Zi /fp:fast /D NDEBUG /D _HAS_ITERATOR_DEBUGGING=0 /TP") + set(CMAKE_CXX_FLAGS_RELEASE "/EHa /Ox /MD /GS- /Zi /fp:fast /D NDEBUG /D _HAS_ITERATOR_DEBUGGING=0") if(CMAKE_SIZEOF_VOID_P EQUAL 4) set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /arch:SSE") endif() - - if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") - set(CMAKE_EXE_LINKER_FLAGS_RELEASE "/INCREMENTAL:NO /DEBUG /OPT:REF /OPT:ICF") - else() - set(CMAKE_EXE_LINKER_FLAGS_RELEASE "/LTCG /INCREMENTAL:NO /DEBUG /OPT:REF /OPT:ICF") - endif() - - set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /SUBSYSTEM:WINDOWS /ENTRY:mainCRTStartup") + set(CMAKE_EXE_LINKER_FLAGS_RELEASE "/INCREMENTAL:NO /DEBUG /OPT:REF /OPT:ICF /SUBSYSTEM:WINDOWS /ENTRY:mainCRTStartup") set(CMAKE_CXX_FLAGS_SEMIDEBUG "/MDd /Zi /Ob0 /O1 /RTC1") @@ -722,6 +720,19 @@ if(MSVC) # Flags for C files (sqlite) # /MD = dynamically link to MSVCRxxx.dll set(CMAKE_C_FLAGS_RELEASE "/O2 /Ob2 /MD") + + # 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") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fuse-ld=lld") + + # Disable pragma-pack warning + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -Wno-pragma-pack") + else() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP") + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /TP /FD /GL") + set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /LTCG") + endif() else() # GCC or compatible compilers such as Clang set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") diff --git a/src/activeobject.h b/src/activeobject.h index 85e160d10..0829858ad 100644 --- a/src/activeobject.h +++ b/src/activeobject.h @@ -120,9 +120,9 @@ public: virtual void setAttachment(int parent_id, const std::string &bone, v3f position, - v3f rotation) {} + v3f rotation, bool force_visible) {} virtual void getAttachment(int *parent_id, std::string *bone, v3f *position, - v3f *rotation) const {} + v3f *rotation, bool *force_visible) const {} virtual void clearChildAttachments() {} virtual void clearParentAttachment() {} virtual void addAttachmentChild(int child_id) {} diff --git a/src/chat.cpp b/src/chat.cpp index c3ed59804..2f65e68b3 100644 --- a/src/chat.cpp +++ b/src/chat.cpp @@ -123,14 +123,14 @@ void ChatBuffer::deleteByAge(f32 maxAge) deleteOldest(count); } -u32 ChatBuffer::getColumns() const +u32 ChatBuffer::getRows() const { - return m_cols; + return m_rows; } -u32 ChatBuffer::getRows() const +void ChatBuffer::scrollTop() { - return m_rows; + m_scroll = getTopScrollPos(); } void ChatBuffer::reformat(u32 cols, u32 rows) @@ -220,11 +220,6 @@ void ChatBuffer::scrollBottom() m_scroll = getBottomScrollPos(); } -void ChatBuffer::scrollTop() -{ - m_scroll = getTopScrollPos(); -} - u32 ChatBuffer::formatChatLine(const ChatLine& line, u32 cols, std::vector<ChatFormattedLine>& destination) const { diff --git a/src/chat.h b/src/chat.h index f84ece206..0b98e4d3c 100644 --- a/src/chat.h +++ b/src/chat.h @@ -94,8 +94,6 @@ public: // Delete lines older than maxAge. void deleteByAge(f32 maxAge); - // Get number of columns, 0 if reformat has not been called yet. - u32 getColumns() const; // Get number of rows, 0 if reformat has not been called yet. u32 getRows() const; // Update console size and reformat all formatted lines. diff --git a/src/client/camera.cpp b/src/client/camera.cpp index c9e8fab6a..398bc8377 100644 --- a/src/client/camera.cpp +++ b/src/client/camera.cpp @@ -30,7 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "wieldmesh.h" #include "noise.h" // easeCurve #include "sound.h" -#include "event.h" +#include "mtevent.h" #include "nodedef.h" #include "util/numeric.h" #include "constants.h" diff --git a/src/client/camera.h b/src/client/camera.h index 3a59637bc..16a1961be 100644 --- a/src/client/camera.h +++ b/src/client/camera.h @@ -169,8 +169,6 @@ public: void removeNametag(Nametag *nametag); - const std::list<Nametag *> &getNametags() { return m_nametags; } - void drawNametags(); inline void addArmInertia(f32 player_yaw); diff --git a/src/client/client.cpp b/src/client/client.cpp index aa3e257ac..9bbb57668 100644 --- a/src/client/client.cpp +++ b/src/client/client.cpp @@ -130,6 +130,7 @@ Client::Client( if (g_settings->getBool("enable_minimap")) { m_minimap = new Minimap(this); } + m_cache_save_interval = g_settings->getU16("server_map_save_interval"); } @@ -241,18 +242,13 @@ void Client::scanModSubfolder(const std::string &mod_name, const std::string &mo infostream << "Client::scanModSubfolder(): Loading \"" << real_path << "\" as \"" << vfs_path << "\"." << std::endl; - std::ifstream is(real_path, std::ios::binary | std::ios::ate); - if(!is.good()) { + std::string contents; + if (!fs::ReadFile(real_path, contents)) { errorstream << "Client::scanModSubfolder(): Can't read file \"" << real_path << "\"." << std::endl; continue; } - auto size = is.tellg(); - std::string contents(size, '\0'); - is.seekg(0); - is.read(&contents[0], size); - infostream << " size: " << size << " bytes" << std::endl; m_mod_vfs.emplace(vfs_path, contents); } } @@ -333,6 +329,8 @@ Client::~Client() } delete m_minimap; + m_minimap = nullptr; + delete m_media_downloader; } @@ -1310,7 +1308,7 @@ void Client::sendPlayerPos(v3f pos) player->last_pitch == player->getPitch() && player->last_yaw == player->getYaw() && player->last_keyPressed == player->keyPressed && - player->last_camera_fov == camera_fov && + player->last_camera_fov == camera_fov && player->last_wanted_range == wanted_range) return; @@ -1671,15 +1669,13 @@ void Client::updateAllMapBlocks() for (s16 Z = currentBlock.Z - 2; Z <= currentBlock.Z + 2; Z++) addUpdateMeshTask(v3s16(X, Y, Z), false, true); - std::map<v2s16, MapSector*> *sectors = m_env.getMap().getSectorsPtr(); + Map &map = m_env.getMap(); - for (auto §or_it : *sectors) { - MapSector *sector = sector_it.second; - MapBlockVect blocks; - sector->getBlocks(blocks); - for (MapBlock *block : blocks) { - addUpdateMeshTask(block->getPos(), false, false); - } + std::vector<v3s16> positions; + map.listAllLoadedBlocks(positions); + + for (v3s16 p : positions) { + addUpdateMeshTask(p, false, false); } } @@ -1693,11 +1689,6 @@ ClientEvent *Client::getClientEvent() return event; } -bool Client::connectedToServer() -{ - return m_con->Connected(); -} - const Address Client::getServerAddress() { return m_con->GetPeerAddress(PEER_ID_SERVER); diff --git a/src/client/client.h b/src/client/client.h index 7455d78dc..979636eba 100644 --- a/src/client/client.h +++ b/src/client/client.h @@ -222,6 +222,7 @@ public: void handleCommand_CSMRestrictionFlags(NetworkPacket *pkt); void handleCommand_PlayerSpeed(NetworkPacket *pkt); void handleCommand_MediaPush(NetworkPacket *pkt); + void handleCommand_MinimapModes(NetworkPacket *pkt); void ProcessData(NetworkPacket *pkt); @@ -338,7 +339,6 @@ public: u16 getProtoVersion() { return m_proto_ver; } - bool connectedToServer(); void confirmRegistration(); bool m_is_registration_confirmation_state = false; bool m_simple_singleplayer_mode; diff --git a/src/client/clientenvironment.cpp b/src/client/clientenvironment.cpp index 3f82bd316..2b50fbf64 100644 --- a/src/client/clientenvironment.cpp +++ b/src/client/clientenvironment.cpp @@ -25,7 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "clientmap.h" #include "scripting_client.h" #include "mapblock_mesh.h" -#include "event.h" +#include "mtevent.h" #include "collision.h" #include "nodedef.h" #include "profiler.h" @@ -183,84 +183,61 @@ void ClientEnvironment::step(float dtime) if(dtime > 0.5) dtime = 0.5; - f32 dtime_downcount = dtime; - /* Stuff that has a maximum time increment */ - u32 loopcount = 0; - do - { - loopcount++; - - f32 dtime_part; - if(dtime_downcount > dtime_max_increment) - { - dtime_part = dtime_max_increment; - dtime_downcount -= dtime_part; - } - else - { - dtime_part = dtime_downcount; - /* - Setting this to 0 (no -=dtime_part) disables an infinite loop - when dtime_part is so small that dtime_downcount -= dtime_part - does nothing - */ - dtime_downcount = 0; - } - + u32 steps = ceil(dtime / dtime_max_increment); + f32 dtime_part = dtime / steps; + for (; steps > 0; --steps) { /* - Handle local player + Local player handling */ - { - // Control local player - lplayer->applyControl(dtime_part, this); - - // Apply physics - if (!free_move && !is_climbing && ! g_settings->getBool("freecam")) { - // Gravity - v3f speed = lplayer->getSpeed(); - if (!lplayer->in_liquid) - speed.Y -= lplayer->movement_gravity * - lplayer->physics_override_gravity * dtime_part * 2.0f; - - // Liquid floating / sinking - if (lplayer->in_liquid && !lplayer->swimming_vertical && - !lplayer->swimming_pitch) - speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2.0f; - - // Liquid resistance - if (lplayer->in_liquid_stable || lplayer->in_liquid) { - // How much the node's viscosity blocks movement, ranges - // between 0 and 1. Should match the scale at which viscosity - // increase affects other liquid attributes. - static const f32 viscosity_factor = 0.3f; - - v3f d_wanted = -speed / lplayer->movement_liquid_fluidity; - f32 dl = d_wanted.getLength(); - if (dl > lplayer->movement_liquid_fluidity_smooth) - dl = lplayer->movement_liquid_fluidity_smooth; - - dl *= (lplayer->liquid_viscosity * viscosity_factor) + - (1 - viscosity_factor); - v3f d = d_wanted.normalize() * (dl * dtime_part * 100.0f); - speed += d; - } - - lplayer->setSpeed(speed); + // Control local player + lplayer->applyControl(dtime_part, this); + + // Apply physics + if (!free_move && !is_climbing && !g_settings->getBool("freecam")) { + // Gravity + v3f speed = lplayer->getSpeed(); + if (!lplayer->in_liquid) + speed.Y -= lplayer->movement_gravity * + lplayer->physics_override_gravity * dtime_part * 2.0f; + + // Liquid floating / sinking + if (lplayer->in_liquid && !lplayer->swimming_vertical && + !lplayer->swimming_pitch) + speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2.0f; + + // Liquid resistance + if (lplayer->in_liquid_stable || lplayer->in_liquid) { + // How much the node's viscosity blocks movement, ranges + // between 0 and 1. Should match the scale at which viscosity + // increase affects other liquid attributes. + static const f32 viscosity_factor = 0.3f; + + v3f d_wanted = -speed / lplayer->movement_liquid_fluidity; + f32 dl = d_wanted.getLength(); + if (dl > lplayer->movement_liquid_fluidity_smooth) + dl = lplayer->movement_liquid_fluidity_smooth; + + dl *= (lplayer->liquid_viscosity * viscosity_factor) + + (1 - viscosity_factor); + v3f d = d_wanted.normalize() * (dl * dtime_part * 100.0f); + speed += d; } - /* - Move the lplayer. - This also does collision detection. - */ - lplayer->move(dtime_part, this, position_max_increment, - &player_collisions); + lplayer->setSpeed(speed); } - } while (dtime_downcount > 0.001); + + /* + Move the lplayer. + This also does collision detection. + */ + lplayer->move(dtime_part, this, position_max_increment, + &player_collisions); + } bool player_immortal = lplayer->getCAO() && lplayer->getCAO()->isImmortal(); @@ -368,21 +345,6 @@ bool isFreeClientActiveObjectId(const u16 id, } -u16 getFreeClientActiveObjectId(ClientActiveObjectMap &objects) -{ - // try to reuse id's as late as possible - static u16 last_used_id = 0; - u16 startid = last_used_id; - for(;;) { - last_used_id ++; - if (isFreeClientActiveObjectId(last_used_id, objects)) - return last_used_id; - - if (last_used_id == startid) - return 0; - } -} - u16 ClientEnvironment::addActiveObject(ClientActiveObject *object) { // Register object. If failed return zero id diff --git a/src/client/clientlauncher.cpp b/src/client/clientlauncher.cpp index ce16797e6..29427f609 100644 --- a/src/client/clientlauncher.cpp +++ b/src/client/clientlauncher.cpp @@ -327,13 +327,13 @@ void ClientLauncher::init_args(GameStartData &start_data, const Settings &cmd_ar // Join a remote server start_data.address = cmd_args.get("address"); start_data.world_path.clear(); + start_data.name = g_settings->get("name"); } if (!start_data.world_path.empty()) { // Start a singleplayer instance start_data.address = ""; } - start_data.name = g_settings->get("name"); if (cmd_args.exists("name")) start_data.name = cmd_args.get("name"); @@ -419,7 +419,6 @@ bool ClientLauncher::launch_game(std::string &error_message, /* Show the GUI menu */ std::string server_name, server_description; - start_data.local_server = false; if (!skip_main_menu) { // Initialize menu data // TODO: Re-use existing structs (GameStartData) @@ -467,6 +466,9 @@ bool ClientLauncher::launch_game(std::string &error_message, start_data.local_server = !menudata.simple_singleplayer_mode && start_data.address.empty(); + } else { + start_data.local_server = !start_data.world_path.empty() && + start_data.address.empty() && !start_data.name.empty(); } if (!RenderingEngine::run()) diff --git a/src/client/clientmap.cpp b/src/client/clientmap.cpp index d41b66741..294687ff8 100644 --- a/src/client/clientmap.cpp +++ b/src/client/clientmap.cpp @@ -36,7 +36,7 @@ ClientMap::ClientMap( MapDrawControl &control, s32 id ): - Map(dout_client, client), + Map(client), scene::ISceneNode(RenderingEngine::get_scene_manager()->getRootSceneNode(), RenderingEngine::get_scene_manager(), id), m_client(client), @@ -122,20 +122,20 @@ void ClientMap::updateDrawList() } m_drawlist.clear(); - v3f camera_position = m_camera_position; - v3f camera_direction = m_camera_direction; - f32 camera_fov = m_camera_fov; + const v3f camera_position = m_camera_position; + const v3f camera_direction = m_camera_direction; // Use a higher fov to accomodate faster camera movements. // Blocks are cropped better when they are drawn. - // Or maybe they aren't? Well whatever. - camera_fov *= 1.2; + const f32 camera_fov = m_camera_fov * 1.1f; v3s16 cam_pos_nodes = floatToInt(camera_position, BS); v3s16 p_blocks_min; v3s16 p_blocks_max; getBlocksInViewRange(cam_pos_nodes, &p_blocks_min, &p_blocks_max); + // Number of blocks currently loaded by the client + u32 blocks_loaded = 0; // Number of blocks with mesh in rendering range u32 blocks_in_range_with_mesh = 0; // Number of blocks occlusion culled @@ -160,6 +160,7 @@ void ClientMap::updateDrawList() MapSector *sector = sector_it.second; v2s16 sp = sector->getPos(); + blocks_loaded += sector->size(); if (!m_control.range_all) { if (sp.X < p_blocks_min.X || sp.X > p_blocks_max.X || sp.Y < p_blocks_min.Z || sp.Y > p_blocks_max.Z) @@ -181,8 +182,12 @@ void ClientMap::updateDrawList() if not seen on display */ - if (block->mesh) + if (block->mesh) { block->mesh->updateCameraOffset(m_camera_offset); + } else { + // Ignore if mesh doesn't exist + continue; + } float range = 100000 * BS; if (!m_control.range_all) @@ -193,13 +198,6 @@ void ClientMap::updateDrawList() camera_direction, camera_fov, range, &d)) continue; - - /* - Ignore if mesh doesn't exist - */ - if (!block->mesh) - continue; - blocks_in_range_with_mesh++; /* @@ -228,6 +226,7 @@ void ClientMap::updateDrawList() g_profiler->avg("MapBlock meshes in range [#]", blocks_in_range_with_mesh); g_profiler->avg("MapBlocks occlusion culled [#]", blocks_occlusion_culled); g_profiler->avg("MapBlocks drawn [#]", m_drawlist.size()); + g_profiler->avg("MapBlocks loaded [#]", blocks_loaded); } struct MeshBufList @@ -293,13 +292,13 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) /* Get animation parameters */ - float animation_time = m_client->getAnimationTime(); - int crack = m_client->getCrackLevel(); - u32 daynight_ratio = m_client->getEnv().getDayNightRatio(); + const float animation_time = m_client->getAnimationTime(); + const int crack = m_client->getCrackLevel(); + const u32 daynight_ratio = m_client->getEnv().getDayNightRatio(); - v3f camera_position = m_camera_position; - v3f camera_direction = m_camera_direction; - f32 camera_fov = m_camera_fov; + 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 diff --git a/src/client/clientmedia.cpp b/src/client/clientmedia.cpp index 8cd3b6bcc..c4c08c05d 100644 --- a/src/client/clientmedia.cpp +++ b/src/client/clientmedia.cpp @@ -260,7 +260,8 @@ void ClientMediaDownloader::initialStep(Client *client) fetch_request.request_id = m_httpfetch_next_id; // == i fetch_request.timeout = m_httpfetch_timeout; fetch_request.connect_timeout = m_httpfetch_timeout; - fetch_request.post_data = required_hash_set; + fetch_request.method = HTTP_POST; + fetch_request.raw_data = required_hash_set; fetch_request.extra_headers.emplace_back( "Content-Type: application/octet-stream"); diff --git a/src/client/clouds.cpp b/src/client/clouds.cpp index 887a62f25..253dee8b9 100644 --- a/src/client/clouds.cpp +++ b/src/client/clouds.cpp @@ -170,8 +170,9 @@ void Clouds::render() // Read noise - bool *grid = new bool[m_cloud_radius_i * 2 * m_cloud_radius_i * 2]; - + std::vector<char> grid(m_cloud_radius_i * 2 * m_cloud_radius_i * 2); // vector<bool> is broken + std::vector<video::S3DVertex> vertices; + vertices.reserve(16 * m_cloud_radius_i * m_cloud_radius_i); for(s16 zi = -m_cloud_radius_i; zi < m_cloud_radius_i; zi++) { u32 si = (zi + m_cloud_radius_i) * m_cloud_radius_i * 2 + m_cloud_radius_i; @@ -195,12 +196,7 @@ void Clouds::render() { s16 zi = zi0; s16 xi = xi0; - // Draw from front to back (needed for transparency) - /*if(zi <= 0) - zi = -m_cloud_radius_i - zi; - if(xi <= 0) - xi = -m_cloud_radius_i - xi;*/ - // Draw from back to front + // Draw from back to front for proper transparency if(zi >= 0) zi = m_cloud_radius_i - zi - 1; if(xi >= 0) @@ -220,17 +216,10 @@ void Clouds::render() video::S3DVertex(0,0,0, 0,0,0, c_top, 0, 0) }; - /*if(zi <= 0 && xi <= 0){ - v[0].Color.setBlue(255); - v[1].Color.setBlue(255); - v[2].Color.setBlue(255); - v[3].Color.setBlue(255); - }*/ - - f32 rx = cloud_size / 2.0f; + const f32 rx = cloud_size / 2.0f; // if clouds are flat, the top layer should be at the given height - f32 ry = m_enable_3d ? m_params.thickness * BS : 0.0f; - f32 rz = cloud_size / 2; + const f32 ry = m_enable_3d ? m_params.thickness * BS : 0.0f; + const f32 rz = cloud_size / 2; for(int i=0; i<num_faces_to_draw; i++) { @@ -320,15 +309,25 @@ void Clouds::render() v3f pos(p0.X, m_params.height * BS, p0.Y); pos -= intToFloat(m_camera_offset, BS); - for (video::S3DVertex &vertex : v) + for (video::S3DVertex &vertex : v) { vertex.Pos += pos; - u16 indices[] = {0,1,2,2,3,0}; - driver->drawVertexPrimitiveList(v, 4, indices, 2, - video::EVT_STANDARD, scene::EPT_TRIANGLES, video::EIT_16BIT); + vertices.push_back(vertex); + } } } - - delete[] grid; + int quad_count = vertices.size() / 4; + std::vector<u16> indices; + indices.reserve(quad_count * 6); + for (int k = 0; k < quad_count; k++) { + indices.push_back(4 * k + 0); + indices.push_back(4 * k + 1); + indices.push_back(4 * k + 2); + indices.push_back(4 * k + 2); + indices.push_back(4 * k + 3); + indices.push_back(4 * k + 0); + } + driver->drawVertexPrimitiveList(vertices.data(), vertices.size(), indices.data(), 2 * quad_count, + video::EVT_STANDARD, scene::EPT_TRIANGLES, video::EIT_16BIT); // Restore fog settings driver->setFog(fog_color, fog_type, fog_start, fog_end, fog_density, @@ -342,14 +341,13 @@ void Clouds::step(float dtime) void Clouds::update(const v3f &camera_p, const video::SColorf &color_diffuse) { + video::SColorf ambient(m_params.color_ambient); + video::SColorf bright(m_params.color_bright); m_camera_pos = camera_p; - m_color.r = MYMIN(MYMAX(color_diffuse.r * m_params.color_bright.getRed(), - m_params.color_ambient.getRed()), 255) / 255.0f; - m_color.g = MYMIN(MYMAX(color_diffuse.g * m_params.color_bright.getGreen(), - m_params.color_ambient.getGreen()), 255) / 255.0f; - m_color.b = MYMIN(MYMAX(color_diffuse.b * m_params.color_bright.getBlue(), - m_params.color_ambient.getBlue()), 255) / 255.0f; - m_color.a = m_params.color_bright.getAlpha() / 255.0f; + m_color.r = core::clamp(color_diffuse.r * bright.r, ambient.r, 1.0f); + m_color.g = core::clamp(color_diffuse.g * bright.g, ambient.g, 1.0f); + m_color.b = core::clamp(color_diffuse.b * bright.b, ambient.b, 1.0f); + m_color.a = bright.a; // is the camera inside the cloud mesh? m_camera_inside_cloud = false; // default diff --git a/src/client/content_cao.cpp b/src/client/content_cao.cpp index e1cebf461..7208212d4 100644 --- a/src/client/content_cao.cpp +++ b/src/client/content_cao.cpp @@ -48,6 +48,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include <cmath> #include "client/shader.h" #include "script/scripting_client.h" +#include "client/minimap.h" class Settings; struct ToolCapabilities; @@ -354,6 +355,8 @@ void GenericCAO::initialize(const std::string &data) m_is_local_player = true; m_is_visible = false; player->setCAO(this); + + m_prop.show_on_minimap = false; } } @@ -372,7 +375,7 @@ void GenericCAO::processInitData(const std::string &data) } // PROTOCOL_VERSION >= 37 - m_name = deSerializeString(is); + m_name = deSerializeString16(is); m_is_player = readU8(is); m_id = readU16(is); m_position = readV3F32(is); @@ -382,7 +385,7 @@ void GenericCAO::processInitData(const std::string &data) const u8 num_messages = readU8(is); for (int i = 0; i < num_messages; i++) { - std::string message = deSerializeLongString(is); + std::string message = deSerializeString32(is); processMessage(message); } @@ -457,18 +460,21 @@ void GenericCAO::setChildrenVisible(bool toset) for (u16 cao_id : m_attachment_child_ids) { GenericCAO *obj = m_env->getGenericCAO(cao_id); if (obj) { - obj->setVisible(toset); + // Check if the entity is forced to appear in first person. + obj->setVisible(obj->m_force_visible ? true : toset); } } } -void GenericCAO::setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation) +void GenericCAO::setAttachment(int parent_id, const std::string &bone, + v3f position, v3f rotation, bool force_visible) { int old_parent = m_attachment_parent_id; m_attachment_parent_id = parent_id; m_attachment_bone = bone; m_attachment_position = position; m_attachment_rotation = rotation; + m_force_visible = force_visible; ClientActiveObject *parent = m_env->getActiveObject(parent_id); @@ -480,17 +486,32 @@ void GenericCAO::setAttachment(int parent_id, const std::string &bone, v3f posit if (parent) parent->addAttachmentChild(m_id); } - + updateAttachments(); + + // Forcibly show attachments if required by set_attach + if (m_force_visible) { + m_is_visible = true; + } else if (!m_is_local_player) { + // Objects attached to the local player should be hidden in first person + m_is_visible = !m_attached_to_local || + m_client->getCamera()->getCameraMode() != CAMERA_MODE_FIRST; + m_force_visible = false; + } else { + // Local players need to have this set, + // otherwise first person attachments fail. + m_is_visible = true; + } } void GenericCAO::getAttachment(int *parent_id, std::string *bone, v3f *position, - v3f *rotation) const + v3f *rotation, bool *force_visible) const { *parent_id = m_attachment_parent_id; *bone = m_attachment_bone; *position = m_attachment_position; *rotation = m_attachment_rotation; + *force_visible = m_force_visible; } void GenericCAO::clearChildAttachments() @@ -500,7 +521,7 @@ void GenericCAO::clearChildAttachments() int child_id = *m_attachment_child_ids.begin(); if (ClientActiveObject *child = m_env->getActiveObject(child_id)) - child->setAttachment(0, "", v3f(), v3f()); + child->setAttachment(0, "", v3f(), v3f(), false); removeAttachmentChild(child_id); } @@ -509,9 +530,9 @@ void GenericCAO::clearChildAttachments() void GenericCAO::clearParentAttachment() { if (m_attachment_parent_id) - setAttachment(0, "", m_attachment_position, m_attachment_rotation); + setAttachment(0, "", m_attachment_position, m_attachment_rotation, false); else - setAttachment(0, "", v3f(), v3f()); + setAttachment(0, "", v3f(), v3f(), false); } void GenericCAO::addAttachmentChild(int child_id) @@ -569,6 +590,9 @@ void GenericCAO::removeFromScene(bool permanent) m_client->getCamera()->removeNametag(m_nametag); m_nametag = nullptr; } + + if (m_marker && m_client->getMinimap()) + m_client->getMinimap()->removeMarker(&m_marker); } void GenericCAO::addToScene(ITextureSource *tsrc) @@ -797,11 +821,13 @@ void GenericCAO::addToScene(ITextureSource *tsrc) node->setParent(m_matrixnode); updateNametag(); + updateMarker(); updateNodePos(); updateAnimation(); updateBonePosition(); updateAttachments(); setNodeLight(m_last_light); + updateMeshCulling(); } void GenericCAO::updateLight(u32 day_night_ratio) @@ -889,6 +915,26 @@ u16 GenericCAO::getLightPosition(v3s16 *pos) return 3; } +void GenericCAO::updateMarker() +{ + if (!m_client->getMinimap()) + return; + + if (!m_prop.show_on_minimap) { + if (m_marker) + m_client->getMinimap()->removeMarker(&m_marker); + return; + } + + if (m_marker) + return; + + scene::ISceneNode *node = getSceneNode(); + if (!node) + return; + m_marker = m_client->getMinimap()->addMarker(node); +} + void GenericCAO::updateNametag() { if (m_is_local_player && ! g_settings->getBool("freecam")) // No nametag for local player @@ -981,13 +1027,13 @@ void GenericCAO::step(float dtime, ClientEnvironment *env) if (controls.sneak && walking && ! g_settings->getBool("no_slow")) new_speed /= 2; - if (walking && (controls.LMB || controls.RMB)) { + if (walking && (controls.dig || controls.place)) { new_anim = player->local_animations[3]; player->last_animation = WD_ANIM; - } else if(walking) { + } else if (walking) { new_anim = player->local_animations[1]; player->last_animation = WALK_ANIM; - } else if(controls.LMB || controls.RMB) { + } else if (controls.dig || controls.place) { new_anim = player->local_animations[2]; player->last_animation = DIG_ANIM; } @@ -1010,9 +1056,9 @@ void GenericCAO::step(float dtime, ClientEnvironment *env) // Update local player animations if ((player->last_animation != old_anim || - m_animation_speed != old_anim_speed) && - player->last_animation != NO_ANIM && allow_update) - updateAnimation(); + m_animation_speed != old_anim_speed) && + player->last_animation != NO_ANIM && allow_update) + updateAnimation(); } } @@ -1182,6 +1228,7 @@ void GenericCAO::updateTexturePos() int row = m_tx_basepos.Y; int col = m_tx_basepos.X; + // Yawpitch goes rightwards if (m_tx_select_horiz_by_yawpitch) { if (cam_to_entity.Y > 0.75) col += 5; @@ -1212,6 +1259,27 @@ void GenericCAO::updateTexturePos() float tys = m_tx_size.Y; setBillboardTextureMatrix(m_spritenode, txs, tys, col, row); } + + else if (m_meshnode) { + if (m_prop.visual == "upright_sprite") { + int row = m_tx_basepos.Y; + int col = m_tx_basepos.X; + + // Animation goes downwards + row += m_anim_frame; + + const auto &tx = m_tx_size; + v2f t[4] = { // cf. vertices in GenericCAO::addToScene() + tx * v2f(col+1, row+1), + tx * v2f(col, row+1), + tx * v2f(col, row), + tx * v2f(col+1, row), + }; + auto mesh = m_meshnode->getMesh(); + setMeshBufferTextureCoords(mesh->getMeshBuffer(0), t, 4); + setMeshBufferTextureCoords(mesh->getMeshBuffer(1), t, 4); + } + } } // Do not pass by reference, see header. @@ -1253,7 +1321,7 @@ void GenericCAO::updateTextures(std::string mod) } } - if (m_animated_meshnode) { + else if (m_animated_meshnode) { if (m_prop.visual == "mesh") { for (u32 i = 0; i < m_prop.textures.size() && i < m_animated_meshnode->getMaterialCount(); ++i) { @@ -1302,8 +1370,8 @@ void GenericCAO::updateTextures(std::string mod) } } } - if(m_meshnode) - { + + else if (m_meshnode) { if(m_prop.visual == "cube") { for (u32 i = 0; i < 6; ++i) @@ -1395,6 +1463,9 @@ void GenericCAO::updateTextures(std::string mod) setMeshColor(mesh, m_prop.colors[0]); } } + // Prevent showing the player after changing texture + if (m_is_local_player) + updateMeshCulling(); } void GenericCAO::updateAnimation() @@ -1564,6 +1635,8 @@ void GenericCAO::processMessage(const std::string &data) u8 cmd = readU8(is); if (cmd == AO_CMD_SET_PROPERTIES) { ObjectProperties newprops; + newprops.show_on_minimap = m_is_player; // default + newprops.deSerialize(is); // Check what exactly changed @@ -1597,6 +1670,8 @@ void GenericCAO::processMessage(const std::string &data) if ((m_is_player && !m_is_local_player) && m_prop.nametag.empty()) m_prop.nametag = m_name; + if (m_is_local_player) + m_prop.show_on_minimap = false; if (expire_visuals) { expireVisuals(); @@ -1609,6 +1684,7 @@ void GenericCAO::processMessage(const std::string &data) updateTextures(m_current_texture_modifier); } updateNametag(); + updateMarker(); } } else if (cmd == AO_CMD_UPDATE_POSITION) { // Not sent by the server if this object is an attachment. @@ -1641,7 +1717,7 @@ void GenericCAO::processMessage(const std::string &data) rot_translator.update(m_rotation, false, update_interval); updateNodePos(); } else if (cmd == AO_CMD_SET_TEXTURE_MOD) { - std::string mod = deSerializeString(is); + std::string mod = deSerializeString16(is); // immediately reset a engine issued texture modifier if a mod sends a different one if (m_reset_textures_timer > 0) { @@ -1724,7 +1800,7 @@ void GenericCAO::processMessage(const std::string &data) m_animation_speed = readF32(is); updateAnimationSpeed(); } else if (cmd == AO_CMD_SET_BONE_POSITION) { - std::string bone = deSerializeString(is); + std::string bone = deSerializeString16(is); v3f position = readV3F32(is); v3f rotation = readV3F32(is); m_bone_position[bone] = core::vector2d<v3f>(position, rotation); @@ -1732,15 +1808,12 @@ void GenericCAO::processMessage(const std::string &data) // updateBonePosition(); now called every step } else if (cmd == AO_CMD_ATTACH_TO) { u16 parent_id = readS16(is); - std::string bone = deSerializeString(is); + std::string bone = deSerializeString16(is); v3f position = readV3F32(is); v3f rotation = readV3F32(is); + bool force_visible = readU8(is); // Returns false for EOF - setAttachment(parent_id, bone, position, rotation); - - // localplayer itself can't be attached to localplayer - if (!m_is_local_player) - m_is_visible = !m_attached_to_local; + setAttachment(parent_id, bone, position, rotation, force_visible); } else if (cmd == AO_CMD_PUNCHED) { u16 result_hp = readU16(is); @@ -1782,7 +1855,7 @@ void GenericCAO::processMessage(const std::string &data) int armor_groups_size = readU16(is); for(int i=0; i<armor_groups_size; i++) { - std::string name = deSerializeString(is); + std::string name = deSerializeString16(is); int rating = readS16(is); m_armor_groups[name] = rating; } @@ -1854,5 +1927,43 @@ std::string GenericCAO::debugInfoText() return os.str(); } +void GenericCAO::updateMeshCulling() +{ + if (!m_is_local_player) + return; + + const bool hidden = m_client->getCamera()->getCameraMode() == CAMERA_MODE_FIRST; + + if (m_meshnode && m_prop.visual == "upright_sprite") { + u32 buffers = m_meshnode->getMesh()->getMeshBufferCount(); + for (u32 i = 0; i < buffers; i++) { + video::SMaterial &mat = m_meshnode->getMesh()->getMeshBuffer(i)->getMaterial(); + // upright sprite has no backface culling + mat.setFlag(video::EMF_FRONT_FACE_CULLING, hidden); + } + return; + } + + irr::scene::ISceneNode *node = getSceneNode(); + if (!node) + return; + + if (hidden) { + // Hide the mesh by culling both front and + // back faces. Serious hackyness but it works for our + // purposes. This also preserves the skeletal armature. + node->setMaterialFlag(video::EMF_BACK_FACE_CULLING, + true); + node->setMaterialFlag(video::EMF_FRONT_FACE_CULLING, + true); + } else { + // Restore mesh visibility. + node->setMaterialFlag(video::EMF_BACK_FACE_CULLING, + m_prop.backface_culling); + node->setMaterialFlag(video::EMF_FRONT_FACE_CULLING, + false); + } +} + // Prototype GenericCAO proto_GenericCAO(NULL, NULL); diff --git a/src/client/content_cao.h b/src/client/content_cao.h index 75882a074..09c26bd9c 100644 --- a/src/client/content_cao.h +++ b/src/client/content_cao.h @@ -30,6 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc., class Camera; class Client; struct Nametag; +struct MinimapMarker; /* SmoothTranslator @@ -84,6 +85,7 @@ private: scene::IBillboardSceneNode *m_spritenode = nullptr; scene::IDummyTransformationSceneNode *m_matrixnode = nullptr; Nametag *m_nametag = nullptr; + MinimapMarker *m_marker = nullptr; v3f m_position = v3f(0.0f, 10.0f * BS, 0); v3f m_velocity; v3f m_acceleration; @@ -109,6 +111,7 @@ private: v3f m_attachment_position; v3f m_attachment_rotation; bool m_attached_to_local = false; + bool m_force_visible = false; int m_anim_frame = 0; int m_anim_num_frames = 1; @@ -241,9 +244,10 @@ public: } void setChildrenVisible(bool toset); - void setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation); + void setAttachment(int parent_id, const std::string &bone, v3f position, + v3f rotation, bool force_visible); void getAttachment(int *parent_id, std::string *bone, v3f *position, - v3f *rotation) const; + v3f *rotation, bool *force_visible) const; void clearChildAttachments(); void clearParentAttachment(); void addAttachmentChild(int child_id); @@ -274,6 +278,8 @@ public: void updateNametag(); + void updateMarker(); + void updateNodePos(); void step(float dtime, ClientEnvironment *env); @@ -308,4 +314,6 @@ public: { return &m_prop; } + + void updateMeshCulling(); }; diff --git a/src/client/content_mapblock.cpp b/src/client/content_mapblock.cpp index 3d06584c4..df2748212 100644 --- a/src/client/content_mapblock.cpp +++ b/src/client/content_mapblock.cpp @@ -723,7 +723,8 @@ void MapblockMeshGenerator::drawGlasslikeFramedNode() for (auto &glass_tile : glass_tiles) glass_tile = tiles[4]; - u8 param2 = n.getParam2(); + // Only respect H/V merge bits when paramtype2 = "glasslikeliquidlevel" (liquid tank) + u8 param2 = (f->param_type_2 == CPT2_GLASSLIKE_LIQUID_LEVEL) ? n.getParam2() : 0; bool H_merge = !(param2 & 128); bool V_merge = !(param2 & 64); param2 &= 63; @@ -1454,10 +1455,10 @@ void MapblockMeshGenerator::generate() } } -void MapblockMeshGenerator::renderSingle(content_t node) +void MapblockMeshGenerator::renderSingle(content_t node, u8 param2) { p = {0, 0, 0}; - n = MapNode(node, 0xff, 0x00); + n = MapNode(node, 0xff, param2); f = &nodedef->get(n); drawNode(); } diff --git a/src/client/content_mapblock.h b/src/client/content_mapblock.h index 97947cdbe..487d84a07 100644 --- a/src/client/content_mapblock.h +++ b/src/client/content_mapblock.h @@ -174,5 +174,5 @@ public: public: MapblockMeshGenerator(MeshMakeData *input, MeshCollector *output); void generate(); - void renderSingle(content_t node); + void renderSingle(content_t node, u8 param2 = 0x00); }; diff --git a/src/client/event_manager.h b/src/client/event_manager.h index 3762e89bf..16f7bcf07 100644 --- a/src/client/event_manager.h +++ b/src/client/event_manager.h @@ -19,7 +19,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #pragma once -#include "event.h" +#include "mtevent.h" #include <list> #include <map> diff --git a/src/client/fontengine.h b/src/client/fontengine.h index 865b2d3ff..c6efa0df4 100644 --- a/src/client/fontengine.h +++ b/src/client/fontengine.h @@ -48,7 +48,7 @@ struct FontSpec { u16 getHash() { - return (mode << 2) | (bold << 1) | italic; + return (mode << 2) | (static_cast<u8>(bold) << 1) | static_cast<u8>(italic); } unsigned int size; diff --git a/src/client/game.cpp b/src/client/game.cpp index 88607d1d8..cc2a1bc43 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -96,7 +96,7 @@ Game::Game() : &settingChangedCallback, this); g_settings->registerChangedCallback("joystick_frustum_sensitivity", &settingChangedCallback, this); - g_settings->registerChangedCallback("repeat_rightclick_time", + g_settings->registerChangedCallback("repeat_place_time", &settingChangedCallback, this); g_settings->registerChangedCallback("noclip", &settingChangedCallback, this); @@ -164,7 +164,7 @@ Game::~Game() &settingChangedCallback, this); g_settings->deregisterChangedCallback("mouse_sensitivity", &settingChangedCallback, this); - g_settings->deregisterChangedCallback("repeat_rightclick_time", + g_settings->deregisterChangedCallback("repeat_place_time", &settingChangedCallback, this); g_settings->deregisterChangedCallback("noclip", &settingChangedCallback, this); @@ -476,7 +476,8 @@ bool Game::createSingleplayerServer(const std::string &map_dir, return false; } - server = new Server(map_dir, gamespec, simple_singleplayer_mode, bind_addr, false); + server = new Server(map_dir, gamespec, simple_singleplayer_mode, bind_addr, + false, nullptr, error_message); server->start(); return true; @@ -545,7 +546,7 @@ bool Game::createClient(const GameStartData &start_data) /* Skybox */ - sky = new Sky(-1, texture_src); + sky = new Sky(-1, texture_src, shader_src); scsf->setSky(sky); skybox = NULL; // This is used/set later on in the main run loop @@ -591,11 +592,9 @@ bool Game::createClient(const GameStartData &start_data) } mapper = client->getMinimap(); - if (mapper) { - mapper->setMinimapMode(MINIMAP_MODE_OFF); - if (client->modsLoaded()) - client->getScript()->on_minimap_ready(mapper); - } + + if (mapper && client->modsLoaded()) + client->getScript()->on_minimap_ready(mapper); return true; } @@ -834,7 +833,10 @@ bool Game::getServerContent(bool *aborted) std::stringstream message; std::fixed(message); message.precision(0); - message << gettext("Media...") << " " << (client->mediaReceiveProgress()*100) << "%"; + float receive = client->mediaReceiveProgress() * 100; + message << gettext("Media..."); + if (receive > 0) + message << " " << receive << "%"; message.precision(2); if ((USE_CURL == 0) || @@ -1228,15 +1230,11 @@ void Game::processItemSelection(u16 *new_playeritem) s32 dir = wheel; - if (input->joystick.wasKeyDown(KeyType::SCROLL_DOWN) || - wasKeyDown(KeyType::HOTBAR_NEXT)) { + if (wasKeyDown(KeyType::HOTBAR_NEXT)) dir = -1; - } - if (input->joystick.wasKeyDown(KeyType::SCROLL_UP) || - wasKeyDown(KeyType::HOTBAR_PREV)) { + if (wasKeyDown(KeyType::HOTBAR_PREV)) dir = 1; - } if (dir < 0) *new_playeritem = *new_playeritem < max_item ? *new_playeritem + 1 : 0; @@ -1289,7 +1287,7 @@ void Game::openInventory() TextDest *txt_dst = new TextDestPlayerInventory(client); auto *&formspec = m_game_ui->updateFormspec(""); GUIFormSpecMenu::create(formspec, client, &input->joystick, fs_src, - txt_dst, client->getFormspecPrepend()); + txt_dst, client->getFormspecPrepend(), sound); formspec->setFormSpec(fs_src->getForm(), inventoryloc); } @@ -1490,52 +1488,37 @@ void Game::toggleMinimap(bool shift_pressed) if (!mapper || !m_game_ui->m_flags.show_hud || !g_settings->getBool("enable_minimap")) return; - if (shift_pressed) { + if (shift_pressed) mapper->toggleMinimapShape(); - return; - } + else + mapper->nextMode(); + // TODO: When legacy minimap is deprecated, keep only HUD minimap stuff here + + // Not so satisying code to keep compatibility with old fixed mode system + // --> u32 hud_flags = client->getEnv().getLocalPlayer()->hud_flags; - MinimapMode mode = MINIMAP_MODE_OFF; - if (hud_flags & HUD_FLAG_MINIMAP_VISIBLE) { - mode = mapper->getMinimapMode(); - mode = (MinimapMode)((int)mode + 1); - // If radar is disabled and in, or switching to, radar mode - if (!(hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE) && mode > 3) - mode = MINIMAP_MODE_OFF; - } + if (!(hud_flags & HUD_FLAG_MINIMAP_VISIBLE)) { + m_game_ui->m_flags.show_minimap = false; + } else { - m_game_ui->m_flags.show_minimap = true; - switch (mode) { - case MINIMAP_MODE_SURFACEx1: - m_game_ui->showTranslatedStatusText("Minimap in surface mode, Zoom x1"); - break; - case MINIMAP_MODE_SURFACEx2: - m_game_ui->showTranslatedStatusText("Minimap in surface mode, Zoom x2"); - break; - case MINIMAP_MODE_SURFACEx4: - m_game_ui->showTranslatedStatusText("Minimap in surface mode, Zoom x4"); - break; - case MINIMAP_MODE_RADARx1: - m_game_ui->showTranslatedStatusText("Minimap in radar mode, Zoom x1"); - break; - case MINIMAP_MODE_RADARx2: - m_game_ui->showTranslatedStatusText("Minimap in radar mode, Zoom x2"); - break; - case MINIMAP_MODE_RADARx4: - m_game_ui->showTranslatedStatusText("Minimap in radar mode, Zoom x4"); - break; - default: - mode = MINIMAP_MODE_OFF; - m_game_ui->m_flags.show_minimap = false; - if (hud_flags & HUD_FLAG_MINIMAP_VISIBLE) - m_game_ui->showTranslatedStatusText("Minimap hidden"); - else - m_game_ui->showTranslatedStatusText("Minimap currently disabled by game or mod"); - } + // If radar is disabled, try to find a non radar mode or fall back to 0 + if (!(hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE)) + while (mapper->getModeIndex() && + mapper->getModeDef().type == MINIMAP_TYPE_RADAR) + mapper->nextMode(); - mapper->setMinimapMode(mode); + m_game_ui->m_flags.show_minimap = mapper->getModeDef().type != + MINIMAP_TYPE_OFF; + } + // <-- + // End of 'not so satifying code' + if ((hud_flags & HUD_FLAG_MINIMAP_VISIBLE) || + (hud && hud->hasElementOfType(HUD_ELEM_MINIMAP))) + m_game_ui->showStatusText(utf8_to_wide(mapper->getModeDef().label)); + else + m_game_ui->showTranslatedStatusText("Minimap currently disabled by game or mod"); } void Game::toggleFog() @@ -1743,8 +1726,8 @@ void Game::updatePlayerControl(const CameraOrientation &cam) isKeyDown(KeyType::SPECIAL1), isKeyDown(KeyType::SNEAK), isKeyDown(KeyType::ZOOM), - input->getLeftState(), - input->getRightState(), + isKeyDown(KeyType::DIG), + isKeyDown(KeyType::PLACE), cam.camera_pitch, cam.camera_yaw, input->joystick.getAxisWithoutDead(JA_SIDEWARD_MOVE), @@ -1759,8 +1742,8 @@ void Game::updatePlayerControl(const CameraOrientation &cam) ( (u32)(isKeyDown(KeyType::JUMP) & 0x1) << 4) | ( (u32)(isKeyDown(KeyType::SPECIAL1) & 0x1) << 5) | ( (u32)(isKeyDown(KeyType::SNEAK) & 0x1) << 6) | - ( (u32)(input->getLeftState() & 0x1) << 7) | - ( (u32)(input->getRightState() & 0x1) << 8) | + ( (u32)(isKeyDown(KeyType::DIG) & 0x1) << 7) | + ( (u32)(isKeyDown(KeyType::PLACE) & 0x1) << 8) | ( (u32)(isKeyDown(KeyType::ZOOM) & 0x1) << 9) ); @@ -1898,7 +1881,7 @@ void Game::handleClientEvent_ShowFormSpec(ClientEvent *event, CameraOrientation auto *&formspec = m_game_ui->updateFormspec(*(event->show_formspec.formname)); GUIFormSpecMenu::create(formspec, client, &input->joystick, - fs_src, txt_dst, client->getFormspecPrepend()); + fs_src, txt_dst, client->getFormspecPrepend(), sound); } delete event->show_formspec.formspec; @@ -1911,7 +1894,7 @@ void Game::handleClientEvent_ShowLocalFormSpec(ClientEvent *event, CameraOrienta LocalFormspecHandler *txt_dst = new LocalFormspecHandler(*event->show_formspec.formname, client); GUIFormSpecMenu::create(m_game_ui->getFormspecGUI(), client, &input->joystick, - fs_src, txt_dst, client->getFormspecPrepend()); + fs_src, txt_dst, client->getFormspecPrepend(), sound); delete event->show_formspec.formspec; delete event->show_formspec.formname; @@ -2246,12 +2229,13 @@ void Game::updateCamera(u32 busy_time, f32 dtime) void Game::updatePlayerCAOVisibility() { + // Make the player visible depending on camera mode. LocalPlayer *player = client->getEnv().getLocalPlayer(); GenericCAO *playercao = player->getCAO(); if (!playercao) return; - playercao->setVisible(camera->getCameraMode() > CAMERA_MODE_FIRST || g_settings->getBool("freecam")); - playercao->setChildrenVisible(camera->getCameraMode() > CAMERA_MODE_FIRST || g_settings->getBool("freecam")); + playercao->updateMeshCulling(); + playercao->setChildrenVisible(camera->getCameraMode() > CAMERA_MODE_FIRST || g_settings->getBool("freecam")); } void Game::updateSound(f32 dtime) @@ -2348,7 +2332,7 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud, bool show_debug) PointedThing pointed = updatePointedThing(shootline, selected_def.liquids_pointable, - !runData.ldown_for_dig, + !runData.btn_down_for_dig, camera_offset); if (pointed != runData.pointed_old) { @@ -2356,20 +2340,18 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud, bool show_debug) hud->updateSelectionMesh(camera_offset); } - if (runData.digging_blocked && !input->getLeftState()) { - // allow digging again if button is not pressed + // Allow digging again if button is not pressed + if (runData.digging_blocked && !isKeyDown(KeyType::DIG)) runData.digging_blocked = false; - } /* Stop digging when - - releasing left mouse button + - releasing dig button - pointing away from node */ if (runData.digging) { - if (input->getLeftReleased()) { - infostream << "Left button released" - << " (stopped digging)" << std::endl; + if (wasKeyReleased(KeyType::DIG)) { + infostream << "Dig button released (stopped digging)" << std::endl; runData.digging = false; } else if (pointed != runData.pointed_old) { if (pointed.type == POINTEDTHING_NODE @@ -2379,8 +2361,7 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud, bool show_debug) // Still pointing to the same node, but a different face. // Don't reset. } else { - infostream << "Pointing away from node" - << " (stopped digging)" << std::endl; + infostream << "Pointing away from node (stopped digging)" << std::endl; runData.digging = false; hud->updateSelectionMesh(camera_offset); } @@ -2391,55 +2372,57 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud, bool show_debug) client->setCrack(-1, v3s16(0, 0, 0)); runData.dig_time = 0.0; } - } else if (runData.dig_instantly && input->getLeftReleased()) { - // Remove e.g. torches faster when clicking instead of holding LMB + } else if (runData.dig_instantly && wasKeyReleased(KeyType::DIG)) { + // Remove e.g. torches faster when clicking instead of holding dig button runData.nodig_delay_timer = 0; runData.dig_instantly = false; } - if (!runData.digging && runData.ldown_for_dig && !input->getLeftState()) { - runData.ldown_for_dig = false; - } + if (!runData.digging && runData.btn_down_for_dig && !isKeyDown(KeyType::DIG)) + runData.btn_down_for_dig = false; - runData.left_punch = false; + runData.punching = false; soundmaker->m_player_leftpunch_sound.name = ""; // Prepare for repeating, unless we're not supposed to - if ((input->getRightState() || g_settings->getBool("autoplace")) && !g_settings->getBool("safe_dig_and_place")) - runData.repeat_rightclick_timer += dtime; + if ((isKeyDown(KeyType::PLACE) || g_settings->getBool("autoplace")) && !g_settings->getBool("safe_dig_and_place")) + runData.repeat_place_timer += dtime; else - runData.repeat_rightclick_timer = 0; + runData.repeat_place_timer = 0; - if (selected_def.usable && input->getLeftState()) { - if (input->getLeftClicked() && (!client->modsLoaded() - || !client->getScript()->on_item_use(selected_item, pointed))) + if (selected_def.usable && isKeyDown(KeyType::DIG)) { + if (wasKeyPressed(KeyType::DIG) && (!client->modsLoaded() || + !client->getScript()->on_item_use(selected_item, pointed))) client->interact(INTERACT_USE, pointed); } else if (pointed.type == POINTEDTHING_NODE) { handlePointingAtNode(pointed, selected_item, hand_item, dtime); } else if (pointed.type == POINTEDTHING_OBJECT) { v3f player_position = player->getPosition(); handlePointingAtObject(pointed, tool_item, player_position, show_debug); - } else if (input->getLeftState()) { + } else if (isKeyDown(KeyType::DIG)) { // When button is held down in air, show continuous animation - runData.left_punch = true; + runData.punching = true; // Run callback even though item is not usable - if (input->getLeftClicked() && client->modsLoaded()) + if (wasKeyPressed(KeyType::DIG) && client->modsLoaded()) client->getScript()->on_item_use(selected_item, pointed); - } else if (input->getRightClicked()) { + } else if (wasKeyPressed(KeyType::PLACE)) { handlePointingAtNothing(selected_item); } runData.pointed_old = pointed; - if (runData.left_punch || input->getLeftClicked()) - camera->setDigging(0); // left click animation + if (runData.punching || wasKeyPressed(KeyType::DIG)) + camera->setDigging(0); // dig animation + + input->clearWasKeyPressed(); + input->clearWasKeyReleased(); - input->resetLeftClicked(); - input->resetRightClicked(); + input->joystick.clearWasKeyDown(KeyType::DIG); + input->joystick.clearWasKeyDown(KeyType::PLACE); - input->resetLeftReleased(); - input->resetRightReleased(); + input->joystick.clearWasKeyReleased(KeyType::DIG); + input->joystick.clearWasKeyReleased(KeyType::PLACE); } @@ -2537,7 +2520,7 @@ PointedThing Game::updatePointedThing( void Game::handlePointingAtNothing(const ItemStack &playerItem) { - infostream << "Right Clicked in Air" << std::endl; + infostream << "Attempted to place item while pointing at nothing" << std::endl; PointedThing fauxPointed; fauxPointed.type = POINTEDTHING_NOTHING; client->interact(INTERACT_ACTIVATE, fauxPointed); @@ -2556,7 +2539,7 @@ void Game::handlePointingAtNode(const PointedThing &pointed, ClientMap &map = client->getEnv().getClientMap(); - if (((runData.nodig_delay_timer <= 0.0 || g_settings->getBool("fastdig")) && (input->getLeftState() || g_settings->getBool("autodig")) + if (((runData.nodig_delay_timer <= 0.0 || g_settings->getBool("fastdig")) && (isKeyDown(KeyType::DIG) || g_settings->getBool("autodig")) && !runData.digging_blocked && client->checkPrivilege("interact")) ) { @@ -2578,14 +2561,14 @@ void Game::handlePointingAtNode(const PointedThing &pointed, } } - if ((input->getRightState() || g_settings->getBool("autoplace")) && - (input->getRightClicked() || - (runData.repeat_rightclick_timer >= (g_settings->getBool("fastplace") ? 0 : m_repeat_right_click_time))) && + if (((wasKeyPressed(KeyType::PLACE) || g_settings->getBool("autoplace")) || + (runData.repeat_place_timer >= (g_settings->getBool("fastplace") ? 0 : m_repeat_place_time))) && client->checkPrivilege("interact")) { - runData.repeat_rightclick_timer = 0; - infostream << "Ground right-clicked" << std::endl; + runData.repeat_place_timer = 0; + infostream << "Place button pressed while looking at ground" << std::endl; - camera->setDigging(1); // right click animation (always shown for feedback) + // Placing animation (always shown for feedback) + camera->setDigging(1); soundmaker->m_player_rightpunch_sound = SimpleSoundSpec(); @@ -2636,7 +2619,7 @@ bool Game::nodePlacement(const ItemDefinition &selected_def, auto *&formspec = m_game_ui->updateFormspec(""); GUIFormSpecMenu::create(formspec, client, &input->joystick, fs_src, - txt_dst, client->getFormspecPrepend()); + txt_dst, client->getFormspecPrepend(), sound); formspec->setFormSpec(meta->getString("formspec"), inventoryloc); return false; @@ -2651,8 +2634,7 @@ bool Game::nodePlacement(const ItemDefinition &selected_def, } verbosestream << "Node placement prediction for " - << selected_def.name << " is " - << prediction << std::endl; + << selected_def.name << " is " << prediction << std::endl; v3s16 p = neighbourpos; // Place inside node itself if buildable_to @@ -2663,6 +2645,7 @@ bool Game::nodePlacement(const ItemDefinition &selected_def, } else { node = map.getNode(p, &is_valid_position); if (is_valid_position && !nodedef->get(node).buildable_to) { + soundmaker->m_player_rightpunch_sound = selected_def.sound_place_failed; // Report to server client->interact(INTERACT_PLACE, pointed); return false; @@ -2735,6 +2718,7 @@ bool Game::nodePlacement(const ItemDefinition &selected_def, pp = p + v3s16(0, -1, 0); if (!nodedef->get(map.getNode(pp)).walkable) { + soundmaker->m_player_rightpunch_sound = selected_def.sound_place_failed; // Report to server client->interact(INTERACT_PLACE, pointed); return false; @@ -2811,7 +2795,7 @@ void Game::handlePointingAtObject(const PointedThing &pointed, m_game_ui->setInfoText(infotext); - if (input->getLeftState() || g_settings->getBool("autohit")) { + if (isKeyDown(KeyType::DIG) || g_settings->getBool("autohit")) { bool do_punch = false; bool do_punch_damage = false; @@ -2821,12 +2805,12 @@ void Game::handlePointingAtObject(const PointedThing &pointed, runData.object_hit_delay_timer = object_hit_delay; } - if (input->getLeftClicked()) + if (wasKeyPressed(KeyType::DIG)) do_punch = true; if (do_punch) { - infostream << "Left-clicked object" << std::endl; - runData.left_punch = true; + infostream << "Punched object" << std::endl; + runData.punching = true; } if (do_punch_damage) { @@ -2842,8 +2826,8 @@ void Game::handlePointingAtObject(const PointedThing &pointed, client->interact(INTERACT_START_DIGGING, pointed); } } - } else if (input->getRightClicked()) { - infostream << "Right-clicked object" << std::endl; + } else if (wasKeyDown(KeyType::PLACE)) { + infostream << "Pressed place button while pointing at object" << std::endl; client->interact(INTERACT_PLACE, pointed); // place } } @@ -2893,7 +2877,7 @@ void Game::handleDigging(const PointedThing &pointed, const v3s16 &nodepos, return; client->interact(INTERACT_START_DIGGING, pointed); runData.digging = true; - runData.ldown_for_dig = true; + runData.btn_down_for_dig = true; } if (!runData.dig_instantly) { @@ -2987,10 +2971,9 @@ void Game::handleDigging(const PointedThing &pointed, const v3s16 &nodepos, client->setCrack(-1, nodepos); } - camera->setDigging(0); // left click animation + camera->setDigging(0); // Dig animation } - void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime, const CameraOrientation &cam) { @@ -3242,7 +3225,7 @@ void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime, /* Update minimap pos and rotation */ - if (mapper && m_game_ui->m_flags.show_minimap && m_game_ui->m_flags.show_hud) { + if (mapper && m_game_ui->m_flags.show_hud) { mapper->setPos(floatToInt(player->getPosition(), BS)); mapper->setAngle(player->getYaw()); } @@ -3250,7 +3233,27 @@ void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime, /* End scene */ - + if (++m_reset_HW_buffer_counter > 500) { + /* + Periodically remove all mesh HW buffers. + + Work around for a quirk in Irrlicht where a HW buffer is only + released after 20000 iterations (triggered from endScene()). + + Without this, all loaded but unused meshes will retain their HW + buffers for at least 5 minutes, at which point looking up the HW buffers + becomes a bottleneck and the framerate drops (as much as 30%). + + Tests showed that numbers between 50 and 1000 are good, so picked 500. + There are no other public Irrlicht APIs that allow interacting with the + HW buffers without tracking the status of every individual mesh. + + The HW buffers for _visible_ meshes will be reinitialized in the next frame. + */ + infostream << "Game::updateFrame(): Removing all HW buffers." << std::endl; + driver->removeAllHardwareBuffers(); + m_reset_HW_buffer_counter = 0; + } driver->endScene(); stats->drawtime = tt_draw.stop(true); @@ -3286,9 +3289,10 @@ inline void Game::limitFps(FpsControl *fps_timings, f32 *dtime) else fps_timings->busy_time = 0; - u32 frametime_min = 1000 / (g_menumgr.pausesGame() - ? g_settings->getFloat("pause_fps_max") - : g_settings->getFloat("fps_max")); + u32 frametime_min = 1000 / ( + device->isWindowFocused() && !g_menumgr.pausesGame() + ? g_settings->getFloat("fps_max") + : g_settings->getFloat("fps_max_unfocused")); if (fps_timings->busy_time < frametime_min) { fps_timings->sleep_time = frametime_min - fps_timings->busy_time; @@ -3354,7 +3358,7 @@ void Game::readSettings() m_cache_enable_fog = g_settings->getBool("enable_fog"); m_cache_mouse_sensitivity = g_settings->getFloat("mouse_sensitivity"); m_cache_joystick_frustum_sensitivity = g_settings->getFloat("joystick_frustum_sensitivity"); - m_repeat_right_click_time = g_settings->getFloat("repeat_rightclick_time"); + m_repeat_place_time = g_settings->getFloat("repeat_place_time"); m_cache_enable_noclip = g_settings->getBool("noclip"); m_cache_enable_free_move = g_settings->getBool("free_move"); @@ -3419,7 +3423,7 @@ void Game::showDeathFormspec() auto *&formspec = m_game_ui->getFormspecGUI(); GUIFormSpecMenu::create(formspec, client, &input->joystick, - fs_src, txt_dst, client->getFormspecPrepend()); + fs_src, txt_dst, client->getFormspecPrepend(), sound); formspec->setFocus("btn_respawn"); } @@ -3446,14 +3450,14 @@ void Game::showPauseMenu() "- %s: move backwards\n" "- %s: move left\n" "- %s: move right\n" - "- %s: jump/climb\n" - "- %s: sneak/go down\n" + "- %s: jump/climb up\n" + "- %s: dig/punch\n" + "- %s: place/use\n" + "- %s: sneak/climb down\n" "- %s: drop item\n" "- %s: inventory\n" "- %s: enderchest\n" "- Mouse: turn/look\n" - "- Mouse left: dig/punch\n" - "- Mouse right: place/use\n" "- Mouse wheel: select item\n" "- %s: chat\n" "- %s: Killaura\n" @@ -3470,6 +3474,8 @@ void Game::showPauseMenu() GET_KEY_NAME(keymap_left), GET_KEY_NAME(keymap_right), GET_KEY_NAME(keymap_jump), + GET_KEY_NAME(keymap_dig), + GET_KEY_NAME(keymap_place), GET_KEY_NAME(keymap_sneak), GET_KEY_NAME(keymap_drop), GET_KEY_NAME(keymap_inventory), @@ -3561,7 +3567,7 @@ void Game::showPauseMenu() auto *&formspec = m_game_ui->getFormspecGUI(); GUIFormSpecMenu::create(formspec, client, &input->joystick, - fs_src, txt_dst, client->getFormspecPrepend()); + fs_src, txt_dst, client->getFormspecPrepend(), sound); formspec->setFocus("btn_continue"); formspec->doPause = true; } diff --git a/src/client/game.h b/src/client/game.h index 51accc679..26117ab86 100644 --- a/src/client/game.h +++ b/src/client/game.h @@ -295,7 +295,7 @@ public: m_sound(sound), m_ndef(ndef), makes_footstep_sound(true), - m_player_step_timer(0), + m_player_step_timer(0.0f), m_player_jump_timer(0.0f) { } @@ -441,14 +441,13 @@ class GameGlobalShaderConstantSetter : public IShaderConstantSetter CachedVertexShaderSetting<float> m_animation_timer_vertex; CachedPixelShaderSetting<float> m_animation_timer_pixel; CachedPixelShaderSetting<float, 3> m_day_light; + CachedPixelShaderSetting<float, 4> m_star_color; CachedPixelShaderSetting<float, 3> m_eye_position_pixel; CachedVertexShaderSetting<float, 3> m_eye_position_vertex; CachedPixelShaderSetting<float, 3> m_minimap_yaw; CachedPixelShaderSetting<float, 3> m_camera_offset_pixel; CachedPixelShaderSetting<float, 3> m_camera_offset_vertex; CachedPixelShaderSetting<SamplerLayer_t> m_base_texture; - CachedPixelShaderSetting<SamplerLayer_t> m_normal_texture; - CachedPixelShaderSetting<SamplerLayer_t> m_texture_flags; Client *m_client; public: @@ -475,14 +474,13 @@ public: m_animation_timer_vertex("animationTimer"), m_animation_timer_pixel("animationTimer"), m_day_light("dayLight"), + m_star_color("starColor"), m_eye_position_pixel("eyePosition"), m_eye_position_vertex("eyePosition"), m_minimap_yaw("yawVec"), m_camera_offset_pixel("cameraOffset"), m_camera_offset_vertex("cameraOffset"), m_base_texture("baseTexture"), - m_normal_texture("normalTexture"), - m_texture_flags("textureFlags"), m_client(client) { g_settings->registerChangedCallback("enable_fog", settingsCallback, this); @@ -528,6 +526,10 @@ public: sunlight.b }; m_day_light.set(dnc, services); + video::SColorf star_color = m_sky->getCurrentStarColor(); + float clr[4] = {star_color.r, star_color.g, star_color.b, star_color.a}; + m_star_color.set(clr, services); + u32 animation_timer = porting::getTimeMs() % 1000000; float animation_timer_f = (float)animation_timer / 100000.f; m_animation_timer_vertex.set(&animation_timer_f, services); @@ -570,12 +572,8 @@ public: m_camera_offset_pixel.set(camera_offset_array, services); m_camera_offset_vertex.set(camera_offset_array, services); - SamplerLayer_t base_tex = 0, - normal_tex = 1, - flags_tex = 2; + SamplerLayer_t base_tex = 0; m_base_texture.set(&base_tex, services); - m_normal_texture.set(&normal_tex, services); - m_texture_flags.set(&flags_tex, services); } }; @@ -621,7 +619,6 @@ public: #endif /**************************************************************************** - ****************************************************************************/ const float object_hit_delay = 0.2; @@ -642,7 +639,8 @@ struct GameRunData { u16 new_playeritem; PointedThing pointed_old; bool digging; - bool ldown_for_dig; + bool punching; + bool btn_down_for_dig; bool dig_instantly; bool digging_blocked; bool left_punch; @@ -650,7 +648,7 @@ struct GameRunData { float nodig_delay_timer; float dig_time; float dig_time_complete; - float repeat_rightclick_timer; + float repeat_place_timer; float object_hit_delay_timer; float time_from_last_punch; ClientActiveObject *selected_object; @@ -802,6 +800,14 @@ public: { return input->wasKeyDown(k); } + inline bool wasKeyPressed(GameKeyType k) + { + return input->wasKeyPressed(k); + } + inline bool wasKeyReleased(GameKeyType k) + { + return input->wasKeyReleased(k); + } #ifdef __ANDROID__ void handleAndroidChatInput(); @@ -891,7 +897,6 @@ public: bool *reconnect_requested; scene::ISceneNode *skybox; - bool random_input; bool simple_singleplayer_mode; /* End 'cache' */ @@ -916,7 +921,7 @@ public: bool m_cache_enable_free_move; f32 m_cache_mouse_sensitivity; f32 m_cache_joystick_frustum_sensitivity; - f32 m_repeat_right_click_time; + f32 m_repeat_place_time; f32 m_cache_cam_smoothing; f32 m_cache_fog_start; @@ -929,6 +934,7 @@ public: CameraOrientation cam_view_target = { 0 }; CameraOrientation cam_view = { 0 }; + int m_reset_HW_buffer_counter = 0; #ifdef __ANDROID__ bool m_cache_hold_aux1; bool m_android_chat_open; diff --git a/src/client/gameui.cpp b/src/client/gameui.cpp index a9057052e..0c1da3915 100644 --- a/src/client/gameui.cpp +++ b/src/client/gameui.cpp @@ -64,17 +64,6 @@ void GameUI::init() m_guitext2 = gui::StaticText::add(guienv, L"", core::rect<s32>(0, 0, 0, 0), false, false, guiroot); - // At the middle of the screen - // Object infos are shown in this - m_guitext_info = gui::StaticText::add(guienv, L"", - core::rect<s32>(0, 0, 400, g_fontengine->getTextHeight() * 5 + 5) - + v2s32(100, 200), false, true, guiroot); - - // Status text (displays info when showing and hiding GUI stuff, etc.) - m_guitext_status = gui::StaticText::add(guienv, L"<Status>", - core::rect<s32>(0, 0, 0, 0), false, false, guiroot); - m_guitext_status->setVisible(false); - // Chat text m_guitext_chat = gui::StaticText::add(guienv, L"", core::rect<s32>(0, 0, 0, 0), //false, false); // Disable word wrap as of now @@ -85,6 +74,20 @@ void GameUI::init() chat_font_size, FM_Unspecified)); } + // At the middle of the screen + // Object infos are shown in this + u32 chat_font_height = m_guitext_chat->getActiveFont()->getDimension(L"Ay").Height; + m_guitext_info = gui::StaticText::add(guienv, L"", + core::rect<s32>(0, 0, 400, g_fontengine->getTextHeight() * 5 + 5) + + v2s32(100, chat_font_height * + (g_settings->getU16("recent_chat_messages") + 3)), + false, true, guiroot); + + // Status text (displays info when showing and hiding GUI stuff, etc.) + m_guitext_status = gui::StaticText::add(guienv, L"<Status>", + core::rect<s32>(0, 0, 0, 0), false, false, guiroot); + m_guitext_status->setVisible(false); + // Profiler text (size is updated when text is updated) m_guitext_profiler = gui::StaticText::add(guienv, L"<Profiler>", core::rect<s32>(0, 0, 0, 0), false, false, guiroot); @@ -132,8 +135,8 @@ void GameUI::update(const RunStats &stats, Client *client, MapDrawControl *draw_ << std::setprecision(1) << " | view range: " << (draw_control->range_all ? "All" : itos(draw_control->wanted_range)) - << std::setprecision(3) - << " | RTT: " << client->getRTT() << "s"; + << std::setprecision(2) + << " | RTT: " << (client->getRTT() * 1000.0f) << "ms"; setStaticText(m_guitext, utf8_to_wide(os.str()).c_str()); m_guitext->setRelativePosition(core::rect<s32>(5, 5, screensize.X, @@ -149,9 +152,9 @@ void GameUI::update(const RunStats &stats, Client *client, MapDrawControl *draw_ << "pos: (" << (player_position.X / BS) << ", " << (player_position.Y / BS) << ", " << (player_position.Z / BS) - << ") | yaw: " << (wrapDegrees_0_360(cam.camera_yaw)) << "\xC2\xB0 " + << ") | yaw: " << (wrapDegrees_0_360(cam.camera_yaw)) << "° " << yawToDirectionString(cam.camera_yaw) - << " | pitch: " << (-wrapDegrees_180(cam.camera_pitch)) << "\xC2\xB0" + << " | pitch: " << (-wrapDegrees_180(cam.camera_pitch)) << "°" << " | seed: " << ((u64)client->getMapSeed()); if (pointed_old.type == POINTEDTHING_NODE) { diff --git a/src/client/guiscalingfilter.cpp b/src/client/guiscalingfilter.cpp index 4262331bd..406c096e6 100644 --- a/src/client/guiscalingfilter.cpp +++ b/src/client/guiscalingfilter.cpp @@ -172,11 +172,8 @@ void draw2DImageFilterScaled(video::IVideoDriver *driver, video::ITexture *txr, void draw2DImage9Slice(video::IVideoDriver *driver, video::ITexture *texture, const core::rect<s32> &rect, const core::rect<s32> &middle, - const core::rect<s32> *cliprect) + const core::rect<s32> *cliprect, const video::SColor *const colors) { - const video::SColor color(255,255,255,255); - const video::SColor colors[] = {color,color,color,color}; - auto originalSize = texture->getOriginalSize(); core::vector2di lowerRightOffset = core::vector2di(originalSize.Width, originalSize.Height) - middle.LowerRightCorner; diff --git a/src/client/guiscalingfilter.h b/src/client/guiscalingfilter.h index b703d91f0..379a4bdb0 100644 --- a/src/client/guiscalingfilter.h +++ b/src/client/guiscalingfilter.h @@ -54,4 +54,5 @@ void draw2DImageFilterScaled(video::IVideoDriver *driver, video::ITexture *txr, */ void draw2DImage9Slice(video::IVideoDriver *driver, video::ITexture *texture, const core::rect<s32> &rect, const core::rect<s32> &middle, - const core::rect<s32> *cliprect = nullptr); + const core::rect<s32> *cliprect = nullptr, + const video::SColor *const colors = nullptr); diff --git a/src/client/hud.cpp b/src/client/hud.cpp index 2b347c1e0..f6497fe25 100644 --- a/src/client/hud.cpp +++ b/src/client/hud.cpp @@ -36,6 +36,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "mesh.h" #include "wieldmesh.h" #include "client/renderingengine.h" +#include "client/minimap.h" #ifdef HAVE_TOUCHSCREENGUI #include "gui/touchscreengui.h" @@ -114,6 +115,28 @@ Hud::Hud(gui::IGUIEnvironment *guienv, Client *client, LocalPlayer *player, } else { m_selection_material.MaterialType = video::EMT_SOLID; } + + // Prepare mesh for compass drawing + m_rotation_mesh_buffer.Vertices.set_used(4); + m_rotation_mesh_buffer.Indices.set_used(6); + + video::SColor white(255, 255, 255, 255); + v3f normal(0.f, 0.f, 1.f); + + m_rotation_mesh_buffer.Vertices[0] = video::S3DVertex(v3f(-1.f, -1.f, 0.f), normal, white, v2f(0.f, 1.f)); + m_rotation_mesh_buffer.Vertices[1] = video::S3DVertex(v3f(-1.f, 1.f, 0.f), normal, white, v2f(0.f, 0.f)); + m_rotation_mesh_buffer.Vertices[2] = video::S3DVertex(v3f( 1.f, 1.f, 0.f), normal, white, v2f(1.f, 0.f)); + m_rotation_mesh_buffer.Vertices[3] = video::S3DVertex(v3f( 1.f, -1.f, 0.f), normal, white, v2f(1.f, 1.f)); + + m_rotation_mesh_buffer.Indices[0] = 0; + m_rotation_mesh_buffer.Indices[1] = 1; + m_rotation_mesh_buffer.Indices[2] = 2; + m_rotation_mesh_buffer.Indices[3] = 2; + m_rotation_mesh_buffer.Indices[4] = 3; + m_rotation_mesh_buffer.Indices[5] = 0; + + m_rotation_mesh_buffer.getMaterial().Lighting = false; + m_rotation_mesh_buffer.getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; } Hud::~Hud() @@ -275,6 +298,18 @@ void Hud::drawItems(v2s32 upperleftpos, v2s32 screen_offset, s32 itemcount, } } +bool Hud::hasElementOfType(HudElementType type) +{ + for (size_t i = 0; i != player->maxHudId(); i++) { + HudElement *e = player->getHud(i); + if (!e) + continue; + if (e->type == type) + return true; + } + return false; +} + // Calculates screen position of waypoint. Returns true if waypoint is visible (in front of the player), else false. bool Hud::calculateScreenPos(const v3s16 &camera_offset, HudElement *e, v2s32 *pos) { @@ -423,6 +458,69 @@ void Hud::drawLuaElements(const v3s16 &camera_offset) core::rect<s32>(core::position2d<s32>(0,0), imgsize), NULL, colors, true); break; } + case HUD_ELEM_COMPASS: { + video::ITexture *texture = tsrc->getTexture(e->text); + if (!texture) + continue; + + // Positionning : + v2s32 dstsize(e->size.X, e->size.Y); + if (e->size.X < 0) + dstsize.X = m_screensize.X * (e->size.X * -0.01); + if (e->size.Y < 0) + dstsize.Y = m_screensize.Y * (e->size.Y * -0.01); + + if (dstsize.X <= 0 || dstsize.Y <= 0) + return; // Avoid zero divides + + // Angle according to camera view + v3f fore(0.f, 0.f, 1.f); + scene::ICameraSceneNode *cam = RenderingEngine::get_scene_manager()->getActiveCamera(); + cam->getAbsoluteTransformation().rotateVect(fore); + int angle = - fore.getHorizontalAngle().Y; + + // Limit angle and ajust with given offset + angle = (angle + (int)e->number) % 360; + + core::rect<s32> dstrect(0, 0, dstsize.X, dstsize.Y); + dstrect += pos + v2s32( + (e->align.X - 1.0) * dstsize.X / 2, + (e->align.Y - 1.0) * dstsize.Y / 2) + + v2s32(e->offset.X * m_hud_scaling, e->offset.Y * m_hud_scaling); + + switch (e->dir) { + case HUD_COMPASS_ROTATE: + drawCompassRotate(e, texture, dstrect, angle); + break; + case HUD_COMPASS_ROTATE_REVERSE: + drawCompassRotate(e, texture, dstrect, -angle); + break; + case HUD_COMPASS_TRANSLATE: + drawCompassTranslate(e, texture, dstrect, angle); + break; + case HUD_COMPASS_TRANSLATE_REVERSE: + drawCompassTranslate(e, texture, dstrect, -angle); + break; + default: + break; + } + break; } + case HUD_ELEM_MINIMAP: { + if (e->size.X <= 0 || e->size.Y <= 0) + break; + if (!client->getMinimap()) + break; + // Draw a minimap of size "size" + v2s32 dstsize(e->size.X * m_scale_factor, + e->size.Y * m_scale_factor); + // (no percent size as minimap would likely be anamorphosed) + v2s32 offset((e->align.X - 1.0) * dstsize.X / 2, + (e->align.Y - 1.0) * dstsize.Y / 2); + core::rect<s32> rect(0, 0, dstsize.X, dstsize.Y); + rect += pos + offset + v2s32(e->offset.X * m_scale_factor, + e->offset.Y * m_scale_factor); + client->getMinimap()->drawMinimap(rect); + break; } default: infostream << "Hud::drawLuaElements: ignoring drawform " << e->type << " of hud element ID " << i << " due to unrecognized type" << std::endl; @@ -430,6 +528,76 @@ void Hud::drawLuaElements(const v3s16 &camera_offset) } } +void Hud::drawCompassTranslate(HudElement *e, video::ITexture *texture, + const core::rect<s32> &rect, int angle) +{ + const video::SColor color(255, 255, 255, 255); + const video::SColor colors[] = {color, color, color, color}; + + // Compute source image scaling + core::dimension2di imgsize(texture->getOriginalSize()); + core::rect<s32> srcrect(0, 0, imgsize.Width, imgsize.Height); + + v2s32 dstsize(rect.getHeight() * e->scale.X * imgsize.Width / imgsize.Height, + rect.getHeight() * e->scale.Y); + + // Avoid infinite loop + if (dstsize.X <= 0 || dstsize.Y <= 0) + return; + + core::rect<s32> tgtrect(0, 0, dstsize.X, dstsize.Y); + tgtrect += v2s32( + (rect.getWidth() - dstsize.X) / 2, + (rect.getHeight() - dstsize.Y) / 2) + + rect.UpperLeftCorner; + + int offset = angle * dstsize.X / 360; + + tgtrect += v2s32(offset, 0); + + // Repeat image as much as needed + while (tgtrect.UpperLeftCorner.X > rect.UpperLeftCorner.X) + tgtrect -= v2s32(dstsize.X, 0); + + draw2DImageFilterScaled(driver, texture, tgtrect, srcrect, &rect, colors, true); + tgtrect += v2s32(dstsize.X, 0); + + while (tgtrect.UpperLeftCorner.X < rect.LowerRightCorner.X) { + draw2DImageFilterScaled(driver, texture, tgtrect, srcrect, &rect, colors, true); + tgtrect += v2s32(dstsize.X, 0); + } +} + +void Hud::drawCompassRotate(HudElement *e, video::ITexture *texture, + const core::rect<s32> &rect, int angle) +{ + core::dimension2di imgsize(texture->getOriginalSize()); + + core::rect<s32> oldViewPort = driver->getViewPort(); + core::matrix4 oldProjMat = driver->getTransform(video::ETS_PROJECTION); + core::matrix4 oldViewMat = driver->getTransform(video::ETS_VIEW); + + core::matrix4 Matrix; + Matrix.makeIdentity(); + Matrix.setRotationDegrees(v3f(0.f, 0.f, angle)); + + driver->setViewPort(rect); + driver->setTransform(video::ETS_PROJECTION, core::matrix4()); + driver->setTransform(video::ETS_VIEW, core::matrix4()); + driver->setTransform(video::ETS_WORLD, Matrix); + + video::SMaterial &material = m_rotation_mesh_buffer.getMaterial(); + material.TextureLayer[0].Texture = texture; + driver->setMaterial(material); + driver->drawMeshBuffer(&m_rotation_mesh_buffer); + + driver->setTransform(video::ETS_WORLD, core::matrix4()); + driver->setTransform(video::ETS_VIEW, oldViewMat); + driver->setTransform(video::ETS_PROJECTION, oldProjMat); + + // restore the view area + driver->setViewPort(oldViewPort); +} void Hud::drawStatbar(v2s32 pos, u16 corner, u16 drawdir, const std::string &texture, const std::string &bgtexture, diff --git a/src/client/hud.h b/src/client/hud.h index ba34d479d..d46545d71 100644 --- a/src/client/hud.h +++ b/src/client/hud.h @@ -81,6 +81,8 @@ public: m_selected_face_normal = face_normal; } + bool hasElementOfType(HudElementType type); + void drawLuaElements(const v3s16 &camera_offset); private: @@ -95,6 +97,12 @@ private: void drawItem(const ItemStack &item, const core::rect<s32> &rect, bool selected); + void drawCompassTranslate(HudElement *e, video::ITexture *texture, + const core::rect<s32> &rect, int way); + + void drawCompassRotate(HudElement *e, video::ITexture *texture, + const core::rect<s32> &rect, int way); + float m_hud_scaling; // cached minetest setting float m_scale_factor; v3s16 m_camera_offset; @@ -115,6 +123,8 @@ private: video::SMaterial m_selection_material; + scene::SMeshBuffer m_rotation_mesh_buffer; + enum { HIGHLIGHT_BOX, diff --git a/src/client/inputhandler.cpp b/src/client/inputhandler.cpp index 418e41931..37ab838c0 100644 --- a/src/client/inputhandler.cpp +++ b/src/client/inputhandler.cpp @@ -37,6 +37,8 @@ void KeyCache::populate() key[KeyType::JUMP] = getKeySetting("keymap_jump"); key[KeyType::SPECIAL1] = getKeySetting("keymap_special1"); key[KeyType::SNEAK] = getKeySetting("keymap_sneak"); + key[KeyType::DIG] = getKeySetting("keymap_dig"); + key[KeyType::PLACE] = getKeySetting("keymap_place"); key[KeyType::AUTOFORWARD] = getKeySetting("keymap_autoforward"); @@ -122,57 +124,81 @@ 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); + keyWasPressed.set(keyCode); + } keyIsDown.set(keyCode); - keyWasDown.set(keyCode); } else { + if (IsKeyDown(keyCode)) + keyWasReleased.set(keyCode); + keyIsDown.unset(keyCode); } + return true; } - } #ifdef HAVE_TOUCHSCREENGUI - // case of touchscreengui we have to handle different events - if (m_touchscreengui && event.EventType == irr::EET_TOUCH_INPUT_EVENT) { + } else if (m_touchscreengui && event.EventType == irr::EET_TOUCH_INPUT_EVENT) { + // In case of touchscreengui, we have to handle different events m_touchscreengui->translateEvent(event); return true; - } #endif - if (event.EventType == irr::EET_JOYSTICK_INPUT_EVENT) { + } else if (event.EventType == irr::EET_JOYSTICK_INPUT_EVENT) { /* TODO add a check like: if (event.JoystickEvent != joystick_we_listen_for) return false; */ return joystick->handleEvent(event.JoystickEvent); - } - // handle mouse events - if (event.EventType == irr::EET_MOUSE_INPUT_EVENT) { - if (isMenuActive()) { - left_active = false; - middle_active = false; - right_active = false; - } else { - left_active = event.MouseInput.isLeftPressed(); - middle_active = event.MouseInput.isMiddlePressed(); - right_active = event.MouseInput.isRightPressed(); - - if (event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) { - leftclicked = true; - } - if (event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN) { - rightclicked = true; - } - if (event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP) { - leftreleased = true; - } - if (event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP) { - rightreleased = true; - } - if (event.MouseInput.Event == EMIE_MOUSE_WHEEL) { - mouse_wheel += event.MouseInput.Wheel; - } + } else if (event.EventType == irr::EET_MOUSE_INPUT_EVENT) { + // Handle mouse events + KeyPress key; + switch (event.MouseInput.Event) { + case EMIE_LMOUSE_PRESSED_DOWN: + key = "KEY_LBUTTON"; + keyIsDown.set(key); + keyWasDown.set(key); + keyWasPressed.set(key); + break; + case EMIE_MMOUSE_PRESSED_DOWN: + key = "KEY_MBUTTON"; + keyIsDown.set(key); + keyWasDown.set(key); + keyWasPressed.set(key); + break; + case EMIE_RMOUSE_PRESSED_DOWN: + key = "KEY_RBUTTON"; + keyIsDown.set(key); + keyWasDown.set(key); + keyWasPressed.set(key); + break; + case EMIE_LMOUSE_LEFT_UP: + key = "KEY_LBUTTON"; + keyIsDown.unset(key); + keyWasReleased.set(key); + break; + case EMIE_MMOUSE_LEFT_UP: + key = "KEY_MBUTTON"; + keyIsDown.unset(key); + keyWasReleased.set(key); + break; + case EMIE_RMOUSE_LEFT_UP: + key = "KEY_RBUTTON"; + keyIsDown.unset(key); + keyWasReleased.set(key); + break; + case EMIE_MOUSE_WHEEL: + mouse_wheel += event.MouseInput.Wheel; + break; + default: break; } } else if (event.EventType == irr::EET_LOG_TEXT_EVENT) { static const LogLevel irr_loglev_conv[] = { @@ -199,38 +225,28 @@ s32 RandomInputHandler::Rand(s32 min, s32 max) return (myrand() % (max - min + 1)) + min; } +struct RandomInputHandlerSimData { + std::string key; + float counter; + int time_max; +}; + void RandomInputHandler::step(float dtime) { - { - static float counter1 = 0; - counter1 -= dtime; - if (counter1 < 0.0) { - counter1 = 0.1 * Rand(1, 40); - keydown.toggle(getKeySetting("keymap_jump")); - } - } - { - static float counter1 = 0; - counter1 -= dtime; - if (counter1 < 0.0) { - counter1 = 0.1 * Rand(1, 40); - keydown.toggle(getKeySetting("keymap_special1")); - } - } - { - static float counter1 = 0; - counter1 -= dtime; - if (counter1 < 0.0) { - counter1 = 0.1 * Rand(1, 40); - keydown.toggle(getKeySetting("keymap_forward")); - } - } - { - static float counter1 = 0; - counter1 -= dtime; - if (counter1 < 0.0) { - counter1 = 0.1 * Rand(1, 40); - keydown.toggle(getKeySetting("keymap_left")); + static RandomInputHandlerSimData rnd_data[] = { + { "keymap_jump", 0.0f, 40 }, + { "keymap_special1", 0.0f, 40 }, + { "keymap_forward", 0.0f, 40 }, + { "keymap_left", 0.0f, 40 }, + { "keymap_dig", 0.0f, 30 }, + { "keymap_place", 0.0f, 15 } + }; + + for (auto &i : rnd_data) { + i.counter -= dtime; + if (i.counter < 0.0) { + i.counter = 0.1 * Rand(1, i.time_max); + keydown.toggle(getKeySetting(i.key.c_str())); } } { @@ -241,29 +257,5 @@ void RandomInputHandler::step(float dtime) mousespeed = v2s32(Rand(-20, 20), Rand(-15, 20)); } } - { - static float counter1 = 0; - counter1 -= dtime; - if (counter1 < 0.0) { - counter1 = 0.1 * Rand(1, 30); - leftdown = !leftdown; - if (leftdown) - leftclicked = true; - if (!leftdown) - leftreleased = true; - } - } - { - static float counter1 = 0; - counter1 -= dtime; - if (counter1 < 0.0) { - counter1 = 0.1 * Rand(1, 15); - rightdown = !rightdown; - if (rightdown) - rightclicked = true; - if (!rightdown) - rightreleased = true; - } - } mousepos += mousespeed; } diff --git a/src/client/inputhandler.h b/src/client/inputhandler.h index e006affb2..b3a7d4ba3 100644 --- a/src/client/inputhandler.h +++ b/src/client/inputhandler.h @@ -144,6 +144,14 @@ public: return b; } + // Checks whether a key was just pressed. State will be cleared + // in the subsequent iteration of Game::processPlayerInteraction + bool WasKeyPressed(const KeyPress &keycode) const { return keyWasPressed[keycode]; } + + // Checks whether a key was just released. State will be cleared + // in the subsequent iteration of Game::processPlayerInteraction + bool WasKeyReleased(const KeyPress &keycode) const { return keyWasReleased[keycode]; } + void listenForKey(const KeyPress &keyCode) { keysListenedFor.set(keyCode); } void dontListenForKeys() { keysListenedFor.clear(); } @@ -158,17 +166,20 @@ public: { keyIsDown.clear(); keyWasDown.clear(); + keyWasPressed.clear(); + keyWasReleased.clear(); - leftclicked = false; - rightclicked = false; - leftreleased = false; - rightreleased = false; + mouse_wheel = 0; + } - left_active = false; - middle_active = false; - right_active = false; + void clearWasKeyPressed() + { + keyWasPressed.clear(); + } - mouse_wheel = 0; + void clearWasKeyReleased() + { + keyWasReleased.clear(); } MyEventReceiver() @@ -178,15 +189,6 @@ public: #endif } - bool leftclicked = false; - bool rightclicked = false; - bool leftreleased = false; - bool rightreleased = false; - - bool left_active = false; - bool middle_active = false; - bool right_active = false; - s32 mouse_wheel = 0; JoystickController *joystick = nullptr; @@ -197,8 +199,16 @@ public: // The current state of keys KeyList keyIsDown; - // Whether a key has been pressed or not + + // Whether a key was down KeyList keyWasDown; + + // Whether a key has just been pressed + KeyList keyWasPressed; + + // Whether a key has just been released + KeyList keyWasReleased; + // List of keys we listen for // TODO perhaps the type of this is not really // performant as KeyList is designed for few but @@ -227,27 +237,19 @@ public: virtual void setKeypress(const KeyPress &keyCode) = 0; virtual void unsetKeypress(const KeyPress &keyCode) = 0; virtual bool wasKeyDown(GameKeyType k) = 0; + virtual bool wasKeyPressed(GameKeyType k) = 0; + virtual bool wasKeyReleased(GameKeyType k) = 0; virtual bool cancelPressed() = 0; + virtual void clearWasKeyPressed() {} + virtual void clearWasKeyReleased() {} + virtual void listenForKey(const KeyPress &keyCode) {} virtual void dontListenForKeys() {} virtual v2s32 getMousePos() = 0; virtual void setMousePos(s32 x, s32 y) = 0; - virtual bool getLeftState() = 0; - virtual bool getRightState() = 0; - - virtual bool getLeftClicked() = 0; - virtual bool getRightClicked() = 0; - virtual void resetLeftClicked() = 0; - virtual void resetRightClicked() = 0; - - virtual bool getLeftReleased() = 0; - virtual bool getRightReleased() = 0; - virtual void resetLeftReleased() = 0; - virtual void resetRightReleased() = 0; - virtual s32 getMouseWheel() = 0; virtual void step(float dtime) {} @@ -285,10 +287,26 @@ public: { return m_receiver->WasKeyDown(keycache.key[k]) || joystick.wasKeyDown(k); } + virtual bool wasKeyPressed(GameKeyType k) + { + return m_receiver->WasKeyPressed(keycache.key[k]) || joystick.wasKeyReleased(k); + } + virtual bool wasKeyReleased(GameKeyType k) + { + return m_receiver->WasKeyReleased(keycache.key[k]) || joystick.wasKeyReleased(k); + } virtual bool cancelPressed() { return wasKeyDown(KeyType::ESC) || m_receiver->WasKeyDown(CancelKey); } + virtual void clearWasKeyPressed() + { + m_receiver->clearWasKeyPressed(); + } + virtual void clearWasKeyReleased() + { + m_receiver->clearWasKeyReleased(); + } virtual void listenForKey(const KeyPress &keyCode) { m_receiver->listenForKey(keyCode); @@ -316,59 +334,6 @@ public: } } - virtual bool getLeftState() - { - return m_receiver->left_active || joystick.isKeyDown(KeyType::MOUSE_L); - } - virtual bool getRightState() - { - return m_receiver->right_active || joystick.isKeyDown(KeyType::MOUSE_R); - } - - virtual bool getLeftClicked() - { - return m_receiver->leftclicked || - joystick.getWasKeyDown(KeyType::MOUSE_L); - } - virtual bool getRightClicked() - { - return m_receiver->rightclicked || - joystick.getWasKeyDown(KeyType::MOUSE_R); - } - - virtual void resetLeftClicked() - { - m_receiver->leftclicked = false; - joystick.clearWasKeyDown(KeyType::MOUSE_L); - } - virtual void resetRightClicked() - { - m_receiver->rightclicked = false; - joystick.clearWasKeyDown(KeyType::MOUSE_R); - } - - virtual bool getLeftReleased() - { - return m_receiver->leftreleased || - joystick.wasKeyReleased(KeyType::MOUSE_L); - } - virtual bool getRightReleased() - { - return m_receiver->rightreleased || - joystick.wasKeyReleased(KeyType::MOUSE_R); - } - - virtual void resetLeftReleased() - { - m_receiver->leftreleased = false; - joystick.clearWasKeyReleased(KeyType::MOUSE_L); - } - virtual void resetRightReleased() - { - m_receiver->rightreleased = false; - joystick.clearWasKeyReleased(KeyType::MOUSE_R); - } - virtual s32 getMouseWheel() { return m_receiver->getMouseWheel(); } void clear() @@ -402,23 +367,12 @@ public: keydown.unset(keyCode); } virtual bool wasKeyDown(GameKeyType k) { return false; } + virtual bool wasKeyPressed(GameKeyType k) { return false; } + virtual bool wasKeyReleased(GameKeyType k) { return false; } virtual bool cancelPressed() { return false; } virtual v2s32 getMousePos() { return mousepos; } virtual void setMousePos(s32 x, s32 y) { mousepos = v2s32(x, y); } - virtual bool getLeftState() { return leftdown; } - virtual bool getRightState() { return rightdown; } - - virtual bool getLeftClicked() { return leftclicked; } - virtual bool getRightClicked() { return rightclicked; } - virtual void resetLeftClicked() { leftclicked = false; } - virtual void resetRightClicked() { rightclicked = false; } - - virtual bool getLeftReleased() { return leftreleased; } - virtual bool getRightReleased() { return rightreleased; } - virtual void resetLeftReleased() { leftreleased = false; } - virtual void resetRightReleased() { rightreleased = false; } - virtual s32 getMouseWheel() { return 0; } virtual void step(float dtime); @@ -429,10 +383,4 @@ private: KeyList keydown; v2s32 mousepos; v2s32 mousespeed; - bool leftdown = false; - bool rightdown = false; - bool leftclicked = false; - bool rightclicked = false; - bool leftreleased = false; - bool rightreleased = false; }; diff --git a/src/client/joystick_controller.cpp b/src/client/joystick_controller.cpp index c29e8b639..742115046 100644 --- a/src/client/joystick_controller.cpp +++ b/src/client/joystick_controller.cpp @@ -74,8 +74,8 @@ JoystickLayout create_default_layout() // Accessible without four modifier button pressed // regardless whether start is pressed or not - JLO_B_PB(KeyType::MOUSE_L, fb | 1 << 4, 1 << 4); - JLO_B_PB(KeyType::MOUSE_R, fb | 1 << 5, 1 << 5); + JLO_B_PB(KeyType::DIG, fb | 1 << 4, 1 << 4); + JLO_B_PB(KeyType::PLACE, fb | 1 << 5, 1 << 5); // Accessible without any modifier pressed JLO_B_PB(KeyType::JUMP, bm | 1 << 0, 1 << 0); @@ -83,9 +83,9 @@ JoystickLayout create_default_layout() // Accessible with start button not pressed, but four pressed // TODO find usage for button 0 - JLO_B_PB(KeyType::DROP, bm | 1 << 1, fb | 1 << 1); - JLO_B_PB(KeyType::SCROLL_UP, bm | 1 << 4, fb | 1 << 4); - JLO_B_PB(KeyType::SCROLL_DOWN,bm | 1 << 5, fb | 1 << 5); + JLO_B_PB(KeyType::DROP, bm | 1 << 1, fb | 1 << 1); + JLO_B_PB(KeyType::HOTBAR_PREV, bm | 1 << 4, fb | 1 << 4); + JLO_B_PB(KeyType::HOTBAR_NEXT, bm | 1 << 5, fb | 1 << 5); // Accessible with start button and four pressed // TODO find usage for buttons 0, 1 and 4, 5 @@ -99,8 +99,8 @@ JoystickLayout create_default_layout() JLO_A_PB(KeyType::RIGHT, 0, -1, 1024); // Scroll buttons - JLO_A_PB(KeyType::SCROLL_UP, 2, -1, 1024); - JLO_A_PB(KeyType::SCROLL_DOWN, 5, -1, 1024); + JLO_A_PB(KeyType::HOTBAR_PREV, 2, -1, 1024); + JLO_A_PB(KeyType::HOTBAR_NEXT, 5, -1, 1024); return jlo; } @@ -134,10 +134,10 @@ JoystickLayout create_xbox_layout() JLO_B_PB(KeyType::SNEAK, 1 << 12, 1 << 12); // right // Triggers - JLO_B_PB(KeyType::MOUSE_L, 1 << 6, 1 << 6); // lt - JLO_B_PB(KeyType::MOUSE_R, 1 << 7, 1 << 7); // rt - JLO_B_PB(KeyType::SCROLL_UP, 1 << 4, 1 << 4); // lb - JLO_B_PB(KeyType::SCROLL_DOWN, 1 << 5, 1 << 5); // rb + JLO_B_PB(KeyType::DIG, 1 << 6, 1 << 6); // lt + JLO_B_PB(KeyType::PLACE, 1 << 7, 1 << 7); // rt + JLO_B_PB(KeyType::HOTBAR_PREV, 1 << 4, 1 << 4); // lb + JLO_B_PB(KeyType::HOTBAR_NEXT, 1 << 5, 1 << 5); // rb // D-PAD JLO_B_PB(KeyType::ZOOM, 1 << 15, 1 << 15); // up diff --git a/src/client/keys.h b/src/client/keys.h index 43a032a7b..828ea3ec4 100644 --- a/src/client/keys.h +++ b/src/client/keys.h @@ -35,6 +35,8 @@ public: SPECIAL1, SNEAK, AUTOFORWARD, + DIG, + PLACE, ESC, @@ -119,12 +121,6 @@ public: SLOT_31, SLOT_32, - // joystick specific keys - MOUSE_L, - MOUSE_R, - SCROLL_UP, - SCROLL_DOWN, - // Fake keycode for array size and internal checks INTERNAL_ENUM_COUNT diff --git a/src/client/localplayer.cpp b/src/client/localplayer.cpp index 3b52b18e1..7d532c602 100644 --- a/src/client/localplayer.cpp +++ b/src/client/localplayer.cpp @@ -19,7 +19,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "localplayer.h" #include <cmath> -#include "event.h" +#include "mtevent.h" #include "collision.h" #include "nodedef.h" #include "settings.h" diff --git a/src/client/mapblock_mesh.cpp b/src/client/mapblock_mesh.cpp index f65864599..7b342fc33 100644 --- a/src/client/mapblock_mesh.cpp +++ b/src/client/mapblock_mesh.cpp @@ -35,11 +35,9 @@ with this program; if not, write to the Free Software Foundation, Inc., MeshMakeData */ -MeshMakeData::MeshMakeData(Client *client, bool use_shaders, - bool use_tangent_vertices): +MeshMakeData::MeshMakeData(Client *client, bool use_shaders): m_client(client), - m_use_shaders(use_shaders), - m_use_tangent_vertices(use_tangent_vertices) + m_use_shaders(use_shaders) {} void MeshMakeData::fillBlockDataBegin(const v3s16 &blockpos) @@ -81,33 +79,6 @@ void MeshMakeData::fill(MapBlock *block) } } -void MeshMakeData::fillSingleNode(MapNode *node) -{ - m_blockpos = v3s16(0,0,0); - - v3s16 blockpos_nodes = v3s16(0,0,0); - VoxelArea area(blockpos_nodes-v3s16(1,1,1)*MAP_BLOCKSIZE, - blockpos_nodes+v3s16(1,1,1)*MAP_BLOCKSIZE*2-v3s16(1,1,1)); - s32 volume = area.getVolume(); - s32 our_node_index = area.index(1,1,1); - - // Allocate this block + neighbors - m_vmanip.clear(); - m_vmanip.addArea(area); - - // Fill in data - MapNode *data = new MapNode[volume]; - for(s32 i = 0; i < volume; i++) - { - if (i == our_node_index) - data[i] = *node; - else - data[i] = MapNode(CONTENT_AIR, LIGHT_MAX, 0); - } - m_vmanip.copyFrom(data, area, area.MinEdge, area.MinEdge, area.getExtent()); - delete[] data; -} - void MeshMakeData::setCrack(int crack_level, v3s16 crack_pos) { if (crack_level >= 0) @@ -422,7 +393,16 @@ static void getNodeVertexDirs(const v3s16 &dir, v3s16 *vertex_dirs) u8 idx = (dir.X + 2 * dir.Y + 3 * dir.Z) & 7; idx = (idx - 1) * 4; +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic push +#if __GNUC__ > 7 +#pragma GCC diagnostic ignored "-Wclass-memaccess" +#endif +#endif memcpy(vertex_dirs, &vertex_dirs_table[idx], 4 * sizeof(v3s16)); +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic pop +#endif } static void getNodeTextureCoords(v3f base, const v3f &scale, const v3s16 &dir, float *u, float *v) @@ -1076,10 +1056,9 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset): for (auto &m : m_mesh) m = new scene::SMesh(); m_enable_shaders = data->m_use_shaders; - m_use_tangent_vertices = data->m_use_tangent_vertices; m_enable_vbo = g_settings->getBool("enable_vbo"); - if (g_settings->getBool("enable_minimap")) { + if (data->m_client->getMinimap()) { m_minimap_mapblock = new MinimapMapblock; m_minimap_mapblock->getMinimapNodes( &data->m_vmanip, data->m_blockpos * MAP_BLOCKSIZE); @@ -1256,28 +1235,12 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset): scene::SMesh *mesh = (scene::SMesh *)m_mesh[layer]; - // Create meshbuffer, add to mesh - if (m_use_tangent_vertices) { - scene::SMeshBufferTangents *buf = - new scene::SMeshBufferTangents(); - buf->Material = material; - buf->Vertices.reallocate(p.vertices.size()); - buf->Indices.reallocate(p.indices.size()); - for (const video::S3DVertex &v: p.vertices) - buf->Vertices.push_back(video::S3DVertexTangents(v.Pos, v.Color, v.TCoords)); - for (u16 i: p.indices) - buf->Indices.push_back(i); - buf->recalculateBoundingBox(); - mesh->addMeshBuffer(buf); - buf->drop(); - } else { - scene::SMeshBuffer *buf = new scene::SMeshBuffer(); - buf->Material = material; - buf->append(&p.vertices[0], p.vertices.size(), - &p.indices[0], p.indices.size()); - mesh->addMeshBuffer(buf); - buf->drop(); - } + scene::SMeshBuffer *buf = new scene::SMeshBuffer(); + buf->Material = material; + buf->append(&p.vertices[0], p.vertices.size(), + &p.indices[0], p.indices.size()); + mesh->addMeshBuffer(buf); + buf->drop(); } /* @@ -1287,12 +1250,6 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset): translateMesh(m_mesh[layer], intToFloat(data->m_blockpos * MAP_BLOCKSIZE - camera_offset, BS)); - if (m_use_tangent_vertices) { - scene::IMeshManipulator* meshmanip = - RenderingEngine::get_scene_manager()->getMeshManipulator(); - meshmanip->recalculateTangents(m_mesh[layer], true, false, false); - } - if (m_mesh[layer]) { #if 0 // Usually 1-700 faces and 1-7 materials diff --git a/src/client/mapblock_mesh.h b/src/client/mapblock_mesh.h index 95fda83e5..ec1faea32 100644 --- a/src/client/mapblock_mesh.h +++ b/src/client/mapblock_mesh.h @@ -45,10 +45,8 @@ struct MeshMakeData Client *m_client; bool m_use_shaders; - bool m_use_tangent_vertices; - MeshMakeData(Client *client, bool use_shaders, - bool use_tangent_vertices = false); + MeshMakeData(Client *client, bool use_shaders); /* Copy block data manually (to allow optimizations by the caller) @@ -63,11 +61,6 @@ struct MeshMakeData void fill(MapBlock *block); /* - Set up with only a single node at (1,1,1) - */ - void fillSingleNode(MapNode *node); - - /* Set the (node) position of a crack */ void setCrack(int crack_level, v3s16 crack_pos); @@ -143,7 +136,6 @@ private: IShaderSource *m_shdrsrc; bool m_enable_shaders; - bool m_use_tangent_vertices; bool m_enable_vbo; // Must animate() be called before rendering? diff --git a/src/client/mesh.cpp b/src/client/mesh.cpp index e1ec22068..2400a374c 100644 --- a/src/client/mesh.cpp +++ b/src/client/mesh.cpp @@ -203,6 +203,15 @@ void setMeshColor(scene::IMesh *mesh, const video::SColor &color) setMeshBufferColor(mesh->getMeshBuffer(j), color); } +void setMeshBufferTextureCoords(scene::IMeshBuffer *buf, const v2f *uv, u32 count) +{ + const u32 stride = getVertexPitchFromType(buf->getVertexType()); + assert(buf->getVertexCount() >= count); + u8 *vertices = (u8 *) buf->getVertices(); + for (u32 i = 0; i < count; i++) + ((video::S3DVertex*) (vertices + i * stride))->TCoords = uv[i]; +} + template <typename F> static void applyToMesh(scene::IMesh *mesh, const F &fn) { diff --git a/src/client/mesh.h b/src/client/mesh.h index 103c61e45..dbc091a06 100644 --- a/src/client/mesh.h +++ b/src/client/mesh.h @@ -58,6 +58,13 @@ void setMeshBufferColor(scene::IMeshBuffer *buf, const video::SColor &color); */ void setMeshColor(scene::IMesh *mesh, const video::SColor &color); + +/* + Sets texture coords for vertices in the mesh buffer. + `uv[]` must have `count` elements +*/ +void setMeshBufferTextureCoords(scene::IMeshBuffer *buf, const v2f *uv, u32 count); + /* Set a constant color for an animated mesh */ diff --git a/src/client/mesh_generator_thread.cpp b/src/client/mesh_generator_thread.cpp index 53b980eeb..c8d1cba26 100644 --- a/src/client/mesh_generator_thread.cpp +++ b/src/client/mesh_generator_thread.cpp @@ -52,9 +52,6 @@ MeshUpdateQueue::MeshUpdateQueue(Client *client): m_client(client) { m_cache_enable_shaders = g_settings->getBool("enable_shaders"); - m_cache_use_tangent_vertices = m_cache_enable_shaders && ( - g_settings->getBool("enable_bumpmapping") || - g_settings->getBool("enable_parallax_occlusion")); m_cache_smooth_lighting = g_settings->getBool("smooth_lighting"); m_meshgen_block_cache_size = g_settings->getS32("meshgen_block_cache_size"); } @@ -207,8 +204,7 @@ CachedMapBlockData* MeshUpdateQueue::getCachedBlock(const v3s16 &p) void MeshUpdateQueue::fillDataFromMapBlockCache(QueuedMeshUpdate *q) { - MeshMakeData *data = new MeshMakeData(m_client, m_cache_enable_shaders, - m_cache_use_tangent_vertices); + MeshMakeData *data = new MeshMakeData(m_client, m_cache_enable_shaders); q->data = data; data->fillBlockDataBegin(q->p); diff --git a/src/client/mesh_generator_thread.h b/src/client/mesh_generator_thread.h index 2bb74589e..8e13dec3e 100644 --- a/src/client/mesh_generator_thread.h +++ b/src/client/mesh_generator_thread.h @@ -88,7 +88,6 @@ private: // TODO: Add callback to update these when g_settings changes bool m_cache_enable_shaders; - bool m_cache_use_tangent_vertices; bool m_cache_smooth_lighting; int m_meshgen_block_cache_size; diff --git a/src/client/minimap.cpp b/src/client/minimap.cpp index 8ea1a0283..60b095712 100644 --- a/src/client/minimap.cpp +++ b/src/client/minimap.cpp @@ -25,7 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "shader.h" #include "mapblock.h" #include "client/renderingengine.h" - +#include "gettext.h" //// //// MinimapUpdateThread @@ -108,8 +108,11 @@ void MinimapUpdateThread::doUpdate() } } - if (data->map_invalidated && data->mode != MINIMAP_MODE_OFF) { - getMap(data->pos, data->map_size, data->scan_height); + + if (data->map_invalidated && ( + data->mode.type == MINIMAP_TYPE_RADAR || + data->mode.type == MINIMAP_TYPE_SURFACE)) { + getMap(data->pos, data->mode.map_size, data->mode.scan_height); data->map_invalidated = false; } } @@ -181,19 +184,26 @@ Minimap::Minimap(Client *client) this->m_ndef = client->getNodeDefManager(); m_angle = 0.f; + m_current_mode_index = 0; // Initialize static settings m_enable_shaders = g_settings->getBool("enable_shaders"); m_surface_mode_scan_height = g_settings->getBool("minimap_double_scan_height") ? 256 : 128; + // Initialize minimap modes + addMode(MINIMAP_TYPE_OFF); + addMode(MINIMAP_TYPE_SURFACE, 256); + addMode(MINIMAP_TYPE_SURFACE, 128); + addMode(MINIMAP_TYPE_SURFACE, 64); + addMode(MINIMAP_TYPE_RADAR, 512); + addMode(MINIMAP_TYPE_RADAR, 256); + addMode(MINIMAP_TYPE_RADAR, 128); + // Initialize minimap data data = new MinimapData; - data->mode = MINIMAP_MODE_OFF; - data->is_radar = false; - data->map_invalidated = true; - data->texture = NULL; - data->heightmap_texture = NULL; + data->map_invalidated = true; + data->minimap_shape_round = g_settings->getBool("minimap_shape_round"); // Get round minimap textures @@ -215,6 +225,8 @@ Minimap::Minimap(Client *client) // Create object marker texture data->object_marker_red = m_tsrc->getTexture("object_marker_red.png"); + setModeIndex(0); + // Create mesh buffer for minimap m_meshbuffer = getMinimapMeshBuffer(); @@ -240,6 +252,10 @@ Minimap::~Minimap() driver->removeTexture(data->minimap_overlay_square); driver->removeTexture(data->object_marker_red); + for (MinimapMarker *m : m_markers) + delete m; + m_markers.clear(); + delete data; delete m_minimap_update_thread; } @@ -280,29 +296,101 @@ MinimapShape Minimap::getMinimapShape() return MINIMAP_SHAPE_SQUARE; } -void Minimap::setMinimapMode(MinimapMode mode) +void Minimap::setModeIndex(size_t index) { - static const MinimapModeDef modedefs[MINIMAP_MODE_COUNT] = { - {false, 0, 0}, - {false, m_surface_mode_scan_height, 256}, - {false, m_surface_mode_scan_height, 128}, - {false, m_surface_mode_scan_height, 64}, - {true, 32, 128}, - {true, 32, 64}, - {true, 32, 32} - }; - - if (mode >= MINIMAP_MODE_COUNT) - return; - MutexAutoLock lock(m_mutex); - data->is_radar = modedefs[mode].is_radar; - data->scan_height = modedefs[mode].scan_height; - data->map_size = modedefs[mode].map_size; - data->mode = mode; + if (index < m_modes.size()) { + data->mode = m_modes[index]; + m_current_mode_index = index; + } else { + data->mode = MinimapModeDef{MINIMAP_TYPE_OFF, N_("Minimap hidden"), 0, 0, ""}; + m_current_mode_index = 0; + } - m_minimap_update_thread->deferUpdate(); + data->map_invalidated = true; + + if (m_minimap_update_thread) + m_minimap_update_thread->deferUpdate(); +} + +void Minimap::addMode(MinimapModeDef mode) +{ + // Check validity + if (mode.type == MINIMAP_TYPE_TEXTURE) { + if (mode.texture.empty()) + return; + if (mode.scale < 1) + mode.scale = 1; + } + + int zoom = -1; + + // Build a default standard label + if (mode.label == "") { + switch (mode.type) { + case MINIMAP_TYPE_OFF: + mode.label = N_("Minimap hidden"); + break; + case MINIMAP_TYPE_SURFACE: + mode.label = N_("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"); + if (mode.map_size > 0) + zoom = 512 / mode.map_size; + break; + case MINIMAP_TYPE_TEXTURE: + mode.label = N_("Minimap in texture mode"); + break; + default: + break; + } + } + + if (zoom >= 0) { + char label_buf[1024]; + porting::mt_snprintf(label_buf, sizeof(label_buf), + mode.label.c_str(), zoom); + mode.label = label_buf; + } + + m_modes.push_back(mode); +} + +void Minimap::addMode(MinimapType type, u16 size, std::string label, + std::string texture, u16 scale) +{ + MinimapModeDef mode; + mode.type = type; + mode.label = label; + mode.map_size = size; + mode.texture = texture; + mode.scale = scale; + switch (type) { + case MINIMAP_TYPE_SURFACE: + mode.scan_height = m_surface_mode_scan_height; + break; + case MINIMAP_TYPE_RADAR: + mode.scan_height = 32; + break; + default: + mode.scan_height = 0; + } + addMode(mode); +} + +void Minimap::nextMode() +{ + if (m_modes.empty()) + return; + m_current_mode_index++; + if (m_current_mode_index >= m_modes.size()) + m_current_mode_index = 0; + + setModeIndex(m_current_mode_index); } void Minimap::setPos(v3s16 pos) @@ -331,16 +419,16 @@ void Minimap::setAngle(f32 angle) void Minimap::blitMinimapPixelsToImageRadar(video::IImage *map_image) { video::SColor c(240, 0, 0, 0); - for (s16 x = 0; x < data->map_size; x++) - for (s16 z = 0; z < data->map_size; z++) { - MinimapPixel *mmpixel = &data->minimap_scan[x + z * data->map_size]; + for (s16 x = 0; x < data->mode.map_size; x++) + for (s16 z = 0; z < data->mode.map_size; z++) { + MinimapPixel *mmpixel = &data->minimap_scan[x + z * data->mode.map_size]; if (mmpixel->air_count > 0) c.setGreen(core::clamp(core::round32(32 + mmpixel->air_count * 8), 0, 255)); else c.setGreen(0); - map_image->setPixel(x, data->map_size - z - 1, c); + map_image->setPixel(x, data->mode.map_size - z - 1, c); } } @@ -349,9 +437,9 @@ void Minimap::blitMinimapPixelsToImageSurface( { // This variable creation/destruction has a 1% cost on rendering minimap video::SColor tilecolor; - for (s16 x = 0; x < data->map_size; x++) - for (s16 z = 0; z < data->map_size; z++) { - MinimapPixel *mmpixel = &data->minimap_scan[x + z * data->map_size]; + for (s16 x = 0; x < data->mode.map_size; x++) + for (s16 z = 0; z < data->mode.map_size; z++) { + MinimapPixel *mmpixel = &data->minimap_scan[x + z * data->mode.map_size]; const ContentFeatures &f = m_ndef->get(mmpixel->n); const TileDef *tile = &f.tiledef[0]; @@ -367,10 +455,10 @@ void Minimap::blitMinimapPixelsToImageSurface( tilecolor.setBlue(tilecolor.getBlue() * f.minimap_color.getBlue() / 255); tilecolor.setAlpha(240); - map_image->setPixel(x, data->map_size - z - 1, tilecolor); + map_image->setPixel(x, data->mode.map_size - z - 1, tilecolor); u32 h = mmpixel->height; - heightmap_image->setPixel(x,data->map_size - z - 1, + heightmap_image->setPixel(x,data->mode.map_size - z - 1, video::SColor(255, h, h, h)); } } @@ -378,21 +466,46 @@ void Minimap::blitMinimapPixelsToImageSurface( video::ITexture *Minimap::getMinimapTexture() { // update minimap textures when new scan is ready - if (data->map_invalidated) + if (data->map_invalidated && data->mode.type != MINIMAP_TYPE_TEXTURE) return data->texture; // create minimap and heightmap images in memory - core::dimension2d<u32> dim(data->map_size, data->map_size); + core::dimension2d<u32> dim(data->mode.map_size, data->mode.map_size); video::IImage *map_image = driver->createImage(video::ECF_A8R8G8B8, dim); video::IImage *heightmap_image = driver->createImage(video::ECF_A8R8G8B8, dim); video::IImage *minimap_image = driver->createImage(video::ECF_A8R8G8B8, core::dimension2d<u32>(MINIMAP_MAX_SX, MINIMAP_MAX_SY)); // Blit MinimapPixels to images - if (data->is_radar) - blitMinimapPixelsToImageRadar(map_image); - else + switch(data->mode.type) { + case MINIMAP_TYPE_OFF: + break; + case MINIMAP_TYPE_SURFACE: blitMinimapPixelsToImageSurface(map_image, heightmap_image); + break; + case MINIMAP_TYPE_RADAR: + blitMinimapPixelsToImageRadar(map_image); + break; + case MINIMAP_TYPE_TEXTURE: + // Want to use texture source, to : 1 find texture, 2 cache it + video::ITexture* texture = m_tsrc->getTexture(data->mode.texture); + video::IImage* image = driver->createImageFromData( + texture->getColorFormat(), texture->getSize(), texture->lock(), true, false); + texture->unlock(); + + auto dim = image->getDimension(); + + map_image->fill(video::SColor(255, 0, 0, 0)); + + image->copyTo(map_image, + irr::core::vector2d<int> { + ((data->mode.map_size - (static_cast<int>(dim.Width))) >> 1) + - data->pos.X / data->mode.scale, + ((data->mode.map_size - (static_cast<int>(dim.Height))) >> 1) + + data->pos.Z / data->mode.scale + }); + image->drop(); + } map_image->copyToScaling(minimap_image); map_image->drop(); @@ -461,21 +574,34 @@ scene::SMeshBuffer *Minimap::getMinimapMeshBuffer() void Minimap::drawMinimap() { + // Non hud managed minimap drawing (legacy minimap) + v2u32 screensize = RenderingEngine::get_instance()->getWindowSize(); + const u32 size = 0.25 * screensize.Y; + + drawMinimap(core::rect<s32>( + screensize.X - size - 10, 10, + screensize.X - 10, size + 10)); +} + +void Minimap::drawMinimap(core::rect<s32> rect) { + video::ITexture *minimap_texture = getMinimapTexture(); if (!minimap_texture) return; + if (data->mode.type == MINIMAP_TYPE_OFF) + return; + updateActiveMarkers(); - v2u32 screensize = RenderingEngine::get_instance()->getWindowSize(); - const u32 size = 0.25 * screensize.Y; core::rect<s32> oldViewPort = driver->getViewPort(); core::matrix4 oldProjMat = driver->getTransform(video::ETS_PROJECTION); core::matrix4 oldViewMat = driver->getTransform(video::ETS_VIEW); - driver->setViewPort(core::rect<s32>( - screensize.X - size * 2 - 10, 10, - screensize.X - size - 10, size + 10)); +// driver->setViewPort(core::rect<s32>( +// screensize.X - size * 2 - 10, 10, +// screensize.X - size - 10, size + 10)); + driver->setViewPort(rect); driver->setTransform(video::ETS_PROJECTION, core::matrix4()); driver->setTransform(video::ETS_VIEW, core::matrix4()); @@ -488,7 +614,7 @@ void Minimap::drawMinimap() material.TextureLayer[0].Texture = minimap_texture; material.TextureLayer[1].Texture = data->heightmap_texture; - if (m_enable_shaders && !data->is_radar) { + if (m_enable_shaders && data->mode.type == MINIMAP_TYPE_SURFACE) { u16 sid = m_shdrsrc->getShader("minimap_shader", 1, 1); material.MaterialType = m_shdrsrc->getShaderInfo(sid).material; } else { @@ -529,14 +655,15 @@ void Minimap::drawMinimap() driver->setViewPort(oldViewPort); // Draw player markers - v2s32 s_pos(screensize.X - size * 2 - 10, 10); +// v2s32 s_pos(screensize.X - size * 2 - 10, 10); + v2s32 s_pos(rect.UpperLeftCorner.X, rect.UpperLeftCorner.Y); core::dimension2di imgsize(data->object_marker_red->getOriginalSize()); core::rect<s32> img_rect(0, 0, imgsize.Width, imgsize.Height); static const video::SColor col(255, 255, 255, 255); static const video::SColor c[4] = {col, col, col, col}; f32 sin_angle = std::sin(m_angle * core::DEGTORAD); f32 cos_angle = std::cos(m_angle * core::DEGTORAD); - s32 marker_size2 = 0.025 * (float)size; + s32 marker_size2 = 0.025 * (float)rect.getWidth();; for (std::list<v2f>::const_iterator i = m_active_markers.begin(); i != m_active_markers.end(); ++i) { @@ -547,8 +674,8 @@ void Minimap::drawMinimap() posf.X = t1; posf.Y = t2; } - posf.X = (posf.X + 0.5) * (float)size; - posf.Y = (posf.Y + 0.5) * (float)size; + posf.X = (posf.X + 0.5) * (float)rect.getWidth(); + posf.Y = (posf.Y + 0.5) * (float)rect.getHeight(); core::rect<s32> dest_rect( s_pos.X + posf.X - marker_size2, s_pos.Y + posf.Y - marker_size2, @@ -559,28 +686,41 @@ void Minimap::drawMinimap() } } +MinimapMarker* Minimap::addMarker(scene::ISceneNode *parent_node) +{ + MinimapMarker *m = new MinimapMarker(parent_node); + m_markers.push_back(m); + return m; +} + +void Minimap::removeMarker(MinimapMarker **m) +{ + m_markers.remove(*m); + delete *m; + *m = nullptr; +} + void Minimap::updateActiveMarkers() { video::IImage *minimap_mask = data->minimap_shape_round ? data->minimap_mask_round : data->minimap_mask_square; - const std::list<Nametag *> &nametags = client->getCamera()->getNametags(); - m_active_markers.clear(); - - for (Nametag *nametag : nametags) { - v3s16 pos = floatToInt(nametag->parent_node->getAbsolutePosition() + - intToFloat(client->getCamera()->getOffset(), BS), BS); - pos -= data->pos - v3s16(data->map_size / 2, - data->scan_height / 2, - data->map_size / 2); - if (pos.X < 0 || pos.X > data->map_size || - pos.Y < 0 || pos.Y > data->scan_height || - pos.Z < 0 || pos.Z > data->map_size) { + v3f cam_offset = intToFloat(client->getCamera()->getOffset(), BS); + v3s16 pos_offset = data->pos - v3s16(data->mode.map_size / 2, + data->mode.scan_height / 2, + data->mode.map_size / 2); + + for (MinimapMarker *marker : m_markers) { + v3s16 pos = floatToInt(marker->parent_node->getAbsolutePosition() + + cam_offset, BS) - pos_offset; + if (pos.X < 0 || pos.X > data->mode.map_size || + pos.Y < 0 || pos.Y > data->mode.scan_height || + pos.Z < 0 || pos.Z > data->mode.map_size) { continue; } - pos.X = ((float)pos.X / data->map_size) * MINIMAP_MAX_SX; - pos.Z = ((float)pos.Z / data->map_size) * MINIMAP_MAX_SY; + pos.X = ((float)pos.X / data->mode.map_size) * MINIMAP_MAX_SX; + pos.Z = ((float)pos.Z / data->mode.map_size) * MINIMAP_MAX_SY; const video::SColor &mask_col = minimap_mask->getPixel(pos.X, pos.Z); if (!mask_col.getAlpha()) { continue; diff --git a/src/client/minimap.h b/src/client/minimap.h index 258d5330d..4a2c462f8 100644 --- a/src/client/minimap.h +++ b/src/client/minimap.h @@ -19,6 +19,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #pragma once +#include "../hud.h" #include "irrlichttypes_extrabloated.h" #include "util/thread.h" #include "voxel.h" @@ -33,28 +34,27 @@ class IShaderSource; #define MINIMAP_MAX_SX 512 #define MINIMAP_MAX_SY 512 -enum MinimapMode { - MINIMAP_MODE_OFF, - MINIMAP_MODE_SURFACEx1, - MINIMAP_MODE_SURFACEx2, - MINIMAP_MODE_SURFACEx4, - MINIMAP_MODE_RADARx1, - MINIMAP_MODE_RADARx2, - MINIMAP_MODE_RADARx4, - MINIMAP_MODE_COUNT, -}; - enum MinimapShape { MINIMAP_SHAPE_SQUARE, MINIMAP_SHAPE_ROUND, }; struct MinimapModeDef { - bool is_radar; + MinimapType type; + std::string label; u16 scan_height; u16 map_size; + std::string texture; + u16 scale; }; +struct MinimapMarker { + MinimapMarker(scene::ISceneNode *parent_node): + parent_node(parent_node) + { + } + scene::ISceneNode *parent_node; +}; struct MinimapPixel { //! The topmost node that the minimap displays. MapNode n; @@ -69,12 +69,9 @@ struct MinimapMapblock { }; struct MinimapData { - bool is_radar; - MinimapMode mode; + MinimapModeDef mode; v3s16 pos; v3s16 old_pos; - u16 scan_height; - u16 map_size; MinimapPixel minimap_scan[MINIMAP_MAX_SX * MINIMAP_MAX_SY]; bool map_invalidated; bool minimap_shape_round; @@ -127,12 +124,22 @@ public: v3s16 getPos() const { return data->pos; } void setAngle(f32 angle); f32 getAngle() const { return m_angle; } - void setMinimapMode(MinimapMode mode); - MinimapMode getMinimapMode() const { return data->mode; } void toggleMinimapShape(); void setMinimapShape(MinimapShape shape); MinimapShape getMinimapShape(); + void clearModes() { m_modes.clear(); }; + void addMode(MinimapModeDef mode); + void addMode(MinimapType type, u16 size = 0, std::string label = "", + std::string texture = "", u16 scale = 1); + + void setModeIndex(size_t index); + size_t getModeIndex() const { return m_current_mode_index; }; + size_t getMaxModeIndex() const { return m_modes.size() - 1; }; + void nextMode(); + + void setModesFromString(std::string modes_string); + MinimapModeDef getModeDef() const { return data->mode; } video::ITexture *getMinimapTexture(); @@ -142,8 +149,12 @@ public: scene::SMeshBuffer *getMinimapMeshBuffer(); + MinimapMarker* addMarker(scene::ISceneNode *parent_node); + void removeMarker(MinimapMarker **marker); + void updateActiveMarkers(); void drawMinimap(); + void drawMinimap(core::rect<s32> rect); video::IVideoDriver *driver; Client* client; @@ -153,11 +164,14 @@ private: ITextureSource *m_tsrc; IShaderSource *m_shdrsrc; const NodeDefManager *m_ndef; - MinimapUpdateThread *m_minimap_update_thread; + MinimapUpdateThread *m_minimap_update_thread = nullptr; scene::SMeshBuffer *m_meshbuffer; bool m_enable_shaders; + std::vector<MinimapModeDef> m_modes; + size_t m_current_mode_index; u16 m_surface_mode_scan_height; f32 m_angle; std::mutex m_mutex; + std::list<MinimapMarker*> m_markers; std::list<v2f> m_active_markers; }; diff --git a/src/client/render/core.cpp b/src/client/render/core.cpp index 1fad4af52..44e3ed744 100644 --- a/src/client/render/core.cpp +++ b/src/client/render/core.cpp @@ -123,34 +123,30 @@ void RenderingCore::drawTracersAndESP() } if (draw_node_esp || draw_node_tracers) { Map &map = env.getMap(); - std::map<v2s16, MapSector*> *sectors = map.getSectorsPtr(); - - for (auto §or_it : *sectors) { - MapSector *sector = sector_it.second; - MapBlockVect blocks; - sector->getBlocks(blocks); - for (MapBlock *block : blocks) { - if (! block->mesh) - continue; - for (v3s16 p : block->mesh->esp_nodes) { - v3f pos = intToFloat(p, BS) - camera_offset; - MapNode node = map.getNode(p); - std::vector<aabb3f> boxes; - node.getSelectionBoxes(client->getNodeDefManager(), &boxes, node.getNeighbors(p, &map)); - video::SColor color = client->getNodeDefManager()->get(node).minimap_color; - - for (aabb3f box : boxes) { - box.MinEdge += pos; - box.MaxEdge += pos; - if (draw_node_esp) - driver->draw3DBox(box, color); - if (draw_node_tracers) - driver->draw3DLine(eye_pos, box.getCenter(), color); - } + + std::vector<v3s16> positions; + map.listAllLoadedBlocks(positions); + + for (v3s16 blockp : positions) { + MapBlock *block = map.getBlockNoCreate(blockp); + if (! block->mesh) + continue; + for (v3s16 p : block->mesh->esp_nodes) { + v3f pos = intToFloat(p, BS) - camera_offset; + MapNode node = map.getNode(p); + std::vector<aabb3f> boxes; + node.getSelectionBoxes(client->getNodeDefManager(), &boxes, node.getNeighbors(p, &map)); + video::SColor color = client->getNodeDefManager()->get(node).minimap_color; + for (aabb3f box : boxes) { + box.MinEdge += pos; + box.MaxEdge += pos; + if (draw_node_esp) + driver->draw3DBox(box, color); + if (draw_node_tracers) + driver->draw3DLine(eye_pos, box.getCenter(), color); } } } - } driver->setMaterial(oldmaterial); diff --git a/src/client/shader.cpp b/src/client/shader.cpp index ee6079f7a..1cec20d2c 100644 --- a/src/client/shader.cpp +++ b/src/client/shader.cpp @@ -37,6 +37,22 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "log.h" #include "gamedef.h" #include "client/tile.h" +#include "config.h" + +#if ENABLE_GLES +#ifdef _IRR_COMPILE_WITH_OGLES1_ +#include <GLES/gl.h> +#else +#include <GLES2/gl2.h> +#endif +#else +#ifndef __APPLE__ +#include <GL/gl.h> +#else +#define GL_SILENCE_DEPRECATION +#include <OpenGL/gl.h> +#endif +#endif /* A cache from shader name to shader path @@ -215,11 +231,24 @@ class MainShaderConstantSetter : public IShaderConstantSetter { CachedVertexShaderSetting<float, 16> m_world_view_proj; CachedVertexShaderSetting<float, 16> m_world; +#if ENABLE_GLES + // Modelview matrix + CachedVertexShaderSetting<float, 16> m_world_view; + // Texture matrix + CachedVertexShaderSetting<float, 16> m_texture; + // Normal matrix + CachedVertexShaderSetting<float, 9> m_normal; +#endif public: MainShaderConstantSetter() : - m_world_view_proj("mWorldViewProj"), - m_world("mWorld") + m_world_view_proj("mWorldViewProj") + , m_world("mWorld") +#if ENABLE_GLES + , m_world_view("mWorldView") + , m_texture("mTexture") + , m_normal("mNormal") +#endif {} ~MainShaderConstantSetter() = default; @@ -229,23 +258,42 @@ public: video::IVideoDriver *driver = services->getVideoDriver(); sanity_check(driver); + // Set world matrix + core::matrix4 world = driver->getTransform(video::ETS_WORLD); + if (is_highlevel) + m_world.set(*reinterpret_cast<float(*)[16]>(world.pointer()), services); + else + services->setVertexShaderConstant(world.pointer(), 4, 4); + // Set clip matrix + core::matrix4 worldView; + worldView = driver->getTransform(video::ETS_VIEW); + worldView *= world; core::matrix4 worldViewProj; worldViewProj = driver->getTransform(video::ETS_PROJECTION); - worldViewProj *= driver->getTransform(video::ETS_VIEW); - worldViewProj *= driver->getTransform(video::ETS_WORLD); + worldViewProj *= worldView; if (is_highlevel) m_world_view_proj.set(*reinterpret_cast<float(*)[16]>(worldViewProj.pointer()), services); else services->setVertexShaderConstant(worldViewProj.pointer(), 0, 4); - // Set world matrix - core::matrix4 world = driver->getTransform(video::ETS_WORLD); - if (is_highlevel) - m_world.set(*reinterpret_cast<float(*)[16]>(world.pointer()), services); - else - services->setVertexShaderConstant(world.pointer(), 4, 4); - +#if ENABLE_GLES + if (is_highlevel) { + core::matrix4 texture = driver->getTransform(video::ETS_TEXTURE_0); + m_world_view.set(*reinterpret_cast<float(*)[16]>(worldView.pointer()), services); + m_texture.set(*reinterpret_cast<float(*)[16]>(texture.pointer()), services); + + core::matrix4 normal; + worldView.getTransposed(normal); + sanity_check(normal.makeInverse()); + float m[9] = { + normal[0], normal[1], normal[2], + normal[4], normal[5], normal[6], + normal[8], normal[9], normal[10], + }; + m_normal.set(m, services); + } +#endif } }; @@ -529,7 +577,6 @@ ShaderInfo generate_shader(const std::string &name, u8 material_type, u8 drawtyp shaderinfo.name = name; shaderinfo.material_type = material_type; shaderinfo.drawtype = drawtype; - shaderinfo.material = video::EMT_SOLID; switch (material_type) { case TILE_MATERIAL_OPAQUE: case TILE_MATERIAL_LIQUID_OPAQUE: @@ -550,6 +597,7 @@ ShaderInfo generate_shader(const std::string &name, u8 material_type, u8 drawtyp shaderinfo.base_material = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; break; } + shaderinfo.material = shaderinfo.base_material; bool enable_shaders = g_settings->getBool("enable_shaders"); if (!enable_shaders) @@ -605,7 +653,62 @@ ShaderInfo generate_shader(const std::string &name, u8 material_type, u8 drawtyp return shaderinfo; // Create shaders header - std::string shaders_header = "#version 120\n"; + bool use_gles = false; +#if ENABLE_GLES + use_gles = driver->getDriverType() == video::EDT_OGLES2; +#endif + std::string shaders_header, vertex_header, pixel_header; // geometry shaders aren’t supported in GLES<3 + if (use_gles) { + shaders_header = + "#version 100\n" + ; + vertex_header = R"( + uniform highp mat4 mWorldView; + uniform highp mat4 mWorldViewProj; + uniform mediump mat4 mTexture; + uniform mediump mat3 mNormal; + + attribute highp vec4 inVertexPosition; + attribute lowp vec4 inVertexColor; + attribute mediump vec4 inTexCoord0; + attribute mediump vec3 inVertexNormal; + attribute mediump vec4 inVertexTangent; + attribute mediump vec4 inVertexBinormal; + )"; + pixel_header = R"( + precision mediump float; + )"; + } else { + shaders_header = R"( + #version 120 + #define lowp + #define mediump + #define highp + )"; + vertex_header = R"( + #define mWorldView gl_ModelViewMatrix + #define mWorldViewProj gl_ModelViewProjectionMatrix + #define mTexture (gl_TextureMatrix[0]) + #define mNormal gl_NormalMatrix + + #define inVertexPosition gl_Vertex + #define inVertexColor gl_Color + #define inTexCoord0 gl_MultiTexCoord0 + #define inVertexNormal gl_Normal + #define inVertexTangent gl_MultiTexCoord1 + #define inVertexBinormal gl_MultiTexCoord2 + )"; + } + + bool use_discard = use_gles; +#ifdef __unix__ + // For renderers that should use discard instead of GL_ALPHA_TEST + const char* gl_renderer = (const char*)glGetString(GL_RENDERER); + if (strstr(gl_renderer, "GC7000")) + use_discard = true; +#endif + if (use_discard && shaderinfo.base_material != video::EMT_SOLID) + shaders_header += "#define USE_DISCARD\n"; static const char* drawTypes[] = { "NDT_NORMAL", @@ -665,73 +768,16 @@ ShaderInfo generate_shader(const std::string &name, u8 material_type, u8 drawtyp shaders_header += itos(drawtype); shaders_header += "\n"; - if (g_settings->getBool("generate_normalmaps")) { - shaders_header += "#define GENERATE_NORMALMAPS 1\n"; - } else { - shaders_header += "#define GENERATE_NORMALMAPS 0\n"; - } - shaders_header += "#define NORMALMAPS_STRENGTH "; - shaders_header += ftos(g_settings->getFloat("normalmaps_strength")); - shaders_header += "\n"; - float sample_step; - int smooth = (int)g_settings->getFloat("normalmaps_smooth"); - switch (smooth){ - case 0: - sample_step = 0.0078125; // 1.0 / 128.0 - break; - case 1: - sample_step = 0.00390625; // 1.0 / 256.0 - break; - case 2: - sample_step = 0.001953125; // 1.0 / 512.0 - break; - default: - sample_step = 0.0078125; - break; - } - shaders_header += "#define SAMPLE_STEP "; - shaders_header += ftos(sample_step); - shaders_header += "\n"; - - if (g_settings->getBool("enable_bumpmapping")) - shaders_header += "#define ENABLE_BUMPMAPPING\n"; - - if (g_settings->getBool("enable_parallax_occlusion")){ - int mode = g_settings->getFloat("parallax_occlusion_mode"); - float scale = g_settings->getFloat("parallax_occlusion_scale"); - float bias = g_settings->getFloat("parallax_occlusion_bias"); - int iterations = g_settings->getFloat("parallax_occlusion_iterations"); - shaders_header += "#define ENABLE_PARALLAX_OCCLUSION\n"; - shaders_header += "#define PARALLAX_OCCLUSION_MODE "; - shaders_header += itos(mode); - shaders_header += "\n"; - shaders_header += "#define PARALLAX_OCCLUSION_SCALE "; - shaders_header += ftos(scale); - shaders_header += "\n"; - shaders_header += "#define PARALLAX_OCCLUSION_BIAS "; - shaders_header += ftos(bias); - shaders_header += "\n"; - shaders_header += "#define PARALLAX_OCCLUSION_ITERATIONS "; - shaders_header += itos(iterations); - shaders_header += "\n"; - } - - shaders_header += "#define USE_NORMALMAPS "; - if (g_settings->getBool("enable_bumpmapping") || g_settings->getBool("enable_parallax_occlusion")) - shaders_header += "1\n"; - else - shaders_header += "0\n"; - if (g_settings->getBool("enable_waving_water")){ shaders_header += "#define ENABLE_WAVING_WATER 1\n"; shaders_header += "#define WATER_WAVE_HEIGHT "; - shaders_header += ftos(g_settings->getFloat("water_wave_height")); + shaders_header += std::to_string(g_settings->getFloat("water_wave_height")); shaders_header += "\n"; shaders_header += "#define WATER_WAVE_LENGTH "; - shaders_header += ftos(g_settings->getFloat("water_wave_length")); + shaders_header += std::to_string(g_settings->getFloat("water_wave_length")); shaders_header += "\n"; shaders_header += "#define WATER_WAVE_SPEED "; - shaders_header += ftos(g_settings->getFloat("water_wave_speed")); + shaders_header += std::to_string(g_settings->getFloat("water_wave_speed")); shaders_header += "\n"; } else{ shaders_header += "#define ENABLE_WAVING_WATER 0\n"; @@ -753,7 +799,7 @@ ShaderInfo generate_shader(const std::string &name, u8 material_type, u8 drawtyp shaders_header += "#define ENABLE_TONE_MAPPING\n"; shaders_header += "#define FOG_START "; - shaders_header += ftos(rangelim(g_settings->getFloat("fog_start"), 0.0f, 0.99f)); + shaders_header += std::to_string(rangelim(g_settings->getFloat("fog_start"), 0.0f, 0.99f)); shaders_header += "\n"; // Call addHighLevelShaderMaterial() or addShaderMaterial() @@ -761,11 +807,11 @@ ShaderInfo generate_shader(const std::string &name, u8 material_type, u8 drawtyp const c8* pixel_program_ptr = 0; const c8* geometry_program_ptr = 0; if (!vertex_program.empty()) { - vertex_program = shaders_header + vertex_program; + vertex_program = shaders_header + vertex_header + vertex_program; vertex_program_ptr = vertex_program.c_str(); } if (!pixel_program.empty()) { - pixel_program = shaders_header + pixel_program; + pixel_program = shaders_header + pixel_header + pixel_program; pixel_program_ptr = pixel_program.c_str(); } if (!geometry_program.empty()) { @@ -847,27 +893,37 @@ void load_shaders(const std::string &name, SourceShaderCache *sourcecache, geometry_program = ""; is_highlevel = false; - if(enable_shaders){ - // Look for high level shaders - if(drivertype == video::EDT_DIRECT3D9){ - // Direct3D 9: HLSL - // (All shaders in one file) - vertex_program = sourcecache->getOrLoad(name, "d3d9.hlsl"); - pixel_program = vertex_program; - geometry_program = vertex_program; - } - else if(drivertype == video::EDT_OPENGL){ - // OpenGL: GLSL - vertex_program = sourcecache->getOrLoad(name, "opengl_vertex.glsl"); - pixel_program = sourcecache->getOrLoad(name, "opengl_fragment.glsl"); - geometry_program = sourcecache->getOrLoad(name, "opengl_geometry.glsl"); - } - if (!vertex_program.empty() || !pixel_program.empty() || !geometry_program.empty()){ - is_highlevel = true; - return; - } - } + if (!enable_shaders) + return; + + // Look for high level shaders + switch (drivertype) { + case video::EDT_DIRECT3D9: + // Direct3D 9: HLSL + // (All shaders in one file) + vertex_program = sourcecache->getOrLoad(name, "d3d9.hlsl"); + pixel_program = vertex_program; + geometry_program = vertex_program; + break; + case video::EDT_OPENGL: +#if ENABLE_GLES + case video::EDT_OGLES2: +#endif + // OpenGL: GLSL + vertex_program = sourcecache->getOrLoad(name, "opengl_vertex.glsl"); + pixel_program = sourcecache->getOrLoad(name, "opengl_fragment.glsl"); + geometry_program = sourcecache->getOrLoad(name, "opengl_geometry.glsl"); + break; + + default: + // e.g. OpenGL ES 1 (with no shader support) + break; + } + if (!vertex_program.empty() || !pixel_program.empty() || !geometry_program.empty()){ + is_highlevel = true; + return; + } } void dumpShaderProgram(std::ostream &output_stream, diff --git a/src/client/sky.cpp b/src/client/sky.cpp index 2e0cbca86..9a2614eda 100644 --- a/src/client/sky.cpp +++ b/src/client/sky.cpp @@ -1,6 +1,7 @@ /* Minetest Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com> +Copyright (C) 2020 numzero, Lobachevskiy Vitaliy <numzer0@yandex.ru> 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 @@ -34,16 +35,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "config.h" using namespace irr::core; -Sky::Sky(s32 id, ITextureSource *tsrc) : - scene::ISceneNode(RenderingEngine::get_scene_manager()->getRootSceneNode(), - RenderingEngine::get_scene_manager(), id) +static video::SMaterial baseMaterial() { - setAutomaticCulling(scene::EAC_OFF); - m_box.MaxEdge.set(0, 0, 0); - m_box.MinEdge.set(0, 0, 0); - - // Create material - video::SMaterial mat; mat.Lighting = false; #if ENABLE_GLES @@ -56,14 +49,31 @@ Sky::Sky(s32 id, ITextureSource *tsrc) : mat.TextureLayer[0].TextureWrapU = video::ETC_CLAMP_TO_EDGE; mat.TextureLayer[0].TextureWrapV = video::ETC_CLAMP_TO_EDGE; mat.BackfaceCulling = false; + return mat; +}; + +Sky::Sky(s32 id, ITextureSource *tsrc, IShaderSource *ssrc) : + scene::ISceneNode(RenderingEngine::get_scene_manager()->getRootSceneNode(), + RenderingEngine::get_scene_manager(), id) +{ + setAutomaticCulling(scene::EAC_OFF); + m_box.MaxEdge.set(0, 0, 0); + m_box.MinEdge.set(0, 0, 0); + + m_enable_shaders = g_settings->getBool("enable_shaders"); - m_materials[0] = mat; + // Create materials - m_materials[1] = mat; + m_materials[0] = baseMaterial(); + m_materials[0].MaterialType = ssrc->getShaderInfo(ssrc->getShader("stars_shader", TILE_MATERIAL_ALPHA, 0)).material; + m_materials[0].Lighting = true; + m_materials[0].ColorMaterial = video::ECM_NONE; + + m_materials[1] = baseMaterial(); //m_materials[1].MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA; m_materials[1].MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; - m_materials[2] = mat; + m_materials[2] = baseMaterial(); m_materials[2].setTexture(0, tsrc->getTextureForMesh("sunrisebg.png")); m_materials[2].MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; //m_materials[2].MaterialType = video::EMT_TRANSPARENT_ADD_COLOR; @@ -80,7 +90,7 @@ Sky::Sky(s32 id, ITextureSource *tsrc) : tsrc->getTexture(m_moon_params.tonemap) : NULL; if (m_sun_texture) { - m_materials[3] = mat; + m_materials[3] = baseMaterial(); m_materials[3].setTexture(0, m_sun_texture); m_materials[3].MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; // Disables texture filtering @@ -92,7 +102,7 @@ Sky::Sky(s32 id, ITextureSource *tsrc) : m_materials[3].Lighting = true; } if (m_moon_texture) { - m_materials[4] = mat; + m_materials[4] = baseMaterial(); m_materials[4].setTexture(0, m_moon_texture); m_materials[4].MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; // Disables texture filtering @@ -105,7 +115,7 @@ Sky::Sky(s32 id, ITextureSource *tsrc) : } for (int i = 5; i < 11; i++) { - m_materials[i] = mat; + m_materials[i] = baseMaterial(); m_materials[i].Lighting = true; m_materials[i].MaterialType = video::EMT_SOLID; } @@ -202,7 +212,7 @@ void Sky::render() const f32 t = 1.0f; const f32 o = 0.0f; - static const u16 indices[4] = {0, 1, 2, 3}; + static const u16 indices[6] = {0, 1, 2, 0, 2, 3}; video::S3DVertex vertices[4]; driver->setMaterial(m_materials[1]); @@ -244,7 +254,7 @@ void Sky::render() vertex.Pos.rotateXZBy(180); } } - driver->drawIndexedTriangleFan(&vertices[0], 4, indices, 2); + driver->drawIndexedTriangleList(&vertices[0], 4, indices, 2); } } @@ -270,7 +280,7 @@ void Sky::render() // Switch from -Z (south) to +Z (north) vertex.Pos.rotateXZBy(-180); } - driver->drawIndexedTriangleFan(&vertices[0], 4, indices, 2); + driver->drawIndexedTriangleList(&vertices[0], 4, indices, 2); } } @@ -301,7 +311,7 @@ void Sky::render() // Switch from -Z (south) to -X (west) vertex.Pos.rotateXZBy(-90); } - driver->drawIndexedTriangleFan(&vertices[0], 4, indices, 2); + driver->drawIndexedTriangleList(&vertices[0], 4, indices, 2); } // Draw sun @@ -337,7 +347,7 @@ void Sky::render() // Switch from -Z (south) to +Z (north) vertex.Pos.rotateXZBy(-180); } - driver->drawIndexedTriangleFan(&vertices[0], 4, indices, 2); + driver->drawIndexedTriangleList(&vertices[0], 4, indices, 2); } // Draw bottom far cloudy fog thing in front of sun, moon and stars @@ -346,7 +356,7 @@ void Sky::render() vertices[1] = video::S3DVertex( 1, -1.0, -1, 0, 1, 0, c, o, t); vertices[2] = video::S3DVertex( 1, -1.0, 1, 0, 1, 0, c, o, o); vertices[3] = video::S3DVertex(-1, -1.0, 1, 0, 1, 0, c, t, o); - driver->drawIndexedTriangleFan(&vertices[0], 4, indices, 2); + driver->drawIndexedTriangleList(&vertices[0], 4, indices, 2); } } } @@ -590,7 +600,7 @@ void Sky::draw_sun(video::IVideoDriver *driver, float sunsize, const video::SCol * wicked_time_of_day: current time of day, to know where should be the sun in the sky */ { - static const u16 indices[4] = {0, 1, 2, 3}; + static const u16 indices[] = {0, 1, 2, 0, 2, 3}; std::array<video::S3DVertex, 4> vertices; if (!m_sun_texture) { driver->setMaterial(m_materials[1]); @@ -608,7 +618,7 @@ void Sky::draw_sun(video::IVideoDriver *driver, float sunsize, const video::SCol for (int i = 0; i < 4; i++) { draw_sky_body(vertices, -sunsizes[i], sunsizes[i], colors[i]); place_sky_body(vertices, 90, wicked_time_of_day * 360 - 90); - driver->drawIndexedTriangleFan(&vertices[0], 4, indices, 2); + driver->drawIndexedTriangleList(&vertices[0], 4, indices, 2); } } else { driver->setMaterial(m_materials[3]); @@ -620,7 +630,7 @@ void Sky::draw_sun(video::IVideoDriver *driver, float sunsize, const video::SCol c = video::SColor(255, 255, 255, 255); draw_sky_body(vertices, -d, d, c); place_sky_body(vertices, 90, wicked_time_of_day * 360 - 90); - driver->drawIndexedTriangleFan(&vertices[0], 4, indices, 2); + driver->drawIndexedTriangleList(&vertices[0], 4, indices, 2); } } @@ -637,7 +647,7 @@ void Sky::draw_moon(video::IVideoDriver *driver, float moonsize, const video::SC * the sky */ { - static const u16 indices[4] = {0, 1, 2, 3}; + static const u16 indices[] = {0, 1, 2, 0, 2, 3}; std::array<video::S3DVertex, 4> vertices; if (!m_moon_texture) { driver->setMaterial(m_materials[1]); @@ -661,7 +671,7 @@ void Sky::draw_moon(video::IVideoDriver *driver, float moonsize, const video::SC for (int i = 0; i < 4; i++) { draw_sky_body(vertices, moonsizes_1[i], moonsizes_2[i], colors[i]); place_sky_body(vertices, -90, wicked_time_of_day * 360 - 90); - driver->drawIndexedTriangleFan(&vertices[0], 4, indices, 2); + driver->drawIndexedTriangleList(&vertices[0], 4, indices, 2); } } else { driver->setMaterial(m_materials[4]); @@ -673,13 +683,12 @@ void Sky::draw_moon(video::IVideoDriver *driver, float moonsize, const video::SC c = video::SColor(255, 255, 255, 255); draw_sky_body(vertices, -d, d, c); place_sky_body(vertices, -90, wicked_time_of_day * 360 - 90); - driver->drawIndexedTriangleFan(&vertices[0], 4, indices, 2); + driver->drawIndexedTriangleList(&vertices[0], 4, indices, 2); } } void Sky::draw_stars(video::IVideoDriver * driver, float wicked_time_of_day) { - driver->setMaterial(m_materials[1]); // Tune values so that stars first appear just after the sun // disappears over the horizon, and disappear just before the sun // appears over the horizon. @@ -687,87 +696,18 @@ void Sky::draw_stars(video::IVideoDriver * driver, float wicked_time_of_day) // to time 4000. float tod = wicked_time_of_day < 0.5f ? wicked_time_of_day : (1.0f - wicked_time_of_day); - float starbrightness = clamp((0.25f - fabsf(tod)) * 20.0f, 0.0f, 1.0f); - - float f = starbrightness; - float d = (0.006 / 2) * m_star_params.scale; - - video::SColor starcolor = m_star_params.starcolor; - starcolor.setAlpha(f * m_star_params.starcolor.getAlpha()); - - // Stars are only drawn when not fully transparent - if (m_star_params.starcolor.getAlpha() < 1) + float starbrightness = (0.25f - fabsf(tod)) * 20.0f; + m_star_color = m_star_params.starcolor; + m_star_color.a *= clamp(starbrightness, 0.0f, 1.0f); + if (m_star_color.a <= 0.0f) // Stars are only drawn when not fully transparent return; -#if ENABLE_GLES - u16 *indices = new u16[m_star_params.count * 3]; - video::S3DVertex *vertices = - new video::S3DVertex[m_star_params.count * 3]; - for (u32 i = 0; i < m_star_params.count; i++) { - indices[i * 3 + 0] = i * 3 + 0; - indices[i * 3 + 1] = i * 3 + 1; - indices[i * 3 + 2] = i * 3 + 2; - v3f r = m_stars[i]; - core::CMatrix4<f32> a; - a.buildRotateFromTo(v3f(0, 1, 0), r); - v3f p = v3f(-d, 1, -d); - v3f p1 = v3f(d, 1, 0); - v3f p2 = v3f(-d, 1, d); - a.rotateVect(p); - a.rotateVect(p1); - a.rotateVect(p2); - p.rotateXYBy(wicked_time_of_day * 360 - 90); - p1.rotateXYBy(wicked_time_of_day * 360 - 90); - p2.rotateXYBy(wicked_time_of_day * 360 - 90); - vertices[i * 3 + 0].Pos = p; - vertices[i * 3 + 0].Color = starcolor; - vertices[i * 3 + 1].Pos = p1; - vertices[i * 3 + 1].Color = starcolor; - vertices[i * 3 + 2].Pos = p2; - vertices[i * 3 + 2].Color = starcolor; - } - driver->drawIndexedTriangleList(vertices, m_star_params.count * 3, - indices, m_star_params.count); - delete[] indices; - delete[] vertices; -#else - u16 *indices = new u16[m_star_params.count * 4]; - video::S3DVertex *vertices = - new video::S3DVertex[m_star_params.count * 4]; - for (u32 i = 0; i < m_star_params.count; i++) { - indices[i * 4 + 0] = i * 4 + 0; - indices[i * 4 + 1] = i * 4 + 1; - indices[i * 4 + 2] = i * 4 + 2; - indices[i * 4 + 3] = i * 4 + 3; - v3f r = m_stars[i]; - core::CMatrix4<f32> a; - a.buildRotateFromTo(v3f(0, 1, 0), r); - v3f p = v3f(-d, 1, -d); - v3f p1 = v3f(d, 1, -d); - v3f p2 = v3f(d, 1, d); - v3f p3 = v3f(-d, 1, d); - a.rotateVect(p); - a.rotateVect(p1); - a.rotateVect(p2); - a.rotateVect(p3); - p.rotateXYBy(wicked_time_of_day * 360 - 90); - p1.rotateXYBy(wicked_time_of_day * 360 - 90); - p2.rotateXYBy(wicked_time_of_day * 360 - 90); - p3.rotateXYBy(wicked_time_of_day * 360 - 90); - vertices[i * 4 + 0].Pos = p; - vertices[i * 4 + 0].Color = starcolor; - vertices[i * 4 + 1].Pos = p1; - vertices[i * 4 + 1].Color = starcolor; - vertices[i * 4 + 2].Pos = p2; - vertices[i * 4 + 2].Color = starcolor; - vertices[i * 4 + 3].Pos = p3; - vertices[i * 4 + 3].Color = starcolor; - } - driver->drawVertexPrimitiveList(vertices, m_star_params.count * 4, - indices, m_star_params.count, video::EVT_STANDARD, - scene::EPT_QUADS, video::EIT_16BIT); - delete[] indices; - delete[] vertices; -#endif + m_materials[0].DiffuseColor = m_materials[0].EmissiveColor = m_star_color.toSColor(); + auto sky_rotation = core::matrix4().setRotationAxisRadians(2.0f * M_PI * (wicked_time_of_day - 0.25f), v3f(0.0f, 0.0f, 1.0f)); + auto world_matrix = driver->getTransform(video::ETS_WORLD); + driver->setTransform(video::ETS_WORLD, world_matrix * sky_rotation); + driver->setMaterial(m_materials[0]); + driver->drawMeshBuffer(m_stars.get()); + driver->setTransform(video::ETS_WORLD, world_matrix); } void Sky::draw_sky_body(std::array<video::S3DVertex, 4> &vertices, float pos_1, float pos_2, const video::SColor &c) @@ -822,7 +762,7 @@ void Sky::setSunTexture(std::string sun_texture, m_sun_texture = tsrc->getTextureForMesh(m_sun_params.texture); if (m_sun_texture) { - m_materials[3] = m_materials[0]; + m_materials[3] = baseMaterial(); m_materials[3].setTexture(0, m_sun_texture); m_materials[3].MaterialType = video:: EMT_TRANSPARENT_ALPHA_CHANNEL; @@ -870,7 +810,7 @@ void Sky::setMoonTexture(std::string moon_texture, m_moon_texture = tsrc->getTextureForMesh(m_moon_params.texture); if (m_moon_texture) { - m_materials[4] = m_materials[0]; + m_materials[4] = baseMaterial(); m_materials[4].setTexture(0, m_moon_texture); m_materials[4].MaterialType = video:: EMT_TRANSPARENT_ALPHA_CHANNEL; @@ -892,19 +832,58 @@ void Sky::setStarCount(u16 star_count, bool force_update) // Allow force updating star count at game init. if (m_star_params.count != star_count || force_update) { m_star_params.count = star_count; - m_stars.clear(); - // Rebuild the stars surrounding the camera - for (u16 i = 0; i < star_count; i++) { - v3f star = v3f( - myrand_range(-10000, 10000), - myrand_range(-10000, 10000), - myrand_range(-10000, 10000) - ); - - star.normalize(); - m_stars.emplace_back(star); - } + m_seed = (u64)myrand() << 32 | myrand(); + updateStars(); + } +} + +void Sky::updateStars() +{ + m_stars.reset(new scene::SMeshBuffer()); + // Stupid IrrLicht doesn’t allow non-indexed rendering, and indexed quad + // rendering is slow due to lack of hardware support. So as indices are + // 16-bit and there are 4 vertices per star... the limit is 2^16/4 = 0x4000. + // That should be well enough actually. + if (m_star_params.count > 0x4000) { + warningstream << "Requested " << m_star_params.count << " stars but " << 0x4000 << " is the max\n"; + m_star_params.count = 0x4000; + } + m_stars->Vertices.reallocate(4 * m_star_params.count); + m_stars->Indices.reallocate(6 * m_star_params.count); + + video::SColor fallback_color = m_star_params.starcolor; // used on GLES 2 “without shaders” + PcgRandom rgen(m_seed); + float d = (0.006 / 2) * m_star_params.scale; + for (u16 i = 0; i < m_star_params.count; i++) { + v3f r = v3f( + rgen.range(-10000, 10000), + rgen.range(-10000, 10000), + rgen.range(-10000, 10000) + ); + core::CMatrix4<f32> a; + a.buildRotateFromTo(v3f(0, 1, 0), r); + v3f p = v3f(-d, 1, -d); + v3f p1 = v3f(d, 1, -d); + v3f p2 = v3f(d, 1, d); + v3f p3 = v3f(-d, 1, d); + a.rotateVect(p); + a.rotateVect(p1); + a.rotateVect(p2); + a.rotateVect(p3); + m_stars->Vertices.push_back(video::S3DVertex(p, {}, fallback_color, {})); + m_stars->Vertices.push_back(video::S3DVertex(p1, {}, fallback_color, {})); + m_stars->Vertices.push_back(video::S3DVertex(p2, {}, fallback_color, {})); + m_stars->Vertices.push_back(video::S3DVertex(p3, {}, fallback_color, {})); + } + for (u16 i = 0; i < m_star_params.count; i++) { + m_stars->Indices.push_back(i * 4 + 0); + m_stars->Indices.push_back(i * 4 + 1); + m_stars->Indices.push_back(i * 4 + 2); + m_stars->Indices.push_back(i * 4 + 2); + m_stars->Indices.push_back(i * 4 + 3); + m_stars->Indices.push_back(i * 4 + 0); } + m_stars->setHardwareMappingHint(scene::EHM_STATIC); } void Sky::setSkyColors(const SkyColor &sky_color) @@ -936,7 +915,7 @@ void Sky::addTextureToSkybox(std::string texture, int material_id, // Keep a list of texture names handy. m_sky_params.textures.emplace_back(texture); video::ITexture *result = tsrc->getTextureForMesh(texture); - m_materials[material_id+5] = m_materials[0]; + m_materials[material_id+5] = baseMaterial(); m_materials[material_id+5].setTexture(0, result); m_materials[material_id+5].MaterialType = video::EMT_SOLID; } diff --git a/src/client/sky.h b/src/client/sky.h index 3227e8f59..10e1cd976 100644 --- a/src/client/sky.h +++ b/src/client/sky.h @@ -21,6 +21,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include <array> #include "camera.h" #include "irrlichttypes_extrabloated.h" +#include "irr_ptr.h" +#include "shader.h" #include "skyparams.h" #pragma once @@ -34,7 +36,7 @@ class Sky : public scene::ISceneNode { public: //! constructor - Sky(s32 id, ITextureSource *tsrc); + Sky(s32 id, ITextureSource *tsrc, IShaderSource *ssrc); virtual void OnRegisterSceneNode(); @@ -77,7 +79,7 @@ public: void setStarsVisible(bool stars_visible) { m_star_params.visible = stars_visible; } void setStarCount(u16 star_count, bool force_update); void setStarColor(video::SColor star_color) { m_star_params.starcolor = star_color; } - void setStarScale(f32 star_scale) { m_star_params.scale = star_scale; } + void setStarScale(f32 star_scale) { m_star_params.scale = star_scale; updateStars(); } bool getCloudsVisible() const { return m_clouds_visible && m_clouds_enabled; } const video::SColorf &getCloudColor() const { return m_cloudcolor_f; } @@ -101,6 +103,8 @@ public: void clearSkyboxTextures() { m_sky_params.textures.clear(); } void addTextureToSkybox(std::string texture, int material_id, ITextureSource *tsrc); + const video::SColorf &getCurrentStarColor() const { return m_star_color; } + private: aabb3f m_box; video::SMaterial m_materials[SKY_MATERIAL_COUNT]; @@ -154,6 +158,7 @@ private: bool m_clouds_enabled = true; // Initialised to true, reset only by set_sky API bool m_directional_colored_fog; bool m_in_clouds = true; // Prevent duplicating bools to remember old values + bool m_enable_shaders = false; video::SColorf m_bgcolor_bright_f = video::SColorf(1.0f, 1.0f, 1.0f, 1.0f); video::SColorf m_skycolor_bright_f = video::SColorf(1.0f, 1.0f, 1.0f, 1.0f); @@ -178,13 +183,17 @@ private: bool m_default_tint = true; - std::vector<v3f> m_stars; + u64 m_seed = 0; + irr_ptr<scene::SMeshBuffer> m_stars; + video::SColorf m_star_color; video::ITexture *m_sun_texture; video::ITexture *m_moon_texture; video::ITexture *m_sun_tonemap; video::ITexture *m_moon_tonemap; + void updateStars(); + void draw_sun(video::IVideoDriver *driver, float sunsize, const video::SColor &suncolor, const video::SColor &suncolor2, float wicked_time_of_day); void draw_moon(video::IVideoDriver *driver, float moonsize, const video::SColor &mooncolor, diff --git a/src/client/sound_openal.cpp b/src/client/sound_openal.cpp index 20a651c1d..f4e61f93e 100644 --- a/src/client/sound_openal.cpp +++ b/src/client/sound_openal.cpp @@ -28,6 +28,7 @@ with this program; ifnot, write to the Free Software Foundation, Inc., #include <alc.h> //#include <alext.h> #elif defined(__APPLE__) + #define OPENAL_DEPRECATED #include <OpenAL/al.h> #include <OpenAL/alc.h> //#include <OpenAL/alext.h> @@ -337,14 +338,12 @@ private: }; std::unordered_map<int, FadeState> m_sounds_fading; - float m_fade_delay; public: OpenALSoundManager(SoundManagerSingleton *smg, OnDemandSoundFetcher *fetcher): m_fetcher(fetcher), m_device(smg->m_device.get()), m_context(smg->m_context.get()), - m_next_id(1), - m_fade_delay(0) + m_next_id(1) { infostream << "Audio: Initialized: OpenAL " << std::endl; } @@ -616,38 +615,45 @@ public: void fadeSound(int soundid, float step, float gain) { - m_sounds_fading[soundid] = FadeState(step, getSoundGain(soundid), gain); + // Ignore the command if step isn't valid. + if (step == 0) + return; + float current_gain = getSoundGain(soundid); + step = gain - current_gain > 0 ? abs(step) : -abs(step); + if (m_sounds_fading.find(soundid) != m_sounds_fading.end()) { + auto current_fade = m_sounds_fading[soundid]; + // Do not replace the fade if it's equivalent. + if (current_fade.target_gain == gain && current_fade.step == step) + return; + m_sounds_fading.erase(soundid); + } + gain = rangelim(gain, 0, 1); + m_sounds_fading[soundid] = FadeState(step, current_gain, gain); } void doFades(float dtime) { - m_fade_delay += dtime; - - if (m_fade_delay < 0.1f) - return; + for (auto i = m_sounds_fading.begin(); i != m_sounds_fading.end();) { + FadeState& fade = i->second; + assert(fade.step != 0); + fade.current_gain += (fade.step * dtime); - float chkGain = 0; - for (auto i = m_sounds_fading.begin(); - i != m_sounds_fading.end();) { - if (i->second.step < 0.f) - chkGain = -(i->second.current_gain); + if (fade.step < 0.f) + fade.current_gain = std::max(fade.current_gain, fade.target_gain); else - chkGain = i->second.current_gain; + fade.current_gain = std::min(fade.current_gain, fade.target_gain); - if (chkGain < i->second.target_gain) { - i->second.current_gain += (i->second.step * m_fade_delay); - i->second.current_gain = rangelim(i->second.current_gain, 0, 1); - - updateSoundGain(i->first, i->second.current_gain); - ++i; - } else { - if (i->second.target_gain <= 0.f) - stopSound(i->first); + if (fade.current_gain <= 0.f) + stopSound(i->first); + else + updateSoundGain(i->first, fade.current_gain); + // The increment must happen during the erase call, or else it'll segfault. + if (fade.current_gain == fade.target_gain) m_sounds_fading.erase(i++); - } + else + i++; } - m_fade_delay = 0; } bool soundExists(int sound) diff --git a/src/client/wieldmesh.cpp b/src/client/wieldmesh.cpp index 8cd3e29a9..ad583210a 100644 --- a/src/client/wieldmesh.cpp +++ b/src/client/wieldmesh.cpp @@ -303,13 +303,24 @@ void WieldMeshSceneNode::setExtruded(const std::string &imagename, } } -scene::SMesh *createSpecialNodeMesh(Client *client, content_t id, std::vector<ItemPartColor> *colors) +scene::SMesh *createSpecialNodeMesh(Client *client, content_t id, std::vector<ItemPartColor> *colors, const ContentFeatures &f) { - MeshMakeData mesh_make_data(client, false, false); + MeshMakeData mesh_make_data(client, false); MeshCollector collector; mesh_make_data.setSmoothLighting(false); MapblockMeshGenerator gen(&mesh_make_data, &collector); - gen.renderSingle(id); + u8 param2 = 0; + if (f.param_type_2 == CPT2_WALLMOUNTED || + f.param_type_2 == CPT2_COLORED_WALLMOUNTED) { + if (f.drawtype == NDT_TORCHLIKE) + param2 = 1; + else if (f.drawtype == NDT_SIGNLIKE || + f.drawtype == NDT_NODEBOX || + f.drawtype == NDT_MESH) + param2 = 4; + } + gen.renderSingle(id, param2); + colors->clear(); scene::SMesh *mesh = new scene::SMesh(); for (auto &prebuffers : collector.prebuffers) @@ -319,8 +330,9 @@ scene::SMesh *createSpecialNodeMesh(Client *client, content_t id, std::vector<It p.layer.texture = frame.texture; p.layer.normal_texture = frame.normal_texture; } - for (video::S3DVertex &v : p.vertices) + for (video::S3DVertex &v : p.vertices) { v.Color.setAlpha(255); + } scene::SMeshBuffer *buf = new scene::SMeshBuffer(); buf->Material.setTexture(0, p.layer.texture); p.layer.applyMaterialOptions(buf->Material); @@ -368,73 +380,61 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client, bool che // Handle nodes // See also CItemDefManager::createClientCached() if (def.type == ITEM_NODE) { - if (f.mesh_ptr[0]) { - // e.g. mesh nodes and nodeboxes - mesh = cloneMesh(f.mesh_ptr[0]); - postProcessNodeMesh(mesh, f, m_enable_shaders, true, - &m_material_type, &m_colors); + bool cull_backface = f.needsBackfaceCulling(); + + // Select rendering method + switch (f.drawtype) { + case NDT_AIRLIKE: + setExtruded("no_texture_airlike.png", "", + v3f(1.0, 1.0, 1.0), tsrc, 1); + break; + case NDT_SIGNLIKE: + case NDT_TORCHLIKE: + case NDT_RAILLIKE: + case NDT_PLANTLIKE: + case NDT_PLANTLIKE_ROOTED: + case NDT_FLOWINGLIQUID: { + v3f wscale = def.wield_scale; + if (f.drawtype == NDT_FLOWINGLIQUID) + wscale.Z *= 0.1f; + setExtruded(tsrc->getTextureName(f.tiles[0].layers[0].texture_id), + tsrc->getTextureName(f.tiles[0].layers[1].texture_id), + wscale, tsrc, + f.tiles[0].layers[0].animation_frame_count); + // Add color + const TileLayer &l0 = f.tiles[0].layers[0]; + m_colors.emplace_back(l0.has_color, l0.color); + const TileLayer &l1 = f.tiles[0].layers[1]; + m_colors.emplace_back(l1.has_color, l1.color); + break; + } + case NDT_NORMAL: + case NDT_ALLFACES: + case NDT_LIQUID: + setCube(f, def.wield_scale); + break; + default: + // Render non-trivial drawtypes like the actual node + mesh = createSpecialNodeMesh(client, id, &m_colors, f); changeToMesh(mesh); mesh->drop(); - // mesh is pre-scaled by BS * f->visual_scale m_meshnode->setScale( - def.wield_scale * WIELD_SCALE_FACTOR - / (BS * f.visual_scale)); - } else { - switch (f.drawtype) { - case NDT_AIRLIKE: { - changeToMesh(nullptr); - break; - } - case NDT_PLANTLIKE: { - setExtruded(tsrc->getTextureName(f.tiles[0].layers[0].texture_id), - tsrc->getTextureName(f.tiles[0].layers[1].texture_id), - def.wield_scale, tsrc, - f.tiles[0].layers[0].animation_frame_count); - // Add color - const TileLayer &l0 = f.tiles[0].layers[0]; - m_colors.emplace_back(l0.has_color, l0.color); - const TileLayer &l1 = f.tiles[0].layers[1]; - m_colors.emplace_back(l1.has_color, l1.color); - break; - } - case NDT_PLANTLIKE_ROOTED: { - setExtruded(tsrc->getTextureName(f.special_tiles[0].layers[0].texture_id), - "", def.wield_scale, tsrc, - f.special_tiles[0].layers[0].animation_frame_count); - // Add color - const TileLayer &l0 = f.special_tiles[0].layers[0]; - m_colors.emplace_back(l0.has_color, l0.color); - break; - } - case NDT_NORMAL: - case NDT_ALLFACES: - case NDT_LIQUID: - case NDT_FLOWINGLIQUID: { - setCube(f, def.wield_scale); - break; - } - default: { - mesh = createSpecialNodeMesh(client, id, &m_colors); - changeToMesh(mesh); - mesh->drop(); - m_meshnode->setScale( - def.wield_scale * WIELD_SCALE_FACTOR - / (BS * f.visual_scale)); - } - } + def.wield_scale * WIELD_SCALE_FACTOR + / (BS * f.visual_scale)); + break; } + u32 material_count = m_meshnode->getMaterialCount(); for (u32 i = 0; i < material_count; ++i) { video::SMaterial &material = m_meshnode->getMaterial(i); material.MaterialType = m_material_type; material.MaterialTypeParam = 0.5f; - material.setFlag(video::EMF_BACK_FACE_CULLING, true); + material.setFlag(video::EMF_BACK_FACE_CULLING, cull_backface); material.setFlag(video::EMF_BILINEAR_FILTER, m_bilinear_filter); material.setFlag(video::EMF_TRILINEAR_FILTER, m_trilinear_filter); } return; - } - else if (!def.inventory_image.empty()) { + } else if (!def.inventory_image.empty()) { setExtruded(def.inventory_image, def.inventory_overlay, def.wield_scale, tsrc, 1); m_colors.emplace_back(); @@ -529,6 +529,8 @@ void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result) // Shading is on by default result->needs_shading = true; + bool cull_backface = f.needsBackfaceCulling(); + // If inventory_image is defined, it overrides everything else if (!def.inventory_image.empty()) { mesh = getExtrudedMesh(tsrc, def.inventory_image, @@ -538,51 +540,56 @@ void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result) result->buffer_colors.emplace_back(true, video::SColor(0xFFFFFFFF)); // Items with inventory images do not need shading result->needs_shading = false; + } else if (def.type == ITEM_NODE && f.drawtype == NDT_AIRLIKE) { + // Fallback image for airlike node + mesh = getExtrudedMesh(tsrc, "no_texture_airlike.png", + def.inventory_overlay); + result->needs_shading = false; } else if (def.type == ITEM_NODE) { - if (f.mesh_ptr[0]) { - mesh = cloneMesh(f.mesh_ptr[0]); - scaleMesh(mesh, v3f(0.12, 0.12, 0.12)); + switch (f.drawtype) { + case NDT_NORMAL: + case NDT_ALLFACES: + case NDT_LIQUID: + case NDT_FLOWINGLIQUID: { + scene::IMesh *cube = g_extrusion_mesh_cache->createCube(); + mesh = cloneMesh(cube); + cube->drop(); + if (f.drawtype == NDT_FLOWINGLIQUID) { + scaleMesh(mesh, v3f(1.2, 0.03, 1.2)); + translateMesh(mesh, v3f(0, -0.57, 0)); + } else + scaleMesh(mesh, v3f(1.2, 1.2, 1.2)); + // add overlays postProcessNodeMesh(mesh, f, false, false, nullptr, - &result->buffer_colors); - } else { - switch (f.drawtype) { - case NDT_PLANTLIKE: { - mesh = getExtrudedMesh(tsrc, - tsrc->getTextureName(f.tiles[0].layers[0].texture_id), - tsrc->getTextureName(f.tiles[0].layers[1].texture_id)); - // Add color - const TileLayer &l0 = f.tiles[0].layers[0]; - result->buffer_colors.emplace_back(l0.has_color, l0.color); - const TileLayer &l1 = f.tiles[0].layers[1]; - result->buffer_colors.emplace_back(l1.has_color, l1.color); - break; - } - case NDT_PLANTLIKE_ROOTED: { - mesh = getExtrudedMesh(tsrc, - tsrc->getTextureName(f.special_tiles[0].layers[0].texture_id), ""); - // Add color - const TileLayer &l0 = f.special_tiles[0].layers[0]; - result->buffer_colors.emplace_back(l0.has_color, l0.color); - break; - } - case NDT_NORMAL: - case NDT_ALLFACES: - case NDT_LIQUID: - case NDT_FLOWINGLIQUID: { - scene::IMesh *cube = g_extrusion_mesh_cache->createCube(); - mesh = cloneMesh(cube); - cube->drop(); - scaleMesh(mesh, v3f(1.2, 1.2, 1.2)); - // add overlays - postProcessNodeMesh(mesh, f, false, false, nullptr, - &result->buffer_colors); - break; - } - default: { - mesh = createSpecialNodeMesh(client, id, &result->buffer_colors); - scaleMesh(mesh, v3f(0.12, 0.12, 0.12)); - } - } + &result->buffer_colors, true); + if (f.drawtype == NDT_ALLFACES) + scaleMesh(mesh, v3f(f.visual_scale)); + break; + } + case NDT_PLANTLIKE: { + mesh = getExtrudedMesh(tsrc, + tsrc->getTextureName(f.tiles[0].layers[0].texture_id), + tsrc->getTextureName(f.tiles[0].layers[1].texture_id)); + // Add color + const TileLayer &l0 = f.tiles[0].layers[0]; + result->buffer_colors.emplace_back(l0.has_color, l0.color); + const TileLayer &l1 = f.tiles[0].layers[1]; + result->buffer_colors.emplace_back(l1.has_color, l1.color); + break; + } + case NDT_PLANTLIKE_ROOTED: { + mesh = getExtrudedMesh(tsrc, + tsrc->getTextureName(f.special_tiles[0].layers[0].texture_id), ""); + // Add color + const TileLayer &l0 = f.special_tiles[0].layers[0]; + result->buffer_colors.emplace_back(l0.has_color, l0.color); + break; + } + default: + // Render non-trivial drawtypes like the actual node + mesh = createSpecialNodeMesh(client, id, &result->buffer_colors, f); + scaleMesh(mesh, v3f(0.12, 0.12, 0.12)); + break; } u32 mc = mesh->getMeshBufferCount(); @@ -593,7 +600,7 @@ void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result) material.MaterialTypeParam = 0.5f; material.setFlag(video::EMF_BILINEAR_FILTER, false); material.setFlag(video::EMF_TRILINEAR_FILTER, false); - material.setFlag(video::EMF_BACK_FACE_CULLING, true); + material.setFlag(video::EMF_BACK_FACE_CULLING, cull_backface); material.setFlag(video::EMF_LIGHTING, false); } diff --git a/src/clientiface.cpp b/src/clientiface.cpp index 602a44c90..01852c5d1 100644 --- a/src/clientiface.cpp +++ b/src/clientiface.cpp @@ -80,10 +80,11 @@ LuaEntitySAO *getAttachedObject(PlayerSAO *sao, ServerEnvironment *env) int id; std::string bone; v3f dummy; - sao->getAttachment(&id, &bone, &dummy, &dummy); + bool force_visible; + sao->getAttachment(&id, &bone, &dummy, &dummy, &force_visible); ServerActiveObject *ao = env->getActiveObject(id); while (id && ao) { - ao->getAttachment(&id, &bone, &dummy, &dummy); + ao->getAttachment(&id, &bone, &dummy, &dummy, &force_visible); if (id) ao = env->getActiveObject(id); } @@ -98,7 +99,6 @@ void RemoteClient::GetNextBlocks ( { // Increment timers m_nothing_to_send_pause_timer -= dtime; - m_nearest_unsent_reset_timer += dtime; if (m_nothing_to_send_pause_timer >= 0) return; @@ -138,34 +138,6 @@ void RemoteClient::GetNextBlocks ( camera_dir.rotateYZBy(sao->getLookPitch()); camera_dir.rotateXZBy(sao->getRotation().Y); - /*infostream<<"camera_dir=("<<camera_dir.X<<","<<camera_dir.Y<<"," - <<camera_dir.Z<<")"<<std::endl;*/ - - /* - Get the starting value of the block finder radius. - */ - - if (m_last_center != center) { - m_nearest_unsent_d = 0; - m_last_center = center; - } - - /*infostream<<"m_nearest_unsent_reset_timer=" - <<m_nearest_unsent_reset_timer<<std::endl;*/ - - // Reset periodically to workaround for some bugs or stuff - if (m_nearest_unsent_reset_timer > 20.0f) { - m_nearest_unsent_reset_timer = 0.0f; - m_nearest_unsent_d = 0; - //infostream<<"Resetting m_nearest_unsent_d for " - // <<server->getPlayerName(peer_id)<<std::endl; - } - - //s16 last_nearest_unsent_d = m_nearest_unsent_d; - s16 d_start = m_nearest_unsent_d; - - //infostream<<"d_start="<<d_start<<std::endl; - u16 max_simul_sends_usually = m_max_simul_sends; /* @@ -197,6 +169,29 @@ void RemoteClient::GetNextBlocks ( s16 wanted_range = sao->getWantedRange() + 1; float camera_fov = sao->getFov(); + /* + Get the starting value of the block finder radius. + */ + if (m_last_center != center) { + m_nearest_unsent_d = 0; + m_last_center = center; + } + // reset the unsent distance if the view angle has changed more that 10% of the fov + // (this matches isBlockInSight which allows for an extra 10%) + if (camera_dir.dotProduct(m_last_camera_dir) < std::cos(camera_fov * 0.1f)) { + m_nearest_unsent_d = 0; + m_last_camera_dir = camera_dir; + } + if (m_nearest_unsent_d > 0) { + // make sure any blocks modified since the last time we sent blocks are resent + for (const v3s16 &p : m_blocks_modified) { + m_nearest_unsent_d = std::min(m_nearest_unsent_d, center.getDistanceFrom(p)); + } + } + m_blocks_modified.clear(); + + s16 d_start = m_nearest_unsent_d; + // Distrust client-sent FOV and get server-set player object property // zoom FOV (degrees) as a check to avoid hacked clients using FOV to load // distant world. @@ -211,13 +206,13 @@ void RemoteClient::GetNextBlocks ( wanted_range); const s16 d_blocks_in_sight = full_d_max * BS * MAP_BLOCKSIZE; - s16 d_max = full_d_max; s16 d_max_gen = std::min(adjustDist(m_max_gen_distance, prop_zoom_fov), wanted_range); - // Don't loop very much at a time, adjust with distance, - // do more work per RTT with greater distances. - s16 max_d_increment_at_time = full_d_max / 9 + 1; + s16 d_max = full_d_max; + + // Don't loop very much at a time + s16 max_d_increment_at_time = 2; if (d_max > d_start + max_d_increment_at_time) d_max = d_start + max_d_increment_at_time; @@ -310,20 +305,14 @@ void RemoteClient::GetNextBlocks ( */ MapBlock *block = env->getMap().getBlockNoCreateNoEx(p); - bool surely_not_found_on_disk = false; - bool block_is_invalid = false; + bool block_not_found = false; if (block) { // Reset usage timer, this block will be of use in the future. block->resetUsageTimer(); - // Block is dummy if data doesn't exist. - // It means it has been not found from disk and not generated - if (block->isDummy()) { - surely_not_found_on_disk = true; - } - - if (!block->isGenerated()) - block_is_invalid = true; + // Check whether the block exists (with data) + if (block->isDummy() || !block->isGenerated()) + block_not_found = true; /* If block is not close, don't send it unless it is near @@ -337,7 +326,7 @@ void RemoteClient::GetNextBlocks ( continue; } - if (m_occ_cull && !block_is_invalid && + if (m_occ_cull && !block_not_found && env->getMap().isBlockOccluded(block, cam_pos_nodes)) { continue; } @@ -347,7 +336,7 @@ void RemoteClient::GetNextBlocks ( If block has been marked to not exist on disk (dummy) or is not generated and generating new ones is not wanted, skip block. */ - if (!generate && (surely_not_found_on_disk || block_is_invalid)) { + if (!generate && block_not_found) { // get next one. continue; } @@ -355,7 +344,7 @@ void RemoteClient::GetNextBlocks ( /* Add inexistent block to emerge queue. */ - if (block == NULL || surely_not_found_on_disk || block_is_invalid) { + if (block == NULL || block_not_found) { if (emerge->enqueueBlockEmerge(peer_id, p, generate)) { if (nearest_emerged_d == -1) nearest_emerged_d = d; @@ -408,21 +397,18 @@ queue_full_break: void RemoteClient::GotBlock(v3s16 p) { - if (m_blocks_modified.find(p) == m_blocks_modified.end()) { - if (m_blocks_sending.find(p) != m_blocks_sending.end()) - m_blocks_sending.erase(p); - else - m_excess_gotblocks++; - + if (m_blocks_sending.find(p) != m_blocks_sending.end()) { + m_blocks_sending.erase(p); + // only add to sent blocks if it actually was sending + // (it might have been modified since) m_blocks_sent.insert(p); + } else { + m_excess_gotblocks++; } } void RemoteClient::SentBlock(v3s16 p) { - if (m_blocks_modified.find(p) != m_blocks_modified.end()) - m_blocks_modified.erase(p); - if (m_blocks_sending.find(p) == m_blocks_sending.end()) m_blocks_sending[p] = 0.0f; else @@ -432,29 +418,24 @@ void RemoteClient::SentBlock(v3s16 p) void RemoteClient::SetBlockNotSent(v3s16 p) { - m_nearest_unsent_d = 0; m_nothing_to_send_pause_timer = 0; - if (m_blocks_sending.find(p) != m_blocks_sending.end()) - m_blocks_sending.erase(p); - if (m_blocks_sent.find(p) != m_blocks_sent.end()) - m_blocks_sent.erase(p); - m_blocks_modified.insert(p); + // remove the block from sending and sent sets, + // and mark as modified if found + if (m_blocks_sending.erase(p) + m_blocks_sent.erase(p) > 0) + m_blocks_modified.insert(p); } void RemoteClient::SetBlocksNotSent(std::map<v3s16, MapBlock*> &blocks) { - m_nearest_unsent_d = 0; m_nothing_to_send_pause_timer = 0; for (auto &block : blocks) { v3s16 p = block.first; - m_blocks_modified.insert(p); - - if (m_blocks_sending.find(p) != m_blocks_sending.end()) - m_blocks_sending.erase(p); - if (m_blocks_sent.find(p) != m_blocks_sent.end()) - m_blocks_sent.erase(p); + // remove the block from sending and sent sets, + // and mark as modified if found + if (m_blocks_sending.erase(p) + m_blocks_sent.erase(p) > 0) + m_blocks_modified.insert(p); } } diff --git a/src/clientiface.h b/src/clientiface.h index 83fa6fe99..eabffb0b6 100644 --- a/src/clientiface.h +++ b/src/clientiface.h @@ -364,7 +364,7 @@ private: std::set<v3s16> m_blocks_sent; s16 m_nearest_unsent_d = 0; v3s16 m_last_center; - float m_nearest_unsent_reset_timer = 0.0f; + v3f m_last_camera_dir; const u16 m_max_simul_sends; const float m_min_time_from_building; @@ -384,10 +384,10 @@ private: std::map<v3s16, float> m_blocks_sending; /* - Blocks that have been modified since last sending them. - These blocks will not be marked as sent, even if the - client reports it has received them to account for blocks - that are being modified while on the line. + Blocks that have been modified since blocks were + sent to the client last (getNextBlocks()). + This is used to reset the unsent distance, so that + modified blocks are resent to the client. List of block positions. */ diff --git a/src/content/CMakeLists.txt b/src/content/CMakeLists.txt index 5adcf6b1e..6dd049418 100644 --- a/src/content/CMakeLists.txt +++ b/src/content/CMakeLists.txt @@ -1,6 +1,5 @@ set(content_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/content.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/packages.cpp ${CMAKE_CURRENT_SOURCE_DIR}/mods.cpp ${CMAKE_CURRENT_SOURCE_DIR}/subgames.cpp PARENT_SCOPE diff --git a/src/content/packages.cpp b/src/content/packages.cpp deleted file mode 100644 index 2d488eb76..000000000 --- a/src/content/packages.cpp +++ /dev/null @@ -1,69 +0,0 @@ -/* -Minetest -Copyright (C) 2018 rubenwardy <rw@rubenwardy.com> - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation; either version 2.1 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License along -with this program; if not, write to the Free Software Foundation, Inc., -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -*/ - -#include "content/packages.h" -#include "log.h" -#include "filesys.h" -#include "porting.h" -#include "settings.h" -#include "content/mods.h" -#include "content/subgames.h" - -std::string Package::getDownloadURL(const std::string &baseURL) const -{ - return baseURL + "/packages/" + author + "/" + name + "/releases/" + - std::to_string(release) + "/download/"; -} - -#if USE_CURL -std::vector<Package> getPackagesFromURL(const std::string &url) -{ - std::vector<std::string> extra_headers; - extra_headers.emplace_back("Accept: application/json"); - - Json::Value json = fetchJsonValue(url, &extra_headers); - if (!json.isArray()) { - errorstream << "Invalid JSON download " << std::endl; - return std::vector<Package>(); - } - - std::vector<Package> packages; - - // Note: `unsigned int` is required to index JSON - for (unsigned int i = 0; i < json.size(); ++i) { - Package package; - - package.author = json[i]["author"].asString(); - package.name = json[i]["name"].asString(); - package.title = json[i]["title"].asString(); - package.type = json[i]["type"].asString(); - package.shortDesc = json[i]["shortDesc"].asString(); - package.release = json[i]["release"].asInt(); - if (json[i].isMember("thumbnail")) - package.thumbnail = json[i]["thumbnail"].asString(); - - if (package.valid()) - packages.push_back(package); - else - errorstream << "Invalid package at " << i << std::endl; - } - - return packages; -} -#endif diff --git a/src/content/packages.h b/src/content/packages.h deleted file mode 100644 index 9029475ef..000000000 --- a/src/content/packages.h +++ /dev/null @@ -1,52 +0,0 @@ -/* -Minetest -Copyright (C) 2018 rubenwardy <rw@rubenwardy.com> - -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 "config.h" -#include "convert_json.h" -#include "irrlichttypes.h" - -struct Package -{ - std::string author; - std::string name; // Technical name - std::string title; - std::string type; // One of "mod", "game", or "txp" - - std::string shortDesc; - u32 release; - std::string thumbnail; - - bool valid() const - { - return !(author.empty() || name.empty() || title.empty() || - type.empty() || release <= 0); - } - - std::string getDownloadURL(const std::string &baseURL) const; -}; - -#if USE_CURL -std::vector<Package> getPackagesFromURL(const std::string &url); -#else -inline std::vector<Package> getPackagesFromURL(const std::string &url) -{ - return std::vector<Package>(); -} -#endif diff --git a/src/content/subgames.cpp b/src/content/subgames.cpp index 170f54e20..c6350f2dd 100644 --- a/src/content/subgames.cpp +++ b/src/content/subgames.cpp @@ -31,12 +31,20 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "client/tile.h" // getImagePath #endif +// The maximum number of identical world names allowed +#define MAX_WORLD_NAMES 100 + +namespace +{ + bool getGameMinetestConfig(const std::string &game_path, Settings &conf) { std::string conf_path = game_path + DIR_DELIM + "minetest.conf"; return conf.readConfigFile(conf_path.c_str()); } +} + struct GameFindPath { std::string path; @@ -213,6 +221,21 @@ bool getWorldExists(const std::string &world_path) fs::PathExists(world_path + DIR_DELIM + "world.mt")); } +//! Try to get the displayed name of a world +std::string getWorldName(const std::string &world_path, const std::string &default_name) +{ + std::string conf_path = world_path + DIR_DELIM + "world.mt"; + Settings conf; + bool succeeded = conf.readConfigFile(conf_path.c_str()); + if (!succeeded) { + return default_name; + } + + if (!conf.exists("world_name")) + return default_name; + return conf.get("world_name"); +} + std::string getWorldGameId(const std::string &world_path, bool can_be_legacy) { std::string conf_path = world_path + DIR_DELIM + "world.mt"; @@ -259,7 +282,7 @@ std::vector<WorldSpec> getAvailableWorlds() if (!dln.dir) continue; std::string fullpath = worldspath + DIR_DELIM + dln.name; - std::string name = dln.name; + std::string name = getWorldName(fullpath, dln.name); // Just allow filling in the gameid always for now bool can_be_legacy = true; std::string gameid = getWorldGameId(fullpath, can_be_legacy); @@ -288,27 +311,47 @@ std::vector<WorldSpec> getAvailableWorlds() return worlds; } -bool loadGameConfAndInitWorld(const std::string &path, const SubgameSpec &gamespec) +void loadGameConfAndInitWorld(const std::string &path, const std::string &name, + const SubgameSpec &gamespec, bool create_world) { + std::string final_path = path; + + // If we're creating a new world, ensure that the path isn't already taken + if (create_world) { + int counter = 1; + while (fs::PathExists(final_path) && counter < MAX_WORLD_NAMES) { + final_path = path + "_" + std::to_string(counter); + counter++; + } + + if (fs::PathExists(final_path)) { + throw BaseException("Too many similar filenames"); + } + } + // Override defaults with those provided by the game. // We clear and reload the defaults because the defaults // might have been overridden by other subgame config // files that were loaded before. g_settings->clearDefaults(); set_default_settings(g_settings); + Settings game_defaults; getGameMinetestConfig(gamespec.path, game_defaults); + game_defaults.removeSecureSettings(); + g_settings->overrideDefaults(&game_defaults); - infostream << "Initializing world at " << path << std::endl; + infostream << "Initializing world at " << final_path << std::endl; - fs::CreateAllDirs(path); + fs::CreateAllDirs(final_path); // Create world.mt if does not already exist - std::string worldmt_path = path + DIR_DELIM "world.mt"; + std::string worldmt_path = final_path + DIR_DELIM "world.mt"; if (!fs::PathExists(worldmt_path)) { Settings conf; + conf.set("world_name", name); conf.set("gameid", gamespec.id); conf.set("backend", "sqlite3"); conf.set("player_backend", "sqlite3"); @@ -316,16 +359,16 @@ bool loadGameConfAndInitWorld(const std::string &path, const SubgameSpec &gamesp conf.setBool("creative_mode", g_settings->getBool("creative_mode")); conf.setBool("enable_damage", g_settings->getBool("enable_damage")); - if (!conf.updateConfigFile(worldmt_path.c_str())) - return false; + if (!conf.updateConfigFile(worldmt_path.c_str())) { + throw BaseException("Failed to update the config file"); + } } // Create map_meta.txt if does not already exist - std::string map_meta_path = path + DIR_DELIM + "map_meta.txt"; + std::string map_meta_path = final_path + DIR_DELIM + "map_meta.txt"; if (!fs::PathExists(map_meta_path)) { verbosestream << "Creating map_meta.txt (" << map_meta_path << ")" << std::endl; - fs::CreateAllDirs(path); std::ostringstream oss(std::ios_base::binary); Settings conf; @@ -338,5 +381,4 @@ bool loadGameConfAndInitWorld(const std::string &path, const SubgameSpec &gamesp fs::safeWriteToFile(map_meta_path, oss.str()); } - return true; } diff --git a/src/content/subgames.h b/src/content/subgames.h index 4198ea860..60392639b 100644 --- a/src/content/subgames.h +++ b/src/content/subgames.h @@ -53,9 +53,6 @@ struct SubgameSpec bool isValid() const { return (!id.empty() && !path.empty()); } }; -// minetest.conf -bool getGameMinetestConfig(const std::string &game_path, Settings &conf); - SubgameSpec findSubgame(const std::string &id); SubgameSpec findWorldSubgame(const std::string &world_path); @@ -63,6 +60,8 @@ std::set<std::string> getAvailableGameIds(); std::vector<SubgameSpec> getAvailableGames(); bool getWorldExists(const std::string &world_path); +//! Try to get the displayed name of a world +std::string getWorldName(const std::string &world_path, const std::string &default_name); std::string getWorldGameId(const std::string &world_path, bool can_be_legacy = false); struct WorldSpec @@ -88,4 +87,5 @@ std::vector<WorldSpec> getAvailableWorlds(); // loads the subgame's config and creates world directory // and world.mt if they don't exist -bool loadGameConfAndInitWorld(const std::string &path, const SubgameSpec &gamespec); +void loadGameConfAndInitWorld(const std::string &path, const std::string &name, + const SubgameSpec &gamespec, bool create_world); diff --git a/src/content_nodemeta.cpp b/src/content_nodemeta.cpp index fc2859d27..39743c11f 100644 --- a/src/content_nodemeta.cpp +++ b/src/content_nodemeta.cpp @@ -43,26 +43,26 @@ static bool content_nodemeta_deserialize_legacy_body( if(id == NODEMETA_GENERIC) // GenericNodeMetadata (0.4-dev) { meta->getInventory()->deSerialize(is); - deSerializeLongString(is); // m_text - deSerializeString(is); // m_owner + deSerializeString32(is); // m_text + deSerializeString16(is); // m_owner - meta->setString("infotext",deSerializeString(is)); - meta->setString("formspec",deSerializeString(is)); + meta->setString("infotext",deSerializeString16(is)); + meta->setString("formspec",deSerializeString16(is)); readU8(is); // m_allow_text_input readU8(is); // m_allow_removal readU8(is); // m_enforce_owner int num_vars = readU32(is); for(int i=0; i<num_vars; i++){ - std::string name = deSerializeString(is); - std::string var = deSerializeLongString(is); + std::string name = deSerializeString16(is); + std::string var = deSerializeString32(is); meta->setString(name, var); } return false; } else if(id == NODEMETA_SIGN) // SignNodeMetadata { - meta->setString("text", deSerializeString(is)); + meta->setString("text", deSerializeString16(is)); //meta->setString("infotext","\"${text}\""); meta->setString("infotext", std::string("\"") + meta->getString("text") + "\""); @@ -87,7 +87,7 @@ static bool content_nodemeta_deserialize_legacy_body( } else if(id == NODEMETA_LOCKABLE_CHEST) // LockingChestNodeMetadata { - meta->setString("owner", deSerializeString(is)); + meta->setString("owner", deSerializeString16(is)); meta->getInventory()->deSerialize(is); // Rename inventory list "0" to "main" @@ -138,7 +138,7 @@ static bool content_nodemeta_deserialize_legacy_meta( s16 id = readS16(is); // Read data - std::string data = deSerializeString(is); + std::string data = deSerializeString16(is); std::istringstream tmp_is(data, std::ios::binary); return content_nodemeta_deserialize_legacy_body(tmp_is, id, meta); } diff --git a/src/database/database-dummy.cpp b/src/database/database-dummy.cpp index a3d8cd579..b56f341c5 100644 --- a/src/database/database-dummy.cpp +++ b/src/database/database-dummy.cpp @@ -22,6 +22,7 @@ Dummy database class */ #include "database-dummy.h" +#include "remoteplayer.h" bool Database_Dummy::saveBlock(const v3s16 &pos, const std::string &data) @@ -57,3 +58,25 @@ void Database_Dummy::listAllLoadableBlocks(std::vector<v3s16> &dst) } } +void Database_Dummy::savePlayer(RemotePlayer *player) +{ + m_player_database.insert(player->getName()); +} + +bool Database_Dummy::loadPlayer(RemotePlayer *player, PlayerSAO *sao) +{ + return m_player_database.find(player->getName()) != m_player_database.end(); +} + +bool Database_Dummy::removePlayer(const std::string &name) +{ + m_player_database.erase(name); + return true; +} + +void Database_Dummy::listPlayers(std::vector<std::string> &res) +{ + for (const auto &player : m_player_database) { + res.emplace_back(player); + } +} diff --git a/src/database/database-dummy.h b/src/database/database-dummy.h index 2d87d58f6..b69919f84 100644 --- a/src/database/database-dummy.h +++ b/src/database/database-dummy.h @@ -32,14 +32,15 @@ public: bool deleteBlock(const v3s16 &pos); void listAllLoadableBlocks(std::vector<v3s16> &dst); - void savePlayer(RemotePlayer *player) {} - bool loadPlayer(RemotePlayer *player, PlayerSAO *sao) { return true; } - bool removePlayer(const std::string &name) { return true; } - void listPlayers(std::vector<std::string> &res) {} + void savePlayer(RemotePlayer *player); + bool loadPlayer(RemotePlayer *player, PlayerSAO *sao); + bool removePlayer(const std::string &name); + void listPlayers(std::vector<std::string> &res); void beginSave() {} void endSave() {} private: std::map<s64, std::string> m_database; + std::set<std::string> m_player_database; }; diff --git a/src/database/database-leveldb.cpp b/src/database/database-leveldb.cpp index 1976ae13d..73cd63f6d 100644 --- a/src/database/database-leveldb.cpp +++ b/src/database/database-leveldb.cpp @@ -145,8 +145,8 @@ void PlayerDatabaseLevelDB::savePlayer(RemotePlayer *player) StringMap stringvars = sao->getMeta().getStrings(); writeU32(os, stringvars.size()); for (const auto &it : stringvars) { - os << serializeString(it.first); - os << serializeLongString(it.second); + os << serializeString16(it.first); + os << serializeString32(it.second); } player->inventory.serialize(os); @@ -183,8 +183,8 @@ bool PlayerDatabaseLevelDB::loadPlayer(RemotePlayer *player, PlayerSAO *sao) u32 attribute_count = readU32(is); for (u32 i = 0; i < attribute_count; i++) { - std::string name = deSerializeString(is); - std::string value = deSerializeLongString(is); + std::string name = deSerializeString16(is); + std::string value = deSerializeString32(is); sao->getMeta().setString(name, value); } sao->getMeta().setModified(false); @@ -247,13 +247,13 @@ bool AuthDatabaseLevelDB::getAuth(const std::string &name, AuthEntry &res) res.id = 1; res.name = name; - res.password = deSerializeString(is); + res.password = deSerializeString16(is); u16 privilege_count = readU16(is); res.privileges.clear(); res.privileges.reserve(privilege_count); for (u16 i = 0; i < privilege_count; i++) { - res.privileges.push_back(deSerializeString(is)); + res.privileges.push_back(deSerializeString16(is)); } res.last_login = readS64(is); @@ -264,14 +264,14 @@ bool AuthDatabaseLevelDB::saveAuth(const AuthEntry &authEntry) { std::ostringstream os; writeU8(os, 1); - os << serializeString(authEntry.password); + os << serializeString16(authEntry.password); size_t privilege_count = authEntry.privileges.size(); FATAL_ERROR_IF(privilege_count > U16_MAX, "Unsupported number of privileges"); writeU16(os, privilege_count); for (const std::string &privilege : authEntry.privileges) { - os << serializeString(privilege); + os << serializeString16(privilege); } writeS64(os, authEntry.last_login); diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index bfcee01f8..8050b533c 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -42,7 +42,7 @@ void set_default_settings(Settings *settings) settings->setDefault("mute_sound", "false"); settings->setDefault("enable_mesh_cache", "false"); settings->setDefault("mesh_generation_interval", "0"); - settings->setDefault("meshgen_block_cache_size", "20"); + settings->setDefault("meshgen_block_cache_size", "40"); settings->setDefault("enable_vbo", "true"); settings->setDefault("free_move", "false"); settings->setDefault("pitch_move", "false"); @@ -52,7 +52,7 @@ void set_default_settings(Settings *settings) settings->setDefault("screenshot_format", "png"); settings->setDefault("screenshot_quality", "0"); settings->setDefault("client_unload_unused_data_timeout", "600"); - settings->setDefault("client_mapblock_limit", "5000"); + settings->setDefault("client_mapblock_limit", "7500"); settings->setDefault("enable_build_where_you_stand", "true"); settings->setDefault("curl_timeout", "5000"); settings->setDefault("curl_parallel_limit", "8"); @@ -155,6 +155,8 @@ void set_default_settings(Settings *settings) settings->setDefault("keymap_right", "KEY_KEY_D"); settings->setDefault("keymap_jump", "KEY_SPACE"); settings->setDefault("keymap_sneak", "KEY_LSHIFT"); + settings->setDefault("keymap_dig", "KEY_LBUTTON"); + settings->setDefault("keymap_place", "KEY_RBUTTON"); settings->setDefault("keymap_drop", "KEY_KEY_Q"); settings->setDefault("keymap_zoom", "KEY_KEY_Z"); settings->setDefault("keymap_inventory", "KEY_KEY_I"); @@ -256,8 +258,8 @@ void set_default_settings(Settings *settings) settings->setDefault("tooltip_show_delay", "400"); settings->setDefault("tooltip_append_itemname", "false"); settings->setDefault("fps_max", "60"); - settings->setDefault("pause_fps_max", "20"); - settings->setDefault("viewing_range", "100"); + settings->setDefault("fps_max_unfocused", "20"); + settings->setDefault("viewing_range", "190"); #if ENABLE_GLES settings->setDefault("near_plane", "0.1"); #endif @@ -321,7 +323,7 @@ void set_default_settings(Settings *settings) settings->setDefault("show_entity_selectionbox", "false"); settings->setDefault("texture_clean_transparent", "false"); settings->setDefault("texture_min_size", "64"); - settings->setDefault("ambient_occlusion_gamma", "2.2"); + settings->setDefault("ambient_occlusion_gamma", "1.8"); #if ENABLE_GLES settings->setDefault("enable_shaders", "false"); #else @@ -342,15 +344,6 @@ void set_default_settings(Settings *settings) settings->setDefault("bilinear_filter", "false"); settings->setDefault("trilinear_filter", "false"); settings->setDefault("tone_mapping", "false"); - settings->setDefault("enable_bumpmapping", "false"); - settings->setDefault("enable_parallax_occlusion", "false"); - settings->setDefault("generate_normalmaps", "false"); - settings->setDefault("normalmaps_strength", "0.6"); - settings->setDefault("normalmaps_smooth", "1"); - settings->setDefault("parallax_occlusion_mode", "1"); - settings->setDefault("parallax_occlusion_iterations", "4"); - settings->setDefault("parallax_occlusion_scale", "0.08"); - settings->setDefault("parallax_occlusion_bias", "0.04"); settings->setDefault("enable_waving_water", "false"); settings->setDefault("water_wave_height", "1.0"); settings->setDefault("water_wave_length", "20.0"); @@ -362,7 +355,7 @@ void set_default_settings(Settings *settings) // Input settings->setDefault("invert_mouse", "false"); settings->setDefault("mouse_sensitivity", "0.2"); - settings->setDefault("repeat_rightclick_time", "0.25"); + settings->setDefault("repeat_place_time", "0.25"); settings->setDefault("safe_dig_and_place", "false"); settings->setDefault("random_input", "false"); settings->setDefault("aux1_descends", "false"); @@ -381,7 +374,6 @@ void set_default_settings(Settings *settings) settings->setDefault("joystick_frustum_sensitivity", "170"); // Main menu - settings->setDefault("main_menu_style", "full"); settings->setDefault("main_menu_path", ""); settings->setDefault("serverlist_file", "favoriteservers.txt"); @@ -421,6 +413,8 @@ void set_default_settings(Settings *settings) // ContentDB settings->setDefault("contentdb_url", "https://content.minetest.net"); + settings->setDefault("contentdb_max_concurrent_downloads", "3"); + #ifdef __ANDROID__ settings->setDefault("contentdb_flag_blacklist", "nonfree, android_default"); #else @@ -442,7 +436,7 @@ void set_default_settings(Settings *settings) settings->setDefault("port", "30000"); settings->setDefault("strict_protocol_version_checking", "false"); settings->setDefault("player_transfer_distance", "0"); - settings->setDefault("max_simultaneous_block_sends_per_client", "40"); + settings->setDefault("max_simultaneous_block_sends_per_client", "128"); settings->setDefault("time_send_interval", "5"); settings->setDefault("default_game", "mineclone2"); @@ -457,11 +451,7 @@ void set_default_settings(Settings *settings) settings->setDefault("disallow_empty_password", "false"); settings->setDefault("disable_anticheat", "false"); settings->setDefault("enable_rollback_recording", "false"); -#ifdef NDEBUG - settings->setDefault("deprecated_lua_api_handling", "legacy"); -#else settings->setDefault("deprecated_lua_api_handling", "log"); -#endif settings->setDefault("kick_msg_shutdown", "Server shutting down."); settings->setDefault("kick_msg_crash", "This server has experienced an internal error. You will now be disconnected."); @@ -469,11 +459,11 @@ void set_default_settings(Settings *settings) settings->setDefault("chat_message_format", "<@name> @message"); settings->setDefault("profiler_print_interval", "0"); - settings->setDefault("active_object_send_range_blocks", "4"); - settings->setDefault("active_block_range", "3"); + settings->setDefault("active_object_send_range_blocks", "8"); + settings->setDefault("active_block_range", "4"); //settings->setDefault("max_simultaneous_block_sends_per_client", "1"); // This causes frametime jitter on client side, or does it? - settings->setDefault("max_block_send_distance", "10"); + settings->setDefault("max_block_send_distance", "12"); settings->setDefault("block_send_optimize_distance", "4"); settings->setDefault("server_side_occlusion_culling", "true"); settings->setDefault("csm_restriction_flags", "62"); @@ -492,15 +482,16 @@ void set_default_settings(Settings *settings) settings->setDefault("dedicated_server_step", "0.09"); settings->setDefault("active_block_mgmt_interval", "2.0"); settings->setDefault("abm_interval", "1.0"); + settings->setDefault("abm_time_budget", "0.2"); settings->setDefault("nodetimer_interval", "0.2"); settings->setDefault("ignore_world_load_errors", "false"); settings->setDefault("remote_media", ""); settings->setDefault("debug_log_level", "action"); settings->setDefault("debug_log_size_max", "50"); settings->setDefault("chat_log_level", "error"); - settings->setDefault("emergequeue_limit_total", "512"); - settings->setDefault("emergequeue_limit_diskonly", "64"); - settings->setDefault("emergequeue_limit_generate", "64"); + settings->setDefault("emergequeue_limit_total", "1024"); + settings->setDefault("emergequeue_limit_diskonly", "128"); + settings->setDefault("emergequeue_limit_generate", "128"); settings->setDefault("num_emerge_threads", "1"); settings->setDefault("secure.enable_security", "true"); settings->setDefault("secure.trusted_mods", ""); @@ -531,7 +522,7 @@ void set_default_settings(Settings *settings) settings->setDefault("mapgen_limit", "31000"); settings->setDefault("chunksize", "5"); settings->setDefault("fixed_map_seed", ""); - settings->setDefault("max_block_generate_distance", "8"); + settings->setDefault("max_block_generate_distance", "10"); settings->setDefault("enable_mapgen_debug_info", "false"); Mapgen::setDefaultSettings(settings); @@ -569,7 +560,7 @@ void set_default_settings(Settings *settings) settings->setDefault("max_block_generate_distance", "5"); settings->setDefault("enable_3d_clouds", "false"); settings->setDefault("fps_max", "30"); - settings->setDefault("pause_fps_max", "10"); + settings->setDefault("fps_max_unfocused", "10"); settings->setDefault("max_objects_per_block", "20"); settings->setDefault("sqlite_synchronous", "1"); settings->setDefault("server_map_save_interval", "15"); diff --git a/src/emerge.cpp b/src/emerge.cpp index 0ac26a682..12e407797 100644 --- a/src/emerge.cpp +++ b/src/emerge.cpp @@ -426,6 +426,10 @@ bool EmergeManager::pushBlockEmergeData( m_qlimit_generate : m_qlimit_diskonly; if (count_peer >= qlimit_peer) return false; + } else { + // limit block enqueue requests for active blocks to 1/2 of total + if (count_peer * 2 >= m_qlimit_total) + return false; } } diff --git a/src/emerge.h b/src/emerge.h index 6f204666d..da845e243 100644 --- a/src/emerge.h +++ b/src/emerge.h @@ -52,7 +52,6 @@ struct BlockMakeData { u64 seed = 0; v3s16 blockpos_min; v3s16 blockpos_max; - v3s16 blockpos_requested; UniqueQueue<v3s16> transforming_liquid; const NodeDefManager *nodedef = nullptr; diff --git a/src/environment.cpp b/src/environment.cpp index 7acad313e..f10f773cf 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -36,6 +36,7 @@ Environment::Environment(IGameDef *gamedef): m_cache_active_block_mgmt_interval = g_settings->getFloat("active_block_mgmt_interval"); m_cache_abm_interval = g_settings->getFloat("abm_interval"); m_cache_nodetimer_interval = g_settings->getFloat("nodetimer_interval"); + m_cache_abm_time_budget = g_settings->getFloat("abm_time_budget"); m_time_of_day = g_settings->getU32("world_start_time"); m_time_of_day_f = (float)m_time_of_day / 24000.0f; diff --git a/src/environment.h b/src/environment.h index 91c33ba15..b4884fdb3 100644 --- a/src/environment.h +++ b/src/environment.h @@ -147,6 +147,7 @@ protected: float m_cache_active_block_mgmt_interval; float m_cache_abm_interval; float m_cache_nodetimer_interval; + float m_cache_abm_time_budget; IGameDef *m_gamedef; diff --git a/src/filesys.cpp b/src/filesys.cpp index 0bc351669..2470b1b64 100644 --- a/src/filesys.cpp +++ b/src/filesys.cpp @@ -750,6 +750,21 @@ bool safeWriteToFile(const std::string &path, const std::string &content) return true; } +bool ReadFile(const std::string &path, std::string &out) +{ + std::ifstream is(path, std::ios::binary | std::ios::ate); + if (!is.good()) { + return false; + } + + auto size = is.tellg(); + out.resize(size); + is.seekg(0); + is.read(&out[0], size); + + return true; +} + bool Rename(const std::string &from, const std::string &to) { return rename(from.c_str(), to.c_str()) == 0; diff --git a/src/filesys.h b/src/filesys.h index 09f129aa3..b2c800c55 100644 --- a/src/filesys.h +++ b/src/filesys.h @@ -128,6 +128,8 @@ const char *GetFilenameFromPath(const char *path); bool safeWriteToFile(const std::string &path, const std::string &content); +bool ReadFile(const std::string &path, std::string &out); + bool Rename(const std::string &from, const std::string &to); } // namespace fs diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 7befba37c..91476ada6 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -16,6 +16,7 @@ set(gui_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/guiKeyChangeMenu.cpp ${CMAKE_CURRENT_SOURCE_DIR}/guiPasswordChange.cpp ${CMAKE_CURRENT_SOURCE_DIR}/guiPathSelectMenu.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/guiScene.cpp ${CMAKE_CURRENT_SOURCE_DIR}/guiScrollBar.cpp ${CMAKE_CURRENT_SOURCE_DIR}/guiScrollContainer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/guiSkin.cpp diff --git a/src/gui/StyleSpec.h b/src/gui/StyleSpec.h index 67caf4f7b..f2844ce28 100644 --- a/src/gui/StyleSpec.h +++ b/src/gui/StyleSpec.h @@ -24,6 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/string.h" #include <algorithm> #include <array> +#include <vector> #pragma once @@ -50,6 +51,10 @@ public: PADDING, FONT, FONT_SIZE, + COLORS, + BORDERCOLORS, + BORDERWIDTHS, + SOUND, NUM_PROPERTIES, NONE }; @@ -106,6 +111,14 @@ public: return FONT; } else if (name == "font_size") { return FONT_SIZE; + } else if (name == "colors") { + return COLORS; + } else if (name == "bordercolors") { + return BORDERCOLORS; + } else if (name == "borderwidths") { + return BORDERWIDTHS; + } else if (name == "sound") { + return SOUND; } else { return NONE; } @@ -187,6 +200,42 @@ public: return color; } + std::array<video::SColor, 4> getColorArray(Property prop, + std::array<video::SColor, 4> def) const + { + const auto &val = properties[prop]; + if (val.empty()) + return def; + + std::vector<std::string> strs; + if (!parseArray(val, strs)) + return def; + + for (size_t i = 0; i <= 3; i++) { + video::SColor color; + if (parseColorString(strs[i], color, false, 0xff)) + def[i] = color; + } + + return def; + } + + std::array<s32, 4> getIntArray(Property prop, std::array<s32, 4> def) const + { + const auto &val = properties[prop]; + if (val.empty()) + return def; + + std::vector<std::string> strs; + if (!parseArray(val, strs)) + return def; + + for (size_t i = 0; i <= 3; i++) + def[i] = stoi(strs[i]); + + return def; + } + irr::core::rect<s32> getRect(Property prop, irr::core::rect<s32> def) const { const auto &val = properties[prop]; @@ -334,6 +383,24 @@ public: } private: + bool parseArray(const std::string &value, std::vector<std::string> &arr) const + { + std::vector<std::string> strs = split(value, ','); + + if (strs.size() == 1) { + arr = {strs[0], strs[0], strs[0], strs[0]}; + } else if (strs.size() == 2) { + arr = {strs[0], strs[1], strs[0], strs[1]}; + } else if (strs.size() == 4) { + arr = strs; + } else { + warningstream << "Invalid array size (" << strs.size() + << " arguments): \"" << value << "\"" << std::endl; + return false; + } + return true; + } + bool parseRect(const std::string &value, irr::core::rect<s32> *parsed_rect) const { irr::core::rect<s32> rect; diff --git a/src/gui/guiBox.cpp b/src/gui/guiBox.cpp index 7f329cc32..443f1064f 100644 --- a/src/gui/guiBox.cpp +++ b/src/gui/guiBox.cpp @@ -20,9 +20,14 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "guiBox.h" GUIBox::GUIBox(gui::IGUIEnvironment *env, gui::IGUIElement *parent, s32 id, - const core::rect<s32> &rectangle, const video::SColor &color) : + const core::rect<s32> &rectangle, + const std::array<video::SColor, 4> &colors, + const std::array<video::SColor, 4> &bordercolors, + const std::array<s32, 4> &borderwidths) : gui::IGUIElement(gui::EGUIET_ELEMENT, env, parent, id, rectangle), - m_color(color) + m_colors(colors), + m_bordercolors(bordercolors), + m_borderwidths(borderwidths) { } @@ -31,8 +36,82 @@ void GUIBox::draw() if (!IsVisible) return; - Environment->getVideoDriver()->draw2DRectangle(m_color, AbsoluteRect, - &AbsoluteClippingRect); + std::array<s32, 4> negative_borders = {0, 0, 0, 0}; + std::array<s32, 4> positive_borders = {0, 0, 0, 0}; + + for (size_t i = 0; i <= 3; i++) { + if (m_borderwidths[i] > 0) + positive_borders[i] = m_borderwidths[i]; + else + negative_borders[i] = m_borderwidths[i]; + } + + v2s32 upperleft = AbsoluteRect.UpperLeftCorner; + v2s32 lowerright = AbsoluteRect.LowerRightCorner; + + v2s32 topleft_border = { + upperleft.X - positive_borders[3], + upperleft.Y - positive_borders[0] + }; + v2s32 topleft_rect = { + upperleft.X - negative_borders[3], + upperleft.Y - negative_borders[0] + }; + + v2s32 lowerright_border = { + lowerright.X + positive_borders[1], + lowerright.Y + positive_borders[2] + }; + v2s32 lowerright_rect = { + lowerright.X + negative_borders[1], + lowerright.Y + negative_borders[2] + }; + + core::rect<s32> main_rect( + topleft_rect.X, + topleft_rect.Y, + lowerright_rect.X, + lowerright_rect.Y + ); + + std::array<core::rect<s32>, 4> border_rects; + + border_rects[0] = core::rect<s32>( + topleft_border.X, + topleft_border.Y, + lowerright_border.X, + topleft_rect.Y + ); + + border_rects[1] = core::rect<s32>( + lowerright_rect.X, + topleft_rect.Y, + lowerright_border.X, + lowerright_rect.Y + ); + + border_rects[2] = core::rect<s32>( + topleft_border.X, + lowerright_rect.Y, + lowerright_border.X, + lowerright_border.Y + ); + + border_rects[3] = core::rect<s32>( + topleft_border.X, + topleft_rect.Y, + topleft_rect.X, + lowerright_rect.Y + ); + + video::IVideoDriver *driver = Environment->getVideoDriver(); + + driver->draw2DRectangle(main_rect, m_colors[0], m_colors[1], m_colors[3], + m_colors[2], &AbsoluteClippingRect); + + for (size_t i = 0; i <= 3; i++) + driver->draw2DRectangle(m_bordercolors[i], border_rects[i], + &AbsoluteClippingRect); IGUIElement::draw(); } diff --git a/src/gui/guiBox.h b/src/gui/guiBox.h index 5306fdf65..ca8f83771 100644 --- a/src/gui/guiBox.h +++ b/src/gui/guiBox.h @@ -19,16 +19,23 @@ with this program; if not, write to the Free Software Foundation, Inc., #pragma once +#include <vector> +#include <array> #include "irrlichttypes_extrabloated.h" class GUIBox : public gui::IGUIElement { public: GUIBox(gui::IGUIEnvironment *env, gui::IGUIElement *parent, s32 id, - const core::rect<s32> &rectangle, const video::SColor &color); + const core::rect<s32> &rectangle, + const std::array<video::SColor, 4> &colors, + const std::array<video::SColor, 4> &bordercolors, + const std::array<s32, 4> &borderwidths); virtual void draw() override; private: - video::SColor m_color; + std::array<video::SColor, 4> m_colors; + std::array<video::SColor, 4> m_bordercolors; + std::array<s32, 4> m_borderwidths; }; diff --git a/src/gui/guiButton.cpp b/src/gui/guiButton.cpp index e0d6337cd..b98e5de82 100644 --- a/src/gui/guiButton.cpp +++ b/src/gui/guiButton.cpp @@ -313,11 +313,12 @@ void GUIButton::draw() // PATCH
video::ITexture* texture = ButtonImages[(u32)imageState].Texture;
+ video::SColor image_colors[] = { BgColor, BgColor, BgColor, BgColor };
if (BgMiddle.getArea() == 0) {
driver->draw2DImage(texture,
ScaleImage? AbsoluteRect : core::rect<s32>(pos, sourceRect.getSize()),
sourceRect, &AbsoluteClippingRect,
- 0, UseAlphaChannel);
+ image_colors, UseAlphaChannel);
} else {
core::rect<s32> middle = BgMiddle;
// `-x` is interpreted as `w - x`
@@ -327,7 +328,7 @@ void GUIButton::draw() middle.LowerRightCorner.Y += texture->getOriginalSize().Height;
draw2DImage9Slice(driver, texture,
ScaleImage ? AbsoluteRect : core::rect<s32>(pos, sourceRect.getSize()),
- middle, &AbsoluteClippingRect);
+ middle, &AbsoluteClippingRect, image_colors);
}
// END PATCH
}
@@ -722,6 +723,8 @@ GUIButton* GUIButton::addButton(IGUIEnvironment *environment, void GUIButton::setColor(video::SColor color)
{
+ BgColor = color;
+
float d = 0.65f;
for (size_t i = 0; i < 4; i++) {
video::SColor base = Environment->getSkin()->getColor((gui::EGUI_DEFAULT_COLOR)i);
@@ -750,22 +753,26 @@ void GUIButton::setFromStyle(const StyleSpec& style) bool pressed = (style.getState() & StyleSpec::STATE_PRESSED) != 0;
if (style.isNotDefault(StyleSpec::BGCOLOR)) {
-
setColor(style.getColor(StyleSpec::BGCOLOR));
// If we have a propagated hover/press color, we need to automatically
// lighten/darken it
if (!Styles[style.getState()].isNotDefault(StyleSpec::BGCOLOR)) {
- for (size_t i = 0; i < 4; i++) {
if (pressed) {
- Colors[i] = multiplyColorValue(Colors[i], COLOR_PRESSED_MOD);
+ BgColor = multiplyColorValue(BgColor, COLOR_PRESSED_MOD);
+
+ for (size_t i = 0; i < 4; i++)
+ Colors[i] = multiplyColorValue(Colors[i], COLOR_PRESSED_MOD);
} else if (hovered) {
- Colors[i] = multiplyColorValue(Colors[i], COLOR_HOVERED_MOD);
+ BgColor = multiplyColorValue(BgColor, COLOR_HOVERED_MOD);
+
+ for (size_t i = 0; i < 4; i++)
+ Colors[i] = multiplyColorValue(Colors[i], COLOR_HOVERED_MOD);
}
- }
}
} else {
+ BgColor = video::SColor(255, 255, 255, 255);
for (size_t i = 0; i < 4; i++) {
video::SColor base =
Environment->getSkin()->getColor((gui::EGUI_DEFAULT_COLOR)i);
diff --git a/src/gui/guiButton.h b/src/gui/guiButton.h index 95fa1a2a1..4e1b04aac 100644 --- a/src/gui/guiButton.h +++ b/src/gui/guiButton.h @@ -338,5 +338,6 @@ private: core::rect<s32> BgMiddle;
core::rect<s32> Padding;
core::vector2d<s32> ContentOffset;
+ video::SColor BgColor;
// END PATCH
};
diff --git a/src/gui/guiChatConsole.cpp b/src/gui/guiChatConsole.cpp index 8de00c12f..ef471106d 100644 --- a/src/gui/guiChatConsole.cpp +++ b/src/gui/guiChatConsole.cpp @@ -136,11 +136,6 @@ void GUIChatConsole::closeConsoleAtOnce() recalculateConsolePosition(); } -f32 GUIChatConsole::getDesiredHeight() const -{ - return m_desired_height_fraction; -} - void GUIChatConsole::replaceAndAddToHistory(const std::wstring &line) { ChatPrompt& prompt = m_chat_backend->getPrompt(); @@ -545,7 +540,7 @@ bool GUIChatConsole::OnEvent(const SEvent& event) if (prompt.getCursorLength() <= 0) return true; std::wstring wselected = prompt.getSelection(); - std::string selected(wselected.begin(), wselected.end()); + std::string selected = wide_to_utf8(wselected); Environment->getOSOperator()->copyToClipboard(selected.c_str()); return true; } @@ -575,7 +570,7 @@ bool GUIChatConsole::OnEvent(const SEvent& event) if (prompt.getCursorLength() <= 0) return true; std::wstring wselected = prompt.getSelection(); - std::string selected(wselected.begin(), wselected.end()); + std::string selected = wide_to_utf8(wselected); Environment->getOSOperator()->copyToClipboard(selected.c_str()); prompt.cursorOperation( ChatPrompt::CURSOROP_DELETE, diff --git a/src/gui/guiChatConsole.h b/src/gui/guiChatConsole.h index 7be40e27c..204f9f9cc 100644 --- a/src/gui/guiChatConsole.h +++ b/src/gui/guiChatConsole.h @@ -55,10 +55,6 @@ public: // Set whether to close the console after the user presses enter. void setCloseOnEnter(bool close) { m_close_on_enter = close; } - // Return the desired height (fraction of screen size) - // Zero if the console is closed or getting closed - f32 getDesiredHeight() const; - // Replace actual line when adding the actual to the history (if there is any) void replaceAndAddToHistory(const std::wstring &line); diff --git a/src/gui/guiConfirmRegistration.cpp b/src/gui/guiConfirmRegistration.cpp index 55c111df8..020a2796a 100644 --- a/src/gui/guiConfirmRegistration.cpp +++ b/src/gui/guiConfirmRegistration.cpp @@ -73,7 +73,11 @@ void GUIConfirmRegistration::regenerateGui(v2u32 screensize) /* Calculate new sizes and positions */ +#ifdef __ANDROID__ + const float s = m_gui_scale * porting::getDisplayDensity() / 2; +#else const float s = m_gui_scale; +#endif DesiredRect = core::rect<s32>( screensize.X / 2 - 600 * s / 2, screensize.Y / 2 - 360 * s / 2, @@ -257,12 +261,19 @@ bool GUIConfirmRegistration::getAndroidUIInput() if (!hasAndroidUIInput() || m_jni_field_name != "password") return false; - std::string text = porting::getInputDialogValue(); - gui::IGUIElement *e = getElementFromId(ID_confirmPassword); - if (e) - e->setText(utf8_to_wide(text).c_str()); + // still waiting + if (porting::getInputDialogState() == -1) + return true; m_jni_field_name.clear(); + + gui::IGUIElement *e = getElementFromId(ID_confirmPassword); + + if (!e || e->getType() != irr::gui::EGUIET_EDIT_BOX) + return false; + + std::string text = porting::getInputDialogValue(); + e->setText(utf8_to_wide(text).c_str()); return false; } #endif diff --git a/src/gui/guiEditBoxWithScrollbar.cpp b/src/gui/guiEditBoxWithScrollbar.cpp index 442406688..169425a9a 100644 --- a/src/gui/guiEditBoxWithScrollbar.cpp +++ b/src/gui/guiEditBoxWithScrollbar.cpp @@ -1028,7 +1028,7 @@ void GUIEditBoxWithScrollBar::breakText() s32 last_line_start = 0; s32 size = Text.size(); s32 length = 0; - s32 el_width = RelativeRect.getWidth() - 6; + s32 el_width = RelativeRect.getWidth() - m_scrollbar_width - 10; wchar_t c; for (s32 i = 0; i < size; ++i) { @@ -1399,8 +1399,6 @@ void GUIEditBoxWithScrollBar::createVScrollBar() m_scrollbar_width = skin ? skin->getSize(gui::EGDS_SCROLLBAR_SIZE) : 16; - RelativeRect.LowerRightCorner.X -= m_scrollbar_width + 4; - 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, diff --git a/src/gui/guiEngine.cpp b/src/gui/guiEngine.cpp index b40707d01..c5ad5c323 100644 --- a/src/gui/guiEngine.cpp +++ b/src/gui/guiEngine.cpp @@ -170,6 +170,7 @@ GUIEngine::GUIEngine(JoystickController *joystick, m_menumanager, NULL /* &client */, m_texture_source, + m_sound_manager, m_formspecgui, m_buttonhandler, "", @@ -297,10 +298,14 @@ void GUIEngine::run() driver->endScene(); + IrrlichtDevice *device = RenderingEngine::get_raw_device(); + u32 frametime_min = 1000 / (device->isWindowFocused() + ? g_settings->getFloat("fps_max") + : g_settings->getFloat("fps_max_unfocused")); if (m_clouds_enabled) - cloudPostProcess(); + cloudPostProcess(frametime_min, device); else - sleep_ms(25); + sleep_ms(frametime_min); m_script->step(); @@ -367,9 +372,8 @@ void GUIEngine::cloudPreProcess() } /******************************************************************************/ -void GUIEngine::cloudPostProcess() +void GUIEngine::cloudPostProcess(u32 frametime_min, IrrlichtDevice *device) { - float fps_max = g_settings->getFloat("pause_fps_max"); // Time of frame without fps limit u32 busytime_u32; @@ -380,12 +384,10 @@ void GUIEngine::cloudPostProcess() else busytime_u32 = 0; - // FPS limiter - u32 frametime_min = 1000./fps_max; - + // FPS limit if (busytime_u32 < frametime_min) { u32 sleeptime = frametime_min - busytime_u32; - RenderingEngine::get_raw_device()->sleep(sleeptime); + device->sleep(sleeptime); } } diff --git a/src/gui/guiEngine.h b/src/gui/guiEngine.h index f9ad0fb0a..eef1ad8aa 100644 --- a/src/gui/guiEngine.h +++ b/src/gui/guiEngine.h @@ -29,22 +29,22 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/enriched_string.h" /******************************************************************************/ -/* Typedefs and macros */ +/* Structs and macros */ /******************************************************************************/ /** texture layer ids */ -typedef enum { +enum texture_layer { TEX_LAYER_BACKGROUND = 0, TEX_LAYER_OVERLAY, TEX_LAYER_HEADER, TEX_LAYER_FOOTER, TEX_LAYER_MAX -} texture_layer; +}; -typedef struct { +struct image_definition { video::ITexture *texture = nullptr; bool tile; unsigned int minsize; -} image_definition; +}; /******************************************************************************/ /* forward declarations */ @@ -277,7 +277,7 @@ private: /** do preprocessing for cloud subsystem */ void cloudPreProcess(); /** do postprocessing for cloud subsystem */ - void cloudPostProcess(); + void cloudPostProcess(u32 frametime_min, IrrlichtDevice *device); /** internam data required for drawing clouds */ struct clouddata { diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp index 601c5c18e..632b15992 100644 --- a/src/gui/guiFormSpecMenu.cpp +++ b/src/gui/guiFormSpecMenu.cpp @@ -48,6 +48,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "settings.h" #include "client/client.h" #include "client/fontengine.h" +#include "client/sound.h" #include "util/hex.h" #include "util/numeric.h" #include "util/string.h" // for parseColorString() @@ -65,6 +66,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "guiScrollContainer.h" #include "intlGUIEditBox.h" #include "guiHyperText.h" +#include "guiScene.h" #define MY_CHECKPOS(a,b) \ if (v_pos.size() != 2) { \ @@ -94,11 +96,13 @@ inline u32 clamp_u8(s32 value) GUIFormSpecMenu::GUIFormSpecMenu(JoystickController *joystick, gui::IGUIElement *parent, s32 id, IMenuManager *menumgr, - Client *client, ISimpleTextureSource *tsrc, IFormSource *fsrc, TextDest *tdst, + Client *client, ISimpleTextureSource *tsrc, ISoundManager *sound_manager, + IFormSource *fsrc, TextDest *tdst, const std::string &formspecPrepend, bool remap_dbl_click): GUIModalMenu(RenderingEngine::get_gui_env(), parent, id, menumgr, remap_dbl_click), m_invmgr(client), m_tsrc(tsrc), + m_sound_manager(sound_manager), m_client(client), m_formspec_prepend(formspecPrepend), m_form_src(fsrc), @@ -142,11 +146,12 @@ GUIFormSpecMenu::~GUIFormSpecMenu() void GUIFormSpecMenu::create(GUIFormSpecMenu *&cur_formspec, Client *client, JoystickController *joystick, IFormSource *fs_src, TextDest *txt_dest, - const std::string &formspecPrepend) + const std::string &formspecPrepend, ISoundManager *sound_manager) { if (cur_formspec == nullptr) { cur_formspec = new GUIFormSpecMenu(joystick, guiroot, -1, &g_menumgr, - client, client->getTextureSource(), fs_src, txt_dest, formspecPrepend); + client, client->getTextureSource(), sound_manager, fs_src, + txt_dest, formspecPrepend); cur_formspec->doPause = false; /* @@ -613,6 +618,9 @@ void GUIFormSpecMenu::parseCheckbox(parserData* data, const std::string &element data->current_parent, spec.fid, spec.flabel.c_str()); auto style = getDefaultStyleForElement("checkbox", name); + + spec.sound = style.get(StyleSpec::Property::SOUND, ""); + e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); if (spec.fname == m_focused_element) { @@ -1019,6 +1027,9 @@ void GUIFormSpecMenu::parseButton(parserData* data, const std::string &element, data->current_parent, spec.fid, spec.flabel.c_str()); auto style = getStyleForElement(type, name, (type != "button") ? "button" : ""); + + spec.sound = style[StyleSpec::STATE_DEFAULT].get(StyleSpec::Property::SOUND, ""); + e->setStyles(style); if (spec.fname == m_focused_element) { @@ -1225,6 +1236,7 @@ void GUIFormSpecMenu::parseTable(parserData* data, const std::string &element) auto style = getDefaultStyleForElement("table", name); e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); + e->setOverrideFont(style.getFont()); m_tables.emplace_back(spec, e); m_fields.push_back(spec); @@ -1302,6 +1314,7 @@ void GUIFormSpecMenu::parseTextList(parserData* data, const std::string &element auto style = getDefaultStyleForElement("textlist", name); e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); + e->setOverrideFont(style.getFont()); m_tables.emplace_back(spec, e); m_fields.push_back(spec); @@ -1378,6 +1391,9 @@ void GUIFormSpecMenu::parseDropDown(parserData* data, const std::string &element e->setSelected(stoi(str_initial_selection)-1); auto style = getDefaultStyleForElement("dropdown", name); + + spec.sound = style.get(StyleSpec::Property::SOUND, ""); + e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); m_fields.push_back(spec); @@ -1738,12 +1754,16 @@ void GUIFormSpecMenu::parseHyperText(parserData *data, const std::string &elemen FieldSpec spec( name, - utf8_to_wide(unescape_string(text)), + translate_string(utf8_to_wide(unescape_string(text))), L"", 258 + m_fields.size() ); spec.ftype = f_HyperText; + + auto style = getDefaultStyleForElement("hypertext", spec.fname); + spec.sound = style.get(StyleSpec::Property::SOUND, ""); + GUIHyperText *e = new GUIHyperText(spec.flabel.c_str(), Environment, data->current_parent, spec.fid, rect, m_client, m_tsrc); e->drop(); @@ -1996,6 +2016,8 @@ void GUIFormSpecMenu::parseImageButton(parserData* data, const std::string &elem auto style = getStyleForElement("image_button", spec.fname); + spec.sound = style[StyleSpec::STATE_DEFAULT].get(StyleSpec::Property::SOUND, ""); + // Override style properties with values specified directly in the element if (!image_name.empty()) style[StyleSpec::STATE_DEFAULT].set(StyleSpec::FGIMG, image_name); @@ -2104,6 +2126,9 @@ void GUIFormSpecMenu::parseTabHeader(parserData* data, const std::string &elemen e->setTabHeight(geom.Y); auto style = getDefaultStyleForElement("tabheader", name); + + spec.sound = style.get(StyleSpec::Property::SOUND, ""); + e->setNotClipped(style.getBool(StyleSpec::NOCLIP, true)); for (const std::string &button : buttons) { @@ -2192,6 +2217,9 @@ void GUIFormSpecMenu::parseItemImageButton(parserData* data, const std::string & item_name, m_client); auto style = getStyleForElement("item_image_button", spec_btn.fname, "image_button"); + + spec_btn.sound = style[StyleSpec::STATE_DEFAULT].get(StyleSpec::Property::SOUND, ""); + e_btn->setStyles(style); if (spec_btn.fname == m_focused_element) { @@ -2209,16 +2237,16 @@ void GUIFormSpecMenu::parseItemImageButton(parserData* data, const std::string & void GUIFormSpecMenu::parseBox(parserData* data, const std::string &element) { - std::vector<std::string> parts = split(element,';'); + std::vector<std::string> parts = split(element, ';'); if ((parts.size() == 3) || ((parts.size() > 3) && (m_formspec_version > FORMSPEC_API_VERSION))) { - std::vector<std::string> v_pos = split(parts[0],','); - std::vector<std::string> v_geom = split(parts[1],','); + std::vector<std::string> v_pos = split(parts[0], ','); + std::vector<std::string> v_geom = split(parts[1], ','); - MY_CHECKPOS("box",0); - MY_CHECKGEOM("box",1); + MY_CHECKPOS("box", 0); + MY_CHECKGEOM("box", 1); v2s32 pos; v2s32 geom; @@ -2232,36 +2260,43 @@ void GUIFormSpecMenu::parseBox(parserData* data, const std::string &element) geom.Y = stof(v_geom[1]) * spacing.Y; } - video::SColor tmp_color; - - if (parseColorString(parts[2], tmp_color, false, 0x8C)) { - FieldSpec spec( - "", - L"", - L"", - 258 + m_fields.size(), - -2 - ); - spec.ftype = f_Box; + FieldSpec spec( + "", + L"", + L"", + 258 + m_fields.size(), + -2 + ); + spec.ftype = f_Box; - core::rect<s32> rect(pos, pos + geom); + auto style = getDefaultStyleForElement("box", spec.fname); - GUIBox *e = new GUIBox(Environment, data->current_parent, spec.fid, - rect, tmp_color); + video::SColor tmp_color; + std::array<video::SColor, 4> colors; + std::array<video::SColor, 4> bordercolors = {0x0, 0x0, 0x0, 0x0}; + std::array<s32, 4> borderwidths = {0, 0, 0, 0}; - auto style = getDefaultStyleForElement("box", spec.fname); - e->setNotClipped(style.getBool(StyleSpec::NOCLIP, m_formspec_version < 3)); + if (parseColorString(parts[2], tmp_color, true, 0x8C)) { + colors = {tmp_color, tmp_color, tmp_color, tmp_color}; + } else { + colors = style.getColorArray(StyleSpec::COLORS, {0x0, 0x0, 0x0, 0x0}); + bordercolors = style.getColorArray(StyleSpec::BORDERCOLORS, + {0x0, 0x0, 0x0, 0x0}); + borderwidths = style.getIntArray(StyleSpec::BORDERWIDTHS, {0, 0, 0, 0}); + } - e->drop(); + core::rect<s32> rect(pos, pos + geom); - m_fields.push_back(spec); + GUIBox *e = new GUIBox(Environment, data->current_parent, spec.fid, rect, + colors, bordercolors, borderwidths); + e->setNotClipped(style.getBool(StyleSpec::NOCLIP, m_formspec_version < 3)); + e->drop(); - } else { - errorstream<< "Invalid Box element(" << parts.size() << "): '" << element << "' INVALID COLOR" << std::endl; - } + m_fields.push_back(spec); return; } - errorstream<< "Invalid Box element(" << parts.size() << "): '" << element << "'" << std::endl; + errorstream << "Invalid Box element(" << parts.size() << "): '" << element + << "'" << std::endl; } void GUIFormSpecMenu::parseBackgroundColor(parserData* data, const std::string &element) @@ -2686,6 +2721,86 @@ void GUIFormSpecMenu::parseSetFocus(const std::string &element) << "'" << std::endl; } +void GUIFormSpecMenu::parseModel(parserData *data, const std::string &element) +{ + std::vector<std::string> parts = split(element, ';'); + + if (parts.size() < 5 || (parts.size() > 8 && + m_formspec_version <= FORMSPEC_API_VERSION)) { + errorstream << "Invalid model element (" << parts.size() << "): '" << element + << "'" << std::endl; + return; + } + + // Avoid length checks by resizing + if (parts.size() < 8) + parts.resize(8); + + std::vector<std::string> v_pos = split(parts[0], ','); + std::vector<std::string> v_geom = split(parts[1], ','); + std::string name = unescape_string(parts[2]); + std::string meshstr = unescape_string(parts[3]); + std::vector<std::string> textures = split(parts[4], ','); + std::vector<std::string> vec_rot = split(parts[5], ','); + bool inf_rotation = is_yes(parts[6]); + bool mousectrl = is_yes(parts[7]) || parts[7].empty(); // default true + + MY_CHECKPOS("model", 0); + MY_CHECKGEOM("model", 1); + + v2s32 pos; + v2s32 geom; + + if (data->real_coordinates) { + pos = getRealCoordinateBasePos(v_pos); + geom = getRealCoordinateGeometry(v_geom); + } else { + pos = getElementBasePos(&v_pos); + geom.X = stof(v_geom[0]) * (float)imgsize.X; + geom.Y = stof(v_geom[1]) * (float)imgsize.Y; + } + + if (!data->explicit_size) + warningstream << "invalid use of model without a size[] element" << std::endl; + + scene::IAnimatedMesh *mesh = m_client->getMesh(meshstr); + + if (!mesh) { + errorstream << "Invalid model element: Unable to load mesh:" + << std::endl << "\t" << meshstr << std::endl; + return; + } + + FieldSpec spec( + name, + L"", + L"", + 258 + m_fields.size() + ); + + core::rect<s32> rect(pos, pos + geom); + + GUIScene *e = new GUIScene(Environment, RenderingEngine::get_scene_manager(), + data->current_parent, rect, spec.fid); + + auto meshnode = e->setMesh(mesh); + + for (u32 i = 0; i < textures.size() && i < meshnode->getMaterialCount(); ++i) + e->setTexture(i, m_tsrc->getTexture(textures[i])); + + if (vec_rot.size() >= 2) + e->setRotation(v2f(stof(vec_rot[0]), stof(vec_rot[1]))); + + e->enableContinuousRotation(inf_rotation); + e->enableMouseControl(mousectrl); + + auto style = getStyleForElement("model", spec.fname); + e->setStyles(style); + e->drop(); + + m_fields.push_back(spec); +} + void GUIFormSpecMenu::parseElement(parserData* data, const std::string &element) { //some prechecks @@ -2882,6 +2997,11 @@ void GUIFormSpecMenu::parseElement(parserData* data, const std::string &element) return; } + if (type == "model") { + parseModel(data, description); + return; + } + // Ignore others infostream << "Unknown DrawSpec: type=" << type << ", data=\"" << description << "\"" << std::endl; @@ -3110,42 +3230,42 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) // and default scaling (1.00). use_imgsize = 0.5555 * screen_dpi * gui_scaling; } else { - // In variable-size mode, we prefer to make the - // inventory image size 1/15 of screen height, - // multiplied by the gui_scaling config parameter. - // If the preferred size won't fit the whole - // form on the screen, either horizontally or - // vertically, then we scale it down to fit. - // (The magic numbers in the computation of what - // fits arise from the scaling factors in the - // following stanza, including the form border, - // help text space, and 0.1 inventory slot spare.) - // However, a minimum size is also set, that - // the image size can't be less than 0.3 inch - // multiplied by gui_scaling, even if this means - // the form doesn't fit the screen. + // Variables for the maximum imgsize that can fit in the screen. + double fitx_imgsize; + double fity_imgsize; + + // Pad the screensize with 5% of the screensize on all sides to ensure + // that even the largest formspecs don't touch the screen borders. + v2f padded_screensize( + mydata.screensize.X * 0.9f, + mydata.screensize.Y * 0.9f + ); + + if (mydata.real_coordinates) { + fitx_imgsize = padded_screensize.X / mydata.invsize.X; + fity_imgsize = padded_screensize.Y / mydata.invsize.Y; + } else { + // The maximum imgsize in the old coordinate system also needs to + // factor in padding and spacing along with 0.1 inventory slot spare + // and help text space, hence the magic numbers. + fitx_imgsize = padded_screensize.X / + ((5.0 / 4.0) * (0.5 + mydata.invsize.X)); + fity_imgsize = padded_screensize.Y / + ((15.0 / 13.0) * (0.85 + mydata.invsize.Y)); + } + #ifdef __ANDROID__ - // For mobile devices these magic numbers are - // different and forms should always use the - // maximum screen space available. - double prefer_imgsize = mydata.screensize.Y / 10 * gui_scaling; - double fitx_imgsize = mydata.screensize.X / - ((12.0 / 8.0) * (0.5 + mydata.invsize.X)); - double fity_imgsize = mydata.screensize.Y / - ((15.0 / 11.0) * (0.85 + mydata.invsize.Y)); - use_imgsize = MYMIN(prefer_imgsize, - MYMIN(fitx_imgsize, fity_imgsize)); + // In Android, the preferred imgsize should be larger to accommodate the + // smaller screensize. + double prefer_imgsize = padded_screensize.Y / 10 * gui_scaling; #else - double prefer_imgsize = mydata.screensize.Y / 15 * gui_scaling; - double fitx_imgsize = mydata.screensize.X / - ((5.0 / 4.0) * (0.5 + mydata.invsize.X)); - double fity_imgsize = mydata.screensize.Y / - ((15.0 / 13.0) * (0.85 * mydata.invsize.Y)); - double screen_dpi = RenderingEngine::getDisplayDensity() * 96; - double min_imgsize = 0.3 * screen_dpi * gui_scaling; - use_imgsize = MYMAX(min_imgsize, MYMIN(prefer_imgsize, - MYMIN(fitx_imgsize, fity_imgsize))); + // Desktop computers have more space, so try to fit 15 coordinates. + double prefer_imgsize = padded_screensize.Y / 15 * gui_scaling; #endif + // Try to use the preferred imgsize, but if that's bigger than the maximum + // size, use the maximum size. + use_imgsize = std::min(prefer_imgsize, + std::min(fitx_imgsize, fity_imgsize)); } // Everything else is scaled in proportion to the @@ -3474,10 +3594,14 @@ void GUIFormSpecMenu::drawMenu() e->setVisible(true); /* - Call base class - (This is where all the drawing happens.) + This is where all the drawing happens. */ - gui::IGUIElement::draw(); + core::list<IGUIElement*>::Iterator it = Children.begin(); + for (; it != Children.end(); ++it) + if ((*it)->isNotClipped() || + AbsoluteClippingRect.isRectCollided( + (*it)->getAbsolutePosition())) + (*it)->draw(); for (gui::IGUIElement *e : m_clickthrough_elements) e->setVisible(false); @@ -4387,6 +4511,8 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) for (GUIFormSpecMenu::FieldSpec &s : m_fields) { if ((s.ftype == f_TabHeader) && (s.fid == event.GUIEvent.Caller->getID())) { + if (!s.sound.empty() && m_sound_manager) + m_sound_manager->playSound(s.sound, false, 1.0f); s.send = true; acceptInput(); s.send = false; @@ -4430,6 +4556,9 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) continue; if (s.ftype == f_Button || s.ftype == f_CheckBox) { + if (!s.sound.empty() && m_sound_manager) + m_sound_manager->playSound(s.sound, false, 1.0f); + s.send = true; if (s.is_exit) { if (m_allowclose) { @@ -4452,6 +4581,8 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) s2.send = false; } } + if (!s.sound.empty() && m_sound_manager) + m_sound_manager->playSound(s.sound, false, 1.0f); s.send = true; acceptInput(quit_mode_no); @@ -4468,6 +4599,8 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) acceptInput(quit_mode_no); s.fdefault = L""; } else if (s.ftype == f_Unknown || s.ftype == f_HyperText) { + if (!s.sound.empty() && m_sound_manager) + m_sound_manager->playSound(s.sound, false, 1.0f); s.send = true; acceptInput(); s.send = false; diff --git a/src/gui/guiFormSpecMenu.h b/src/gui/guiFormSpecMenu.h index 613acaa04..37106cb65 100644 --- a/src/gui/guiFormSpecMenu.h +++ b/src/gui/guiFormSpecMenu.h @@ -38,10 +38,10 @@ with this program; if not, write to the Free Software Foundation, Inc., class InventoryManager; class ISimpleTextureSource; class Client; -class TexturePool; class GUIScrollContainer; +class ISoundManager; -typedef enum { +enum FormspecFieldType { f_Button, f_Table, f_TabHeader, @@ -53,13 +53,13 @@ typedef enum { f_HyperText, f_AnimatedImage, f_Unknown -} FormspecFieldType; +}; -typedef enum { +enum FormspecQuitMode { quit_mode_no, quit_mode_accept, quit_mode_cancel -} FormspecQuitMode; +}; struct TextDest { @@ -128,6 +128,7 @@ class GUIFormSpecMenu : public GUIModalMenu int priority; core::rect<s32> rect; gui::ECURSOR_ICON fcursor_icon; + std::string sound; }; struct TooltipSpec @@ -152,6 +153,7 @@ public: IMenuManager *menumgr, Client *client, ISimpleTextureSource *tsrc, + ISoundManager *sound_manager, IFormSource* fs_src, TextDest* txt_dst, const std::string &formspecPrepend, @@ -161,7 +163,7 @@ public: static void create(GUIFormSpecMenu *&cur_formspec, Client *client, JoystickController *joystick, IFormSource *fs_src, TextDest *txt_dest, - const std::string &formspecPrepend); + const std::string &formspecPrepend, ISoundManager *sound_manager); void setFormSpec(const std::string &formspec_string, const InventoryLocation ¤t_inventory_location) @@ -294,6 +296,7 @@ protected: InventoryManager *m_invmgr; ISimpleTextureSource *m_tsrc; + ISoundManager *m_sound_manager; Client *m_client; std::string m_formspec_string; @@ -353,7 +356,7 @@ private: JoystickController *m_joystick; bool m_show_debug = false; - typedef struct { + struct parserData { bool explicit_size; bool real_coordinates; u8 simple_field_count; @@ -381,16 +384,16 @@ private: // used to restore table selection/scroll/treeview state std::unordered_map<std::string, GUITable::DynamicData> table_dyndata; - } parserData; + }; - typedef struct { + struct fs_key_pending { bool key_up; bool key_down; bool key_enter; bool key_escape; - } fs_key_pendig; + }; - fs_key_pendig current_keys_pending; + fs_key_pending current_keys_pending; std::string current_field_enter_pending = ""; std::vector<std::string> m_hovered_item_tooltips; @@ -444,6 +447,7 @@ private: void parseAnchor(parserData *data, const std::string &element); bool parseStyle(parserData *data, const std::string &element, bool style_type); void parseSetFocus(const std::string &element); + void parseModel(parserData *data, const std::string &element); void tryClose(); diff --git a/src/gui/guiPasswordChange.cpp b/src/gui/guiPasswordChange.cpp index 5311c6fef..74cd62f5b 100644 --- a/src/gui/guiPasswordChange.cpp +++ b/src/gui/guiPasswordChange.cpp @@ -79,7 +79,11 @@ void GUIPasswordChange::regenerateGui(v2u32 screensize) /* Calculate new sizes and positions */ +#ifdef __ANDROID__ + const float s = m_gui_scale * porting::getDisplayDensity() / 2; +#else const float s = m_gui_scale; +#endif DesiredRect = core::rect<s32>( screensize.X / 2 - 580 * s / 2, screensize.Y / 2 - 300 * s / 2, @@ -289,6 +293,10 @@ bool GUIPasswordChange::getAndroidUIInput() if (!hasAndroidUIInput()) return false; + // still waiting + if (porting::getInputDialogState() == -1) + return true; + gui::IGUIElement *e = nullptr; if (m_jni_field_name == "old_password") e = getElementFromId(ID_oldPassword); @@ -296,12 +304,13 @@ bool GUIPasswordChange::getAndroidUIInput() e = getElementFromId(ID_newPassword1); else if (m_jni_field_name == "new_password_2") e = getElementFromId(ID_newPassword2); - - if (e) { - std::string text = porting::getInputDialogValue(); - e->setText(utf8_to_wide(text).c_str()); - } m_jni_field_name.clear(); + + if (!e || e->getType() != irr::gui::EGUIET_EDIT_BOX) + return false; + + std::string text = porting::getInputDialogValue(); + e->setText(utf8_to_wide(text).c_str()); return false; } #endif diff --git a/src/gui/guiScene.cpp b/src/gui/guiScene.cpp new file mode 100644 index 000000000..08f119e07 --- /dev/null +++ b/src/gui/guiScene.cpp @@ -0,0 +1,257 @@ +/* +Minetest +Copyright (C) 2020 Jean-Patrick Guerrero <jeanpatrick.guerrero@gmail.com> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "guiScene.h" + +#include <SViewFrustum.h> +#include <IAnimatedMeshSceneNode.h> +#include <ILightSceneNode.h> +#include "porting.h" + +GUIScene::GUIScene(gui::IGUIEnvironment *env, scene::ISceneManager *smgr, + gui::IGUIElement *parent, core::recti rect, s32 id) + : IGUIElement(gui::EGUIET_ELEMENT, env, parent, id, rect) +{ + m_driver = env->getVideoDriver(); + m_smgr = smgr->createNewSceneManager(false); + + 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); +} + +GUIScene::~GUIScene() +{ + setMesh(nullptr); + + m_smgr->drop(); +} + +scene::IAnimatedMeshSceneNode *GUIScene::setMesh(scene::IAnimatedMesh *mesh) +{ + if (m_mesh) { + m_mesh->remove(); + m_mesh = nullptr; + } + + if (!mesh) + return nullptr; + + m_mesh = m_smgr->addAnimatedMeshSceneNode(mesh); + m_mesh->setPosition(-m_mesh->getBoundingBox().getCenter()); + m_mesh->animateJoints(); + return m_mesh; +} + +void GUIScene::setTexture(u32 idx, video::ITexture *texture) +{ + video::SMaterial &material = m_mesh->getMaterial(idx); + material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; + material.MaterialTypeParam = 0.5f; + material.TextureLayer[0].Texture = texture; + material.setFlag(video::EMF_LIGHTING, false); + material.setFlag(video::EMF_FOG_ENABLE, true); + material.setFlag(video::EMF_BILINEAR_FILTER, false); + material.setFlag(video::EMF_BACK_FACE_CULLING, false); +} + +void GUIScene::draw() +{ + // Control rotation speed based on time + u64 new_time = porting::getTimeMs(); + u64 dtime_ms = 0; + if (m_last_time != 0) + dtime_ms = porting::getDeltaMs(m_last_time, new_time); + m_last_time = new_time; + + core::rect<s32> oldViewPort = m_driver->getViewPort(); + m_driver->setViewPort(getAbsoluteClippingRect()); + core::recti borderRect = Environment->getRootGUIElement()->getAbsoluteClippingRect(); + + if (m_bgcolor != 0) { + Environment->getSkin()->draw3DSunkenPane( + this, m_bgcolor, false, true, borderRect, 0); + } + + core::dimension2d<s32> size = getAbsoluteClippingRect().getSize(); + m_smgr->getActiveCamera()->setAspectRatio((f32)size.Width / (f32)size.Height); + + if (!m_target) { + updateCamera(m_smgr->addEmptySceneNode()); + rotateCamera(v3f(0.f)); + m_cam->bindTargetAndRotation(true); + } + + cameraLoop(); + + // Continuous rotation + if (m_inf_rot) + rotateCamera(v3f(0.f, -0.03f * (float)dtime_ms, 0.f)); + + m_smgr->drawAll(); + + if (m_initial_rotation && m_mesh) { + rotateCamera(v3f(m_custom_rot.X, m_custom_rot.Y, 0.f)); + calcOptimalDistance(); + + m_initial_rotation = false; + } + + m_driver->setViewPort(oldViewPort); +} + +bool GUIScene::OnEvent(const SEvent &event) +{ + if (m_mouse_ctrl && event.EventType == EET_MOUSE_INPUT_EVENT) { + if (event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) { + m_last_pos = v2f((f32)event.MouseInput.X, (f32)event.MouseInput.Y); + return true; + } else if (event.MouseInput.Event == EMIE_MOUSE_MOVED) { + if (event.MouseInput.isLeftPressed()) { + m_curr_pos = v2f((f32)event.MouseInput.X, (f32)event.MouseInput.Y); + + rotateCamera(v3f( + m_last_pos.Y - m_curr_pos.Y, + m_curr_pos.X - m_last_pos.X, 0.f)); + + m_last_pos = m_curr_pos; + return true; + } + } + } + + return gui::IGUIElement::OnEvent(event); +} + +void GUIScene::setStyles(const std::array<StyleSpec, StyleSpec::NUM_STATES> &styles) +{ + StyleSpec::State state = StyleSpec::STATE_DEFAULT; + StyleSpec style = StyleSpec::getStyleFromStatePropagation(styles, state); + + setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); + setBackgroundColor(style.getColor(StyleSpec::BGCOLOR, m_bgcolor)); +} + +/* Camera control functions */ + +inline void GUIScene::calcOptimalDistance() +{ + core::aabbox3df box = m_mesh->getBoundingBox(); + f32 width = box.MaxEdge.X - box.MinEdge.X; + f32 height = box.MaxEdge.Y - box.MinEdge.Y; + f32 depth = box.MaxEdge.Z - box.MinEdge.Z; + f32 max_width = width > depth ? width : depth; + + const scene::SViewFrustum *f = m_cam->getViewFrustum(); + f32 cam_far = m_cam->getFarValue(); + f32 far_width = core::line3df(f->getFarLeftUp(), f->getFarRightUp()).getLength(); + f32 far_height = core::line3df(f->getFarLeftUp(), f->getFarLeftDown()).getLength(); + + core::recti rect = getAbsolutePosition(); + f32 zoomX = rect.getWidth() / max_width; + f32 zoomY = rect.getHeight() / height; + f32 dist; + + if (zoomX < zoomY) + dist = (max_width / (far_width / cam_far)) + (0.5f * max_width); + else + dist = (height / (far_height / cam_far)) + (0.5f * max_width); + + m_cam_distance = dist; + m_update_cam = true; +} + +void GUIScene::updateCamera(scene::ISceneNode *target) +{ + m_target = target; + updateTargetPos(); + + m_last_target_pos = m_target_pos; + updateCameraPos(); + + m_update_cam = true; +} + +void GUIScene::updateTargetPos() +{ + m_last_target_pos = m_target_pos; + m_target->updateAbsolutePosition(); + m_target_pos = m_target->getAbsolutePosition(); +} + +void GUIScene::setCameraRotation(v3f rot) +{ + correctBounds(rot); + + core::matrix4 mat; + mat.setRotationDegrees(rot); + + m_cam_pos = v3f(0.f, 0.f, m_cam_distance); + mat.rotateVect(m_cam_pos); + + m_cam_pos += m_target_pos; + m_cam->setPosition(m_cam_pos); + m_update_cam = false; +} + +bool GUIScene::correctBounds(v3f &rot) +{ + const float ROTATION_MAX_1 = 60.0f; + const float ROTATION_MAX_2 = 300.0f; + + // Limit and correct the rotation when needed + if (rot.X < 90.f) { + if (rot.X > ROTATION_MAX_1) { + rot.X = ROTATION_MAX_1; + return true; + } + } else if (rot.X < ROTATION_MAX_2) { + rot.X = ROTATION_MAX_2; + return true; + } + + // Not modified + return false; +} + +void GUIScene::cameraLoop() +{ + updateCameraPos(); + updateTargetPos(); + + if (m_target_pos != m_last_target_pos) + m_update_cam = true; + + if (m_update_cam) { + m_cam_pos = m_target_pos + (m_cam_pos - m_target_pos).normalize() * m_cam_distance; + + v3f rot = getCameraRotation(); + if (correctBounds(rot)) + setCameraRotation(rot); + + m_cam->setPosition(m_cam_pos); + m_cam->setTarget(m_target_pos); + + m_update_cam = false; + } +} diff --git a/src/gui/guiScene.h b/src/gui/guiScene.h new file mode 100644 index 000000000..707e6f66a --- /dev/null +++ b/src/gui/guiScene.h @@ -0,0 +1,85 @@ +/* +Minetest +Copyright (C) 2020 Jean-Patrick Guerrero <jeanpatrick.guerrero@gmail.com> + +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 "irrlichttypes_extrabloated.h" +#include "ICameraSceneNode.h" +#include "StyleSpec.h" + +using namespace irr; + +class GUIScene : public gui::IGUIElement +{ +public: + GUIScene(gui::IGUIEnvironment *env, scene::ISceneManager *smgr, + gui::IGUIElement *parent, core::recti rect, s32 id = -1); + + ~GUIScene(); + + scene::IAnimatedMeshSceneNode *setMesh(scene::IAnimatedMesh *mesh = nullptr); + void setTexture(u32 idx, video::ITexture *texture); + void setBackgroundColor(const video::SColor &color) noexcept { m_bgcolor = color; }; + 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; }; + void setStyles(const std::array<StyleSpec, StyleSpec::NUM_STATES> &styles); + + virtual void draw(); + virtual bool OnEvent(const SEvent &event); + +private: + void calcOptimalDistance(); + void updateTargetPos(); + void updateCamera(scene::ISceneNode *target); + void setCameraRotation(v3f rot); + /// @return true indicates that the rotation was corrected + bool correctBounds(v3f &rot); + void cameraLoop(); + + void updateCameraPos() { m_cam_pos = m_cam->getPosition(); }; + v3f getCameraRotation() const { return (m_cam_pos - m_target_pos).getHorizontalAngle(); }; + void rotateCamera(const v3f &delta) { setCameraRotation(getCameraRotation() + delta); }; + + scene::ISceneManager *m_smgr; + video::IVideoDriver *m_driver; + scene::ICameraSceneNode *m_cam; + scene::ISceneNode *m_target = nullptr; + scene::IAnimatedMeshSceneNode *m_mesh = nullptr; + + f32 m_cam_distance = 50.f; + + u64 m_last_time = 0; + + v3f m_cam_pos; + v3f m_target_pos; + v3f m_last_target_pos; + // Cursor positions + v2f m_curr_pos; + v2f m_last_pos; + // Initial rotation + v2f m_custom_rot; + + bool m_mouse_ctrl = true; + bool m_update_cam = false; + bool m_inf_rot = false; + bool m_initial_rotation = true; + + video::SColor m_bgcolor = 0; +}; diff --git a/src/gui/guiScrollBar.cpp b/src/gui/guiScrollBar.cpp index b04ccb9d5..c6a03f3e4 100644 --- a/src/gui/guiScrollBar.cpp +++ b/src/gui/guiScrollBar.cpp @@ -21,7 +21,7 @@ GUIScrollBar::GUIScrollBar(IGUIEnvironment *environment, IGUIElement *parent, s3 is_horizontal(horizontal), is_auto_scaling(auto_scale), dragged_by_slider(false), tray_clicked(false), scroll_pos(0), draw_center(0), thumb_size(0), min_pos(0), max_pos(100), small_step(10), - large_step(50), last_change(0), drag_offset(0), page_size(100), border_size(0) + large_step(50), drag_offset(0), page_size(100), border_size(0) { refreshControls(); setNotClipped(false); diff --git a/src/gui/guiScrollBar.h b/src/gui/guiScrollBar.h index 29493bb99..d18f8e875 100644 --- a/src/gui/guiScrollBar.h +++ b/src/gui/guiScrollBar.h @@ -68,7 +68,6 @@ private: s32 max_pos; s32 small_step; s32 large_step; - u32 last_change; s32 drag_offset; s32 page_size; s32 border_size; diff --git a/src/gui/guiScrollContainer.cpp b/src/gui/guiScrollContainer.cpp index 88cdc7057..0fe4c41bd 100644 --- a/src/gui/guiScrollContainer.cpp +++ b/src/gui/guiScrollContainer.cpp @@ -56,6 +56,18 @@ bool GUIScrollContainer::OnEvent(const SEvent &event) return IGUIElement::OnEvent(event); } +void GUIScrollContainer::draw() +{ + if (isVisible()) { + core::list<IGUIElement *>::Iterator it = Children.begin(); + for (; it != Children.end(); ++it) + if ((*it)->isNotClipped() || + AbsoluteClippingRect.isRectCollided( + (*it)->getAbsolutePosition())) + (*it)->draw(); + } +} + void GUIScrollContainer::updateScrolling() { s32 pos = m_scrollbar->getPos(); diff --git a/src/gui/guiScrollContainer.h b/src/gui/guiScrollContainer.h index a0306291e..9e3ec6e93 100644 --- a/src/gui/guiScrollContainer.h +++ b/src/gui/guiScrollContainer.h @@ -32,6 +32,8 @@ public: virtual bool OnEvent(const SEvent &event) override; + virtual void draw() override; + inline void onScrollEvent(gui::IGUIElement *caller) { if (caller == m_scrollbar) diff --git a/src/gui/guiSkin.cpp b/src/gui/guiSkin.cpp index 8892a00b4..e09209bd9 100644 --- a/src/gui/guiSkin.cpp +++ b/src/gui/guiSkin.cpp @@ -29,7 +29,7 @@ GUISkin::GUISkin(EGUI_SKIN_TYPE type, video::IVideoDriver* driver) {
Colors[EGDC_3D_DARK_SHADOW] = video::SColor(101,50,50,50);
Colors[EGDC_3D_SHADOW] = video::SColor(101,130,130,130);
- Colors[EGDC_3D_FACE] = video::SColor(101,210,210,210);
+ Colors[EGDC_3D_FACE] = video::SColor(220,100,100,100);
Colors[EGDC_3D_HIGH_LIGHT] = video::SColor(101,255,255,255);
Colors[EGDC_3D_LIGHT] = video::SColor(101,210,210,210);
Colors[EGDC_ACTIVE_BORDER] = video::SColor(101,16,14,115);
diff --git a/src/gui/guiTable.cpp b/src/gui/guiTable.cpp index c705e17fb..cab2e19fd 100644 --- a/src/gui/guiTable.cpp +++ b/src/gui/guiTable.cpp @@ -56,7 +56,7 @@ GUITable::GUITable(gui::IGUIEnvironment *env, m_font = skin->getFont(); if (m_font) { m_font->grab(); - m_rowheight = m_font->getDimension(L"A").Height + 4; + m_rowheight = m_font->getDimension(L"Ay").Height + 4; m_rowheight = MYMAX(m_rowheight, 1); } @@ -586,6 +586,31 @@ void GUITable::setSelected(s32 index) } } +void GUITable::setOverrideFont(IGUIFont *font) +{ + if (m_font == font) + return; + + if (font == nullptr) + font = Environment->getSkin()->getFont(); + + if (m_font) + m_font->drop(); + + m_font = font; + m_font->grab(); + + m_rowheight = m_font->getDimension(L"Ay").Height + 4; + m_rowheight = MYMAX(m_rowheight, 1); + + updateScrollBar(); +} + +IGUIFont *GUITable::getOverrideFont() const +{ + return m_font; +} + GUITable::DynamicData GUITable::getDynamicData() const { DynamicData dyndata; diff --git a/src/gui/guiTable.h b/src/gui/guiTable.h index 11093ea72..76a0e94d0 100644 --- a/src/gui/guiTable.h +++ b/src/gui/guiTable.h @@ -123,6 +123,12 @@ public: // Autoscroll to make the selected row fully visible void setSelected(s32 index); + //! Sets another skin independent font. If this is set to zero, the button uses the font of the skin. + virtual void setOverrideFont(gui::IGUIFont *font = nullptr); + + //! Gets the override font (if any) + virtual gui::IGUIFont *getOverrideFont() const; + /* Get selection, scroll position and opened (sub)trees */ DynamicData getDynamicData() const; diff --git a/src/gui/intlGUIEditBox.cpp b/src/gui/intlGUIEditBox.cpp index 10395423c..8be63fd6f 100644 --- a/src/gui/intlGUIEditBox.cpp +++ b/src/gui/intlGUIEditBox.cpp @@ -1165,7 +1165,7 @@ void intlGUIEditBox::breakText() s32 lastLineStart = 0; s32 size = Text.size(); s32 length = 0; - s32 elWidth = RelativeRect.getWidth() - 6; + s32 elWidth = RelativeRect.getWidth() - m_scrollbar_width - 10; wchar_t c; for (s32 i=0; i<size; ++i) @@ -1478,8 +1478,6 @@ void intlGUIEditBox::createVScrollBar() } } - RelativeRect.LowerRightCorner.X -= m_scrollbar_width + 4; - irr::core::rect<s32> scrollbarrect = FrameRect; scrollbarrect.UpperLeftCorner.X += FrameRect.getWidth() - m_scrollbar_width; m_vscrollbar = new GUIScrollBar(Environment, getParent(), -1, diff --git a/src/httpfetch.cpp b/src/httpfetch.cpp index 326b5052f..65202ce3e 100644 --- a/src/httpfetch.cpp +++ b/src/httpfetch.cpp @@ -294,13 +294,11 @@ HTTPFetchOngoing::HTTPFetchOngoing(const HTTPFetchRequest &request_, curl_easy_setopt(curl, CURLOPT_WRITEDATA, &oss); } - // Set POST (or GET) data - if (request.post_fields.empty() && request.post_data.empty()) { - curl_easy_setopt(curl, CURLOPT_HTTPGET, 1); - } else if (request.multipart) { + // Set data from fields or raw_data + if (request.multipart) { curl_httppost *last = NULL; - for (StringMap::iterator it = request.post_fields.begin(); - it != request.post_fields.end(); ++it) { + for (StringMap::iterator it = request.fields.begin(); + it != request.fields.end(); ++it) { curl_formadd(&post, &last, CURLFORM_NAMELENGTH, it->first.size(), CURLFORM_PTRNAME, it->first.c_str(), @@ -311,28 +309,42 @@ HTTPFetchOngoing::HTTPFetchOngoing(const HTTPFetchRequest &request_, curl_easy_setopt(curl, CURLOPT_HTTPPOST, post); // request.post_fields must now *never* be // modified until CURLOPT_HTTPPOST is cleared - } else if (request.post_data.empty()) { - curl_easy_setopt(curl, CURLOPT_POST, 1); - std::string str; - for (auto &post_field : request.post_fields) { - if (!str.empty()) - str += "&"; - str += urlencode(post_field.first); - str += "="; - str += urlencode(post_field.second); - } - curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, - str.size()); - curl_easy_setopt(curl, CURLOPT_COPYPOSTFIELDS, - str.c_str()); } else { - curl_easy_setopt(curl, CURLOPT_POST, 1); - curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, - request.post_data.size()); - curl_easy_setopt(curl, CURLOPT_POSTFIELDS, - request.post_data.c_str()); - // request.post_data must now *never* be - // modified until CURLOPT_POSTFIELDS is cleared + switch (request.method) { + case HTTP_GET: + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1); + break; + case HTTP_POST: + curl_easy_setopt(curl, CURLOPT_POST, 1); + break; + case HTTP_PUT: + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT"); + break; + case HTTP_DELETE: + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE"); + break; + } + if (request.method != HTTP_GET) { + if (!request.raw_data.empty()) { + curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, + request.raw_data.size()); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, + request.raw_data.c_str()); + } else if (!request.fields.empty()) { + std::string str; + for (auto &field : request.fields) { + if (!str.empty()) + str += "&"; + str += urlencode(field.first); + str += "="; + str += urlencode(field.second); + } + curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, + str.size()); + curl_easy_setopt(curl, CURLOPT_COPYPOSTFIELDS, + str.c_str()); + } + } } // Set additional HTTP headers for (const std::string &extra_header : request.extra_headers) { diff --git a/src/httpfetch.h b/src/httpfetch.h index ae8b5afb5..3b9f17f0a 100644 --- a/src/httpfetch.h +++ b/src/httpfetch.h @@ -28,6 +28,15 @@ with this program; if not, write to the Free Software Foundation, Inc., #define HTTPFETCH_DISCARD 0 #define HTTPFETCH_SYNC 1 +// Methods +enum HttpMethod : u8 +{ + HTTP_GET, + HTTP_POST, + HTTP_PUT, + HTTP_DELETE, +}; + struct HTTPFetchRequest { std::string url = ""; @@ -50,12 +59,15 @@ struct HTTPFetchRequest // application/x-www-form-urlencoded. POST-only. bool multipart = false; - // POST fields. Fields are escaped properly. - // If this is empty a GET request is done instead. - StringMap post_fields; + // The Method to use default = GET + // Avaible methods GET, POST, PUT, DELETE + HttpMethod method = HTTP_GET; + + // Fields of the request + StringMap fields; - // Raw POST data, overrides post_fields. - std::string post_data; + // Raw data of the request overrides fields + std::string raw_data; // If not empty, should contain entries such as "Accept: text/html" std::vector<std::string> extra_headers; diff --git a/src/hud.cpp b/src/hud.cpp index 3079b5cd8..1791e04df 100644 --- a/src/hud.cpp +++ b/src/hud.cpp @@ -28,6 +28,8 @@ const struct EnumString es_HudElementType[] = {HUD_ELEM_INVENTORY, "inventory"}, {HUD_ELEM_WAYPOINT, "waypoint"}, {HUD_ELEM_IMAGE_WAYPOINT, "image_waypoint"}, + {HUD_ELEM_COMPASS, "compass"}, + {HUD_ELEM_MINIMAP, "minimap"}, {0, NULL}, }; @@ -40,6 +42,7 @@ const struct EnumString es_HudElementStat[] = {HUD_STAT_TEXT, "text"}, {HUD_STAT_NUMBER, "number"}, {HUD_STAT_ITEM, "item"}, + {HUD_STAT_ITEM, "precision"}, {HUD_STAT_DIR, "direction"}, {HUD_STAT_ALIGN, "alignment"}, {HUD_STAT_OFFSET, "offset"}, @@ -51,7 +51,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #define HUD_HOTBAR_ITEMCOUNT_DEFAULT 8 #define HUD_HOTBAR_ITEMCOUNT_MAX 32 - #define HOTBAR_IMAGE_SIZE 48 enum HudElementType { @@ -60,7 +59,9 @@ enum HudElementType { HUD_ELEM_STATBAR = 2, HUD_ELEM_INVENTORY = 3, HUD_ELEM_WAYPOINT = 4, - HUD_ELEM_IMAGE_WAYPOINT = 5 + HUD_ELEM_IMAGE_WAYPOINT = 5, + HUD_ELEM_COMPASS = 6, + HUD_ELEM_MINIMAP = 7 }; enum HudElementStat { @@ -79,6 +80,13 @@ enum HudElementStat { HUD_STAT_TEXT2, }; +enum HudCompassDir { + HUD_COMPASS_ROTATE = 0, + HUD_COMPASS_ROTATE_REVERSE, + HUD_COMPASS_TRANSLATE, + HUD_COMPASS_TRANSLATE_REVERSE, +}; + struct HudElement { HudElementType type; v2f pos; @@ -100,3 +108,12 @@ extern const EnumString es_HudElementType[]; extern const EnumString es_HudElementStat[]; extern const EnumString es_HudBuiltinElement[]; +// Minimap stuff + +enum MinimapType { + MINIMAP_TYPE_OFF, + MINIMAP_TYPE_SURFACE, + MINIMAP_TYPE_RADAR, + MINIMAP_TYPE_TEXTURE, +}; + diff --git a/src/inventory.cpp b/src/inventory.cpp index 349ee503d..1ef9b13cd 100644 --- a/src/inventory.cpp +++ b/src/inventory.cpp @@ -258,6 +258,20 @@ std::string ItemStack::getDescription(IItemDefManager *itemdef) const return desc.empty() ? name : desc; } +std::string ItemStack::getShortDescription(IItemDefManager *itemdef) const +{ + std::string desc = metadata.getString("short_description"); + if (desc.empty()) + desc = getDefinition(itemdef).short_description; + if (!desc.empty()) + return desc; + // no short_description because of old server version or modified builtin + // return first line of description + std::stringstream sstr(getDescription(itemdef)); + std::getline(sstr, desc, '\n'); + return desc; +} + ItemStack ItemStack::addItem(ItemStack newitem, IItemDefManager *itemdef) { @@ -732,17 +746,17 @@ void InventoryList::moveItemSomewhere(u32 i, InventoryList *dest, u32 count) u32 InventoryList::moveItem(u32 i, InventoryList *dest, u32 dest_i, u32 count, bool swap_if_needed, bool *did_swap) { - if(this == dest && i == dest_i) + if (this == dest && i == dest_i) return count; // Take item from source list ItemStack item1; - if(count == 0) + if (count == 0) item1 = changeItem(i, ItemStack()); else item1 = takeItem(i, count); - if(item1.empty()) + if (item1.empty()) return 0; // Try to add the item to destination list @@ -750,8 +764,7 @@ u32 InventoryList::moveItem(u32 i, InventoryList *dest, u32 dest_i, item1 = dest->addItem(dest_i, item1); // If something is returned, the item was not fully added - if(!item1.empty()) - { + if (!item1.empty()) { // If olditem is returned, nothing was added. bool nothing_added = (item1.count == oldcount); diff --git a/src/inventory.h b/src/inventory.h index 67a7859ed..f36bc57cf 100644 --- a/src/inventory.h +++ b/src/inventory.h @@ -49,6 +49,7 @@ struct ItemStack std::string getItemString(bool include_meta = true) const; // Returns the tooltip std::string getDescription(IItemDefManager *itemdef) const; + std::string getShortDescription(IItemDefManager *itemdef) const; /* Quantity methods diff --git a/src/inventorymanager.cpp b/src/inventorymanager.cpp index b6f464901..635bd2e4b 100644 --- a/src/inventorymanager.cpp +++ b/src/inventorymanager.cpp @@ -154,6 +154,93 @@ IMoveAction::IMoveAction(std::istream &is, bool somewhere) : } } +void IMoveAction::swapDirections() +{ + std::swap(from_inv, to_inv); + std::swap(from_list, to_list); + std::swap(from_i, to_i); +} + +void IMoveAction::onPutAndOnTake(const ItemStack &src_item, ServerActiveObject *player) const +{ + ServerScripting *sa = PLAYER_TO_SA(player); + if (to_inv.type == InventoryLocation::DETACHED) + sa->detached_inventory_OnPut(*this, src_item, player); + else if (to_inv.type == InventoryLocation::NODEMETA) + sa->nodemeta_inventory_OnPut(*this, src_item, player); + else if (to_inv.type == InventoryLocation::PLAYER) + sa->player_inventory_OnPut(*this, src_item, player); + else + assert(false); + + if (from_inv.type == InventoryLocation::DETACHED) + sa->detached_inventory_OnTake(*this, src_item, player); + else if (from_inv.type == InventoryLocation::NODEMETA) + sa->nodemeta_inventory_OnTake(*this, src_item, player); + else if (from_inv.type == InventoryLocation::PLAYER) + sa->player_inventory_OnTake(*this, src_item, player); + else + assert(false); +} + +void IMoveAction::onMove(int count, ServerActiveObject *player) const +{ + ServerScripting *sa = PLAYER_TO_SA(player); + if (from_inv.type == InventoryLocation::DETACHED) + sa->detached_inventory_OnMove(*this, count, player); + else if (from_inv.type == InventoryLocation::NODEMETA) + sa->nodemeta_inventory_OnMove(*this, count, player); + else if (from_inv.type == InventoryLocation::PLAYER) + sa->player_inventory_OnMove(*this, count, player); + else + assert(false); +} + +int IMoveAction::allowPut(const ItemStack &dst_item, ServerActiveObject *player) const +{ + ServerScripting *sa = PLAYER_TO_SA(player); + int dst_can_put_count = 0xffff; + if (to_inv.type == InventoryLocation::DETACHED) + dst_can_put_count = sa->detached_inventory_AllowPut(*this, dst_item, player); + else if (to_inv.type == InventoryLocation::NODEMETA) + dst_can_put_count = sa->nodemeta_inventory_AllowPut(*this, dst_item, player); + else if (to_inv.type == InventoryLocation::PLAYER) + dst_can_put_count = sa->player_inventory_AllowPut(*this, dst_item, player); + else + assert(false); + return dst_can_put_count; +} + +int IMoveAction::allowTake(const ItemStack &src_item, ServerActiveObject *player) const +{ + ServerScripting *sa = PLAYER_TO_SA(player); + int src_can_take_count = 0xffff; + if (from_inv.type == InventoryLocation::DETACHED) + src_can_take_count = sa->detached_inventory_AllowTake(*this, src_item, player); + else if (from_inv.type == InventoryLocation::NODEMETA) + src_can_take_count = sa->nodemeta_inventory_AllowTake(*this, src_item, player); + else if (from_inv.type == InventoryLocation::PLAYER) + src_can_take_count = sa->player_inventory_AllowTake(*this, src_item, player); + else + assert(false); + return src_can_take_count; +} + +int IMoveAction::allowMove(int try_take_count, ServerActiveObject *player) const +{ + ServerScripting *sa = PLAYER_TO_SA(player); + int src_can_take_count = 0xffff; + if (from_inv.type == InventoryLocation::DETACHED) + src_can_take_count = sa->detached_inventory_AllowMove(*this, try_take_count, player); + else if (from_inv.type == InventoryLocation::NODEMETA) + src_can_take_count = sa->nodemeta_inventory_AllowMove(*this, try_take_count, player); + else if (from_inv.type == InventoryLocation::PLAYER) + src_can_take_count = sa->player_inventory_AllowMove(*this, try_take_count, player); + else + assert(false); + return src_can_take_count; +} + void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGameDef *gamedef) { Inventory *inv_from = mgr->getInventory(from_inv); @@ -251,93 +338,80 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame Collect information of endpoints */ - int try_take_count = count; - if (try_take_count == 0) - try_take_count = list_from->getItem(from_i).count; + ItemStack src_item = list_from->getItem(from_i); + if (count > 0) + src_item.count = count; + if (src_item.empty()) + return; int src_can_take_count = 0xffff; int dst_can_put_count = 0xffff; - /* Query detached inventories */ + // this is needed for swapping items inside one inventory to work + ItemStack restitem; + bool allow_swap = !list_to->itemFits(to_i, src_item, &restitem) + && restitem.count == src_item.count + && !caused_by_move_somewhere; - // Move occurs in the same detached inventory - if (from_inv.type == InventoryLocation::DETACHED && - from_inv == to_inv) { - src_can_take_count = PLAYER_TO_SA(player)->detached_inventory_AllowMove( - *this, try_take_count, player); - dst_can_put_count = src_can_take_count; - } else { - // Destination is detached - if (to_inv.type == InventoryLocation::DETACHED) { - ItemStack src_item = list_from->getItem(from_i); - src_item.count = try_take_count; - dst_can_put_count = PLAYER_TO_SA(player)->detached_inventory_AllowPut( - *this, src_item, player); - } - // Source is detached - if (from_inv.type == InventoryLocation::DETACHED) { - ItemStack src_item = list_from->getItem(from_i); - src_item.count = try_take_count; - src_can_take_count = PLAYER_TO_SA(player)->detached_inventory_AllowTake( - *this, src_item, player); - } - } - - /* Query node metadata inventories */ + // Shift-click: Cannot fill this stack, proceed with next slot + if (caused_by_move_somewhere && restitem.count == src_item.count) + return; - // Both endpoints are nodemeta - // Move occurs in the same nodemeta inventory - if (from_inv.type == InventoryLocation::NODEMETA && - from_inv == to_inv) { - src_can_take_count = PLAYER_TO_SA(player)->nodemeta_inventory_AllowMove( - *this, try_take_count, player); - dst_can_put_count = src_can_take_count; - } else { - // Destination is nodemeta - if (to_inv.type == InventoryLocation::NODEMETA) { - ItemStack src_item = list_from->getItem(from_i); - src_item.count = try_take_count; - dst_can_put_count = PLAYER_TO_SA(player)->nodemeta_inventory_AllowPut( - *this, src_item, player); + if (allow_swap) { + // Swap will affect the entire stack if it can performed. + src_item = list_from->getItem(from_i); + count = src_item.count; + } + + if (from_inv == to_inv) { + // Move action within the same inventory + src_can_take_count = allowMove(src_item.count, player); + + bool swap_expected = allow_swap; + allow_swap = allow_swap + && (src_can_take_count == -1 || src_can_take_count >= src_item.count); + if (allow_swap) { + int try_put_count = list_to->getItem(to_i).count; + swapDirections(); + dst_can_put_count = allowMove(try_put_count, player); + allow_swap = allow_swap + && (dst_can_put_count == -1 || dst_can_put_count >= try_put_count); + swapDirections(); + } else { + dst_can_put_count = src_can_take_count; } - // Source is nodemeta - if (from_inv.type == InventoryLocation::NODEMETA) { - ItemStack src_item = list_from->getItem(from_i); - src_item.count = try_take_count; - src_can_take_count = PLAYER_TO_SA(player)->nodemeta_inventory_AllowTake( - *this, src_item, player); - } - } - - // Query player inventories - - // Move occurs in the same player inventory - if (from_inv.type == InventoryLocation::PLAYER && - from_inv == to_inv) { - src_can_take_count = PLAYER_TO_SA(player)->player_inventory_AllowMove( - *this, try_take_count, player); - dst_can_put_count = src_can_take_count; + if (swap_expected != allow_swap) + src_can_take_count = dst_can_put_count = 0; } else { - // Destination is a player - if (to_inv.type == InventoryLocation::PLAYER) { - ItemStack src_item = list_from->getItem(from_i); - src_item.count = try_take_count; - dst_can_put_count = PLAYER_TO_SA(player)->player_inventory_AllowPut( - *this, src_item, player); - } - // Source is a player - if (from_inv.type == InventoryLocation::PLAYER) { - ItemStack src_item = list_from->getItem(from_i); - src_item.count = try_take_count; - src_can_take_count = PLAYER_TO_SA(player)->player_inventory_AllowTake( - *this, src_item, player); + // Take from one inventory, put into another + dst_can_put_count = allowPut(src_item, player); + src_can_take_count = allowTake(src_item, player); + + bool swap_expected = allow_swap; + allow_swap = allow_swap + && (src_can_take_count == -1 || src_can_take_count >= src_item.count) + && (dst_can_put_count == -1 || dst_can_put_count >= src_item.count); + // A swap is expected, which means that we have to + // run the "allow" callbacks a second time with swapped inventories + if (allow_swap) { + ItemStack dst_item = list_to->getItem(to_i); + swapDirections(); + + int src_can_take = allowPut(dst_item, player); + int dst_can_put = allowTake(dst_item, player); + allow_swap = allow_swap + && (src_can_take == -1 || src_can_take >= dst_item.count) + && (dst_can_put == -1 || dst_can_put >= dst_item.count); + swapDirections(); } + if (swap_expected != allow_swap) + src_can_take_count = dst_can_put_count = 0; } int old_count = count; /* Modify count according to collected data */ - count = try_take_count; + count = src_item.count; if (src_can_take_count != -1 && count > src_can_take_count) count = src_can_take_count; if (dst_can_put_count != -1 && count > dst_can_put_count) @@ -367,7 +441,7 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame return; } - ItemStack src_item = list_from->getItem(from_i); + src_item = list_from->getItem(from_i); src_item.count = count; ItemStack from_stack_was = list_from->getItem(from_i); ItemStack to_stack_was = list_to->getItem(to_i); @@ -380,7 +454,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, !caused_by_move_somewhere, &did_swap); + list_to, to_i, count, allow_swap, &did_swap); + assert(allow_swap == did_swap); // If source is infinite, reset it's stack if (src_can_take_count == -1) { @@ -471,69 +546,29 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame Report move to endpoints */ - /* Detached inventories */ - - // Both endpoints are same detached - if (from_inv.type == InventoryLocation::DETACHED && - from_inv == to_inv) { - PLAYER_TO_SA(player)->detached_inventory_OnMove( - *this, count, player); - } else { - // Destination is detached - if (to_inv.type == InventoryLocation::DETACHED) { - PLAYER_TO_SA(player)->detached_inventory_OnPut( - *this, src_item, player); - } - // Source is detached - if (from_inv.type == InventoryLocation::DETACHED) { - PLAYER_TO_SA(player)->detached_inventory_OnTake( - *this, src_item, player); + // Source = destination => move + if (from_inv == to_inv) { + onMove(count, player); + if (did_swap) { + // Item is now placed in source list + src_item = list_from->getItem(from_i); + swapDirections(); + onMove(src_item.count, player); + swapDirections(); } - } - - /* Node metadata inventories */ - - // Both endpoints are same nodemeta - if (from_inv.type == InventoryLocation::NODEMETA && - from_inv == to_inv) { - PLAYER_TO_SA(player)->nodemeta_inventory_OnMove( - *this, count, player); - } else { - // Destination is nodemeta - if (to_inv.type == InventoryLocation::NODEMETA) { - PLAYER_TO_SA(player)->nodemeta_inventory_OnPut( - *this, src_item, player); - } - // Source is nodemeta - if (from_inv.type == InventoryLocation::NODEMETA) { - PLAYER_TO_SA(player)->nodemeta_inventory_OnTake( - *this, src_item, player); - } - } - - // Player inventories - - // Both endpoints are same player inventory - if (from_inv.type == InventoryLocation::PLAYER && - from_inv == to_inv) { - PLAYER_TO_SA(player)->player_inventory_OnMove( - *this, count, player); + mgr->setInventoryModified(from_inv); } else { - // Destination is player inventory - if (to_inv.type == InventoryLocation::PLAYER) { - PLAYER_TO_SA(player)->player_inventory_OnPut( - *this, src_item, player); + onPutAndOnTake(src_item, player); + if (did_swap) { + // Item is now placed in source list + src_item = list_from->getItem(from_i); + swapDirections(); + onPutAndOnTake(src_item, player); + swapDirections(); } - // Source is player inventory - if (from_inv.type == InventoryLocation::PLAYER) { - PLAYER_TO_SA(player)->player_inventory_OnTake( - *this, src_item, player); - } - } - - mgr->setInventoryModified(from_inv); - if (inv_from != inv_to) mgr->setInventoryModified(to_inv); + mgr->setInventoryModified(from_inv); + } } void IMoveAction::clientApply(InventoryManager *mgr, IGameDef *gamedef) diff --git a/src/inventorymanager.h b/src/inventorymanager.h index 69bf30169..4ad5d3f49 100644 --- a/src/inventorymanager.h +++ b/src/inventorymanager.h @@ -183,6 +183,18 @@ struct IMoveAction : public InventoryAction, public MoveAction void apply(InventoryManager *mgr, ServerActiveObject *player, IGameDef *gamedef); void clientApply(InventoryManager *mgr, IGameDef *gamedef); + + void swapDirections(); + + void onPutAndOnTake(const ItemStack &src_item, ServerActiveObject *player) const; + + void onMove(int count, ServerActiveObject *player) const; + + int allowPut(const ItemStack &dst_item, ServerActiveObject *player) const; + + int allowTake(const ItemStack &src_item, ServerActiveObject *player) const; + + int allowMove(int try_take_count, ServerActiveObject *player) const; }; struct IDropAction : public InventoryAction, public MoveAction diff --git a/src/itemdef.cpp b/src/itemdef.cpp index 8e0492827..5fb1e4c47 100644 --- a/src/itemdef.cpp +++ b/src/itemdef.cpp @@ -62,6 +62,7 @@ ItemDefinition& ItemDefinition::operator=(const ItemDefinition &def) type = def.type; name = def.name; description = def.description; + short_description = def.short_description; inventory_image = def.inventory_image; inventory_overlay = def.inventory_overlay; wield_image = def.wield_image; @@ -102,6 +103,7 @@ void ItemDefinition::reset() type = ITEM_NONE; name = ""; description = ""; + short_description = ""; inventory_image = ""; inventory_overlay = ""; wield_image = ""; @@ -128,10 +130,10 @@ void ItemDefinition::serialize(std::ostream &os, u16 protocol_version) const u8 version = 6; writeU8(os, version); writeU8(os, type); - os << serializeString(name); - os << serializeString(description); - os << serializeString(inventory_image); - os << serializeString(wield_image); + os << serializeString16(name); + os << serializeString16(description); + os << serializeString16(inventory_image); + os << serializeString16(wield_image); writeV3F32(os, wield_scale); writeS16(os, stack_max); writeU8(os, usable); @@ -143,25 +145,27 @@ void ItemDefinition::serialize(std::ostream &os, u16 protocol_version) const tool_capabilities->serialize(tmp_os, protocol_version); tool_capabilities_s = tmp_os.str(); } - os << serializeString(tool_capabilities_s); + os << serializeString16(tool_capabilities_s); writeU16(os, groups.size()); for (const auto &group : groups) { - os << serializeString(group.first); + os << serializeString16(group.first); writeS16(os, group.second); } - os << serializeString(node_placement_prediction); + os << serializeString16(node_placement_prediction); // Version from ContentFeatures::serialize to keep in sync sound_place.serialize(os, CONTENTFEATURES_VERSION); sound_place_failed.serialize(os, CONTENTFEATURES_VERSION); writeF32(os, range); - os << serializeString(palette_image); + os << serializeString16(palette_image); writeARGB8(os, color); - os << serializeString(inventory_overlay); - os << serializeString(wield_overlay); + os << serializeString16(inventory_overlay); + os << serializeString16(wield_overlay); + + os << serializeString16(short_description); } void ItemDefinition::deSerialize(std::istream &is) @@ -175,16 +179,16 @@ void ItemDefinition::deSerialize(std::istream &is) throw SerializationError("unsupported ItemDefinition version"); type = (enum ItemType)readU8(is); - name = deSerializeString(is); - description = deSerializeString(is); - inventory_image = deSerializeString(is); - wield_image = deSerializeString(is); + name = deSerializeString16(is); + description = deSerializeString16(is); + inventory_image = deSerializeString16(is); + wield_image = deSerializeString16(is); wield_scale = readV3F32(is); stack_max = readS16(is); usable = readU8(is); liquids_pointable = readU8(is); - std::string tool_capabilities_s = deSerializeString(is); + std::string tool_capabilities_s = deSerializeString16(is); if (!tool_capabilities_s.empty()) { std::istringstream tmp_is(tool_capabilities_s, std::ios::binary); tool_capabilities = new ToolCapabilities; @@ -194,27 +198,28 @@ void ItemDefinition::deSerialize(std::istream &is) groups.clear(); u32 groups_size = readU16(is); for(u32 i=0; i<groups_size; i++){ - std::string name = deSerializeString(is); + std::string name = deSerializeString16(is); int value = readS16(is); groups[name] = value; } - node_placement_prediction = deSerializeString(is); + node_placement_prediction = deSerializeString16(is); // Version from ContentFeatures::serialize to keep in sync sound_place.deSerialize(is, CONTENTFEATURES_VERSION); sound_place_failed.deSerialize(is, CONTENTFEATURES_VERSION); range = readF32(is); - palette_image = deSerializeString(is); + palette_image = deSerializeString16(is); color = readARGB8(is); - inventory_overlay = deSerializeString(is); - wield_overlay = deSerializeString(is); + inventory_overlay = deSerializeString16(is); + wield_overlay = deSerializeString16(is); // If you add anything here, insert it primarily inside the try-catch // block to not need to increase the version. - //try { - //} catch(SerializationError &e) {}; + try { + short_description = deSerializeString16(is); + } catch(SerializationError &e) {}; } @@ -521,14 +526,14 @@ public: // Serialize ItemDefinition and write wrapped in a string std::ostringstream tmp_os(std::ios::binary); def->serialize(tmp_os, protocol_version); - os << serializeString(tmp_os.str()); + os << serializeString16(tmp_os.str()); } writeU16(os, m_aliases.size()); for (const auto &it : m_aliases) { - os << serializeString(it.first); - os << serializeString(it.second); + os << serializeString16(it.first); + os << serializeString16(it.second); } } void deSerialize(std::istream &is) @@ -543,7 +548,7 @@ public: for(u16 i=0; i<count; i++) { // Deserialize a string and grab an ItemDefinition from it - std::istringstream tmp_is(deSerializeString(is), std::ios::binary); + std::istringstream tmp_is(deSerializeString16(is), std::ios::binary); ItemDefinition def; def.deSerialize(tmp_is); // Register @@ -552,8 +557,8 @@ public: u16 num_aliases = readU16(is); for(u16 i=0; i<num_aliases; i++) { - std::string name = deSerializeString(is); - std::string convert_to = deSerializeString(is); + std::string name = deSerializeString16(is); + std::string convert_to = deSerializeString16(is); registerAlias(name, convert_to); } } diff --git a/src/itemdef.h b/src/itemdef.h index f47e6cbe7..ebf0d3527 100644 --- a/src/itemdef.h +++ b/src/itemdef.h @@ -56,6 +56,7 @@ struct ItemDefinition ItemType type; std::string name; // "" = hand std::string description; // Shown in tooltip. + std::string short_description; /* Visual properties diff --git a/src/light.cpp b/src/light.cpp index 8196fedff..d5389b450 100644 --- a/src/light.cpp +++ b/src/light.cpp @@ -18,6 +18,7 @@ with this program; if not, write to the Free Software Foundation, Inc., */ #include "light.h" +#include <algorithm> #include <cmath> #include "util/numeric.h" #include "settings.h" @@ -81,9 +82,11 @@ void set_light_table(float gamma) // Strictly speaking, rangelim is not necessary here—if the implementation // is conforming. But we don’t want problems in any case. light_LUT[i] = rangelim((s32)(255.0f * brightness), 0, 255); + // Ensure light brightens with each level - if (i > 1 && light_LUT[i] <= light_LUT[i - 1]) - light_LUT[i] = light_LUT[i - 1] + 1; + if (i > 0 && light_LUT[i] <= light_LUT[i - 1]) { + light_LUT[i] = std::min((u8)254, light_LUT[i - 1]) + 1; + } } } diff --git a/src/log.cpp b/src/log.cpp index 54442c39b..3c61414e9 100644 --- a/src/log.cpp +++ b/src/log.cpp @@ -97,16 +97,7 @@ LogBuffer verbose_buf(g_logger, LL_VERBOSE); std::ostream *dout_con_ptr = &null_stream; std::ostream *derr_con_ptr = &verbosestream; -// Server -std::ostream *dout_server_ptr = &infostream; -std::ostream *derr_server_ptr = &errorstream; - -#ifndef SERVER -// Client -std::ostream *dout_client_ptr = &infostream; -std::ostream *derr_client_ptr = &errorstream; -#endif - +// Common streams std::ostream rawstream(&raw_buf); std::ostream dstream(&none_buf); std::ostream errorstream(&error_buf); @@ -192,14 +192,8 @@ extern std::ostream null_stream; extern std::ostream *dout_con_ptr; extern std::ostream *derr_con_ptr; -extern std::ostream *dout_server_ptr; extern std::ostream *derr_server_ptr; -#ifndef SERVER -extern std::ostream *dout_client_ptr; -extern std::ostream *derr_client_ptr; -#endif - extern Logger g_logger; // Writes directly to all LL_NONE log outputs for g_logger with no prefix. @@ -222,8 +216,4 @@ extern std::ostream dstream; #define dout_con (*dout_con_ptr) #define derr_con (*derr_con_ptr) -#define dout_server (*dout_server_ptr) -#ifndef SERVER - #define dout_client (*dout_client_ptr) -#endif diff --git a/src/map.cpp b/src/map.cpp index b9ab7c066..6d53351ef 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -62,8 +62,7 @@ with this program; if not, write to the Free Software Foundation, Inc., Map */ -Map::Map(std::ostream &dout, IGameDef *gamedef): - m_dout(dout), +Map::Map(IGameDef *gamedef): m_gamedef(gamedef), m_nodedef(gamedef->ndef()) { @@ -140,6 +139,21 @@ MapBlock * Map::getBlockNoCreate(v3s16 p3d) return block; } +void Map::listAllLoadedBlocks(std::vector<v3s16> &dst) +{ + for (auto §or_it : m_sectors) { + MapSector *sector = sector_it.second; + + MapBlockVect blocks; + sector->getBlocks(blocks); + + for (MapBlock *block : blocks) { + v3s16 p = block->getPos(); + dst.push_back(p); + } + } +} + bool Map::isNodeUnderground(v3s16 p) { v3s16 blockpos = getNodeBlockPos(p); @@ -1201,7 +1215,7 @@ bool Map::isBlockOccluded(MapBlock *block, v3s16 cam_pos_nodes) */ ServerMap::ServerMap(const std::string &savedir, IGameDef *gamedef, EmergeManager *emerge, MetricsBackend *mb): - Map(dout_server, gamedef), + Map(gamedef), settings_mgr(g_settings, savedir + DIR_DELIM + "map_meta.txt"), m_emerge(emerge) { @@ -1327,11 +1341,6 @@ u64 ServerMap::getSeed() return getMapgenParams()->seed; } -s16 ServerMap::getWaterLevel() -{ - return getMapgenParams()->water_level; -} - bool ServerMap::blockpos_over_mapgen_limit(v3s16 p) { const s16 mapgen_limit_bp = rangelim( @@ -1351,6 +1360,9 @@ bool ServerMap::initBlockMake(v3s16 blockpos, BlockMakeData *data) v3s16 bpmin = EmergeManager::getContainingChunk(blockpos, csize); v3s16 bpmax = bpmin + v3s16(1, 1, 1) * (csize - 1); + if (!m_chunks_in_progress.insert(bpmin).second) + return false; + bool enable_mapgen_debug_info = m_emerge->enable_mapgen_debug_info; EMERGE_DBG_OUT("initBlockMake(): " PP(bpmin) " - " PP(bpmax)); @@ -1366,7 +1378,6 @@ bool ServerMap::initBlockMake(v3s16 blockpos, BlockMakeData *data) data->seed = getSeed(); data->blockpos_min = bpmin; data->blockpos_max = bpmax; - data->blockpos_requested = blockpos; data->nodedef = m_nodedef; /* @@ -1488,6 +1499,7 @@ void ServerMap::finishBlockMake(BlockMakeData *data, NOTE: Will be saved later. */ //save(MOD_STATE_WRITE_AT_UNLOAD); + m_chunks_in_progress.erase(bpmin); } MapSector *ServerMap::createSector(v2s16 p2d) @@ -1732,59 +1744,6 @@ void ServerMap::updateVManip(v3s16 pos) vm->m_is_dirty = true; } -s16 ServerMap::findGroundLevel(v2s16 p2d) -{ -#if 0 - /* - Uh, just do something random... - */ - // Find existing map from top to down - s16 max=63; - s16 min=-64; - v3s16 p(p2d.X, max, p2d.Y); - for(; p.Y>min; p.Y--) - { - MapNode n = getNodeNoEx(p); - if(n.getContent() != CONTENT_IGNORE) - break; - } - if(p.Y == min) - goto plan_b; - // If this node is not air, go to plan b - if(getNodeNoEx(p).getContent() != CONTENT_AIR) - goto plan_b; - // Search existing walkable and return it - for(; p.Y>min; p.Y--) - { - MapNode n = getNodeNoEx(p); - if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE) - return p.Y; - } - - // Move to plan b -plan_b: -#endif - - /* - Determine from map generator noise functions - */ - - s16 level = m_emerge->getGroundLevelAtPoint(p2d); - return level; - - //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT; - //return (s16)level; -} - -void ServerMap::createDirs(const std::string &path) -{ - if (!fs::CreateAllDirs(path)) { - m_dout<<"ServerMap: Failed to create directory " - <<"\""<<path<<"\""<<std::endl; - throw BaseException("ServerMap failed to create directory"); - } -} - void ServerMap::save(ModifiedState save_level) { if (!m_map_saving_enabled) { @@ -1864,21 +1823,6 @@ void ServerMap::listAllLoadableBlocks(std::vector<v3s16> &dst) dbase_ro->listAllLoadableBlocks(dst); } -void ServerMap::listAllLoadedBlocks(std::vector<v3s16> &dst) -{ - for (auto §or_it : m_sectors) { - MapSector *sector = sector_it.second; - - MapBlockVect blocks; - sector->getBlocks(blocks); - - for (MapBlock *block : blocks) { - v3s16 p = block->getPos(); - dst.push_back(p); - } - } -} - MapDatabase *ServerMap::createDatabase( const std::string &name, const std::string &savedir, @@ -123,7 +123,7 @@ class Map /*: public NodeContainer*/ { public: - Map(std::ostream &dout, IGameDef *gamedef); + Map(IGameDef *gamedef); virtual ~Map(); DISABLE_CLASS_COPY(Map); @@ -149,8 +149,6 @@ public: MapSector * getSectorNoGenerateNoLock(v2s16 p2d); // Same as the above (there exists no lock anymore) MapSector * getSectorNoGenerate(v2s16 p2d); - // Gets an existing sector or creates an empty one - //MapSector * getSectorCreate(v2s16 p2d); /* This is overloaded by ClientMap and ServerMap to allow @@ -162,7 +160,9 @@ public: MapBlock * getBlockNoCreate(v3s16 p); // Returns NULL if not found MapBlock * getBlockNoCreateNoEx(v3s16 p); - + + void listAllLoadedBlocks(std::vector<v3s16> &dst); + /* Server overrides */ virtual MapBlock * emergeBlock(v3s16 p, bool create_blank=true) { return getBlockNoCreateNoEx(p); } @@ -269,11 +269,6 @@ public: void removeNodeTimer(v3s16 p); /* - Misc. - */ - std::map<v2s16, MapSector*> *getSectorsPtr(){return &m_sectors;} - - /* Variables */ @@ -283,8 +278,6 @@ public: protected: friend class LuaVoxelManip; - std::ostream &m_dout; // A bit deprecated, could be removed - IGameDef *m_gamedef; std::set<MapEventReceiver*> m_event_receivers; @@ -374,15 +367,6 @@ public: */ MapBlock *getBlockOrEmerge(v3s16 p3d); - // Helper for placing objects on ground level - s16 findGroundLevel(v2s16 p2d); - - /* - Misc. helper functions for fiddling with directory and file - names when saving - */ - void createDirs(const std::string &path); - /* Database functions */ @@ -394,7 +378,6 @@ public: void save(ModifiedState save_level); void listAllLoadableBlocks(std::vector<v3s16> &dst); - void listAllLoadedBlocks(std::vector<v3s16> &dst); MapgenParams *getMapgenParams(); @@ -414,7 +397,6 @@ public: bool isSavingEnabled(){ return m_map_saving_enabled; } u64 getSeed(); - s16 getWaterLevel(); /*! * Fixes lighting in one map block. @@ -442,6 +424,7 @@ private: // Chunks core::map<v2s16, MapChunk*> m_chunks; #endif + std::set<v3s16> m_chunks_in_progress; /* Metadata is re-written on disk only if this is true. diff --git a/src/map_settings_manager.cpp b/src/map_settings_manager.cpp index 4f070e910..9c447b3d0 100644 --- a/src/map_settings_manager.cpp +++ b/src/map_settings_manager.cpp @@ -32,6 +32,10 @@ MapSettingsManager::MapSettingsManager(Settings *user_settings, m_user_settings(user_settings) { assert(m_user_settings != NULL); + + Mapgen::setDefaultSettings(m_map_settings); + // This inherits the combined defaults provided by loadGameConfAndInitWorld. + m_map_settings->overrideDefaults(user_settings); } @@ -179,20 +183,8 @@ MapgenParams *MapSettingsManager::makeMapgenParams() params->mgtype = mgtype; - // Load the mapgen param defaults - /* FIXME: Why is it done like this? MapgenParams should just - * set the defaults in its constructor instead. */ - { - Settings default_settings; - Mapgen::setDefaultSettings(&default_settings); - params->MapgenParams::readParams(&default_settings); - params->readParams(&default_settings); - } - // Load the rest of the mapgen params from our active settings - params->MapgenParams::readParams(m_user_settings); params->MapgenParams::readParams(m_map_settings); - params->readParams(m_user_settings); params->readParams(m_map_settings); // Hold onto our params diff --git a/src/mapblock.cpp b/src/mapblock.cpp index 5b755b7a6..8bfecd755 100644 --- a/src/mapblock.cpp +++ b/src/mapblock.cpp @@ -533,7 +533,7 @@ void MapBlock::deSerialize(std::istream &is, u8 version, bool disk) // Timestamp TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos()) <<": Timestamp"<<std::endl); - setTimestamp(readU32(is)); + setTimestampNoChangedFlag(readU32(is)); m_disk_timestamp = m_timestamp; // Dynamically re-set ids based on node names @@ -668,13 +668,13 @@ void MapBlock::deSerialize_pre22(std::istream &is, u8 version, bool disk) // Ignore errors try { if (version <= 15) { - std::string data = deSerializeString(is); + std::string data = deSerializeString16(is); std::istringstream iss(data, std::ios_base::binary); content_nodemeta_deserialize_legacy(iss, &m_node_metadata, &m_node_timers, m_gamedef->idef()); } else { - //std::string data = deSerializeLongString(is); + //std::string data = deSerializeString32(is); std::ostringstream oss(std::ios_base::binary); decompressZlib(is, oss); std::istringstream iss(oss.str(), std::ios_base::binary); @@ -716,10 +716,10 @@ void MapBlock::deSerialize_pre22(std::istream &is, u8 version, bool disk) // Timestamp if (version >= 17) { - setTimestamp(readU32(is)); + setTimestampNoChangedFlag(readU32(is)); m_disk_timestamp = m_timestamp; } else { - setTimestamp(BLOCK_TIMESTAMP_UNDEFINED); + setTimestampNoChangedFlag(BLOCK_TIMESTAMP_UNDEFINED); } // Dynamically re-set ids based on node names diff --git a/src/mapgen/mapgen.cpp b/src/mapgen/mapgen.cpp index f57529082..e0dfd2d71 100644 --- a/src/mapgen/mapgen.cpp +++ b/src/mapgen/mapgen.cpp @@ -58,6 +58,7 @@ FlagDesc flagdesc_mapgen[] = { {"light", MG_LIGHT}, {"decorations", MG_DECORATIONS}, {"biomes", MG_BIOMES}, + {"ores", MG_ORES}, {NULL, 0} }; @@ -217,7 +218,7 @@ void Mapgen::getMapgenNames(std::vector<const char *> *mgnames, bool include_hid void Mapgen::setDefaultSettings(Settings *settings) { settings->setDefault("mg_flags", flagdesc_mapgen, - MG_CAVES | MG_DUNGEONS | MG_LIGHT | MG_DECORATIONS | MG_BIOMES); + MG_CAVES | MG_DUNGEONS | MG_LIGHT | MG_DECORATIONS | MG_BIOMES | MG_ORES); for (int i = 0; i < (int)MAPGEN_INVALID; ++i) { MapgenParams *params = createMapgenParams((MapgenType)i); @@ -243,26 +244,6 @@ u32 Mapgen::getBlockSeed2(v3s16 p, s32 seed) } -// Returns Y one under area minimum if not found -s16 Mapgen::findGroundLevelFull(v2s16 p2d) -{ - const v3s16 &em = vm->m_area.getExtent(); - s16 y_nodes_max = vm->m_area.MaxEdge.Y; - s16 y_nodes_min = vm->m_area.MinEdge.Y; - u32 i = vm->m_area.index(p2d.X, y_nodes_max, p2d.Y); - s16 y; - - for (y = y_nodes_max; y >= y_nodes_min; y--) { - MapNode &n = vm->m_data[i]; - if (ndef->get(n).walkable) - break; - - VoxelArea::add_y(em, i, -1); - } - return (y >= y_nodes_min) ? y : y_nodes_min - 1; -} - - // Returns -MAX_MAP_GENERATION_LIMIT if not found s16 Mapgen::findGroundLevel(v2s16 p2d, s16 ymin, s16 ymax) { @@ -983,19 +964,6 @@ GenerateNotifier::GenerateNotifier(u32 notify_on, } -void GenerateNotifier::setNotifyOn(u32 notify_on) -{ - m_notify_on = notify_on; -} - - -void GenerateNotifier::setNotifyOnDecoIds( - const std::set<u32> *notify_on_deco_ids) -{ - m_notify_on_deco_ids = notify_on_deco_ids; -} - - bool GenerateNotifier::addEvent(GenNotifyType type, v3s16 pos, u32 id) { if (!(m_notify_on & (1 << type))) diff --git a/src/mapgen/mapgen.h b/src/mapgen/mapgen.h index a92b3b0d0..1487731e2 100644 --- a/src/mapgen/mapgen.h +++ b/src/mapgen/mapgen.h @@ -37,6 +37,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #define MG_LIGHT 0x10 #define MG_DECORATIONS 0x20 #define MG_BIOMES 0x40 +#define MG_ORES 0x80 typedef u16 biome_t; // copy from mg_biome.h to avoid an unnecessary include @@ -87,19 +88,17 @@ struct GenNotifyEvent { class GenerateNotifier { public: + // Use only for temporary Mapgen objects with no map generation! GenerateNotifier() = default; GenerateNotifier(u32 notify_on, const std::set<u32> *notify_on_deco_ids); - void setNotifyOn(u32 notify_on); - void setNotifyOnDecoIds(const std::set<u32> *notify_on_deco_ids); - bool addEvent(GenNotifyType type, v3s16 pos, u32 id=0); void getEvents(std::map<std::string, std::vector<v3s16> > &event_map); void clearEvents(); private: u32 m_notify_on = 0; - const std::set<u32> *m_notify_on_deco_ids; + const std::set<u32> *m_notify_on_deco_ids = nullptr; std::list<GenNotifyEvent> m_notify_events; }; @@ -185,7 +184,6 @@ public: static u32 getBlockSeed(v3s16 p, s32 seed); static u32 getBlockSeed2(v3s16 p, s32 seed); - s16 findGroundLevelFull(v2s16 p2d); s16 findGroundLevel(v2s16 p2d, s16 ymin, s16 ymax); s16 findLiquidSurface(v2s16 p2d, s16 ymin, s16 ymax); void updateHeightmap(v3s16 nmin, v3s16 nmax); diff --git a/src/mapgen/mapgen_carpathian.cpp b/src/mapgen/mapgen_carpathian.cpp index feb9b428c..b3a0bd270 100644 --- a/src/mapgen/mapgen_carpathian.cpp +++ b/src/mapgen/mapgen_carpathian.cpp @@ -260,12 +260,6 @@ void MapgenCarpathian::makeChunk(BlockMakeData *data) // Pre-conditions assert(data->vmanip); assert(data->nodedef); - assert(data->blockpos_requested.X >= data->blockpos_min.X && - data->blockpos_requested.Y >= data->blockpos_min.Y && - data->blockpos_requested.Z >= data->blockpos_min.Z); - assert(data->blockpos_requested.X <= data->blockpos_max.X && - data->blockpos_requested.Y <= data->blockpos_max.Y && - data->blockpos_requested.Z <= data->blockpos_max.Z); this->generating = true; this->vm = data->vmanip; @@ -315,7 +309,8 @@ void MapgenCarpathian::makeChunk(BlockMakeData *data) } // Generate the registered ores - m_emerge->oremgr->placeAllOres(this, blockseed, node_min, node_max); + if (flags & MG_ORES) + m_emerge->oremgr->placeAllOres(this, blockseed, node_min, node_max); // Generate dungeons if (flags & MG_DUNGEONS) diff --git a/src/mapgen/mapgen_flat.cpp b/src/mapgen/mapgen_flat.cpp index 369777ad2..342455029 100644 --- a/src/mapgen/mapgen_flat.cpp +++ b/src/mapgen/mapgen_flat.cpp @@ -1,7 +1,7 @@ /* Minetest -Copyright (C) 2015-2018 paramat -Copyright (C) 2015-2018 kwolekr, Ryan Kwolek <kwolekr@minetest.net> +Copyright (C) 2015-2020 paramat +Copyright (C) 2015-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net> 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 @@ -39,8 +39,9 @@ with this program; if not, write to the Free Software Foundation, Inc., FlagDesc flagdesc_mapgen_flat[] = { - {"lakes", MGFLAT_LAKES}, - {"hills", MGFLAT_HILLS}, + {"lakes", MGFLAT_LAKES}, + {"hills", MGFLAT_HILLS}, + {"caverns", MGFLAT_CAVERNS}, {NULL, 0} }; @@ -52,17 +53,21 @@ MapgenFlat::MapgenFlat(MapgenFlatParams *params, EmergeParams *emerge) { spflags = params->spflags; ground_level = params->ground_level; - large_cave_depth = params->large_cave_depth; + lake_threshold = params->lake_threshold; + lake_steepness = params->lake_steepness; + hill_threshold = params->hill_threshold; + hill_steepness = params->hill_steepness; + + cave_width = params->cave_width; small_cave_num_min = params->small_cave_num_min; small_cave_num_max = params->small_cave_num_max; large_cave_num_min = params->large_cave_num_min; large_cave_num_max = params->large_cave_num_max; + large_cave_depth = params->large_cave_depth; large_cave_flooded = params->large_cave_flooded; - cave_width = params->cave_width; - lake_threshold = params->lake_threshold; - lake_steepness = params->lake_steepness; - hill_threshold = params->hill_threshold; - hill_steepness = params->hill_steepness; + cavern_limit = params->cavern_limit; + cavern_taper = params->cavern_taper; + cavern_threshold = params->cavern_threshold; dungeon_ymin = params->dungeon_ymin; dungeon_ymax = params->dungeon_ymax; @@ -71,9 +76,11 @@ MapgenFlat::MapgenFlat(MapgenFlatParams *params, EmergeParams *emerge) if ((spflags & MGFLAT_LAKES) || (spflags & MGFLAT_HILLS)) noise_terrain = new Noise(¶ms->np_terrain, seed, csize.X, csize.Z); + // 3D noise MapgenBasic::np_cave1 = params->np_cave1; MapgenBasic::np_cave2 = params->np_cave2; + MapgenBasic::np_cavern = params->np_cavern; MapgenBasic::np_dungeons = params->np_dungeons; } @@ -88,11 +95,12 @@ MapgenFlat::~MapgenFlat() MapgenFlatParams::MapgenFlatParams(): - np_terrain (0, 1, v3f(600, 600, 600), 7244, 5, 0.6, 2.0), - np_filler_depth (0, 1.2, v3f(150, 150, 150), 261, 3, 0.7, 2.0), - np_cave1 (0, 12, v3f(61, 61, 61), 52534, 3, 0.5, 2.0), - np_cave2 (0, 12, v3f(67, 67, 67), 10325, 3, 0.5, 2.0), - np_dungeons (0.9, 0.5, v3f(500, 500, 500), 0, 2, 0.8, 2.0) + np_terrain (0, 1, v3f(600, 600, 600), 7244, 5, 0.6, 2.0), + np_filler_depth (0, 1.2, v3f(150, 150, 150), 261, 3, 0.7, 2.0), + np_cavern (0.0, 1.0, v3f(384, 128, 384), 723, 5, 0.63, 2.0), + np_cave1 (0, 12, v3f(61, 61, 61), 52534, 3, 0.5, 2.0), + np_cave2 (0, 12, v3f(67, 67, 67), 10325, 3, 0.5, 2.0), + np_dungeons (0.9, 0.5, v3f(500, 500, 500), 0, 2, 0.8, 2.0) { } @@ -112,11 +120,15 @@ void MapgenFlatParams::readParams(const Settings *settings) settings->getFloatNoEx("mgflat_lake_steepness", lake_steepness); settings->getFloatNoEx("mgflat_hill_threshold", hill_threshold); settings->getFloatNoEx("mgflat_hill_steepness", hill_steepness); + settings->getS16NoEx("mgflat_cavern_limit", cavern_limit); + settings->getS16NoEx("mgflat_cavern_taper", cavern_taper); + settings->getFloatNoEx("mgflat_cavern_threshold", cavern_threshold); settings->getS16NoEx("mgflat_dungeon_ymin", dungeon_ymin); settings->getS16NoEx("mgflat_dungeon_ymax", dungeon_ymax); settings->getNoiseParams("mgflat_np_terrain", np_terrain); settings->getNoiseParams("mgflat_np_filler_depth", np_filler_depth); + settings->getNoiseParams("mgflat_np_cavern", np_cavern); settings->getNoiseParams("mgflat_np_cave1", np_cave1); settings->getNoiseParams("mgflat_np_cave2", np_cave2); settings->getNoiseParams("mgflat_np_dungeons", np_dungeons); @@ -138,11 +150,15 @@ void MapgenFlatParams::writeParams(Settings *settings) const settings->setFloat("mgflat_lake_steepness", lake_steepness); settings->setFloat("mgflat_hill_threshold", hill_threshold); settings->setFloat("mgflat_hill_steepness", hill_steepness); + settings->setS16("mgflat_cavern_limit", cavern_limit); + settings->setS16("mgflat_cavern_taper", cavern_taper); + settings->setFloat("mgflat_cavern_threshold", cavern_threshold); settings->setS16("mgflat_dungeon_ymin", dungeon_ymin); settings->setS16("mgflat_dungeon_ymax", dungeon_ymax); settings->setNoiseParams("mgflat_np_terrain", np_terrain); settings->setNoiseParams("mgflat_np_filler_depth", np_filler_depth); + settings->setNoiseParams("mgflat_np_cavern", np_cavern); settings->setNoiseParams("mgflat_np_cave1", np_cave1); settings->setNoiseParams("mgflat_np_cave2", np_cave2); settings->setNoiseParams("mgflat_np_dungeons", np_dungeons); @@ -193,12 +209,6 @@ void MapgenFlat::makeChunk(BlockMakeData *data) // Pre-conditions assert(data->vmanip); assert(data->nodedef); - assert(data->blockpos_requested.X >= data->blockpos_min.X && - data->blockpos_requested.Y >= data->blockpos_min.Y && - data->blockpos_requested.Z >= data->blockpos_min.Z); - assert(data->blockpos_requested.X <= data->blockpos_max.X && - data->blockpos_requested.Y <= data->blockpos_max.Y && - data->blockpos_requested.Z <= data->blockpos_max.Z); this->generating = true; this->vm = data->vmanip; @@ -226,15 +236,30 @@ void MapgenFlat::makeChunk(BlockMakeData *data) generateBiomes(); } + // Generate tunnels, caverns and large randomwalk caves if (flags & MG_CAVES) { - // Generate tunnels + // Generate tunnels first as caverns confuse them generateCavesNoiseIntersection(stone_surface_max_y); + + // Generate caverns + bool near_cavern = false; + if (spflags & MGFLAT_CAVERNS) + near_cavern = generateCavernsNoise(stone_surface_max_y); + // Generate large randomwalk caves - generateCavesRandomWalk(stone_surface_max_y, large_cave_depth); + if (near_cavern) + // Disable large randomwalk caves in this mapchunk by setting + // 'large cave depth' to world base. Avoids excessive liquid in + // large caverns and floating blobs of overgenerated liquid. + generateCavesRandomWalk(stone_surface_max_y, + -MAX_MAP_GENERATION_LIMIT); + else + generateCavesRandomWalk(stone_surface_max_y, large_cave_depth); } // Generate the registered ores - m_emerge->oremgr->placeAllOres(this, blockseed, node_min, node_max); + if (flags & MG_ORES) + m_emerge->oremgr->placeAllOres(this, blockseed, node_min, node_max); if (flags & MG_DUNGEONS) generateDungeons(stone_surface_max_y); diff --git a/src/mapgen/mapgen_flat.h b/src/mapgen/mapgen_flat.h index 4902a802c..4b46aff27 100644 --- a/src/mapgen/mapgen_flat.h +++ b/src/mapgen/mapgen_flat.h @@ -1,7 +1,7 @@ /* Minetest -Copyright (C) 2015-2018 paramat -Copyright (C) 2015-2018 kwolekr, Ryan Kwolek <kwolekr@minetest.net> +Copyright (C) 2015-2020 paramat +Copyright (C) 2015-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net> 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 @@ -25,6 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc., /////// Mapgen Flat flags #define MGFLAT_LAKES 0x01 #define MGFLAT_HILLS 0x02 +#define MGFLAT_CAVERNS 0x04 class BiomeManager; @@ -33,22 +34,27 @@ extern FlagDesc flagdesc_mapgen_flat[]; struct MapgenFlatParams : public MapgenParams { s16 ground_level = 8; - s16 large_cave_depth = -33; + float lake_threshold = -0.45f; + float lake_steepness = 48.0f; + float hill_threshold = 0.45f; + float hill_steepness = 64.0f; + + float cave_width = 0.09f; u16 small_cave_num_min = 0; u16 small_cave_num_max = 0; u16 large_cave_num_min = 0; u16 large_cave_num_max = 2; + s16 large_cave_depth = -33; float large_cave_flooded = 0.5f; - float cave_width = 0.09f; - float lake_threshold = -0.45f; - float lake_steepness = 48.0f; - float hill_threshold = 0.45f; - float hill_steepness = 64.0f; + s16 cavern_limit = -256; + s16 cavern_taper = 256; + float cavern_threshold = 0.7f; s16 dungeon_ymin = -31000; s16 dungeon_ymax = 31000; NoiseParams np_terrain; NoiseParams np_filler_depth; + NoiseParams np_cavern; NoiseParams np_cave1; NoiseParams np_cave2; NoiseParams np_dungeons; diff --git a/src/mapgen/mapgen_fractal.cpp b/src/mapgen/mapgen_fractal.cpp index cb55bc288..fabb1b2b1 100644 --- a/src/mapgen/mapgen_fractal.cpp +++ b/src/mapgen/mapgen_fractal.cpp @@ -209,12 +209,6 @@ void MapgenFractal::makeChunk(BlockMakeData *data) // Pre-conditions assert(data->vmanip); assert(data->nodedef); - assert(data->blockpos_requested.X >= data->blockpos_min.X && - data->blockpos_requested.Y >= data->blockpos_min.Y && - data->blockpos_requested.Z >= data->blockpos_min.Z); - assert(data->blockpos_requested.X <= data->blockpos_max.X && - data->blockpos_requested.Y <= data->blockpos_max.Y && - data->blockpos_requested.Z <= data->blockpos_max.Z); //TimeTaker t("makeChunk"); @@ -250,7 +244,8 @@ void MapgenFractal::makeChunk(BlockMakeData *data) } // Generate the registered ores - m_emerge->oremgr->placeAllOres(this, blockseed, node_min, node_max); + if (flags & MG_ORES) + m_emerge->oremgr->placeAllOres(this, blockseed, node_min, node_max); // Generate dungeons if (flags & MG_DUNGEONS) diff --git a/src/mapgen/mapgen_singlenode.cpp b/src/mapgen/mapgen_singlenode.cpp index cade9e7a8..5382423fa 100644 --- a/src/mapgen/mapgen_singlenode.cpp +++ b/src/mapgen/mapgen_singlenode.cpp @@ -50,12 +50,6 @@ void MapgenSinglenode::makeChunk(BlockMakeData *data) // Pre-conditions assert(data->vmanip); assert(data->nodedef); - assert(data->blockpos_requested.X >= data->blockpos_min.X && - data->blockpos_requested.Y >= data->blockpos_min.Y && - data->blockpos_requested.Z >= data->blockpos_min.Z); - assert(data->blockpos_requested.X <= data->blockpos_max.X && - data->blockpos_requested.Y <= data->blockpos_max.Y && - data->blockpos_requested.Z <= data->blockpos_max.Z); this->generating = true; this->vm = data->vmanip; diff --git a/src/mapgen/mapgen_v5.cpp b/src/mapgen/mapgen_v5.cpp index 124667e5d..87e54755f 100644 --- a/src/mapgen/mapgen_v5.cpp +++ b/src/mapgen/mapgen_v5.cpp @@ -201,12 +201,6 @@ void MapgenV5::makeChunk(BlockMakeData *data) // Pre-conditions assert(data->vmanip); assert(data->nodedef); - assert(data->blockpos_requested.X >= data->blockpos_min.X && - data->blockpos_requested.Y >= data->blockpos_min.Y && - data->blockpos_requested.Z >= data->blockpos_min.Z); - assert(data->blockpos_requested.X <= data->blockpos_max.X && - data->blockpos_requested.Y <= data->blockpos_max.Y && - data->blockpos_requested.Z <= data->blockpos_max.Z); this->generating = true; this->vm = data->vmanip; @@ -257,7 +251,8 @@ void MapgenV5::makeChunk(BlockMakeData *data) } // Generate the registered ores - m_emerge->oremgr->placeAllOres(this, blockseed, node_min, node_max); + if (flags & MG_ORES) + m_emerge->oremgr->placeAllOres(this, blockseed, node_min, node_max); // Generate dungeons and desert temples if (flags & MG_DUNGEONS) diff --git a/src/mapgen/mapgen_v6.cpp b/src/mapgen/mapgen_v6.cpp index e9692246c..e04180f96 100644 --- a/src/mapgen/mapgen_v6.cpp +++ b/src/mapgen/mapgen_v6.cpp @@ -341,12 +341,6 @@ float MapgenV6::baseTerrainLevelFromMap(int index) } -s16 MapgenV6::find_ground_level_from_noise(u64 seed, v2s16 p2d, s16 precision) -{ - return baseTerrainLevelFromNoise(p2d) + MGV6_AVERAGE_MUD_AMOUNT; -} - - int MapgenV6::getGroundLevelAtPoint(v2s16 p) { return baseTerrainLevelFromNoise(p) + MGV6_AVERAGE_MUD_AMOUNT; @@ -518,12 +512,6 @@ void MapgenV6::makeChunk(BlockMakeData *data) // Pre-conditions assert(data->vmanip); assert(data->nodedef); - assert(data->blockpos_requested.X >= data->blockpos_min.X && - data->blockpos_requested.Y >= data->blockpos_min.Y && - data->blockpos_requested.Z >= data->blockpos_min.Z); - assert(data->blockpos_requested.X <= data->blockpos_max.X && - data->blockpos_requested.Y <= data->blockpos_max.Y && - data->blockpos_requested.Z <= data->blockpos_max.Z); this->generating = true; this->vm = data->vmanip; @@ -652,7 +640,8 @@ void MapgenV6::makeChunk(BlockMakeData *data) m_emerge->decomgr->placeAllDecos(this, blockseed, node_min, node_max); // Generate the registered ores - m_emerge->oremgr->placeAllOres(this, blockseed, node_min, node_max); + if (flags & MG_ORES) + m_emerge->oremgr->placeAllOres(this, blockseed, node_min, node_max); // Calculate lighting if (flags & MG_LIGHT) diff --git a/src/mapgen/mapgen_v6.h b/src/mapgen/mapgen_v6.h index ff565edec..a6e6da8c6 100644 --- a/src/mapgen/mapgen_v6.h +++ b/src/mapgen/mapgen_v6.h @@ -150,7 +150,6 @@ public: s16 find_stone_level(v2s16 p2d); bool block_is_underground(u64 seed, v3s16 blockpos); - s16 find_ground_level_from_noise(u64 seed, v2s16 p2d, s16 precision); float getHumidity(v2s16 p); float getTreeAmount(v2s16 p); diff --git a/src/mapgen/mapgen_v7.cpp b/src/mapgen/mapgen_v7.cpp index e93dc9140..cc5f5726d 100644 --- a/src/mapgen/mapgen_v7.cpp +++ b/src/mapgen/mapgen_v7.cpp @@ -317,12 +317,6 @@ void MapgenV7::makeChunk(BlockMakeData *data) // Pre-conditions assert(data->vmanip); assert(data->nodedef); - assert(data->blockpos_requested.X >= data->blockpos_min.X && - data->blockpos_requested.Y >= data->blockpos_min.Y && - data->blockpos_requested.Z >= data->blockpos_min.Z); - assert(data->blockpos_requested.X <= data->blockpos_max.X && - data->blockpos_requested.Y <= data->blockpos_max.Y && - data->blockpos_requested.Z <= data->blockpos_max.Z); //TimeTaker t("makeChunk"); @@ -342,10 +336,6 @@ void MapgenV7::makeChunk(BlockMakeData *data) // Generate base and mountain terrain s16 stone_surface_max_y = generateTerrain(); - // Generate rivers - if (spflags & MGV7_RIDGES) - generateRidgeTerrain(); - // Create heightmap updateHeightmap(node_min, node_max); @@ -377,7 +367,8 @@ void MapgenV7::makeChunk(BlockMakeData *data) } // Generate the registered ores - m_emerge->oremgr->placeAllOres(this, blockseed, node_min, node_max); + if (flags & MG_ORES) + m_emerge->oremgr->placeAllOres(this, blockseed, node_min, node_max); // Generate dungeons if (flags & MG_DUNGEONS) @@ -466,6 +457,23 @@ bool MapgenV7::getMountainTerrainFromMap(int idx_xyz, int idx_xz, s16 y) } +bool MapgenV7::getRiverChannelFromMap(int idx_xyz, int idx_xz, s16 y) +{ + // Maximum width of river channel. Creates the vertical canyon walls + float width = 0.2f; + float absuwatern = std::fabs(noise_ridge_uwater->result[idx_xz]) * 2.0f; + if (absuwatern > width) + return false; + + float altitude = y - water_level; + float height_mod = (altitude + 17.0f) / 2.5f; + float width_mod = width - absuwatern; + float nridge = noise_ridge->result[idx_xyz] * std::fmax(altitude, 0.0f) / 7.0f; + + return nridge + width_mod * height_mod >= 0.6f; +} + + bool MapgenV7::getFloatlandTerrainFromMap(int idx_xyz, float float_offset) { return noise_floatland->result[idx_xyz] + floatland_density - float_offset >= 0.0f; @@ -520,6 +528,15 @@ int MapgenV7::generateTerrain() } } + // 'Generate rivers in this mapchunk' bool for + // simplification of condition checks in y-loop. + bool gen_rivers = (spflags & MGV7_RIDGES) && node_max.Y >= water_level - 16 && + !gen_floatlands; + if (gen_rivers) { + noise_ridge->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z); + noise_ridge_uwater->perlinMap2D(node_min.X, node_min.Z); + } + //// Place nodes const v3s16 &em = vm->m_area.getExtent(); s16 stone_surface_max_y = -MAX_MAP_GENERATION_LIMIT; @@ -543,10 +560,13 @@ int MapgenV7::generateTerrain() if (vm->m_data[vi].getContent() != CONTENT_IGNORE) continue; - if (y <= surface_y) { + bool is_river_channel = gen_rivers && + getRiverChannelFromMap(index3d, index2d, y); + if (y <= surface_y && !is_river_channel) { vm->m_data[vi] = n_stone; // Base terrain } else if ((spflags & MGV7_MOUNTAINS) && - getMountainTerrainFromMap(index3d, index2d, y)) { + getMountainTerrainFromMap(index3d, index2d, y) && + !is_river_channel) { vm->m_data[vi] = n_stone; // Mountain terrain if (y > stone_surface_max_y) stone_surface_max_y = y; @@ -568,45 +588,3 @@ int MapgenV7::generateTerrain() return stone_surface_max_y; } - - -void MapgenV7::generateRidgeTerrain() -{ - if (node_max.Y < water_level - 16 || - (node_max.Y >= floatland_ymin && node_min.Y <= floatland_ymax)) - return; - - noise_ridge->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z); - noise_ridge_uwater->perlinMap2D(node_min.X, node_min.Z); - - MapNode n_water(c_water_source); - MapNode n_air(CONTENT_AIR); - u32 index3d = 0; - float width = 0.2f; - - for (s16 z = node_min.Z; z <= node_max.Z; z++) - for (s16 y = node_min.Y - 1; y <= node_max.Y + 1; y++) { - u32 vi = vm->m_area.index(node_min.X, y, z); - for (s16 x = node_min.X; x <= node_max.X; x++, index3d++, vi++) { - u32 index2d = (z - node_min.Z) * csize.X + (x - node_min.X); - float uwatern = noise_ridge_uwater->result[index2d] * 2.0f; - if (std::fabs(uwatern) > width) - continue; - // Optimises, but also avoids removing nodes placed by mods in - // 'on-generated', when generating outside mapchunk. - content_t c = vm->m_data[vi].getContent(); - if (c != c_stone) - continue; - - float altitude = y - water_level; - float height_mod = (altitude + 17.0f) / 2.5f; - float width_mod = width - std::fabs(uwatern); - float nridge = noise_ridge->result[index3d] * - std::fmax(altitude, 0.0f) / 7.0f; - if (nridge + width_mod * height_mod < 0.6f) - continue; - - vm->m_data[vi] = (y > water_level) ? n_air : n_water; - } - } -} diff --git a/src/mapgen/mapgen_v7.h b/src/mapgen/mapgen_v7.h index 4020cd935..5db10a304 100644 --- a/src/mapgen/mapgen_v7.h +++ b/src/mapgen/mapgen_v7.h @@ -94,10 +94,10 @@ public: float baseTerrainLevelFromMap(int index); bool getMountainTerrainAtPoint(s16 x, s16 y, s16 z); bool getMountainTerrainFromMap(int idx_xyz, int idx_xz, s16 y); + bool getRiverChannelFromMap(int idx_xyz, int idx_xz, s16 y); bool getFloatlandTerrainFromMap(int idx_xyz, float float_offset); int generateTerrain(); - void generateRidgeTerrain(); private: s16 mount_zero_level; diff --git a/src/mapgen/mapgen_valleys.cpp b/src/mapgen/mapgen_valleys.cpp index efcc8ee85..c4234857e 100644 --- a/src/mapgen/mapgen_valleys.cpp +++ b/src/mapgen/mapgen_valleys.cpp @@ -210,12 +210,6 @@ void MapgenValleys::makeChunk(BlockMakeData *data) // Pre-conditions assert(data->vmanip); assert(data->nodedef); - assert(data->blockpos_requested.X >= data->blockpos_min.X && - data->blockpos_requested.Y >= data->blockpos_min.Y && - data->blockpos_requested.Z >= data->blockpos_min.Z); - assert(data->blockpos_requested.X <= data->blockpos_max.X && - data->blockpos_requested.Y <= data->blockpos_max.Y && - data->blockpos_requested.Z <= data->blockpos_max.Z); //TimeTaker t("makeChunk"); @@ -268,7 +262,8 @@ void MapgenValleys::makeChunk(BlockMakeData *data) } // Generate the registered ores - m_emerge->oremgr->placeAllOres(this, blockseed, node_min, node_max); + if (flags & MG_ORES) + m_emerge->oremgr->placeAllOres(this, blockseed, node_min, node_max); // Dungeon creation if (flags & MG_DUNGEONS) diff --git a/src/mapgen/mg_ore.cpp b/src/mapgen/mg_ore.cpp index b50ed6a32..5814f433a 100644 --- a/src/mapgen/mg_ore.cpp +++ b/src/mapgen/mg_ore.cpp @@ -498,7 +498,11 @@ void OreVein::generate(MMVManip *vm, int mapseed, u32 blockseed, } // randval ranges from -1..1 - float randval = (float)pr.next() / (pr.RANDOM_RANGE / 2) - 1.f; + /* + Note: can generate values slightly larger than 1 + but this can't be changed as mapgen must be deterministic accross versions. + */ + float randval = (float)pr.next() / float(pr.RANDOM_RANGE / 2) - 1.f; float noiseval = contour(noise->result[index]); float noiseval2 = contour(noise2->result[index]); if (noiseval * noiseval2 + randval * random_factor < nthresh) diff --git a/src/mapgen/mg_schematic.cpp b/src/mapgen/mg_schematic.cpp index ba102d997..dfd414709 100644 --- a/src/mapgen/mg_schematic.cpp +++ b/src/mapgen/mg_schematic.cpp @@ -314,7 +314,7 @@ bool Schematic::deserializeFromMts(std::istream *is, //// Read node names u16 nidmapcount = readU16(ss); for (int i = 0; i != nidmapcount; i++) { - std::string name = deSerializeString(ss); + std::string name = deSerializeString16(ss); // Instances of "ignore" from v1 are converted to air (and instances // are fixed to have MTSCHEM_PROB_NEVER later on). @@ -372,7 +372,7 @@ bool Schematic::serializeToMts(std::ostream *os, writeU16(ss, names.size()); // name count for (size_t i = 0; i != names.size(); i++) - ss << serializeString(names[i]); // node names + ss << serializeString16(names[i]); // node names // compressed bulk node data MapNode::serializeBulk(ss, SER_FMT_VER_HIGHEST_WRITE, diff --git a/src/mapgen/treegen.cpp b/src/mapgen/treegen.cpp index e7e30c880..e633d800a 100644 --- a/src/mapgen/treegen.cpp +++ b/src/mapgen/treegen.cpp @@ -527,19 +527,6 @@ treegen::error make_ltree(MMVManip &vmanip, v3s16 p0, } -void tree_node_placement(MMVManip &vmanip, v3f p0, MapNode node) -{ - v3s16 p1 = v3s16(myround(p0.X), myround(p0.Y), myround(p0.Z)); - if (!vmanip.m_area.contains(p1)) - return; - u32 vi = vmanip.m_area.index(p1); - if (vmanip.m_data[vi].getContent() != CONTENT_AIR - && vmanip.m_data[vi].getContent() != CONTENT_IGNORE) - return; - vmanip.m_data[vmanip.m_area.index(p1)] = node; -} - - void tree_trunk_placement(MMVManip &vmanip, v3f p0, TreeDef &tree_definition) { v3s16 p1 = v3s16(myround(p0.X), myround(p0.Y), myround(p0.Z)); diff --git a/src/mapgen/treegen.h b/src/mapgen/treegen.h index 447baabb3..59a418824 100644 --- a/src/mapgen/treegen.h +++ b/src/mapgen/treegen.h @@ -76,8 +76,6 @@ namespace treegen { const NodeDefManager *ndef, const TreeDef &tree_definition); // L-System tree gen helper functions - void tree_node_placement(MMVManip &vmanip, v3f p0, - MapNode node); void tree_trunk_placement(MMVManip &vmanip, v3f p0, TreeDef &tree_definition); void tree_leaves_placement(MMVManip &vmanip, v3f p0, diff --git a/src/mapsector.h b/src/mapsector.h index dede364f6..ffd4cdd1d 100644 --- a/src/mapsector.h +++ b/src/mapsector.h @@ -62,6 +62,7 @@ public: bool empty() const { return m_blocks.empty(); } + int size() const { return m_blocks.size(); } protected: // The pile of MapBlocks diff --git a/src/event.h b/src/mtevent.h index 149f7eecd..149f7eecd 100644 --- a/src/event.h +++ b/src/mtevent.h diff --git a/src/nameidmapping.cpp b/src/nameidmapping.cpp index bd5bb1fc8..05cfae069 100644 --- a/src/nameidmapping.cpp +++ b/src/nameidmapping.cpp @@ -27,7 +27,7 @@ void NameIdMapping::serialize(std::ostream &os) const writeU16(os, m_id_to_name.size()); for (const auto &i : m_id_to_name) { writeU16(os, i.first); - os << serializeString(i.second); + os << serializeString16(i.second); } } @@ -41,7 +41,7 @@ void NameIdMapping::deSerialize(std::istream &is) m_name_to_id.clear(); for (u32 i = 0; i < count; i++) { u16 id = readU16(is); - std::string name = deSerializeString(is); + std::string name = deSerializeString16(is); m_id_to_name[id] = name; m_name_to_id[name] = id; } diff --git a/src/network/CMakeLists.txt b/src/network/CMakeLists.txt index c6995ab22..d2e2f52e9 100644 --- a/src/network/CMakeLists.txt +++ b/src/network/CMakeLists.txt @@ -16,8 +16,3 @@ if (BUILD_CLIENT) PARENT_SCOPE ) endif() - -# Haiku networking support -if(HAIKU) - set(PLATFORM_LIBS -lnetwork ${PLATFORM_LIBS}) -endif() diff --git a/src/network/clientopcodes.cpp b/src/network/clientopcodes.cpp index f812a08a1..55cfdd4dc 100644 --- a/src/network/clientopcodes.cpp +++ b/src/network/clientopcodes.cpp @@ -122,6 +122,7 @@ const ToClientCommandHandler toClientCommandTable[TOCLIENT_NUM_MSG_TYPES] = null_command_handler, { "TOCLIENT_SRP_BYTES_S_B", TOCLIENT_STATE_NOT_CONNECTED, &Client::handleCommand_SrpBytesSandB }, // 0x60 { "TOCLIENT_FORMSPEC_PREPEND", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_FormspecPrepend }, // 0x61, + { "TOCLIENT_MINIMAP_MODES", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_MinimapModes }, // 0x62, }; const static ServerCommandFactory null_command_factory = { "TOSERVER_NULL", 0, false }; diff --git a/src/network/clientpackethandler.cpp b/src/network/clientpackethandler.cpp index a3f1e668d..c39586f2c 100644 --- a/src/network/clientpackethandler.cpp +++ b/src/network/clientpackethandler.cpp @@ -504,7 +504,7 @@ void Client::handleCommand_ActiveObjectMessages(NetworkPacket* pkt) if (!is.good()) break; - std::string message = deSerializeString(is); + std::string message = deSerializeString16(is); // Pass on to the environment m_env.processActiveObjectMessage(id, message); @@ -1017,7 +1017,7 @@ void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt) p.minsize = readF32(is); p.maxsize = readF32(is); p.collisiondetection = readU8(is); - p.texture = deSerializeLongString(is); + p.texture = deSerializeString32(is); server_id = readU32(is); @@ -1185,26 +1185,32 @@ void Client::handleCommand_HudSetFlags(NetworkPacket* pkt) player->hud_flags |= flags; if (g_settings->getBool("hud_flags_bypass")) - player->hud_flags = HUD_FLAG_HOTBAR_VISIBLE | HUD_FLAG_HEALTHBAR_VISIBLE | + player->hud_flags = HUD_FLAG_HOTBAR_VISIBLE | HUD_FLAG_HEALTHBAR_VISIBLE | HUD_FLAG_CROSSHAIR_VISIBLE | HUD_FLAG_WIELDITEM_VISIBLE | HUD_FLAG_BREATHBAR_VISIBLE | HUD_FLAG_MINIMAP_VISIBLE | HUD_FLAG_MINIMAP_RADAR_VISIBLE; - m_minimap_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE); bool m_minimap_radar_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE); + // Not so satisying code to keep compatibility with old fixed mode system + // --> + // Hide minimap if it has been disabled by the server if (m_minimap && m_minimap_disabled_by_server && was_minimap_visible) // defers a minimap update, therefore only call it if really // needed, by checking that minimap was visible before - m_minimap->setMinimapMode(MINIMAP_MODE_OFF); - - // Switch to surface mode if radar disabled by server - if (m_minimap && m_minimap_radar_disabled_by_server && was_minimap_radar_visible) - m_minimap->setMinimapMode(MINIMAP_MODE_SURFACEx1); - - + m_minimap->setModeIndex(0); + + // If radar has been disabled, try to find a non radar mode or fall back to 0 + if (m_minimap && m_minimap_radar_disabled_by_server + && was_minimap_radar_visible) { + while (m_minimap->getModeIndex() > 0 && + m_minimap->getModeDef().type == MINIMAP_TYPE_RADAR) + m_minimap->nextMode(); + } + // <-- + // End of 'not so satifying code' } void Client::handleCommand_HudSetParam(NetworkPacket* pkt) @@ -1239,11 +1245,11 @@ void Client::handleCommand_HudSetSky(NetworkPacket* pkt) SkyboxParams skybox; skybox.bgcolor = video::SColor(readARGB8(is)); - skybox.type = std::string(deSerializeString(is)); + skybox.type = std::string(deSerializeString16(is)); u16 count = readU16(is); for (size_t i = 0; i < count; i++) - skybox.textures.emplace_back(deSerializeString(is)); + skybox.textures.emplace_back(deSerializeString16(is)); skybox.clouds = true; try { @@ -1645,3 +1651,30 @@ void Client::handleCommand_ModChannelSignal(NetworkPacket *pkt) if (valid_signal) m_script->on_modchannel_signal(channel, signal); } + +void Client::handleCommand_MinimapModes(NetworkPacket *pkt) +{ + u16 count; // modes + u16 mode; // wanted current mode index after change + + *pkt >> count >> mode; + + if (m_minimap) + m_minimap->clearModes(); + + for (size_t index = 0; index < count; index++) { + u16 type; + std::string label; + u16 size; + std::string texture; + u16 scale; + + *pkt >> type >> label >> size >> texture >> scale; + + if (m_minimap) + m_minimap->addMode(MinimapType(type), size, label, texture, scale); + } + + if (m_minimap) + m_minimap->setModeIndex(mode); +} diff --git a/src/network/connection.cpp b/src/network/connection.cpp index 3692e45a9..0ba8c36b2 100644 --- a/src/network/connection.cpp +++ b/src/network/connection.cpp @@ -1269,7 +1269,8 @@ bool Connection::deletePeer(session_t peer_id, bool timeout) return false; peer = m_peers[peer_id]; m_peers.erase(peer_id); - m_peer_ids.remove(peer_id); + auto it = std::find(m_peer_ids.begin(), m_peer_ids.end(), peer_id); + m_peer_ids.erase(it); } Address peer_address; @@ -1565,7 +1566,7 @@ void Connection::sendAck(session_t peer_id, u8 channelnum, u16 seqnum) UDPPeer* Connection::createServerPeer(Address& address) { - if (getPeerNoEx(PEER_ID_SERVER) != 0) + if (ConnectedToServer()) { throw ConnectionException("Already connected to a server"); } diff --git a/src/network/connection.h b/src/network/connection.h index 47b0805ce..24cd4fe4a 100644 --- a/src/network/connection.h +++ b/src/network/connection.h @@ -30,7 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "networkprotocol.h" #include <iostream> #include <fstream> -#include <list> +#include <vector> #include <map> class NetworkPacket; @@ -795,7 +795,7 @@ protected: void PrintInfo(std::ostream &out); - std::list<session_t> getPeerIDs() + std::vector<session_t> getPeerIDs() { MutexAutoLock peerlock(m_peers_mutex); return m_peer_ids; @@ -809,6 +809,11 @@ protected: void putEvent(ConnectionEvent &e); void TriggerSend(); + + bool ConnectedToServer() + { + return getPeerNoEx(PEER_ID_SERVER) != nullptr; + } private: MutexedQueue<ConnectionEvent> m_event_queue; @@ -816,7 +821,7 @@ private: u32 m_protocol_id; std::map<session_t, Peer *> m_peers; - std::list<session_t> m_peer_ids; + std::vector<session_t> m_peer_ids; std::mutex m_peers_mutex; std::unique_ptr<ConnectionSendThread> m_sendThread; diff --git a/src/network/connectionthreads.cpp b/src/network/connectionthreads.cpp index 9a6617a1c..7b62bc792 100644 --- a/src/network/connectionthreads.cpp +++ b/src/network/connectionthreads.cpp @@ -144,7 +144,7 @@ void ConnectionSendThread::Trigger() bool ConnectionSendThread::packetsQueued() { - std::list<session_t> peerIds = m_connection->getPeerIDs(); + std::vector<session_t> peerIds = m_connection->getPeerIDs(); if (!m_outgoing_queue.empty() && !peerIds.empty()) return true; @@ -171,8 +171,8 @@ bool ConnectionSendThread::packetsQueued() void ConnectionSendThread::runTimeouts(float dtime) { - std::list<session_t> timeouted_peers; - std::list<session_t> peerIds = m_connection->getPeerIDs(); + std::vector<session_t> timeouted_peers; + std::vector<session_t> peerIds = m_connection->getPeerIDs(); for (session_t &peerId : peerIds) { PeerHelper peer = m_connection->getPeerNoEx(peerId); @@ -548,7 +548,7 @@ void ConnectionSendThread::disconnect() // Send to all - std::list<session_t> peerids = m_connection->getPeerIDs(); + std::vector<session_t> peerids = m_connection->getPeerIDs(); for (session_t peerid : peerids) { sendAsPacket(peerid, 0, data, false); @@ -620,7 +620,7 @@ void ConnectionSendThread::sendReliable(ConnectionCommand &c) void ConnectionSendThread::sendToAll(u8 channelnum, const SharedBuffer<u8> &data) { - std::list<session_t> peerids = m_connection->getPeerIDs(); + std::vector<session_t> peerids = m_connection->getPeerIDs(); for (session_t peerid : peerids) { send(peerid, channelnum, data); @@ -629,7 +629,7 @@ void ConnectionSendThread::sendToAll(u8 channelnum, const SharedBuffer<u8> &data void ConnectionSendThread::sendToAllReliable(ConnectionCommand &c) { - std::list<session_t> peerids = m_connection->getPeerIDs(); + std::vector<session_t> peerids = m_connection->getPeerIDs(); for (session_t peerid : peerids) { PeerHelper peer = m_connection->getPeerNoEx(peerid); @@ -643,8 +643,8 @@ void ConnectionSendThread::sendToAllReliable(ConnectionCommand &c) void ConnectionSendThread::sendPackets(float dtime) { - std::list<session_t> peerIds = m_connection->getPeerIDs(); - std::list<session_t> pendingDisconnect; + std::vector<session_t> peerIds = m_connection->getPeerIDs(); + std::vector<session_t> pendingDisconnect; std::map<session_t, bool> pending_unreliable; const unsigned int peer_packet_quota = m_iteration_packets_avaialble @@ -843,13 +843,11 @@ void *ConnectionReceiveThread::run() if (debug_print_timer > 20.0) { debug_print_timer -= 20.0; - std::list<session_t> peerids = m_connection->getPeerIDs(); + std::vector<session_t> peerids = m_connection->getPeerIDs(); - for (std::list<session_t>::iterator i = peerids.begin(); - i != peerids.end(); - i++) + for (auto id : peerids) { - PeerHelper peer = m_connection->getPeerNoEx(*i); + PeerHelper peer = m_connection->getPeerNoEx(id); if (!peer) continue; @@ -958,8 +956,11 @@ void ConnectionReceiveThread::receive(SharedBuffer<u8> &packetdata, // command was sent reliably. } - /* The peer was not found in our lists. Add it. */ if (peer_id == PEER_ID_INEXISTENT) { + /* Ignore it if we are a client */ + if (m_connection->ConnectedToServer()) + return; + /* The peer was not found in our lists. Add it. */ peer_id = m_connection->createPeer(sender, MTP_MINETEST_RELIABLE_UDP, 0); } @@ -1039,7 +1040,7 @@ void ConnectionReceiveThread::receive(SharedBuffer<u8> &packetdata, bool ConnectionReceiveThread::getFromBuffers(session_t &peer_id, SharedBuffer<u8> &dst) { - std::list<session_t> peerids = m_connection->getPeerIDs(); + std::vector<session_t> peerids = m_connection->getPeerIDs(); for (session_t peerid : peerids) { PeerHelper peer = m_connection->getPeerNoEx(peerid); diff --git a/src/network/networkpacket.cpp b/src/network/networkpacket.cpp index 4d531b611..6d0abb12c 100644 --- a/src/network/networkpacket.cpp +++ b/src/network/networkpacket.cpp @@ -223,13 +223,6 @@ NetworkPacket& NetworkPacket::operator>>(char& dst) return *this; } -char NetworkPacket::getChar(u32 offset) -{ - checkReadOffset(offset, 1); - - return readU8(&m_data[offset]); -} - NetworkPacket& NetworkPacket::operator<<(char src) { checkDataSize(1); diff --git a/src/network/networkpacket.h b/src/network/networkpacket.h index fc8617651..e77bfb744 100644 --- a/src/network/networkpacket.h +++ b/src/network/networkpacket.h @@ -64,7 +64,6 @@ public: std::string readLongString(); - char getChar(u32 offset); NetworkPacket &operator>>(char &dst); NetworkPacket &operator<<(char src); diff --git a/src/network/networkprotocol.h b/src/network/networkprotocol.h index e57a7a794..98d143c89 100644 --- a/src/network/networkprotocol.h +++ b/src/network/networkprotocol.h @@ -204,6 +204,7 @@ with this program; if not, write to the Free Software Foundation, Inc., PROTOCOL VERSION 39: Updated set_sky packet Adds new sun, moon and stars packets + Minimap modes */ #define LATEST_PROTOCOL_VERSION 40 @@ -764,6 +765,17 @@ enum ToClientCommand u8[len] formspec */ + TOCLIENT_MINIMAP_MODES = 0x62, + /* + u16 count // modes + u16 mode // wanted current mode index after change + for each mode + u16 type + std::string label + u16 size + std::string extra + */ + TOCLIENT_NUM_MSG_TYPES = 0x63, }; @@ -1034,7 +1046,7 @@ const static std::string accessDeniedStrings[SERVER_ACCESSDENIED_MAX] = { "This server has experienced an internal error. You will now be disconnected." }; -enum PlayerListModifer: u8 +enum PlayerListModifer : u8 { PLAYER_LIST_INIT, PLAYER_LIST_ADD, diff --git a/src/network/serveropcodes.cpp b/src/network/serveropcodes.cpp index 2fc3197c2..aea5d7174 100644 --- a/src/network/serveropcodes.cpp +++ b/src/network/serveropcodes.cpp @@ -221,4 +221,5 @@ const ClientCommandFactory clientCommandFactoryTable[TOCLIENT_NUM_MSG_TYPES] = null_command_factory, // 0x5f { "TOSERVER_SRP_BYTES_S_B", 0, true }, // 0x60 { "TOCLIENT_FORMSPEC_PREPEND", 0, true }, // 0x61 + { "TOCLIENT_MINIMAP_MODES", 0, true }, // 0x62 }; diff --git a/src/network/serverpackethandler.cpp b/src/network/serverpackethandler.cpp index b3008bb50..3db4eb286 100644 --- a/src/network/serverpackethandler.cpp +++ b/src/network/serverpackethandler.cpp @@ -491,17 +491,18 @@ void Server::process_PlayerPos(RemotePlayer *player, PlayerSAO *playersao, playersao->setPlayerYaw(yaw); playersao->setFov(fov); playersao->setWantedRange(wanted_range); + player->keyPressed = keyPressed; - player->control.up = (keyPressed & 1); - player->control.down = (keyPressed & 2); - player->control.left = (keyPressed & 4); - player->control.right = (keyPressed & 8); - player->control.jump = (keyPressed & 16); - player->control.aux1 = (keyPressed & 32); - player->control.sneak = (keyPressed & 64); - player->control.LMB = (keyPressed & 128); - player->control.RMB = (keyPressed & 256); - player->control.zoom = (keyPressed & 512); + player->control.up = (keyPressed & (0x1 << 0)); + player->control.down = (keyPressed & (0x1 << 1)); + player->control.left = (keyPressed & (0x1 << 2)); + player->control.right = (keyPressed & (0x1 << 3)); + player->control.jump = (keyPressed & (0x1 << 4)); + player->control.aux1 = (keyPressed & (0x1 << 5)); + player->control.sneak = (keyPressed & (0x1 << 6)); + player->control.dig = (keyPressed & (0x1 << 7)); + player->control.place = (keyPressed & (0x1 << 8)); + player->control.zoom = (keyPressed & (0x1 << 9)); if (playersao->checkMovementCheat()) { // Call callbacks @@ -599,7 +600,7 @@ void Server::handleCommand_InventoryAction(NetworkPacket* pkt) << std::endl; std::istringstream is(datastring, std::ios_base::binary); // Create an action - InventoryAction *a = InventoryAction::deSerialize(is); + std::unique_ptr<InventoryAction> a(InventoryAction::deSerialize(is)); if (!a) { infostream << "TOSERVER_INVENTORY_ACTION: " << "InventoryAction::deSerialize() returned NULL" @@ -616,11 +617,30 @@ void Server::handleCommand_InventoryAction(NetworkPacket* pkt) where the client made a bad prediction. */ + const bool player_has_interact = checkPriv(player->getName(), "interact"); + + auto check_inv_access = [player, player_has_interact] ( + 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) { + infostream << "Cannot modify foreign inventory: " + << "No interact privilege" << std::endl; + return false; + } + return true; + }; + /* Handle restrictions and special cases of the move action */ if (a->getType() == IAction::Move) { - IMoveAction *ma = (IMoveAction*)a; + IMoveAction *ma = (IMoveAction*)a.get(); ma->from_inv.applyCurrentPlayer(player->getName()); ma->to_inv.applyCurrentPlayer(player->getName()); @@ -629,15 +649,11 @@ void Server::handleCommand_InventoryAction(NetworkPacket* pkt) if (ma->from_inv != ma->to_inv) m_inventory_mgr->setInventoryModified(ma->to_inv); - bool from_inv_is_current_player = - (ma->from_inv.type == InventoryLocation::PLAYER) && - (ma->from_inv.name == player->getName()); - - bool to_inv_is_current_player = - (ma->to_inv.type == InventoryLocation::PLAYER) && - (ma->to_inv.name == player->getName()); + if (!check_inv_access(ma->from_inv) || + !check_inv_access(ma->to_inv)) + return; - InventoryLocation *remote = from_inv_is_current_player ? + InventoryLocation *remote = ma->from_inv.type == InventoryLocation::PLAYER ? &ma->to_inv : &ma->from_inv; // Check for out-of-range interaction @@ -657,7 +673,6 @@ void Server::handleCommand_InventoryAction(NetworkPacket* pkt) << (ma->from_inv.dump()) << ":" << ma->from_list << " to " << (ma->to_inv.dump()) << ":" << ma->to_list << " because src is " << ma->from_list << std::endl; - delete a; return; } @@ -669,18 +684,6 @@ void Server::handleCommand_InventoryAction(NetworkPacket* pkt) << (ma->from_inv.dump()) << ":" << ma->from_list << " to " << (ma->to_inv.dump()) << ":" << ma->to_list << " because dst is " << ma->to_list << std::endl; - delete a; - return; - } - - // Disallow moving items in elsewhere than player's inventory - // if not allowed to interact - if (!checkPriv(player->getName(), "interact") && - (!from_inv_is_current_player || - !to_inv_is_current_player)) { - infostream << "Cannot move outside of player's inventory: " - << "No interact privilege" << std::endl; - delete a; return; } } @@ -688,7 +691,7 @@ void Server::handleCommand_InventoryAction(NetworkPacket* pkt) Handle restrictions and special cases of the drop action */ else if (a->getType() == IAction::Drop) { - IDropAction *da = (IDropAction*)a; + IDropAction *da = (IDropAction*)a.get(); da->from_inv.applyCurrentPlayer(player->getName()); @@ -701,22 +704,18 @@ void Server::handleCommand_InventoryAction(NetworkPacket* pkt) infostream << "Ignoring IDropAction from " << (da->from_inv.dump()) << ":" << da->from_list << " because src is " << da->from_list << std::endl; - delete a; return; } // Disallow dropping items if not allowed to interact - if (!checkPriv(player->getName(), "interact")) { - delete a; + if (!player_has_interact || !check_inv_access(da->from_inv)) return; - } // Disallow dropping items if dead if (playersao->isDead()) { infostream << "Ignoring IDropAction from " << (da->from_inv.dump()) << ":" << da->from_list << " because player is dead." << std::endl; - delete a; return; } } @@ -724,29 +723,28 @@ void Server::handleCommand_InventoryAction(NetworkPacket* pkt) Handle restrictions and special cases of the craft action */ else if (a->getType() == IAction::Craft) { - ICraftAction *ca = (ICraftAction*)a; + ICraftAction *ca = (ICraftAction*)a.get(); ca->craft_inv.applyCurrentPlayer(player->getName()); m_inventory_mgr->setInventoryModified(ca->craft_inv); - //bool craft_inv_is_current_player = - // (ca->craft_inv.type == InventoryLocation::PLAYER) && - // (ca->craft_inv.name == player->getName()); - // Disallow crafting if not allowed to interact - if (!checkPriv(player->getName(), "interact")) { + if (!player_has_interact) { infostream << "Cannot craft: " << "No interact privilege" << std::endl; - delete a; return; } + + if (!check_inv_access(ca->craft_inv)) + return; + } else { + // Unknown action. Ignored. + return; } // Do the action a->apply(m_inventory_mgr.get(), playersao, this); - // Eat the action - delete a; } void Server::handleCommand_ChatMessage(NetworkPacket* pkt) @@ -862,6 +860,15 @@ void Server::handleCommand_PlayerItem(NetworkPacket* pkt) *pkt >> item; + if (item >= player->getHotbarItemcount()) { + actionstream << "Player: " << player->getName() + << " tried to access item=" << item + << " out of hotbar_itemcount=" + << player->getHotbarItemcount() + << "; ignoring." << std::endl; + return; + } + playersao->getPlayer()->setWieldIndex(item); } @@ -977,11 +984,17 @@ void Server::handleCommand_Interact(NetworkPacket *pkt) v3f player_pos = playersao->getLastGoodPosition(); // Update wielded item - playersao->getPlayer()->setWieldIndex(item_i); - // Get pointed to node (undefined if not POINTEDTYPE_NODE) - v3s16 p_under = pointed.node_undersurface; - v3s16 p_above = pointed.node_abovesurface; + if (item_i >= player->getHotbarItemcount()) { + actionstream << "Player: " << player->getName() + << " tried to access item=" << item_i + << " out of hotbar_itemcount=" + << player->getHotbarItemcount() + << "; ignoring." << std::endl; + return; + } + + playersao->getPlayer()->setWieldIndex(item_i); // Get pointed to object (NULL if not POINTEDTYPE_OBJECT) ServerActiveObject *pointed_object = NULL; @@ -995,17 +1008,6 @@ void Server::handleCommand_Interact(NetworkPacket *pkt) } - v3f pointed_pos_under = player_pos; - v3f pointed_pos_above = player_pos; - if (pointed.type == POINTEDTHING_NODE) { - pointed_pos_under = intToFloat(p_under, BS); - pointed_pos_above = intToFloat(p_above, BS); - } - else if (pointed.type == POINTEDTHING_OBJECT) { - pointed_pos_under = pointed_object->getBasePosition(); - pointed_pos_above = pointed_pos_under; - } - /* Make sure the player is allowed to do it */ @@ -1013,16 +1015,19 @@ void Server::handleCommand_Interact(NetworkPacket *pkt) actionstream << player->getName() << " attempted to interact with " << pointed.dump() << " without 'interact' privilege" << std::endl; + if (pointed.type != POINTEDTHING_NODE) + return; + // Re-send block to revert change on client-side RemoteClient *client = getClient(peer_id); // Digging completed -> under if (action == INTERACT_DIGGING_COMPLETED) { - v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS)); + v3s16 blockpos = getNodeBlockPos(pointed.node_undersurface); client->SetBlockNotSent(blockpos); } // Placement -> above else if (action == INTERACT_PLACE) { - v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS)); + v3s16 blockpos = getNodeBlockPos(pointed.node_abovesurface); client->SetBlockNotSent(blockpos); } return; @@ -1030,7 +1035,6 @@ void Server::handleCommand_Interact(NetworkPacket *pkt) /* Check that target is reasonably close - (only when digging or placing things) */ static thread_local const bool enable_anticheat = !g_settings->getBool("disable_anticheat"); @@ -1038,13 +1042,21 @@ void Server::handleCommand_Interact(NetworkPacket *pkt) if ((action == INTERACT_START_DIGGING || action == INTERACT_DIGGING_COMPLETED || action == INTERACT_PLACE || action == INTERACT_USE) && enable_anticheat && !isSingleplayer()) { - float d = playersao->getEyePosition().getDistanceFrom(pointed_pos_under); + v3f target_pos = player_pos; + if (pointed.type == POINTEDTHING_NODE) { + target_pos = intToFloat(pointed.node_undersurface, BS); + } else if (pointed.type == POINTEDTHING_OBJECT) { + target_pos = pointed_object->getBasePosition(); + } + float d = playersao->getEyePosition().getDistanceFrom(target_pos); if (!checkInteractDistance(player, d, pointed.dump())) { - // Re-send block to revert change on client-side - RemoteClient *client = getClient(peer_id); - v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS)); - client->SetBlockNotSent(blockpos); + if (pointed.type == POINTEDTHING_NODE) { + // Re-send block to revert change on client-side + RemoteClient *client = getClient(peer_id); + v3s16 blockpos = getNodeBlockPos(pointed.node_undersurface); + client->SetBlockNotSent(blockpos); + } return; } } @@ -1055,20 +1067,20 @@ void Server::handleCommand_Interact(NetworkPacket *pkt) RollbackScopeActor rollback_scope(m_rollback, std::string("player:")+player->getName()); - /* - 0: start digging or punch object - */ - if (action == INTERACT_START_DIGGING) { + switch (action) { + // Start digging or punch object + case INTERACT_START_DIGGING: { if (pointed.type == POINTEDTHING_NODE) { MapNode n(CONTENT_IGNORE); bool pos_ok; + v3s16 p_under = pointed.node_undersurface; n = m_env->getMap().getNode(p_under, &pos_ok); if (!pos_ok) { infostream << "Server: Not punching: Node not found. " "Adding block to emerge queue." << std::endl; - m_emerge->enqueueBlockEmerge(peer_id, getNodeBlockPos(p_above), - false); + m_emerge->enqueueBlockEmerge(peer_id, + getNodeBlockPos(pointed.node_abovesurface), false); } if (n.getContent() != CONTENT_IGNORE) @@ -1076,159 +1088,155 @@ void Server::handleCommand_Interact(NetworkPacket *pkt) // Cheat prevention playersao->noCheatDigStart(p_under); + + return; } - else if (pointed.type == POINTEDTHING_OBJECT) { - // Skip if object can't be interacted with anymore - if (pointed_object->isGone()) - return; - ItemStack selected_item, hand_item; - ItemStack tool_item = playersao->getWieldedItem(&selected_item, &hand_item); - ToolCapabilities toolcap = - tool_item.getToolCapabilities(m_itemdef); - v3f dir = (pointed_object->getBasePosition() - - (playersao->getBasePosition() + playersao->getEyeOffset()) - ).normalize(); - float time_from_last_punch = - playersao->resetTimeFromLastPunch(); - - u16 src_original_hp = pointed_object->getHP(); - u16 dst_origin_hp = playersao->getHP(); - - u16 wear = pointed_object->punch(dir, &toolcap, playersao, - time_from_last_punch); - - // Callback may have changed item, so get it again - playersao->getWieldedItem(&selected_item); - bool changed = selected_item.addWear(wear, m_itemdef); - if (changed) - playersao->setWieldedItem(selected_item); - - // If the object is a player and its HP changed - if (src_original_hp != pointed_object->getHP() && - pointed_object->getType() == ACTIVEOBJECT_TYPE_PLAYER) { - SendPlayerHPOrDie((PlayerSAO *)pointed_object, - PlayerHPChangeReason(PlayerHPChangeReason::PLAYER_PUNCH, playersao)); - } + // Skip if the object can't be interacted with anymore + if (pointed.type != POINTEDTHING_OBJECT || pointed_object->isGone()) + return; - // If the puncher is a player and its HP changed - if (dst_origin_hp != playersao->getHP()) - SendPlayerHPOrDie(playersao, - PlayerHPChangeReason(PlayerHPChangeReason::PLAYER_PUNCH, pointed_object)); + ItemStack selected_item, hand_item; + ItemStack tool_item = playersao->getWieldedItem(&selected_item, &hand_item); + ToolCapabilities toolcap = + tool_item.getToolCapabilities(m_itemdef); + v3f dir = (pointed_object->getBasePosition() - + (playersao->getBasePosition() + playersao->getEyeOffset()) + ).normalize(); + float time_from_last_punch = + playersao->resetTimeFromLastPunch(); + + u16 src_original_hp = pointed_object->getHP(); + u16 dst_origin_hp = playersao->getHP(); + + u16 wear = pointed_object->punch(dir, &toolcap, playersao, + time_from_last_punch); + + // Callback may have changed item, so get it again + playersao->getWieldedItem(&selected_item); + bool changed = selected_item.addWear(wear, m_itemdef); + if (changed) + playersao->setWieldedItem(selected_item); + + // If the object is a player and its HP changed + if (src_original_hp != pointed_object->getHP() && + pointed_object->getType() == ACTIVEOBJECT_TYPE_PLAYER) { + SendPlayerHPOrDie((PlayerSAO *)pointed_object, + PlayerHPChangeReason(PlayerHPChangeReason::PLAYER_PUNCH, playersao)); } + // If the puncher is a player and its HP changed + if (dst_origin_hp != playersao->getHP()) + SendPlayerHPOrDie(playersao, + PlayerHPChangeReason(PlayerHPChangeReason::PLAYER_PUNCH, pointed_object)); + + return; } // action == INTERACT_START_DIGGING - /* - 1: stop digging - */ - else if (action == INTERACT_STOP_DIGGING) { - } // action == INTERACT_STOP_DIGGING + case INTERACT_STOP_DIGGING: + // Nothing to do + return; - /* - 2: Digging completed - */ - else if (action == INTERACT_DIGGING_COMPLETED) { + case INTERACT_DIGGING_COMPLETED: { // Only digging of nodes - if (pointed.type == POINTEDTHING_NODE) { - bool pos_ok; - MapNode n = m_env->getMap().getNode(p_under, &pos_ok); - if (!pos_ok) { - infostream << "Server: Not finishing digging: Node not found. " - "Adding block to emerge queue." << std::endl; - m_emerge->enqueueBlockEmerge(peer_id, getNodeBlockPos(p_above), - false); - } - - /* Cheat prevention */ - bool is_valid_dig = true; - if (enable_anticheat && !isSingleplayer()) { - v3s16 nocheat_p = playersao->getNoCheatDigPos(); - float nocheat_t = playersao->getNoCheatDigTime(); - playersao->noCheatDigEnd(); - // If player didn't start digging this, ignore dig - if (nocheat_p != p_under) { - infostream << "Server: " << player->getName() - << " started digging " - << PP(nocheat_p) << " and completed digging " - << PP(p_under) << "; not digging." << std::endl; - is_valid_dig = false; - // Call callbacks - m_script->on_cheat(playersao, "finished_unknown_dig"); - } + if (pointed.type != POINTEDTHING_NODE) + return; + bool pos_ok; + v3s16 p_under = pointed.node_undersurface; + MapNode n = m_env->getMap().getNode(p_under, &pos_ok); + if (!pos_ok) { + infostream << "Server: Not finishing digging: Node not found. " + "Adding block to emerge queue." << std::endl; + m_emerge->enqueueBlockEmerge(peer_id, + getNodeBlockPos(pointed.node_abovesurface), false); + } - // Get player's wielded item - // See also: Game::handleDigging - ItemStack selected_item, hand_item; - playersao->getPlayer()->getWieldedItem(&selected_item, &hand_item); - - // Get diggability and expected digging time - DigParams params = getDigParams(m_nodedef->get(n).groups, - &selected_item.getToolCapabilities(m_itemdef)); - // If can't dig, try hand - if (!params.diggable) { - params = getDigParams(m_nodedef->get(n).groups, - &hand_item.getToolCapabilities(m_itemdef)); - } - // If can't dig, ignore dig - if (!params.diggable) { - infostream << "Server: " << player->getName() - << " completed digging " << PP(p_under) - << ", which is not diggable with tool; not digging." - << std::endl; - is_valid_dig = false; - // Call callbacks - m_script->on_cheat(playersao, "dug_unbreakable"); - } - // Check digging time - // If already invalidated, we don't have to - if (!is_valid_dig) { - // Well not our problem then - } - // Clean and long dig - else if (params.time > 2.0 && nocheat_t * 1.2 > params.time) { - // All is good, but grab time from pool; don't care if - // it's actually available - playersao->getDigPool().grab(params.time); - } - // Short or laggy dig - // Try getting the time from pool - else if (playersao->getDigPool().grab(params.time)) { - // All is good - } - // Dig not possible - else { - infostream << "Server: " << player->getName() - << " completed digging " << PP(p_under) - << "too fast; not digging." << std::endl; - is_valid_dig = false; - // Call callbacks - m_script->on_cheat(playersao, "dug_too_fast"); - } + /* Cheat prevention */ + bool is_valid_dig = true; + if (enable_anticheat && !isSingleplayer()) { + v3s16 nocheat_p = playersao->getNoCheatDigPos(); + float nocheat_t = playersao->getNoCheatDigTime(); + playersao->noCheatDigEnd(); + // If player didn't start digging this, ignore dig + if (nocheat_p != p_under) { + infostream << "Server: " << player->getName() + << " started digging " + << PP(nocheat_p) << " and completed digging " + << PP(p_under) << "; not digging." << std::endl; + is_valid_dig = false; + // Call callbacks + m_script->on_cheat(playersao, "finished_unknown_dig"); } - /* Actually dig node */ - - if (is_valid_dig && n.getContent() != CONTENT_IGNORE) - m_script->node_on_dig(p_under, n, playersao); - - v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS)); - RemoteClient *client = getClient(peer_id); - // Send unusual result (that is, node not being removed) - if (m_env->getMap().getNode(p_under).getContent() != CONTENT_AIR) { - // Re-send block to revert change on client-side - client->SetBlockNotSent(blockpos); + // Get player's wielded item + // See also: Game::handleDigging + ItemStack selected_item, hand_item; + playersao->getPlayer()->getWieldedItem(&selected_item, &hand_item); + + // Get diggability and expected digging time + DigParams params = getDigParams(m_nodedef->get(n).groups, + &selected_item.getToolCapabilities(m_itemdef)); + // If can't dig, try hand + if (!params.diggable) { + params = getDigParams(m_nodedef->get(n).groups, + &hand_item.getToolCapabilities(m_itemdef)); + } + // If can't dig, ignore dig + if (!params.diggable) { + infostream << "Server: " << player->getName() + << " completed digging " << PP(p_under) + << ", which is not diggable with tool; not digging." + << std::endl; + is_valid_dig = false; + // Call callbacks + m_script->on_cheat(playersao, "dug_unbreakable"); + } + // Check digging time + // If already invalidated, we don't have to + if (!is_valid_dig) { + // Well not our problem then + } + // Clean and long dig + else if (params.time > 2.0 && nocheat_t * 1.2 > params.time) { + // All is good, but grab time from pool; don't care if + // it's actually available + playersao->getDigPool().grab(params.time); } + // Short or laggy dig + // Try getting the time from pool + else if (playersao->getDigPool().grab(params.time)) { + // All is good + } + // Dig not possible else { - client->ResendBlockIfOnWire(blockpos); + infostream << "Server: " << player->getName() + << " completed digging " << PP(p_under) + << "too fast; not digging." << std::endl; + is_valid_dig = false; + // Call callbacks + m_script->on_cheat(playersao, "dug_too_fast"); } } + + /* Actually dig node */ + + if (is_valid_dig && n.getContent() != CONTENT_IGNORE) + m_script->node_on_dig(p_under, n, playersao); + + v3s16 blockpos = getNodeBlockPos(p_under); + RemoteClient *client = getClient(peer_id); + // Send unusual result (that is, node not being removed) + if (m_env->getMap().getNode(p_under).getContent() != CONTENT_AIR) + // Re-send block to revert change on client-side + client->SetBlockNotSent(blockpos); + else + client->ResendBlockIfOnWire(blockpos); + + return; } // action == INTERACT_DIGGING_COMPLETED - /* - 3: place block or right-click object - */ - else if (action == INTERACT_PLACE) { + // Place block or right-click object + case INTERACT_PLACE: { ItemStack selected_item; playersao->getWieldedItem(&selected_item, nullptr); @@ -1257,59 +1265,54 @@ void Server::handleCommand_Interact(NetworkPacket *pkt) } pointed_object->rightClick(playersao); - } else if (m_script->item_OnPlace( - selected_item, playersao, pointed)) { + } else if (m_script->item_OnPlace(selected_item, playersao, pointed)) { // Placement was handled in lua // Apply returned ItemStack - if (playersao->setWieldedItem(selected_item)) { + if (playersao->setWieldedItem(selected_item)) SendInventory(playersao, true); - } } + if (pointed.type != POINTEDTHING_NODE) + return; + // If item has node placement prediction, always send the // blocks to make sure the client knows what exactly happened RemoteClient *client = getClient(peer_id); - v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS)); - v3s16 blockpos2 = getNodeBlockPos(floatToInt(pointed_pos_under, BS)); - if (!selected_item.getDefinition(m_itemdef).node_placement_prediction.empty()) { + v3s16 blockpos = getNodeBlockPos(pointed.node_abovesurface); + v3s16 blockpos2 = getNodeBlockPos(pointed.node_undersurface); + if (!selected_item.getDefinition(m_itemdef + ).node_placement_prediction.empty()) { client->SetBlockNotSent(blockpos); - if (blockpos2 != blockpos) { + if (blockpos2 != blockpos) client->SetBlockNotSent(blockpos2); - } - } - else { + } else { client->ResendBlockIfOnWire(blockpos); - if (blockpos2 != blockpos) { + if (blockpos2 != blockpos) client->ResendBlockIfOnWire(blockpos2); - } } + + return; } // action == INTERACT_PLACE - /* - 4: use - */ - else if (action == INTERACT_USE) { + case INTERACT_USE: { ItemStack selected_item; playersao->getWieldedItem(&selected_item, nullptr); actionstream << player->getName() << " uses " << selected_item.name << ", pointing at " << pointed.dump() << std::endl; - if (m_script->item_OnUse( - selected_item, playersao, pointed)) { + if (m_script->item_OnUse(selected_item, playersao, pointed)) { // Apply returned ItemStack - if (playersao->setWieldedItem(selected_item)) { + if (playersao->setWieldedItem(selected_item)) SendInventory(playersao, true); - } } - } // action == INTERACT_USE + return; + } - /* - 5: rightclick air - */ - else if (action == INTERACT_ACTIVATE) { + // Rightclick air + case INTERACT_ACTIVATE: { ItemStack selected_item; playersao->getWieldedItem(&selected_item, nullptr); @@ -1318,21 +1321,17 @@ void Server::handleCommand_Interact(NetworkPacket *pkt) pointed.type = POINTEDTHING_NOTHING; // can only ever be NOTHING - if (m_script->item_OnSecondaryUse( - selected_item, playersao, pointed)) { - if (playersao->setWieldedItem(selected_item)) { + if (m_script->item_OnSecondaryUse(selected_item, playersao, pointed)) { + if (playersao->setWieldedItem(selected_item)) SendInventory(playersao, true); - } } - } // action == INTERACT_ACTIVATE + return; + } + + default: + warningstream << "Server: Invalid action " << action << std::endl; - /* - Catch invalid actions - */ - else { - warningstream << "Server: Invalid action " - << action << std::endl; } } @@ -1658,19 +1657,18 @@ void Server::handleCommand_SrpBytesM(NetworkPacket* pkt) bool wantSudo = (cstate == CS_Active); - verbosestream << "Server: Received TOCLIENT_SRP_BYTES_M." << std::endl; + verbosestream << "Server: Received TOSERVER_SRP_BYTES_M." << std::endl; if (!((cstate == CS_HelloSent) || (cstate == CS_Active))) { - actionstream << "Server: got SRP _M packet in wrong state " - << cstate << " from " << addr_s - << ". Ignoring." << std::endl; + warningstream << "Server: got SRP_M packet in wrong state " + << cstate << " from " << addr_s << ". Ignoring." << std::endl; return; } if (client->chosen_mech != AUTH_MECHANISM_SRP && client->chosen_mech != AUTH_MECHANISM_LEGACY_PASSWORD) { - actionstream << "Server: got SRP _M packet, while auth" - << "is going on with mech " << client->chosen_mech << " from " + warningstream << "Server: got SRP_M packet, while auth " + "is going on with mech " << client->chosen_mech << " from " << addr_s << " (wantSudo=" << wantSudo << "). Denying." << std::endl; if (wantSudo) { DenySudoAccess(peer_id); @@ -1718,7 +1716,7 @@ void Server::handleCommand_SrpBytesM(NetworkPacket* pkt) std::string checkpwd; // not used, but needed for passing something if (!m_script->getAuth(playername, &checkpwd, NULL)) { - actionstream << "Server: " << playername << + errorstream << "Server: " << playername << " cannot be authenticated (auth handler does not work?)" << std::endl; DenyAccess(peer_id, SERVER_ACCESSDENIED_SERVER_FAIL); diff --git a/src/nodedef.cpp b/src/nodedef.cpp index 540ed6086..4d4fc7a7a 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -207,7 +207,7 @@ void TileDef::serialize(std::ostream &os, u16 protocol_version) const u8 version = 6; writeU8(os, version); - os << serializeString(name); + os << serializeString16(name); animation.serialize(os, version); bool has_scale = scale > 0; u16 flags = 0; @@ -241,7 +241,7 @@ void TileDef::deSerialize(std::istream &is, u8 contentfeatures_version, int version = readU8(is); if (version < 6) throw SerializationError("unsupported TileDef version"); - name = deSerializeString(is); + name = deSerializeString16(is); animation.deSerialize(is, version); u16 flags = readU16(is); backface_culling = flags & TILE_FLAG_BACKFACE_CULLING; @@ -266,9 +266,6 @@ void TextureSettings::readSettings() { connected_glass = g_settings->getBool("connected_glass"); opaque_water = g_settings->getBool("opaque_water"); - bool enable_shaders = g_settings->getBool("enable_shaders"); - bool enable_bumpmapping = g_settings->getBool("enable_bumpmapping"); - bool enable_parallax_occlusion = g_settings->getBool("enable_parallax_occlusion"); bool smooth_lighting = g_settings->getBool("smooth_lighting"); enable_mesh_cache = g_settings->getBool("enable_mesh_cache"); enable_minimap = g_settings->getBool("enable_minimap"); @@ -281,8 +278,6 @@ void TextureSettings::readSettings() if (smooth_lighting) enable_mesh_cache = false; - use_normal_texture = enable_shaders && - (enable_bumpmapping || enable_parallax_occlusion); if (leaves_style_str == "fancy") { leaves_style = LEAVES_FANCY; } else if (leaves_style_str == "simple") { @@ -416,10 +411,10 @@ void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const writeU8(os, version); // general - os << serializeString(name); + os << serializeString16(name); writeU16(os, groups.size()); for (const auto &group : groups) { - os << serializeString(group.first); + os << serializeString16(group.first); writeS16(os, group.second); } writeU8(os, param_type); @@ -427,7 +422,7 @@ void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const // visual writeU8(os, drawtype); - os << serializeString(mesh); + os << serializeString16(mesh); writeF32(os, visual_scale); writeU8(os, 6); for (const TileDef &td : tiledef) @@ -442,7 +437,7 @@ void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const writeU8(os, color.getRed()); writeU8(os, color.getGreen()); writeU8(os, color.getBlue()); - os << serializeString(palette_name); + os << serializeString16(palette_name); writeU8(os, waving); writeU8(os, connect_sides); writeU16(os, connects_to_ids.size()); @@ -470,8 +465,8 @@ void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const // liquid writeU8(os, liquid_type); - os << serializeString(liquid_alternative_flowing); - os << serializeString(liquid_alternative_source); + os << serializeString16(liquid_alternative_flowing); + os << serializeString16(liquid_alternative_source); writeU8(os, liquid_viscosity); writeU8(os, liquid_renewable); writeU8(os, liquid_range); @@ -492,7 +487,7 @@ void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const writeU8(os, legacy_facedir_simple); writeU8(os, legacy_wallmounted); - os << serializeString(node_dig_prediction); + os << serializeString16(node_dig_prediction); writeU8(os, leveled_max); } @@ -519,11 +514,11 @@ void ContentFeatures::deSerialize(std::istream &is) throw SerializationError("unsupported ContentFeatures version"); // general - name = deSerializeString(is); + name = deSerializeString16(is); groups.clear(); u32 groups_size = readU16(is); for (u32 i = 0; i < groups_size; i++) { - std::string name = deSerializeString(is); + std::string name = deSerializeString16(is); int value = readS16(is); groups[name] = value; } @@ -532,7 +527,7 @@ void ContentFeatures::deSerialize(std::istream &is) // visual drawtype = (enum NodeDrawType) readU8(is); - mesh = deSerializeString(is); + mesh = deSerializeString16(is); visual_scale = readF32(is); if (readU8(is) != 6) throw SerializationError("unsupported tile count"); @@ -548,7 +543,7 @@ void ContentFeatures::deSerialize(std::istream &is) color.setRed(readU8(is)); color.setGreen(readU8(is)); color.setBlue(readU8(is)); - palette_name = deSerializeString(is); + palette_name = deSerializeString16(is); waving = readU8(is); connect_sides = readU8(is); u16 connects_to_size = readU16(is); @@ -578,8 +573,8 @@ void ContentFeatures::deSerialize(std::istream &is) // liquid liquid_type = (enum LiquidType) readU8(is); - liquid_alternative_flowing = deSerializeString(is); - liquid_alternative_source = deSerializeString(is); + liquid_alternative_flowing = deSerializeString16(is); + liquid_alternative_source = deSerializeString16(is); liquid_viscosity = readU8(is); liquid_renewable = readU8(is); liquid_range = readU8(is); @@ -601,7 +596,7 @@ void ContentFeatures::deSerialize(std::istream &is) legacy_wallmounted = readU8(is); try { - node_dig_prediction = deSerializeString(is); + node_dig_prediction = deSerializeString16(is); u8 tmp_leveled_max = readU8(is); if (is.eof()) /* readU8 doesn't throw exceptions so we have to do this */ throw SerializationError(""); @@ -635,10 +630,6 @@ static void fillTileAttribs(ITextureSource *tsrc, TileLayer *layer, if (!tile.world_aligned) layer->scale = 1; - // Normal texture and shader flags texture - if (tsettings.use_normal_texture) { - layer->normal_texture = tsrc->getNormalTexture(tiledef.name); - } layer->flags_texture = tsrc->getShaderFlagsTexture(layer->normal_texture ? true : false); // Material flags @@ -695,9 +686,54 @@ static void fillTileAttribs(ITextureSource *tsrc, TileLayer *layer, } } } -#endif -#ifndef SERVER +bool ContentFeatures::textureAlphaCheck(ITextureSource *tsrc, const TileDef *tiles, int length) +{ + video::IVideoDriver *driver = RenderingEngine::get_video_driver(); + static thread_local bool long_warning_printed = false; + std::set<std::string> seen; + for (int i = 0; i < length; i++) { + if (seen.find(tiles[i].name) != seen.end()) + continue; + seen.insert(tiles[i].name); + + // Load the texture and see if there's any transparent pixels + video::ITexture *texture = tsrc->getTexture(tiles[i].name); + video::IImage *image = driver->createImage(texture, + core::position2d<s32>(0, 0), texture->getOriginalSize()); + if (!image) + continue; + core::dimension2d<u32> dim = image->getDimension(); + bool ok = true; + for (u16 x = 0; x < dim.Width; x++) { + for (u16 y = 0; y < dim.Height; y++) { + if (image->getPixel(x, y).getAlpha() < 255) { + ok = false; + goto break_loop; + } + } + } + +break_loop: + image->drop(); + if (!ok) { + warningstream << "Texture \"" << tiles[i].name << "\" of " + << name << " has transparent pixels, assuming " + "use_texture_alpha = true." << std::endl; + if (!long_warning_printed) { + warningstream << " This warning can be a false-positive if " + "unused pixels in the texture are transparent. However if " + "it is meant to be transparent, you *MUST* update the " + "nodedef and set use_texture_alpha = true! This compatibility " + "code will be removed in a few releases." << std::endl; + long_warning_printed = true; + } + return true; + } + } + return false; +} + bool isWorldAligned(AlignStyle style, WorldAlignMode mode, NodeDrawType drawtype) { if (style == ALIGN_STYLE_WORLD) @@ -814,13 +850,19 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc break; case NDT_MESH: case NDT_NODEBOX: + if (alpha == 255 && textureAlphaCheck(tsrc, tdef, 6)) + alpha = 0; + solidness = 0; if (waving == 1) material_type = TILE_MATERIAL_WAVING_PLANTS; else if (waving == 2) material_type = TILE_MATERIAL_WAVING_LEAVES; else if (waving == 3) - material_type = TILE_MATERIAL_WAVING_LIQUID_BASIC; + material_type = (alpha == 255) ? TILE_MATERIAL_WAVING_LIQUID_OPAQUE : + TILE_MATERIAL_WAVING_LIQUID_BASIC; + else if (alpha == 255) + material_type = TILE_MATERIAL_OPAQUE; break; case NDT_TORCHLIKE: case NDT_SIGNLIKE: @@ -1353,6 +1395,7 @@ void NodeDefManager::applyTextureOverrides(const std::vector<TextureOverride> &o ContentFeatures &nodedef = m_content_features[id]; + // Override tiles if (texture_override.hasTarget(OverrideTarget::TOP)) nodedef.tiledef[0].name = texture_override.texture; @@ -1370,6 +1413,26 @@ void NodeDefManager::applyTextureOverrides(const std::vector<TextureOverride> &o if (texture_override.hasTarget(OverrideTarget::FRONT)) nodedef.tiledef[5].name = texture_override.texture; + + + // Override special tiles, if applicable + if (texture_override.hasTarget(OverrideTarget::SPECIAL_1)) + nodedef.tiledef_special[0].name = texture_override.texture; + + if (texture_override.hasTarget(OverrideTarget::SPECIAL_2)) + nodedef.tiledef_special[1].name = texture_override.texture; + + if (texture_override.hasTarget(OverrideTarget::SPECIAL_3)) + nodedef.tiledef_special[2].name = texture_override.texture; + + if (texture_override.hasTarget(OverrideTarget::SPECIAL_4)) + nodedef.tiledef_special[3].name = texture_override.texture; + + if (texture_override.hasTarget(OverrideTarget::SPECIAL_5)) + nodedef.tiledef_special[4].name = texture_override.texture; + + if (texture_override.hasTarget(OverrideTarget::SPECIAL_6)) + nodedef.tiledef_special[5].name = texture_override.texture; } } @@ -1416,7 +1479,7 @@ void NodeDefManager::serialize(std::ostream &os, u16 protocol_version) const // strict version incompatibilities std::ostringstream wrapper_os(std::ios::binary); f->serialize(wrapper_os, protocol_version); - os2<<serializeString(wrapper_os.str()); + os2<<serializeString16(wrapper_os.str()); // must not overflow u16 next = count + 1; @@ -1424,7 +1487,7 @@ void NodeDefManager::serialize(std::ostream &os, u16 protocol_version) const count++; } writeU16(os, count); - os << serializeLongString(os2.str()); + os << serializeString32(os2.str()); } @@ -1435,13 +1498,13 @@ void NodeDefManager::deSerialize(std::istream &is) if (version != 1) throw SerializationError("unsupported NodeDefinitionManager version"); u16 count = readU16(is); - std::istringstream is2(deSerializeLongString(is), std::ios::binary); + std::istringstream is2(deSerializeString32(is), std::ios::binary); ContentFeatures f; for (u16 n = 0; n < count; n++) { u16 i = readU16(is2); // Read it from the string wrapper - std::string wrapper = deSerializeString(is2); + std::string wrapper = deSerializeString16(is2); std::istringstream wrapper_is(wrapper, std::ios::binary); f.deSerialize(wrapper_is); diff --git a/src/nodedef.h b/src/nodedef.h index cf03abaae..66c21cc07 100644 --- a/src/nodedef.h +++ b/src/nodedef.h @@ -158,7 +158,6 @@ public: int node_texture_size; bool opaque_water; bool connected_glass; - bool use_normal_texture; bool enable_mesh_cache; bool enable_minimap; @@ -261,6 +260,11 @@ struct TileDef NodeDrawType drawtype); }; +// Defines the number of special tiles per nodedef +// +// NOTE: When changing this value, the enum entries of OverrideTarget and +// parser in TextureOverrideSource must be updated so that all special +// tiles can be overridden. #define CF_SPECIAL_COUNT 6 struct ContentFeatures @@ -413,6 +417,7 @@ struct ContentFeatures void reset(); void serialize(std::ostream &os, u16 protocol_version) const; void deSerialize(std::istream &is); + /*! * Since vertex alpha is no longer supported, this method * adds opacity directly to the texture pixels. @@ -422,9 +427,36 @@ struct ContentFeatures */ void correctAlpha(TileDef *tiles, int length); +#ifndef SERVER + /* + * Checks if any tile texture has any transparent pixels. + * Prints a warning and returns true if that is the case, false otherwise. + * This is supposed to be used for use_texture_alpha backwards compatibility. + */ + bool textureAlphaCheck(ITextureSource *tsrc, const TileDef *tiles, + int length); +#endif + + /* Some handy methods */ + bool needsBackfaceCulling() const + { + switch (drawtype) { + case NDT_TORCHLIKE: + case NDT_SIGNLIKE: + case NDT_FIRELIKE: + case NDT_RAILLIKE: + case NDT_PLANTLIKE: + case NDT_PLANTLIKE_ROOTED: + case NDT_MESH: + return false; + default: + return true; + } + } + bool isLiquid() const{ return (liquid_type != LIQUID_NONE); } diff --git a/src/nodemetadata.cpp b/src/nodemetadata.cpp index b84ffc8cb..6447c8785 100644 --- a/src/nodemetadata.cpp +++ b/src/nodemetadata.cpp @@ -49,8 +49,8 @@ void NodeMetadata::serialize(std::ostream &os, u8 version, bool disk) const if (!disk && priv) continue; - os << serializeString(sv.first); - os << serializeLongString(sv.second); + os << serializeString16(sv.first); + os << serializeString32(sv.second); if (version >= 2) writeU8(os, (priv) ? 1 : 0); } @@ -63,8 +63,8 @@ void NodeMetadata::deSerialize(std::istream &is, u8 version) clear(); int num_vars = readU32(is); for(int i=0; i<num_vars; i++){ - std::string name = deSerializeString(is); - std::string var = deSerializeLongString(is); + std::string name = deSerializeString16(is); + std::string var = deSerializeString32(is); m_stringvars[name] = var; if (version >= 2) { if (readU8(is) == 1) diff --git a/src/noise.cpp b/src/noise.cpp index 5a1d989cb..e16564b05 100644 --- a/src/noise.cpp +++ b/src/noise.cpp @@ -424,7 +424,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) { - memcpy(&np, np_, sizeof(np)); + np = *np_; this->seed = seed; this->sx = sx; this->sy = sy; diff --git a/src/noise.h b/src/noise.h index 7b5e83251..613879890 100644 --- a/src/noise.h +++ b/src/noise.h @@ -29,6 +29,13 @@ #include "exceptions.h" #include "util/string.h" +#if defined(RANDOM_MIN) +#undef RANDOM_MIN +#endif +#if defined(RANDOM_MAX) +#undef RANDOM_MAX +#endif + extern FlagDesc flagdesc_noiseparams[]; // Note: this class is not polymorphic so that its high level of diff --git a/src/object_properties.cpp b/src/object_properties.cpp index 8d51bcbfa..f31773060 100644 --- a/src/object_properties.cpp +++ b/src/object_properties.cpp @@ -70,6 +70,7 @@ std::string ObjectProperties::dump() os << ", use_texture_alpha=" << use_texture_alpha; os << ", damage_texture_modifier=" << damage_texture_modifier; os << ", shaded=" << shaded; + os << ", show_on_minimap=" << show_on_minimap; return os.str(); } @@ -84,11 +85,11 @@ void ObjectProperties::serialize(std::ostream &os) const writeV3F32(os, selectionbox.MinEdge); writeV3F32(os, selectionbox.MaxEdge); writeU8(os, pointable); - os << serializeString(visual); + os << serializeString16(visual); writeV3F32(os, visual_size); writeU16(os, textures.size()); for (const std::string &texture : textures) { - os << serializeString(texture); + os << serializeString16(texture); } writeV2S16(os, spritediv); writeV2S16(os, initial_sprite_basepos); @@ -96,7 +97,7 @@ void ObjectProperties::serialize(std::ostream &os) const writeU8(os, makes_footstep_sound); writeF32(os, automatic_rotate); // Added in protocol version 14 - os << serializeString(mesh); + os << serializeString16(mesh); writeU16(os, colors.size()); for (video::SColor color : colors) { writeARGB8(os, color); @@ -106,18 +107,19 @@ void ObjectProperties::serialize(std::ostream &os) const writeU8(os, automatic_face_movement_dir); writeF32(os, automatic_face_movement_dir_offset); writeU8(os, backface_culling); - os << serializeString(nametag); + os << serializeString16(nametag); writeARGB8(os, nametag_color); writeF32(os, automatic_face_movement_max_rotation_per_sec); - os << serializeString(infotext); - os << serializeString(wield_item); + os << serializeString16(infotext); + os << serializeString16(wield_item); writeS8(os, glow); writeU16(os, breath_max); writeF32(os, eye_height); writeF32(os, zoom_fov); writeU8(os, use_texture_alpha); - os << serializeString(damage_texture_modifier); + os << serializeString16(damage_texture_modifier); writeU8(os, shaded); + writeU8(os, show_on_minimap); // Add stuff only at the bottom. // Never remove anything, because we don't want new versions of this @@ -137,19 +139,19 @@ void ObjectProperties::deSerialize(std::istream &is) selectionbox.MinEdge = readV3F32(is); selectionbox.MaxEdge = readV3F32(is); pointable = readU8(is); - visual = deSerializeString(is); + visual = deSerializeString16(is); visual_size = readV3F32(is); textures.clear(); u32 texture_count = readU16(is); for (u32 i = 0; i < texture_count; i++){ - textures.push_back(deSerializeString(is)); + textures.push_back(deSerializeString16(is)); } spritediv = readV2S16(is); initial_sprite_basepos = readV2S16(is); is_visible = readU8(is); makes_footstep_sound = readU8(is); automatic_rotate = readF32(is); - mesh = deSerializeString(is); + mesh = deSerializeString16(is); colors.clear(); u32 color_count = readU16(is); for (u32 i = 0; i < color_count; i++){ @@ -160,21 +162,25 @@ void ObjectProperties::deSerialize(std::istream &is) automatic_face_movement_dir = readU8(is); automatic_face_movement_dir_offset = readF32(is); backface_culling = readU8(is); - nametag = deSerializeString(is); + nametag = deSerializeString16(is); nametag_color = readARGB8(is); automatic_face_movement_max_rotation_per_sec = readF32(is); - infotext = deSerializeString(is); - wield_item = deSerializeString(is); + infotext = deSerializeString16(is); + wield_item = deSerializeString16(is); glow = readS8(is); breath_max = readU16(is); eye_height = readF32(is); zoom_fov = readF32(is); use_texture_alpha = readU8(is); try { - damage_texture_modifier = deSerializeString(is); + damage_texture_modifier = deSerializeString16(is); u8 tmp = readU8(is); if (is.eof()) - throw SerializationError(""); + return; shaded = tmp; + tmp = readU8(is); + if (is.eof()) + return; + show_on_minimap = tmp; } catch (SerializationError &e) {} } diff --git a/src/object_properties.h b/src/object_properties.h index f010c1daf..adb483527 100644 --- a/src/object_properties.h +++ b/src/object_properties.h @@ -62,6 +62,7 @@ struct ObjectProperties float zoom_fov = 0.0f; bool use_texture_alpha = false; bool shaded = true; + bool show_on_minimap = false; ObjectProperties(); std::string dump(); diff --git a/src/particles.cpp b/src/particles.cpp index fd81238dc..14c987958 100644 --- a/src/particles.cpp +++ b/src/particles.cpp @@ -28,7 +28,7 @@ void ParticleParameters::serialize(std::ostream &os, u16 protocol_ver) const writeF32(os, expirationtime); writeF32(os, size); writeU8(os, collisiondetection); - os << serializeLongString(texture); + os << serializeString32(texture); writeU8(os, vertical); writeU8(os, collision_removal); animation.serialize(os, 6); /* NOT the protocol ver */ @@ -47,7 +47,7 @@ void ParticleParameters::deSerialize(std::istream &is, u16 protocol_ver) expirationtime = readF32(is); size = readF32(is); collisiondetection = readU8(is); - texture = deSerializeLongString(is); + texture = deSerializeString32(is); vertical = readU8(is); collision_removal = readU8(is); animation.deSerialize(is, 6); /* NOT the protocol ver */ diff --git a/src/player.h b/src/player.h index f1b848a2a..9a4a41fa6 100644 --- a/src/player.h +++ b/src/player.h @@ -57,8 +57,8 @@ struct PlayerControl bool a_aux1, bool a_sneak, bool a_zoom, - bool a_LMB, - bool a_RMB, + bool a_dig, + bool a_place, float a_pitch, float a_yaw, float a_sidew_move_joystick_axis, @@ -73,8 +73,8 @@ struct PlayerControl aux1 = a_aux1; sneak = a_sneak; zoom = a_zoom; - LMB = a_LMB; - RMB = a_RMB; + dig = a_dig; + place = a_place; pitch = a_pitch; yaw = a_yaw; sidew_move_joystick_axis = a_sidew_move_joystick_axis; @@ -88,8 +88,8 @@ struct PlayerControl bool aux1 = false; bool sneak = false; bool zoom = false; - bool LMB = false; - bool RMB = false; + bool dig = false; + bool place = false; float pitch = 0.0f; float yaw = 0.0f; float sidew_move_joystick_axis = 0.0f; diff --git a/src/porting.cpp b/src/porting.cpp index d902d3737..e7ed4e090 100644 --- a/src/porting.cpp +++ b/src/porting.cpp @@ -25,7 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "porting.h" -#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__DragonFly__) +#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__DragonFly__) || defined(__OpenBSD__) #include <sys/types.h> #include <sys/sysctl.h> extern char **environ; @@ -56,6 +56,10 @@ with this program; if not, write to the Free Software Foundation, Inc., #include <crt_externs.h> #endif +#if defined(__HAIKU__) + #include <FindDirectory.h> +#endif + #include "config.h" #include "debug.h" #include "filesys.h" @@ -321,6 +325,12 @@ bool getCurrentExecPath(char *buf, size_t len) return true; } +#elif defined(__HAIKU__) + +bool getCurrentExecPath(char *buf, size_t len) +{ + return find_path(B_APP_IMAGE_SYMBOL, B_FIND_PATH_IMAGE_PATH, NULL, buf, len) == B_OK; +} //// Solaris #elif defined(__sun) || defined(sun) diff --git a/src/porting.h b/src/porting.h index f50f0a950..c7adf12a2 100644 --- a/src/porting.h +++ b/src/porting.h @@ -309,6 +309,8 @@ inline const char *getPlatformName() #else "SunOS" #endif +#elif defined(__HAIKU__) + "Haiku" #elif defined(__CYGWIN__) "Cygwin" #elif defined(__unix__) || defined(__unix) diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index 78c46f0e0..04f4c335c 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -57,6 +57,7 @@ void read_item_definition(lua_State* L, int index, es_ItemType, ITEM_NONE); getstringfield(L, index, "name", def.name); getstringfield(L, index, "description", def.description); + getstringfield(L, index, "short_description", def.short_description); getstringfield(L, index, "inventory_image", def.inventory_image); getstringfield(L, index, "inventory_overlay", def.inventory_overlay); getstringfield(L, index, "wield_image", def.wield_image); @@ -143,6 +144,8 @@ 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"); lua_pushstring(L, type.c_str()); lua_setfield(L, -2, "type"); lua_pushstring(L, i.inventory_image.c_str()); @@ -331,6 +334,7 @@ void read_object_properties(lua_State *L, int index, getfloatfield(L, -1, "zoom_fov", prop->zoom_fov); getboolfield(L, -1, "use_texture_alpha", prop->use_texture_alpha); getboolfield(L, -1, "shaded", prop->shaded); + getboolfield(L, -1, "show_on_minimap", prop->show_on_minimap); getstringfield(L, -1, "damage_texture_modifier", prop->damage_texture_modifier); } @@ -419,6 +423,8 @@ void push_object_properties(lua_State *L, ObjectProperties *prop) lua_setfield(L, -2, "shaded"); lua_pushlstring(L, prop->damage_texture_modifier.c_str(), prop->damage_texture_modifier.size()); lua_setfield(L, -2, "damage_texture_modifier"); + lua_pushboolean(L, prop->show_on_minimap); + lua_setfield(L, -2, "show_on_minimap"); } /******************************************************************************/ @@ -491,13 +497,11 @@ TileDef read_tiledef(lua_State *L, int index, u8 drawtype) } /******************************************************************************/ -ContentFeatures read_content_features(lua_State *L, int index) +void read_content_features(lua_State *L, ContentFeatures &f, int index) { if(index < 0) index = lua_gettop(L) + 1 + index; - ContentFeatures f; - /* Cache existence of some callbacks */ lua_getfield(L, index, "on_construct"); if(!lua_isnil(L, -1)) f.has_on_construct = true; @@ -800,7 +804,6 @@ ContentFeatures read_content_features(lua_State *L, int index) getstringfield(L, index, "node_dig_prediction", f.node_dig_prediction); - return f; } void push_content_features(lua_State *L, const ContentFeatures &c) @@ -1977,9 +1980,10 @@ void push_hud_element(lua_State *L, HudElement *elem) HudElementStat read_hud_change(lua_State *L, HudElement *elem, void **value) { HudElementStat stat = HUD_STAT_NUMBER; + std::string statstr; if (lua_isstring(L, 3)) { int statint; - std::string statstr = lua_tostring(L, 3); + statstr = lua_tostring(L, 3); stat = string_to_enum(es_HudElementStat, statint, statstr) ? (HudElementStat)statint : stat; } @@ -2007,6 +2011,8 @@ HudElementStat read_hud_change(lua_State *L, HudElement *elem, void **value) break; case HUD_STAT_ITEM: elem->item = luaL_checknumber(L, 4); + if (elem->type == HUD_ELEM_WAYPOINT && statstr == "precision") + elem->item++; *value = &elem->item; break; case HUD_STAT_DIR: diff --git a/src/script/common/c_content.h b/src/script/common/c_content.h index eeef71c36..08c74c292 100644 --- a/src/script/common/c_content.h +++ b/src/script/common/c_content.h @@ -67,7 +67,8 @@ struct collisionMoveResult; extern struct EnumString es_TileAnimationType[]; -ContentFeatures read_content_features (lua_State *L, int index); +void read_content_features (lua_State *L, ContentFeatures &f, + int index); void push_content_features (lua_State *L, const ContentFeatures &c); diff --git a/src/script/common/c_converter.cpp b/src/script/common/c_converter.cpp index eb6ab5331..c00401b58 100644 --- a/src/script/common/c_converter.cpp +++ b/src/script/common/c_converter.cpp @@ -679,48 +679,3 @@ size_t write_array_slice_float( return elem_index - 1; } - - -size_t write_array_slice_u16( - lua_State *L, - int table_index, - u16 *data, - v3u16 data_size, - v3u16 slice_offset, - v3u16 slice_size) -{ - v3u16 pmin, pmax(data_size); - - if (slice_offset.X > 0) { - slice_offset.X--; - pmin.X = slice_offset.X; - pmax.X = MYMIN(slice_offset.X + slice_size.X, data_size.X); - } - - if (slice_offset.Y > 0) { - slice_offset.Y--; - pmin.Y = slice_offset.Y; - pmax.Y = MYMIN(slice_offset.Y + slice_size.Y, data_size.Y); - } - - if (slice_offset.Z > 0) { - slice_offset.Z--; - pmin.Z = slice_offset.Z; - pmax.Z = MYMIN(slice_offset.Z + slice_size.Z, data_size.Z); - } - - const u32 ystride = data_size.X; - const u32 zstride = data_size.X * data_size.Y; - - u32 elem_index = 1; - for (u32 z = pmin.Z; z != pmax.Z; z++) - for (u32 y = pmin.Y; y != pmax.Y; y++) - for (u32 x = pmin.X; x != pmax.X; x++) { - u32 i = z * zstride + y * ystride + x; - lua_pushinteger(L, data[i]); - lua_rawseti(L, table_index, elem_index); - elem_index++; - } - - return elem_index - 1; -} diff --git a/src/script/common/c_converter.h b/src/script/common/c_converter.h index a4a7079fd..6ad6f3212 100644 --- a/src/script/common/c_converter.h +++ b/src/script/common/c_converter.h @@ -136,5 +136,3 @@ void warn_if_field_exists(lua_State *L, int table, size_t write_array_slice_float(lua_State *L, int table_index, float *data, v3u16 data_size, v3u16 slice_offset, v3u16 slice_size); -size_t write_array_slice_u16(lua_State *L, int table_index, u16 *data, - v3u16 data_size, v3u16 slice_offset, v3u16 slice_size); diff --git a/src/script/common/c_internal.cpp b/src/script/common/c_internal.cpp index 6df1f8b7b..ad5f836c5 100644 --- a/src/script/common/c_internal.cpp +++ b/src/script/common/c_internal.cpp @@ -155,24 +155,28 @@ static void script_log(lua_State *L, const std::string &message, infostream << script_get_backtrace(L) << std::endl; } -void log_deprecated(lua_State *L, const std::string &message, int stack_depth) +DeprecatedHandlingMode get_deprecated_handling_mode() { static thread_local bool configured = false; - static thread_local bool do_log = false; - static thread_local bool do_error = false; + static thread_local DeprecatedHandlingMode ret = DeprecatedHandlingMode::Ignore; // Only read settings on first call if (!configured) { std::string value = g_settings->get("deprecated_lua_api_handling"); if (value == "log") { - do_log = true; + ret = DeprecatedHandlingMode::Log; } else if (value == "error") { - do_log = true; - do_error = true; + ret = DeprecatedHandlingMode::Error; } configured = true; } - if (do_log) - script_log(L, message, warningstream, do_error, stack_depth); + return ret; +} + +void log_deprecated(lua_State *L, const std::string &message, int stack_depth) +{ + DeprecatedHandlingMode mode = get_deprecated_handling_mode(); + if (mode != DeprecatedHandlingMode::Ignore) + script_log(L, message, warningstream, mode == DeprecatedHandlingMode::Error, stack_depth); } diff --git a/src/script/common/c_internal.h b/src/script/common/c_internal.h index 442546332..452c2dd5e 100644 --- a/src/script/common/c_internal.h +++ b/src/script/common/c_internal.h @@ -114,5 +114,25 @@ void script_error(lua_State *L, int pcall_result, const char *mod, const char *f void script_run_callbacks_f(lua_State *L, int nargs, RunCallbacksMode mode, const char *fxn); +enum class DeprecatedHandlingMode { + Ignore, + Log, + Error +}; + +/** + * Reads `deprecated_lua_api_handling` in settings, returns cached value. + * + * @return DeprecatedHandlingMode + */ +DeprecatedHandlingMode get_deprecated_handling_mode(); + +/** + * Handles a deprecation warning based on user settings + * + * @param L Lua State + * @param message The deprecation method + * @param stack_depth How far on the stack to the first user function (ie: not builtin or core) + */ void log_deprecated(lua_State *L, const std::string &message, int stack_depth=1); diff --git a/src/script/common/helper.cpp b/src/script/common/helper.cpp index f53a2b7e8..488144790 100644 --- a/src/script/common/helper.cpp +++ b/src/script/common/helper.cpp @@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include <cmath> #include <sstream> #include <irr_v2d.h> +#include <irr_v3d.h> #include "c_types.h" #include "c_internal.h" @@ -54,17 +55,14 @@ template <> bool LuaHelper::readParam(lua_State *L, int index) return lua_toboolean(L, index) != 0; } -template <> bool LuaHelper::readParam(lua_State *L, int index, const bool &default_value) +template <> s16 LuaHelper::readParam(lua_State *L, int index) { - if (lua_isnil(L, index)) - return default_value; - - return lua_toboolean(L, index) != 0; + return lua_tonumber(L, index); } -template <> s16 LuaHelper::readParam(lua_State *L, int index) +template <> int LuaHelper::readParam(lua_State *L, int index) { - return lua_tonumber(L, index); + return luaL_checkint(L, index); } template <> float LuaHelper::readParam(lua_State *L, int index) @@ -105,6 +103,25 @@ template <> v2f LuaHelper::readParam(lua_State *L, int index) return p; } +template <> v3f LuaHelper::readParam(lua_State *L, int index) +{ + v3f p; + CHECK_POS_TAB(index); + lua_getfield(L, index, "x"); + CHECK_POS_COORD("x"); + p.X = readParam<float>(L, -1); + lua_pop(L, 1); + lua_getfield(L, index, "y"); + CHECK_POS_COORD("y"); + p.Y = readParam<float>(L, -1); + lua_pop(L, 1); + lua_getfield(L, index, "z"); + CHECK_POS_COORD("z"); + p.Z = readParam<float>(L, -1); + lua_pop(L, 1); + return p; +} + template <> std::string LuaHelper::readParam(lua_State *L, int index) { size_t length; @@ -113,16 +130,3 @@ template <> std::string LuaHelper::readParam(lua_State *L, int index) result.assign(str, length); return result; } - -template <> -std::string LuaHelper::readParam( - lua_State *L, int index, const std::string &default_value) -{ - std::string result; - const char *str = lua_tostring(L, index); - if (str) - result.append(str); - else - result = default_value; - return result; -} diff --git a/src/script/common/helper.h b/src/script/common/helper.h index d639d6e16..7a794dc9b 100644 --- a/src/script/common/helper.h +++ b/src/script/common/helper.h @@ -50,5 +50,8 @@ protected: * @return read value from Lua or default value if nil */ template <typename T> - static T readParam(lua_State *L, int index, const T &default_value); + static inline T readParam(lua_State *L, int index, const T &default_value) + { + return lua_isnoneornil(L, index) ? default_value : readParam<T>(L, index); + } }; diff --git a/src/script/cpp_api/s_security.cpp b/src/script/cpp_api/s_security.cpp index 9d65819c0..d5db43db9 100644 --- a/src/script/cpp_api/s_security.cpp +++ b/src/script/cpp_api/s_security.cpp @@ -632,7 +632,11 @@ int ScriptApiSecurity::sl_g_loadfile(lua_State *L) { #ifndef SERVER lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_SCRIPTAPI); +#if INDIRECT_SCRIPTAPI_RIDX + ScriptApiBase *script = (ScriptApiBase *) *(void**)(lua_touserdata(L, -1)); +#else ScriptApiBase *script = (ScriptApiBase *) lua_touserdata(L, -1); +#endif lua_pop(L, 1); // Client implementation diff --git a/src/script/lua_api/l_base.cpp b/src/script/lua_api/l_base.cpp index 011434845..fce6282a5 100644 --- a/src/script/lua_api/l_base.cpp +++ b/src/script/lua_api/l_base.cpp @@ -105,32 +105,21 @@ bool ModApiBase::registerFunction(lua_State *L, const char *name, return true; } -std::unordered_map<std::string, luaL_Reg> ModApiBase::m_deprecated_wrappers; -bool ModApiBase::m_error_deprecated_calls = false; - -int ModApiBase::l_deprecated_function(lua_State *L) +int ModApiBase::l_deprecated_function(lua_State *L, const char *good, const char *bad, lua_CFunction func) { thread_local std::vector<u64> deprecated_logged; + DeprecatedHandlingMode dep_mode = get_deprecated_handling_mode(); + if (dep_mode == DeprecatedHandlingMode::Ignore) + return func(L); + u64 start_time = porting::getTimeUs(); lua_Debug ar; - // Get function name for lookup - FATAL_ERROR_IF(!lua_getstack(L, 0, &ar), "lua_getstack() failed"); - FATAL_ERROR_IF(!lua_getinfo(L, "n", &ar), "lua_getinfo() failed"); - - // Combine name with line and script backtrace + // Get caller name with line and script backtrace FATAL_ERROR_IF(!lua_getstack(L, 1, &ar), "lua_getstack() failed"); FATAL_ERROR_IF(!lua_getinfo(L, "Sl", &ar), "lua_getinfo() failed"); - // Get parent class to get the wrappers map - luaL_checktype(L, 1, LUA_TUSERDATA); - void *ud = lua_touserdata(L, 1); - ModApiBase *o = *(ModApiBase**)ud; - - // New function and new function name - auto it = o->m_deprecated_wrappers.find(ar.name); - // Get backtrace and hash it to reduce the warning flood std::string backtrace = ar.short_src; backtrace.append(":").append(std::to_string(ar.currentline)); @@ -140,45 +129,16 @@ int ModApiBase::l_deprecated_function(lua_State *L) == deprecated_logged.end()) { deprecated_logged.emplace_back(hash); - warningstream << "Call to deprecated function '" << ar.name << "', please use '" - << it->second.name << "' at " << backtrace << std::endl; + warningstream << "Call to deprecated function '" << bad << "', please use '" + << good << "' at " << backtrace << std::endl; - if (m_error_deprecated_calls) + if (dep_mode == DeprecatedHandlingMode::Error) script_error(L, LUA_ERRRUN, NULL, NULL); } u64 end_time = porting::getTimeUs(); g_profiler->avg("l_deprecated_function", end_time - start_time); - return it->second.func(L); + return func(L); } -void ModApiBase::markAliasDeprecated(luaL_Reg *reg) -{ - std::string value = g_settings->get("deprecated_lua_api_handling"); - m_error_deprecated_calls = value == "error"; - - if (!m_error_deprecated_calls && value != "log") - return; - - const char *last_name = nullptr; - lua_CFunction last_func = nullptr; - - // ! Null termination ! - while (reg->func) { - if (last_func == reg->func) { - // Duplicate found - luaL_Reg original_reg; - // Do not inline struct. Breaks MSVC or is error-prone - original_reg.name = last_name; - original_reg.func = reg->func; - m_deprecated_wrappers.emplace( - std::pair<std::string, luaL_Reg>(reg->name, original_reg)); - reg->func = l_deprecated_function; - } - - last_func = reg->func; - last_name = reg->name; - ++reg; - } -} diff --git a/src/script/lua_api/l_base.h b/src/script/lua_api/l_base.h index 0cbee7756..9ba50dabf 100644 --- a/src/script/lua_api/l_base.h +++ b/src/script/lua_api/l_base.h @@ -42,7 +42,6 @@ class Environment; class ServerInventoryManager; class ModApiBase : protected LuaHelper { - public: static ScriptApiBase* getScriptApiBase(lua_State *L); static Server* getServer(lua_State *L); @@ -76,10 +75,18 @@ public: lua_CFunction func, int top); - static int l_deprecated_function(lua_State *L); - static void markAliasDeprecated(luaL_Reg *reg); -private: - // <old_name> = { <new_name>, <new_function> } - static std::unordered_map<std::string, luaL_Reg> m_deprecated_wrappers; - static bool m_error_deprecated_calls; + /** + * A wrapper for deprecated functions. + * + * When called, handles the deprecation according to user settings and then calls `func`. + * + * @throws Lua Error if required by the user settings. + * + * @param L Lua state + * @param good Name of good function/method + * @param bad Name of deprecated function/method + * @param func Actual implementation of function + * @return value from `func` + */ + static int l_deprecated_function(lua_State *L, const char *good, const char *bad, lua_CFunction func); }; diff --git a/src/script/lua_api/l_camera.cpp b/src/script/lua_api/l_camera.cpp index bfa60be67..40251154c 100644 --- a/src/script/lua_api/l_camera.cpp +++ b/src/script/lua_api/l_camera.cpp @@ -63,7 +63,8 @@ int LuaCamera::l_set_camera_mode(lua_State *L) return 0; camera->setCameraMode((CameraMode)((int)lua_tonumber(L, 2))); - playercao->setVisible(camera->getCameraMode() > CAMERA_MODE_FIRST); + // Make the player visible depending on camera mode. + playercao->updateMeshCulling(); playercao->setChildrenVisible(camera->getCameraMode() > CAMERA_MODE_FIRST); return 0; } diff --git a/src/script/lua_api/l_env.cpp b/src/script/lua_api/l_env.cpp index ee384ad10..dbb0a5ab7 100644 --- a/src/script/lua_api/l_env.cpp +++ b/src/script/lua_api/l_env.cpp @@ -407,6 +407,46 @@ int ModApiEnvMod::l_get_node_light(lua_State *L) return 1; } + +// get_natural_light(pos, timeofday) +// pos = {x=num, y=num, z=num} +// timeofday: nil = current time, 0 = night, 0.5 = day +int ModApiEnvMod::l_get_natural_light(lua_State *L) +{ + GET_ENV_PTR; + + v3s16 pos = read_v3s16(L, 1); + + bool is_position_ok; + MapNode n = env->getMap().getNode(pos, &is_position_ok); + if (!is_position_ok) + return 0; + + // If the daylight is 0, nothing needs to be calculated + u8 daylight = n.param1 & 0x0f; + if (daylight == 0) { + lua_pushinteger(L, 0); + return 1; + } + + u32 time_of_day; + if (lua_isnumber(L, 2)) { + time_of_day = 24000.0 * lua_tonumber(L, 2); + time_of_day %= 24000; + } else { + time_of_day = env->getTimeOfDay(); + } + u32 dnr = time_to_daynight_ratio(time_of_day, true); + + // If it's the same as the artificial light, the sunlight needs to be + // searched for because the value may not emanate from the sun + if (daylight == n.param1 >> 4) + daylight = env->findSunlight(pos); + + lua_pushinteger(L, dnr * daylight / 1000); + return 1; +} + // place_node(pos, node) // pos = {x=num, y=num, z=num} int ModApiEnvMod::l_place_node(lua_State *L) @@ -711,8 +751,9 @@ int ModApiEnvMod::l_set_timeofday(lua_State *L) // Do it float timeofday_f = readParam<float>(L, 1); - sanity_check(timeofday_f >= 0.0 && timeofday_f <= 1.0); - int timeofday_mh = (int)(timeofday_f * 24000.0); + luaL_argcheck(L, timeofday_f >= 0.0f && timeofday_f <= 1.0f, 1, + "value must be between 0 and 1"); + int timeofday_mh = (int)(timeofday_f * 24000.0f); // This should be set directly in the environment but currently // such changes aren't immediately sent to the clients, so call // the server instead. @@ -1498,9 +1539,9 @@ int ModApiEnvMod::l_get_translated_string(lua_State * L) GET_ENV_PTR; std::string lang_code = luaL_checkstring(L, 1); std::string string = luaL_checkstring(L, 2); - getServer(L)->loadTranslationLanguage(lang_code); - string = wide_to_utf8(translate_string(utf8_to_wide(string), - &(*g_server_translations)[lang_code])); + + auto *translations = getServer(L)->getTranslationLanguage(lang_code); + string = wide_to_utf8(translate_string(utf8_to_wide(string), translations)); lua_pushstring(L, string.c_str()); return 1; } @@ -1516,6 +1557,7 @@ void ModApiEnvMod::Initialize(lua_State *L, int top) API_FCT(get_node); API_FCT(get_node_or_nil); API_FCT(get_node_light); + API_FCT(get_natural_light); API_FCT(place_node); API_FCT(dig_node); API_FCT(punch_node); diff --git a/src/script/lua_api/l_env.h b/src/script/lua_api/l_env.h index e1b89494b..ad9a0f509 100644 --- a/src/script/lua_api/l_env.h +++ b/src/script/lua_api/l_env.h @@ -56,6 +56,11 @@ private: // timeofday: nil = current time, 0 = night, 0.5 = day static int l_get_node_light(lua_State *L); + // get_natural_light(pos, timeofday) + // pos = {x=num, y=num, z=num} + // timeofday: nil = current time, 0 = night, 0.5 = day + static int l_get_natural_light(lua_State *L); + // place_node(pos, node) // pos = {x=num, y=num, z=num} static int l_place_node(lua_State *L); diff --git a/src/script/lua_api/l_http.cpp b/src/script/lua_api/l_http.cpp index 84837e71b..5a28cb369 100644 --- a/src/script/lua_api/l_http.cpp +++ b/src/script/lua_api/l_http.cpp @@ -49,17 +49,40 @@ void ModApiHttp::read_http_fetch_request(lua_State *L, HTTPFetchRequest &req) req.multipart = getboolfield_default(L, 1, "multipart", false); req.timeout = getintfield_default(L, 1, "timeout", 3) * 1000; - // post_data: if table, post form data, otherwise raw data + lua_getfield(L, 1, "method"); + if (lua_isstring(L, -1)) { + std::string mth = getstringfield_default(L, 1, "method", ""); + if (mth == "GET") + req.method = HTTP_GET; + else if (mth == "POST") + req.method = HTTP_POST; + else if (mth == "PUT") + req.method = HTTP_PUT; + else if (mth == "DELETE") + req.method = HTTP_DELETE; + } + lua_pop(L, 1); + + // post_data: if table, post form data, otherwise raw data DEPRECATED use data and method instead lua_getfield(L, 1, "post_data"); + if (lua_isnil(L, 2)) { + lua_pop(L, 1); + lua_getfield(L, 1, "data"); + } + else { + req.method = HTTP_POST; + } + if (lua_istable(L, 2)) { lua_pushnil(L); while (lua_next(L, 2) != 0) { - req.post_fields[readParam<std::string>(L, -2)] = readParam<std::string>(L, -1); + req.fields[readParam<std::string>(L, -2)] = readParam<std::string>(L, -1); lua_pop(L, 1); } } else if (lua_isstring(L, 2)) { - req.post_data = readParam<std::string>(L, 2); + req.raw_data = readParam<std::string>(L, 2); } + lua_pop(L, 1); lua_getfield(L, 1, "extra_headers"); diff --git a/src/script/lua_api/l_http.h b/src/script/lua_api/l_http.h index de6e51b37..c3a2a5276 100644 --- a/src/script/lua_api/l_http.h +++ b/src/script/lua_api/l_http.h @@ -32,10 +32,10 @@ private: static void read_http_fetch_request(lua_State *L, HTTPFetchRequest &req); static void push_http_fetch_result(lua_State *L, HTTPFetchResult &res, bool completed = true); - // http_fetch_sync({url=, timeout=, post_data=}) + // http_fetch_sync({url=, timeout=, data=}) static int l_http_fetch_sync(lua_State *L); - // http_fetch_async({url=, timeout=, post_data=}) + // http_fetch_async({url=, timeout=, data=}) static int l_http_fetch_async(lua_State *L); // http_fetch_async_get(handle) diff --git a/src/script/lua_api/l_internal.h b/src/script/lua_api/l_internal.h index a86eeaf79..672e535ca 100644 --- a/src/script/lua_api/l_internal.h +++ b/src/script/lua_api/l_internal.h @@ -29,7 +29,16 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "common/c_internal.h" #define luamethod(class, name) {#name, class::l_##name} -#define luamethod_aliased(class, name, alias) {#name, class::l_##name}, {#alias, class::l_##name} + +#define luamethod_dep(class, good, bad) \ + {#bad, [](lua_State *L) -> int { \ + return l_deprecated_function(L, #good, #bad, &class::l_##good); \ + }} + +#define luamethod_aliased(class, good, bad) \ + luamethod(class, good), \ + luamethod_dep(class, good, bad) + #define API_FCT(name) registerFunction(L, #name, l_##name, top) // For future use diff --git a/src/script/lua_api/l_inventory.cpp b/src/script/lua_api/l_inventory.cpp index e41b5cb41..434d0a76c 100644 --- a/src/script/lua_api/l_inventory.cpp +++ b/src/script/lua_api/l_inventory.cpp @@ -280,6 +280,7 @@ int InvRef::l_set_lists(lua_State *L) Server *server = getServer(L); lua_pushnil(L); + luaL_checktype(L, 2, LUA_TTABLE); while (lua_next(L, 2)) { const char *listname = lua_tostring(L, -2); read_inventory_list(L, -1, tempInv, listname, server); diff --git a/src/script/lua_api/l_item.cpp b/src/script/lua_api/l_item.cpp index bea7d122f..b098eccf0 100644 --- a/src/script/lua_api/l_item.cpp +++ b/src/script/lua_api/l_item.cpp @@ -198,6 +198,16 @@ int LuaItemStack::l_get_description(lua_State *L) return 1; } +// get_short_description(self) +int LuaItemStack::l_get_short_description(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + LuaItemStack *o = checkobject(L, 1); + std::string desc = o->m_stack.getShortDescription(getGameDef(L)->idef()); + lua_pushstring(L, desc.c_str()); + return 1; +} + // clear(self) -> true int LuaItemStack::l_clear(lua_State *L) { @@ -498,6 +508,7 @@ const luaL_Reg LuaItemStack::methods[] = { luamethod(LuaItemStack, get_metadata), luamethod(LuaItemStack, set_metadata), luamethod(LuaItemStack, get_description), + luamethod(LuaItemStack, get_short_description), luamethod(LuaItemStack, clear), luamethod(LuaItemStack, replace), luamethod(LuaItemStack, to_string), @@ -564,7 +575,8 @@ int ModApiItemMod::l_register_item_raw(lua_State *L) // Read the node definition (content features) and register it if (def.type == ITEM_NODE) { - ContentFeatures f = read_content_features(L, table); + ContentFeatures f; + read_content_features(L, f, table); // when a mod reregisters ignore, only texture changes and such should // be done if (f.name == "ignore") diff --git a/src/script/lua_api/l_item.h b/src/script/lua_api/l_item.h index 98744c071..16878c101 100644 --- a/src/script/lua_api/l_item.h +++ b/src/script/lua_api/l_item.h @@ -72,6 +72,9 @@ private: // get_description(self) static int l_get_description(lua_State *L); + // get_short_description(self) + static int l_get_short_description(lua_State *L); + // clear(self) -> true static int l_clear(lua_State *L); diff --git a/src/script/lua_api/l_localplayer.cpp b/src/script/lua_api/l_localplayer.cpp index 1cb6ff156..3f4147227 100644 --- a/src/script/lua_api/l_localplayer.cpp +++ b/src/script/lua_api/l_localplayer.cpp @@ -303,8 +303,8 @@ int LuaLocalPlayer::l_get_control(lua_State *L) set("aux1", c.aux1); set("sneak", c.sneak); set("zoom", c.zoom); - set("LMB", c.LMB); - set("RMB", c.RMB); + set("dig", c.dig); + set("place", c.place); return 1; } diff --git a/src/script/lua_api/l_mainmenu.cpp b/src/script/lua_api/l_mainmenu.cpp index f32c477c2..0aa2760e9 100644 --- a/src/script/lua_api/l_mainmenu.cpp +++ b/src/script/lua_api/l_mainmenu.cpp @@ -29,7 +29,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "porting.h" #include "filesys.h" #include "convert_json.h" -#include "content/packages.h" #include "content/content.h" #include "content/subgames.h" #include "serverlist.h" @@ -618,7 +617,7 @@ int ModApiMainMenu::l_create_world(lua_State *L) std::string path = porting::path_user + DIR_DELIM "worlds" + DIR_DELIM - + name; + + sanitizeDirName(name, "world_"); std::vector<SubgameSpec> games = getAvailableGames(); @@ -626,10 +625,11 @@ int ModApiMainMenu::l_create_world(lua_State *L) (gameidx < (int) games.size())) { // Create world if it doesn't exist - if (!loadGameConfAndInitWorld(path, games[gameidx])) { - lua_pushstring(L, "Failed to initialize world"); - } else { + try { + loadGameConfAndInitWorld(path, name, games[gameidx], true); lua_pushnil(L); + } catch (const BaseException &e) { + lua_pushstring(L, (std::string("Failed to initialize world: ") + e.what()).c_str()); } } else { lua_pushstring(L, "Invalid game index"); diff --git a/src/script/lua_api/l_minimap.cpp b/src/script/lua_api/l_minimap.cpp index 5fba76eb8..3bbb6e5e3 100644 --- a/src/script/lua_api/l_minimap.cpp +++ b/src/script/lua_api/l_minimap.cpp @@ -89,7 +89,7 @@ int LuaMinimap::l_get_mode(lua_State *L) LuaMinimap *ref = checkobject(L, 1); Minimap *m = getobject(ref); - lua_pushinteger(L, m->getMinimapMode()); + lua_pushinteger(L, m->getModeIndex()); return 1; } @@ -98,13 +98,11 @@ int LuaMinimap::l_set_mode(lua_State *L) LuaMinimap *ref = checkobject(L, 1); Minimap *m = getobject(ref); - s32 mode = lua_tointeger(L, 2); - if (mode < MINIMAP_MODE_OFF || - mode >= MINIMAP_MODE_COUNT) { + u32 mode = lua_tointeger(L, 2); + if (mode >= m->getMaxModeIndex()) return 0; - } - m->setMinimapMode((MinimapMode) mode); + m->setModeIndex(mode); return 1; } @@ -140,8 +138,11 @@ int LuaMinimap::l_show(lua_State *L) LuaMinimap *ref = checkobject(L, 1); Minimap *m = getobject(ref); - if (m->getMinimapMode() == MINIMAP_MODE_OFF) - m->setMinimapMode(MINIMAP_MODE_SURFACEx1); + // This is not very adapted to new minimap mode management. Btw, tried + // to do something compatible. + + if (m->getModeIndex() == 0 && m->getMaxModeIndex() > 0) + m->setModeIndex(1); client->showMinimap(true); return 1; @@ -155,8 +156,11 @@ int LuaMinimap::l_hide(lua_State *L) LuaMinimap *ref = checkobject(L, 1); Minimap *m = getobject(ref); - if (m->getMinimapMode() != MINIMAP_MODE_OFF) - m->setMinimapMode(MINIMAP_MODE_OFF); + // This is not very adapted to new minimap mode management. Btw, tried + // to do something compatible. + + if (m->getModeIndex() != 0) + m->setModeIndex(0); client->showMinimap(false); return 1; diff --git a/src/script/lua_api/l_noise.cpp b/src/script/lua_api/l_noise.cpp index 9aeb15709..e0861126a 100644 --- a/src/script/lua_api/l_noise.cpp +++ b/src/script/lua_api/l_noise.cpp @@ -122,7 +122,6 @@ void LuaPerlinNoise::Register(lua_State *L) lua_pop(L, 1); - markAliasDeprecated(methods); luaL_openlib(L, 0, methods, 0); lua_pop(L, 1); @@ -381,7 +380,6 @@ void LuaPerlinNoiseMap::Register(lua_State *L) lua_pop(L, 1); - markAliasDeprecated(methods); luaL_openlib(L, 0, methods, 0); lua_pop(L, 1); diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index e7394133a..bc59bd55c 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -44,43 +44,44 @@ ObjectRef* ObjectRef::checkobject(lua_State *L, int narg) { luaL_checktype(L, narg, LUA_TUSERDATA); void *ud = luaL_checkudata(L, narg, className); - if (!ud) luaL_typerror(L, narg, className); + if (ud == nullptr) + luaL_typerror(L, narg, className); return *(ObjectRef**)ud; // unbox pointer } ServerActiveObject* ObjectRef::getobject(ObjectRef *ref) { - ServerActiveObject *co = ref->m_object; - if (co && co->isGone()) - return NULL; - return co; + ServerActiveObject *sao = ref->m_object; + if (sao && sao->isGone()) + return nullptr; + return sao; } LuaEntitySAO* ObjectRef::getluaobject(ObjectRef *ref) { - ServerActiveObject *obj = getobject(ref); - if (obj == NULL) - return NULL; - if (obj->getType() != ACTIVEOBJECT_TYPE_LUAENTITY) - return NULL; - return (LuaEntitySAO*)obj; + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) + return nullptr; + if (sao->getType() != ACTIVEOBJECT_TYPE_LUAENTITY) + return nullptr; + return (LuaEntitySAO*)sao; } PlayerSAO* ObjectRef::getplayersao(ObjectRef *ref) { - ServerActiveObject *obj = getobject(ref); - if (obj == NULL) - return NULL; - if (obj->getType() != ACTIVEOBJECT_TYPE_PLAYER) - return NULL; - return (PlayerSAO*)obj; + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) + return nullptr; + if (sao->getType() != ACTIVEOBJECT_TYPE_PLAYER) + return nullptr; + return (PlayerSAO*)sao; } RemotePlayer *ObjectRef::getplayer(ObjectRef *ref) { PlayerSAO *playersao = getplayersao(ref); - if (playersao == NULL) - return NULL; + if (playersao == nullptr) + return nullptr; return playersao->getPlayer(); } @@ -88,9 +89,8 @@ RemotePlayer *ObjectRef::getplayer(ObjectRef *ref) // garbage collector int ObjectRef::gc_object(lua_State *L) { - ObjectRef *o = *(ObjectRef **)(lua_touserdata(L, 1)); - //infostream<<"ObjectRef::gc_object: o="<<o<<std::endl; - delete o; + ObjectRef *obj = *(ObjectRef **)(lua_touserdata(L, 1)); + delete obj; return 0; } @@ -100,29 +100,30 @@ int ObjectRef::l_remove(lua_State *L) GET_ENV_PTR; ObjectRef *ref = checkobject(L, 1); - ServerActiveObject *co = getobject(ref); - if (co == NULL) + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) return 0; - if (co->getType() == ACTIVEOBJECT_TYPE_PLAYER) + if (sao->getType() == ACTIVEOBJECT_TYPE_PLAYER) return 0; - co->clearChildAttachments(); - co->clearParentAttachment(); + sao->clearChildAttachments(); + sao->clearParentAttachment(); - verbosestream << "ObjectRef::l_remove(): id=" << co->getId() << std::endl; - co->m_pending_removal = true; + verbosestream << "ObjectRef::l_remove(): id=" << sao->getId() << std::endl; + sao->m_pending_removal = true; return 0; } // get_pos(self) -// returns: {x=num, y=num, z=num} int ObjectRef::l_get_pos(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - ServerActiveObject *co = getobject(ref); - if (co == NULL) return 0; - push_v3f(L, co->getBasePosition() / BS); + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) + return 0; + + push_v3f(L, sao->getBasePosition() / BS); return 1; } @@ -131,28 +132,29 @@ int ObjectRef::l_set_pos(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - ServerActiveObject *co = getobject(ref); - if (co == NULL) return 0; - // pos + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) + return 0; + v3f pos = checkFloatPos(L, 2); - // Do it - co->setPos(pos); + + sao->setPos(pos); return 0; } -// move_to(self, pos, continuous=false) +// move_to(self, pos, continuous) int ObjectRef::l_move_to(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - ServerActiveObject *co = getobject(ref); - if (co == NULL) return 0; - // pos + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) + return 0; + v3f pos = checkFloatPos(L, 2); - // continuous bool continuous = readParam<bool>(L, 3); - // Do it - co->moveTo(pos, continuous); + + sao->moveTo(pos, continuous); return 0; } @@ -162,32 +164,26 @@ int ObjectRef::l_punch(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); ObjectRef *puncher_ref = checkobject(L, 2); - ServerActiveObject *co = getobject(ref); + ServerActiveObject *sao = getobject(ref); ServerActiveObject *puncher = getobject(puncher_ref); - if (!co || !puncher) + if (sao == nullptr || puncher == nullptr) return 0; - v3f dir; - if (lua_type(L, 5) != LUA_TTABLE) - dir = co->getBasePosition() - puncher->getBasePosition(); - else - dir = read_v3f(L, 5); - float time_from_last_punch = 1000000; - if (lua_isnumber(L, 3)) - time_from_last_punch = lua_tonumber(L, 3); + + float time_from_last_punch = readParam<float>(L, 3, 1000000.0f); ToolCapabilities toolcap = read_tool_capabilities(L, 4); - dir.normalize(); + v3f dir = readParam<v3f>(L, 5, sao->getBasePosition() - puncher->getBasePosition()); - u16 src_original_hp = co->getHP(); + dir.normalize(); + u16 src_original_hp = sao->getHP(); u16 dst_origin_hp = puncher->getHP(); - // Do it - u16 wear = co->punch(dir, &toolcap, puncher, time_from_last_punch); + u16 wear = sao->punch(dir, &toolcap, puncher, time_from_last_punch); lua_pushnumber(L, wear); // If the punched is a player, and its HP changed - if (src_original_hp != co->getHP() && - co->getType() == ACTIVEOBJECT_TYPE_PLAYER) { - getServer(L)->SendPlayerHPOrDie((PlayerSAO *)co, + if (src_original_hp != sao->getHP() && + sao->getType() == ACTIVEOBJECT_TYPE_PLAYER) { + getServer(L)->SendPlayerHPOrDie((PlayerSAO *)sao, PlayerHPChangeReason(PlayerHPChangeReason::PLAYER_PUNCH, puncher)); } @@ -195,45 +191,38 @@ int ObjectRef::l_punch(lua_State *L) if (dst_origin_hp != puncher->getHP() && puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) { getServer(L)->SendPlayerHPOrDie((PlayerSAO *)puncher, - PlayerHPChangeReason(PlayerHPChangeReason::PLAYER_PUNCH, co)); + PlayerHPChangeReason(PlayerHPChangeReason::PLAYER_PUNCH, sao)); } return 1; } -// right_click(self, clicker); clicker = an another ObjectRef +// right_click(self, clicker) int ObjectRef::l_right_click(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); ObjectRef *ref2 = checkobject(L, 2); - ServerActiveObject *co = getobject(ref); - ServerActiveObject *co2 = getobject(ref2); - if (co == NULL) return 0; - if (co2 == NULL) return 0; - // Do it - co->rightClick(co2); + ServerActiveObject *sao = getobject(ref); + ServerActiveObject *sao2 = getobject(ref2); + if (sao == nullptr || sao2 == nullptr) + return 0; + + sao->rightClick(sao2); return 0; } -// set_hp(self, hp) -// hp = number of hitpoints (2 * number of hearts) -// returns: nil +// set_hp(self, hp, reason) int ObjectRef::l_set_hp(lua_State *L) { NO_MAP_LOCK_REQUIRED; - - // Get Object ObjectRef *ref = checkobject(L, 1); - luaL_checknumber(L, 2); - ServerActiveObject *co = getobject(ref); - if (co == NULL) + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) return 0; - // Get HP - int hp = lua_tonumber(L, 2); - - // Get Reason + int hp = readParam<float>(L, 2); PlayerHPChangeReason reason(PlayerHPChangeReason::SET_HP); + reason.from_mod = true; if (lua_istable(L, 3)) { lua_pushvalue(L, 3); @@ -248,35 +237,28 @@ int ObjectRef::l_set_hp(lua_State *L) reason.lua_reference = luaL_ref(L, LUA_REGISTRYINDEX); } - // Do it - co->setHP(hp, reason); - if (co->getType() == ACTIVEOBJECT_TYPE_PLAYER) - getServer(L)->SendPlayerHPOrDie((PlayerSAO *)co, reason); - + sao->setHP(hp, reason); + if (sao->getType() == ACTIVEOBJECT_TYPE_PLAYER) + getServer(L)->SendPlayerHPOrDie((PlayerSAO *)sao, reason); if (reason.hasLuaReference()) luaL_unref(L, LUA_REGISTRYINDEX, reason.lua_reference); - - // Return return 0; } // get_hp(self) -// returns: number of hitpoints (2 * number of hearts) -// 0 if not applicable to this type of object int ObjectRef::l_get_hp(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - ServerActiveObject *co = getobject(ref); - if (co == NULL) { + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) { // Default hp is 1 lua_pushnumber(L, 1); return 1; } - int hp = co->getHP(); - /*infostream<<"ObjectRef::l_get_hp(): id="<<co->getId() - <<" hp="<<hp<<std::endl;*/ - // Return + + int hp = sao->getHP(); + lua_pushnumber(L, hp); return 1; } @@ -286,11 +268,12 @@ int ObjectRef::l_get_inventory(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - ServerActiveObject *co = getobject(ref); - if (co == NULL) return 0; - // Do it - InventoryLocation loc = co->getInventoryLocation(); - if (getServerInventoryMgr(L)->getInventory(loc) != NULL) + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) + return 0; + + InventoryLocation loc = sao->getInventoryLocation(); + if (getServerInventoryMgr(L)->getInventory(loc) != nullptr) InvRef::create(L, loc); else lua_pushnil(L); // An object may have no inventory (nil) @@ -302,11 +285,11 @@ int ObjectRef::l_get_wield_list(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - ServerActiveObject *co = getobject(ref); - if (!co) + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) return 0; - lua_pushstring(L, co->getWieldList().c_str()); + lua_pushstring(L, sao->getWieldList().c_str()); return 1; } @@ -315,11 +298,11 @@ int ObjectRef::l_get_wield_index(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - ServerActiveObject *co = getobject(ref); - if (!co) + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) return 0; - lua_pushinteger(L, co->getWieldIndex() + 1); + lua_pushinteger(L, sao->getWieldIndex() + 1); return 1; } @@ -328,31 +311,33 @@ int ObjectRef::l_get_wielded_item(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - ServerActiveObject *co = getobject(ref); - if (!co) { + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) { // Empty ItemStack LuaItemStack::create(L, ItemStack()); return 1; } ItemStack selected_item; - co->getWieldedItem(&selected_item, nullptr); + sao->getWieldedItem(&selected_item, nullptr); LuaItemStack::create(L, selected_item); return 1; } -// set_wielded_item(self, itemstack or itemstring or table or nil) +// set_wielded_item(self, item) int ObjectRef::l_set_wielded_item(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - ServerActiveObject *co = getobject(ref); - if (co == NULL) return 0; - // Do it + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) + return 0; + ItemStack item = read_item(L, 2, getServer(L)->idef()); - bool success = co->setWieldedItem(item); - if (success && co->getType() == ACTIVEOBJECT_TYPE_PLAYER) { - getServer(L)->SendInventory((PlayerSAO *)co, true); + + bool success = sao->setWieldedItem(item); + if (success && sao->getType() == ACTIVEOBJECT_TYPE_PLAYER) { + getServer(L)->SendInventory((PlayerSAO *)sao, true); } lua_pushboolean(L, success); return 1; @@ -363,12 +348,14 @@ int ObjectRef::l_set_armor_groups(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - ServerActiveObject *co = getobject(ref); - if (co == NULL) return 0; - // Do it + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) + return 0; + ItemGroupList groups; + read_groups(L, 2, groups); - co->setArmorGroups(groups); + sao->setArmorGroups(groups); return 0; } @@ -377,77 +364,11 @@ int ObjectRef::l_get_armor_groups(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - ServerActiveObject *co = getobject(ref); - if (co == NULL) + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) return 0; - // Do it - push_groups(L, co->getArmorGroups()); - return 1; -} -// set_physics_override(self, physics_override_speed, physics_override_jump, -// physics_override_gravity, sneak, sneak_glitch, new_move) -int ObjectRef::l_set_physics_override(lua_State *L) -{ - NO_MAP_LOCK_REQUIRED; - ObjectRef *ref = checkobject(L, 1); - PlayerSAO *co = (PlayerSAO *) getobject(ref); - if (co == NULL) return 0; - // Do it - if (lua_istable(L, 2)) { - co->m_physics_override_speed = getfloatfield_default( - L, 2, "speed", co->m_physics_override_speed); - co->m_physics_override_jump = getfloatfield_default( - L, 2, "jump", co->m_physics_override_jump); - co->m_physics_override_gravity = getfloatfield_default( - L, 2, "gravity", co->m_physics_override_gravity); - co->m_physics_override_sneak = getboolfield_default( - L, 2, "sneak", co->m_physics_override_sneak); - co->m_physics_override_sneak_glitch = getboolfield_default( - L, 2, "sneak_glitch", co->m_physics_override_sneak_glitch); - co->m_physics_override_new_move = getboolfield_default( - L, 2, "new_move", co->m_physics_override_new_move); - co->m_physics_override_sent = false; - } else { - // old, non-table format - if (!lua_isnil(L, 2)) { - co->m_physics_override_speed = lua_tonumber(L, 2); - co->m_physics_override_sent = false; - } - if (!lua_isnil(L, 3)) { - co->m_physics_override_jump = lua_tonumber(L, 3); - co->m_physics_override_sent = false; - } - if (!lua_isnil(L, 4)) { - co->m_physics_override_gravity = lua_tonumber(L, 4); - co->m_physics_override_sent = false; - } - } - return 0; -} - -// get_physics_override(self) -int ObjectRef::l_get_physics_override(lua_State *L) -{ - NO_MAP_LOCK_REQUIRED; - ObjectRef *ref = checkobject(L, 1); - PlayerSAO *co = (PlayerSAO *)getobject(ref); - if (co == NULL) - return 0; - // Do it - lua_newtable(L); - lua_pushnumber(L, co->m_physics_override_speed); - lua_setfield(L, -2, "speed"); - lua_pushnumber(L, co->m_physics_override_jump); - lua_setfield(L, -2, "jump"); - lua_pushnumber(L, co->m_physics_override_gravity); - lua_setfield(L, -2, "gravity"); - lua_pushboolean(L, co->m_physics_override_sneak); - lua_setfield(L, -2, "sneak"); - lua_pushboolean(L, co->m_physics_override_sneak_glitch); - lua_setfield(L, -2, "sneak_glitch"); - lua_pushboolean(L, co->m_physics_override_new_move); - lua_setfield(L, -2, "new_move"); + push_groups(L, sao->getArmorGroups()); return 1; } @@ -456,22 +377,16 @@ int ObjectRef::l_set_animation(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - ServerActiveObject *co = getobject(ref); - if (co == NULL) return 0; - // Do it - v2f frames = v2f(1, 1); - if (!lua_isnil(L, 2)) - frames = readParam<v2f>(L, 2); - float frame_speed = 15; - if (!lua_isnil(L, 3)) - frame_speed = lua_tonumber(L, 3); - float frame_blend = 0; - if (!lua_isnil(L, 4)) - frame_blend = lua_tonumber(L, 4); - bool frame_loop = true; - if (lua_isboolean(L, 5)) - frame_loop = readParam<bool>(L, 5); - co->setAnimation(frames, frame_speed, frame_blend, frame_loop); + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) + return 0; + + v2f frame_range = readParam<v2f>(L, 2, v2f(1, 1)); + float frame_speed = readParam<float>(L, 3, 15.0f); + float frame_blend = readParam<float>(L, 4, 0.0f); + bool frame_loop = readParam<bool>(L, 5, true); + + sao->setAnimation(frame_range, frame_speed, frame_blend, frame_loop); return 0; } @@ -480,16 +395,16 @@ int ObjectRef::l_get_animation(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - ServerActiveObject *co = getobject(ref); - if (co == NULL) + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) return 0; - // Do it + v2f frames = v2f(1,1); float frame_speed = 15; float frame_blend = 0; bool frame_loop = true; - co->getAnimation(&frames, &frame_speed, &frame_blend, &frame_loop); + sao->getAnimation(&frames, &frame_speed, &frame_blend, &frame_loop); push_v2f(L, frames); lua_pushnumber(L, frame_speed); lua_pushnumber(L, frame_blend); @@ -497,23 +412,21 @@ int ObjectRef::l_get_animation(lua_State *L) return 4; } -// set_local_animation(self, {stand/idle}, {walk}, {dig}, {walk+dig}, frame_speed) +// set_local_animation(self, idle, walk, dig, walk_while_dig, frame_speed) int ObjectRef::l_set_local_animation(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - if (player == NULL) + if (player == nullptr) return 0; - // Do it + v2s32 frames[4]; for (int i=0;i<4;i++) { if (!lua_isnil(L, 2+1)) frames[i] = read_v2s32(L, 2+i); } - float frame_speed = 30; - if (!lua_isnil(L, 6)) - frame_speed = lua_tonumber(L, 6); + float frame_speed = readParam<float>(L, 6, 30.0f); getServer(L)->setLocalPlayerAnimations(player, frames, frame_speed); lua_pushboolean(L, true); @@ -526,7 +439,7 @@ int ObjectRef::l_get_local_animation(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - if (player == NULL) + if (player == nullptr) return 0; v2s32 frames[4]; @@ -541,27 +454,22 @@ int ObjectRef::l_get_local_animation(lua_State *L) return 5; } -// set_eye_offset(self, v3f first pv, v3f third pv) +// set_eye_offset(self, firstperson, thirdperson) int ObjectRef::l_set_eye_offset(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - if (player == NULL) + if (player == nullptr) return 0; - // Do it - v3f offset_first = v3f(0, 0, 0); - v3f offset_third = v3f(0, 0, 0); - if (!lua_isnil(L, 2)) - offset_first = read_v3f(L, 2); - if (!lua_isnil(L, 3)) - offset_third = read_v3f(L, 3); + v3f offset_first = read_v3f(L, 2); + v3f offset_third = read_v3f(L, 3); // Prevent abuse of offset values (keep player always visible) offset_third.X = rangelim(offset_third.X,-10,10); offset_third.Z = rangelim(offset_third.Z,-5,5); - /* TODO: if possible: improve the camera colision detetion to allow Y <= -1.5) */ + /* TODO: if possible: improve the camera collision detection to allow Y <= -1.5) */ offset_third.Y = rangelim(offset_third.Y,-10,15); //1.5*BS getServer(L)->setPlayerEyeOffset(player, offset_first, offset_third); @@ -575,9 +483,9 @@ int ObjectRef::l_get_eye_offset(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - if (player == NULL) + if (player == nullptr) return 0; - // Do it + push_v3f(L, player->eye_offset_first); push_v3f(L, player->eye_offset_third); return 2; @@ -588,14 +496,14 @@ int ObjectRef::l_send_mapblock(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - RemotePlayer *player = getplayer(ref); - if (!player) + if (player == nullptr) return 0; - v3s16 p = read_v3s16(L, 2); + + v3s16 pos = read_v3s16(L, 2); session_t peer_id = player->getPeerId(); - bool r = getServer(L)->SendBlock(peer_id, p); + bool r = getServer(L)->SendBlock(peer_id, pos); lua_pushboolean(L, r); return 1; @@ -606,14 +514,13 @@ int ObjectRef::l_set_animation_frame_speed(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - ServerActiveObject *co = getobject(ref); - if (co == NULL) + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) return 0; - // Do it if (!lua_isnil(L, 2)) { - float frame_speed = lua_tonumber(L, 2); - co->setAnimationSpeed(frame_speed); + float frame_speed = readParam<float>(L, 2); + sao->setAnimationSpeed(frame_speed); lua_pushboolean(L, true); } else { lua_pushboolean(L, false); @@ -621,24 +528,20 @@ int ObjectRef::l_set_animation_frame_speed(lua_State *L) return 1; } -// set_bone_position(self, std::string bone, v3f position, v3f rotation) +// set_bone_position(self, bone, position, rotation) int ObjectRef::l_set_bone_position(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - ServerActiveObject *co = getobject(ref); - if (co == NULL) return 0; - // Do it - std::string bone; - if (!lua_isnil(L, 2)) - bone = readParam<std::string>(L, 2); - v3f position = v3f(0, 0, 0); - if (!lua_isnil(L, 3)) - position = check_v3f(L, 3); - v3f rotation = v3f(0, 0, 0); - if (!lua_isnil(L, 4)) - rotation = check_v3f(L, 4); - co->setBonePosition(bone, position, rotation); + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) + return 0; + + std::string bone = readParam<std::string>(L, 2); + v3f position = check_v3f(L, 3); + v3f rotation = check_v3f(L, 4); + + sao->setBonePosition(bone, position, rotation); return 0; } @@ -647,63 +550,53 @@ int ObjectRef::l_get_bone_position(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - ServerActiveObject *co = getobject(ref); - if (co == NULL) + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) return 0; - // Do it - std::string bone; - if (!lua_isnil(L, 2)) - bone = readParam<std::string>(L, 2); + + std::string bone = readParam<std::string>(L, 2); v3f position = v3f(0, 0, 0); v3f rotation = v3f(0, 0, 0); - co->getBonePosition(bone, &position, &rotation); + sao->getBonePosition(bone, &position, &rotation); push_v3f(L, position); push_v3f(L, rotation); return 2; } -// set_attach(self, parent, bone, position, rotation) +// set_attach(self, parent, bone, position, rotation, force_visible) int ObjectRef::l_set_attach(lua_State *L) { GET_ENV_PTR; - ObjectRef *ref = checkobject(L, 1); ObjectRef *parent_ref = checkobject(L, 2); - ServerActiveObject *co = getobject(ref); + ServerActiveObject *sao = getobject(ref); ServerActiveObject *parent = getobject(parent_ref); - if (co == NULL) - return 0; - - if (parent == NULL) + if (sao == nullptr || parent == nullptr) return 0; - - if (co == parent) + if (sao == parent) throw LuaError("ObjectRef::set_attach: attaching object to itself is not allowed."); - // Do it int parent_id = 0; std::string bone; v3f position = v3f(0, 0, 0); v3f rotation = v3f(0, 0, 0); - co->getAttachment(&parent_id, &bone, &position, &rotation); + bool force_visible; + + sao->getAttachment(&parent_id, &bone, &position, &rotation, &force_visible); if (parent_id) { ServerActiveObject *old_parent = env->getActiveObject(parent_id); - old_parent->removeAttachmentChild(co->getId()); + old_parent->removeAttachmentChild(sao->getId()); } - bone = ""; - if (!lua_isnil(L, 3)) - bone = readParam<std::string>(L, 3); - position = v3f(0, 0, 0); - if (!lua_isnil(L, 4)) - position = read_v3f(L, 4); - rotation = v3f(0, 0, 0); - if (!lua_isnil(L, 5)) - rotation = read_v3f(L, 5); - co->setAttachment(parent->getId(), bone, position, rotation); - parent->addAttachmentChild(co->getId()); + bone = readParam<std::string>(L, 3, ""); + position = read_v3f(L, 4); + rotation = read_v3f(L, 5); + force_visible = readParam<bool>(L, 6, false); + + sao->setAttachment(parent->getId(), bone, position, rotation, force_visible); + parent->addAttachmentChild(sao->getId()); return 0; } @@ -711,40 +604,61 @@ int ObjectRef::l_set_attach(lua_State *L) int ObjectRef::l_get_attach(lua_State *L) { GET_ENV_PTR; - ObjectRef *ref = checkobject(L, 1); - ServerActiveObject *co = getobject(ref); - if (co == NULL) + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) return 0; - // Do it int parent_id = 0; std::string bone; v3f position = v3f(0, 0, 0); v3f rotation = v3f(0, 0, 0); - co->getAttachment(&parent_id, &bone, &position, &rotation); - if (!parent_id) + bool force_visible; + + sao->getAttachment(&parent_id, &bone, &position, &rotation, &force_visible); + if (parent_id == 0) return 0; - ServerActiveObject *parent = env->getActiveObject(parent_id); + ServerActiveObject *parent = env->getActiveObject(parent_id); getScriptApiBase(L)->objectrefGetOrCreate(L, parent); lua_pushlstring(L, bone.c_str(), bone.size()); push_v3f(L, position); push_v3f(L, rotation); - return 4; + lua_pushboolean(L, force_visible); + return 5; +} + +// get_children(self) +int ObjectRef::l_get_children(lua_State *L) +{ + GET_ENV_PTR; + ObjectRef *ref = checkobject(L, 1); + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) + return 0; + + const std::unordered_set<int> child_ids = sao->getAttachmentChildIds(); + int i = 0; + + lua_createtable(L, child_ids.size(), 0); + for (const int id : child_ids) { + ServerActiveObject *child = env->getActiveObject(id); + getScriptApiBase(L)->objectrefGetOrCreate(L, child); + lua_rawseti(L, -2, ++i); + } + return 1; } // set_detach(self) int ObjectRef::l_set_detach(lua_State *L) { GET_ENV_PTR; - ObjectRef *ref = checkobject(L, 1); - ServerActiveObject *co = getobject(ref); - if (co == NULL) + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) return 0; - co->clearParentAttachment(); + sao->clearParentAttachment(); return 0; } @@ -753,16 +667,16 @@ int ObjectRef::l_set_properties(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - ServerActiveObject *co = getobject(ref); - if (!co) + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) return 0; - ObjectProperties *prop = co->accessObjectProperties(); - if (!prop) + ObjectProperties *prop = sao->accessObjectProperties(); + if (prop == nullptr) return 0; - read_object_properties(L, 2, co, prop, getServer(L)->idef()); - co->notifyObjectPropertiesModified(); + read_object_properties(L, 2, sao, prop, getServer(L)->idef()); + sao->notifyObjectPropertiesModified(); return 0; } @@ -771,12 +685,14 @@ int ObjectRef::l_get_properties(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - ServerActiveObject *co = getobject(ref); - if (co == NULL) + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) return 0; - ObjectProperties *prop = co->accessObjectProperties(); - if (!prop) + + ObjectProperties *prop = sao->accessObjectProperties(); + if (prop == nullptr) return 0; + push_object_properties(L, prop); return 1; } @@ -787,7 +703,7 @@ int ObjectRef::l_is_player(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - lua_pushboolean(L, (player != NULL)); + lua_pushboolean(L, (player != nullptr)); return 1; } @@ -796,12 +712,12 @@ int ObjectRef::l_set_nametag_attributes(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - ServerActiveObject *co = getobject(ref); - - if (co == NULL) + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) return 0; - ObjectProperties *prop = co->accessObjectProperties(); - if (!prop) + + ObjectProperties *prop = sao->accessObjectProperties(); + if (prop == nullptr) return 0; lua_getfield(L, 2, "color"); @@ -815,7 +731,7 @@ int ObjectRef::l_set_nametag_attributes(lua_State *L) std::string nametag = getstringfield_default(L, 2, "text", ""); prop->nametag = nametag; - co->notifyObjectPropertiesModified(); + sao->notifyObjectPropertiesModified(); lua_pushboolean(L, true); return 1; } @@ -825,11 +741,11 @@ int ObjectRef::l_get_nametag_attributes(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - ServerActiveObject *co = getobject(ref); - - if (co == NULL) + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) return 0; - ObjectProperties *prop = co->accessObjectProperties(); + + ObjectProperties *prop = sao->accessObjectProperties(); if (!prop) return 0; @@ -845,30 +761,41 @@ int ObjectRef::l_get_nametag_attributes(lua_State *L) /* LuaEntitySAO-only */ -// set_velocity(self, {x=num, y=num, z=num}) +// set_velocity(self, velocity) int ObjectRef::l_set_velocity(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - LuaEntitySAO *co = getluaobject(ref); - if (co == NULL) return 0; - v3f pos = checkFloatPos(L, 2); - // Do it - co->setVelocity(pos); + LuaEntitySAO *sao = getluaobject(ref); + if (sao == nullptr) + return 0; + + v3f vel = checkFloatPos(L, 2); + + sao->setVelocity(vel); return 0; } -// add_velocity(self, {x=num, y=num, z=num}) +// add_velocity(self, velocity) int ObjectRef::l_add_velocity(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - LuaEntitySAO *co = getluaobject(ref); - if (!co) + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) return 0; - v3f pos = checkFloatPos(L, 2); - // Do it - co->addVelocity(pos); + + v3f vel = checkFloatPos(L, 2); + + if (sao->getType() == ACTIVEOBJECT_TYPE_LUAENTITY) { + LuaEntitySAO *entitysao = dynamic_cast<LuaEntitySAO*>(sao); + entitysao->addVelocity(vel); + } else if (sao->getType() == ACTIVEOBJECT_TYPE_PLAYER) { + PlayerSAO *playersao = dynamic_cast<PlayerSAO*>(sao); + playersao->setMaxSpeedOverride(vel); + getServer(L)->SendPlayerSpeed(playersao->getPeerID(), vel); + } + return 0; } @@ -877,25 +804,37 @@ int ObjectRef::l_get_velocity(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - LuaEntitySAO *co = getluaobject(ref); - if (co == NULL) return 0; - // Do it - v3f v = co->getVelocity(); - pushFloatPos(L, v); + ServerActiveObject *sao = getobject(ref); + if (sao == nullptr) + return 0; + + if (sao->getType() == ACTIVEOBJECT_TYPE_LUAENTITY) { + LuaEntitySAO *entitysao = dynamic_cast<LuaEntitySAO*>(sao); + v3f vel = entitysao->getVelocity(); + pushFloatPos(L, vel); + return 1; + } else if (sao->getType() == ACTIVEOBJECT_TYPE_PLAYER) { + RemotePlayer *player = dynamic_cast<PlayerSAO*>(sao)->getPlayer(); + push_v3f(L, player->getSpeed() / BS); + return 1; + } + + lua_pushnil(L); return 1; } -// set_acceleration(self, {x=num, y=num, z=num}) +// set_acceleration(self, acceleration) int ObjectRef::l_set_acceleration(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - LuaEntitySAO *co = getluaobject(ref); - if (co == NULL) return 0; - // pos - v3f pos = checkFloatPos(L, 2); - // Do it - co->setAcceleration(pos); + LuaEntitySAO *entitysao = getluaobject(ref); + if (entitysao == nullptr) + return 0; + + v3f acceleration = checkFloatPos(L, 2); + + entitysao->setAcceleration(acceleration); return 0; } @@ -904,59 +843,61 @@ int ObjectRef::l_get_acceleration(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - LuaEntitySAO *co = getluaobject(ref); - if (co == NULL) return 0; - // Do it - v3f v = co->getAcceleration(); - pushFloatPos(L, v); + LuaEntitySAO *entitysao = getluaobject(ref); + if (entitysao == nullptr) + return 0; + + v3f acceleration = entitysao->getAcceleration(); + pushFloatPos(L, acceleration); return 1; } -// set_rotation(self, {x=num, y=num, z=num}) -// Each 'num' is in radians +// set_rotation(self, rotation) int ObjectRef::l_set_rotation(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - LuaEntitySAO *co = getluaobject(ref); - if (!co) + LuaEntitySAO *entitysao = getluaobject(ref); + if (entitysao == nullptr) return 0; v3f rotation = check_v3f(L, 2) * core::RADTODEG; - co->setRotation(rotation); + + entitysao->setRotation(rotation); return 0; } // get_rotation(self) -// returns: {x=num, y=num, z=num} -// Each 'num' is in radians int ObjectRef::l_get_rotation(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - LuaEntitySAO *co = getluaobject(ref); - if (!co) + LuaEntitySAO *entitysao = getluaobject(ref); + if (entitysao == nullptr) return 0; + v3f rotation = entitysao->getRotation() * core::DEGTORAD; + lua_newtable(L); - v3f rotation = co->getRotation() * core::DEGTORAD; push_v3f(L, rotation); return 1; } -// set_yaw(self, radians) +// set_yaw(self, yaw) int ObjectRef::l_set_yaw(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - LuaEntitySAO *co = getluaobject(ref); + LuaEntitySAO *entitysao = getluaobject(ref); + if (entitysao == nullptr) + return 0; - if (co == NULL) return 0; if (isNaN(L, 2)) throw LuaError("ObjectRef::set_yaw: NaN value is not allowed."); float yaw = readParam<float>(L, 2) * core::RADTODEG; - co->setRotation(v3f(0, yaw, 0)); + + entitysao->setRotation(v3f(0, yaw, 0)); return 0; } @@ -965,11 +906,12 @@ int ObjectRef::l_get_yaw(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - LuaEntitySAO *co = getluaobject(ref); - if (!co) + LuaEntitySAO *entitysao = getluaobject(ref); + if (entitysao == nullptr) return 0; - float yaw = co->getRotation().Y * core::DEGTORAD; + float yaw = entitysao->getRotation().Y * core::DEGTORAD; + lua_pushnumber(L, yaw); return 1; } @@ -979,11 +921,13 @@ int ObjectRef::l_set_texture_mod(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - LuaEntitySAO *co = getluaobject(ref); - if (co == NULL) return 0; - // Do it - std::string mod = luaL_checkstring(L, 2); - co->setTextureMod(mod); + LuaEntitySAO *entitysao = getluaobject(ref); + if (entitysao == nullptr) + return 0; + + std::string mod = readParam<std::string>(L, 2); + + entitysao->setTextureMod(mod); return 0; } @@ -992,36 +936,31 @@ int ObjectRef::l_get_texture_mod(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - LuaEntitySAO *co = getluaobject(ref); - if (co == NULL) return 0; - // Do it - std::string mod = co->getTextureMod(); + LuaEntitySAO *entitysao = getluaobject(ref); + if (entitysao == nullptr) + return 0; + + std::string mod = entitysao->getTextureMod(); + lua_pushstring(L, mod.c_str()); return 1; } -// set_sprite(self, p={x=0,y=0}, num_frames=1, framelength=0.2, -// select_horiz_by_yawpitch=false) +// set_sprite(self, start_frame, num_frames, framelength, select_x_by_camera) int ObjectRef::l_set_sprite(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - LuaEntitySAO *co = getluaobject(ref); - if (co == NULL) return 0; - // Do it - v2s16 p(0,0); - if (!lua_isnil(L, 2)) - p = readParam<v2s16>(L, 2); - int num_frames = 1; - if (!lua_isnil(L, 3)) - num_frames = lua_tonumber(L, 3); - float framelength = 0.2; - if (!lua_isnil(L, 4)) - framelength = lua_tonumber(L, 4); - bool select_horiz_by_yawpitch = false; - if (!lua_isnil(L, 5)) - select_horiz_by_yawpitch = readParam<bool>(L, 5); - co->setSprite(p, num_frames, framelength, select_horiz_by_yawpitch); + LuaEntitySAO *entitysao = getluaobject(ref); + if (entitysao == nullptr) + return 0; + + v2s16 start_frame = readParam<v2s16>(L, 2, v2s16(0,0)); + int num_frames = readParam<int>(L, 3, 1); + float framelength = readParam<float>(L, 4, 0.2f); + bool select_x_by_camera = readParam<bool>(L, 5, false); + + entitysao->setSprite(start_frame, num_frames, framelength, select_x_by_camera); return 0; } @@ -1031,11 +970,13 @@ int ObjectRef::l_get_entity_name(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - LuaEntitySAO *co = getluaobject(ref); + LuaEntitySAO *entitysao = getluaobject(ref); log_deprecated(L,"Deprecated call to \"get_entity_name"); - if (co == NULL) return 0; - // Do it - std::string name = co->getName(); + if (entitysao == nullptr) + return 0; + + std::string name = entitysao->getName(); + lua_pushstring(L, name.c_str()); return 1; } @@ -1045,87 +986,45 @@ int ObjectRef::l_get_luaentity(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - LuaEntitySAO *co = getluaobject(ref); - if (co == NULL) return 0; - // Do it - luaentity_get(L, co->getId()); + LuaEntitySAO *entitysao = getluaobject(ref); + if (entitysao == nullptr) + return 0; + + luaentity_get(L, entitysao->getId()); return 1; } /* Player-only */ -// is_player_connected(self) -int ObjectRef::l_is_player_connected(lua_State *L) -{ - NO_MAP_LOCK_REQUIRED; - // This method was once added for a bugfix, but never documented - log_deprecated(L, "is_player_connected is undocumented and " - "will be removed in a future release"); - ObjectRef *ref = checkobject(L, 1); - RemotePlayer *player = getplayer(ref); - lua_pushboolean(L, (player != NULL && player->getPeerId() != PEER_ID_INEXISTENT)); - return 1; -} - // get_player_name(self) int ObjectRef::l_get_player_name(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - if (player == NULL) { + if (player == nullptr) { lua_pushlstring(L, "", 0); return 1; } - // Do it - lua_pushstring(L, player->getName()); - return 1; -} -// get_player_velocity(self) -int ObjectRef::l_get_player_velocity(lua_State *L) -{ - NO_MAP_LOCK_REQUIRED; - ObjectRef *ref = checkobject(L, 1); - RemotePlayer *player = getplayer(ref); - if (player == NULL) { - lua_pushnil(L); - return 1; - } - // Do it - push_v3f(L, player->getSpeed() / BS); + lua_pushstring(L, player->getName()); return 1; } -// add_player_velocity(self, {x=num, y=num, z=num}) -int ObjectRef::l_add_player_velocity(lua_State *L) -{ - NO_MAP_LOCK_REQUIRED; - ObjectRef *ref = checkobject(L, 1); - v3f vel = checkFloatPos(L, 2); - - PlayerSAO *co = getplayersao(ref); - if (!co) - return 0; - - // Do it - co->setMaxSpeedOverride(vel); - getServer(L)->SendPlayerSpeed(co->getPeerID(), vel); - return 0; -} - // get_look_dir(self) int ObjectRef::l_get_look_dir(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - PlayerSAO* co = getplayersao(ref); - if (co == NULL) return 0; - // Do it - float pitch = co->getRadLookPitchDep(); - float yaw = co->getRadYawDep(); + PlayerSAO* playersao = getplayersao(ref); + if (playersao == nullptr) + return 0; + + float pitch = playersao->getRadLookPitchDep(); + float yaw = playersao->getRadYawDep(); v3f v(std::cos(pitch) * std::cos(yaw), std::sin(pitch), std::cos(pitch) * std::sin(yaw)); + push_v3f(L, v); return 1; } @@ -1140,10 +1039,11 @@ int ObjectRef::l_get_look_pitch(lua_State *L) "Deprecated call to get_look_pitch, use get_look_vertical instead"); ObjectRef *ref = checkobject(L, 1); - PlayerSAO* co = getplayersao(ref); - if (co == NULL) return 0; - // Do it - lua_pushnumber(L, co->getRadLookPitchDep()); + PlayerSAO* playersao = getplayersao(ref); + if (playersao == nullptr) + return 0; + + lua_pushnumber(L, playersao->getRadLookPitchDep()); return 1; } @@ -1157,34 +1057,37 @@ int ObjectRef::l_get_look_yaw(lua_State *L) "Deprecated call to get_look_yaw, use get_look_horizontal instead"); ObjectRef *ref = checkobject(L, 1); - PlayerSAO* co = getplayersao(ref); - if (co == NULL) return 0; - // Do it - lua_pushnumber(L, co->getRadYawDep()); + PlayerSAO* playersao = getplayersao(ref); + if (playersao == nullptr) + return 0; + + lua_pushnumber(L, playersao->getRadYawDep()); return 1; } -// get_look_pitch2(self) +// get_look_vertical(self) int ObjectRef::l_get_look_vertical(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - PlayerSAO* co = getplayersao(ref); - if (co == NULL) return 0; - // Do it - lua_pushnumber(L, co->getRadLookPitch()); + PlayerSAO* playersao = getplayersao(ref); + if (playersao == nullptr) + return 0; + + lua_pushnumber(L, playersao->getRadLookPitch()); return 1; } -// get_look_yaw2(self) +// get_look_horizontal(self) int ObjectRef::l_get_look_horizontal(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - PlayerSAO* co = getplayersao(ref); - if (co == NULL) return 0; - // Do it - lua_pushnumber(L, co->getRadRotation().Y); + PlayerSAO* playersao = getplayersao(ref); + if (playersao == nullptr) + return 0; + + lua_pushnumber(L, playersao->getRadRotation().Y); return 1; } @@ -1193,11 +1096,13 @@ int ObjectRef::l_set_look_vertical(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - PlayerSAO* co = getplayersao(ref); - if (co == NULL) return 0; + PlayerSAO* playersao = getplayersao(ref); + if (playersao == nullptr) + return 0; + float pitch = readParam<float>(L, 2) * core::RADTODEG; - // Do it - co->setLookPitchAndSend(pitch); + + playersao->setLookPitchAndSend(pitch); return 1; } @@ -1206,11 +1111,13 @@ int ObjectRef::l_set_look_horizontal(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - PlayerSAO* co = getplayersao(ref); - if (co == NULL) return 0; + PlayerSAO* playersao = getplayersao(ref); + if (playersao == nullptr) + return 0; + float yaw = readParam<float>(L, 2) * core::RADTODEG; - // Do it - co->setPlayerYawAndSend(yaw); + + playersao->setPlayerYawAndSend(yaw); return 1; } @@ -1224,11 +1131,13 @@ int ObjectRef::l_set_look_pitch(lua_State *L) "Deprecated call to set_look_pitch, use set_look_vertical instead."); ObjectRef *ref = checkobject(L, 1); - PlayerSAO* co = getplayersao(ref); - if (co == NULL) return 0; + PlayerSAO* playersao = getplayersao(ref); + if (playersao == nullptr) + return 0; + float pitch = readParam<float>(L, 2) * core::RADTODEG; - // Do it - co->setLookPitchAndSend(pitch); + + playersao->setLookPitchAndSend(pitch); return 1; } @@ -1242,30 +1151,32 @@ int ObjectRef::l_set_look_yaw(lua_State *L) "Deprecated call to set_look_yaw, use set_look_horizontal instead."); ObjectRef *ref = checkobject(L, 1); - PlayerSAO* co = getplayersao(ref); - if (co == NULL) return 0; + PlayerSAO* playersao = getplayersao(ref); + if (playersao == nullptr) + return 0; + float yaw = readParam<float>(L, 2) * core::RADTODEG; - // Do it - co->setPlayerYawAndSend(yaw); + + playersao->setPlayerYawAndSend(yaw); return 1; } -// set_fov(self, degrees[, is_multiplier, transition_time]) +// set_fov(self, degrees, is_multiplier, transition_time) int ObjectRef::l_set_fov(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - if (!player) + if (player == nullptr) return 0; - player->setFov({ - static_cast<f32>(luaL_checknumber(L, 2)), - readParam<bool>(L, 3, false), - lua_isnumber(L, 4) ? static_cast<f32>(luaL_checknumber(L, 4)) : 0.0f - }); - getServer(L)->SendPlayerFov(player->getPeerId()); + float degrees = static_cast<f32>(luaL_checknumber(L, 2)); + bool is_multiplier = readParam<bool>(L, 3, false); + float transition_time = lua_isnumber(L, 4) ? + static_cast<f32>(luaL_checknumber(L, 4)) : 0.0f; + player->setFov({degrees, is_multiplier, transition_time}); + getServer(L)->SendPlayerFov(player->getPeerId()); return 0; } @@ -1275,14 +1186,14 @@ int ObjectRef::l_get_fov(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - if (!player) + if (player == nullptr) return 0; PlayerFovSpec fov_spec = player->getFov(); + lua_pushnumber(L, fov_spec.fov); lua_pushboolean(L, fov_spec.is_multiplier); lua_pushnumber(L, fov_spec.transition_time); - return 3; } @@ -1291,11 +1202,13 @@ int ObjectRef::l_set_breath(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - PlayerSAO* co = getplayersao(ref); - if (co == NULL) return 0; + PlayerSAO* playersao = getplayersao(ref); + if (playersao == nullptr) + return 0; + u16 breath = luaL_checknumber(L, 2); - co->setBreath(breath); + playersao->setBreath(breath); return 0; } @@ -1304,11 +1217,13 @@ int ObjectRef::l_get_breath(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); - PlayerSAO* co = getplayersao(ref); - if (co == NULL) return 0; - // Do it - u16 breath = co->getBreath(); - lua_pushinteger (L, breath); + PlayerSAO* playersao = getplayersao(ref); + if (playersao == nullptr) + return 0; + + u16 breath = playersao->getBreath(); + + lua_pushinteger(L, breath); return 1; } @@ -1319,16 +1234,16 @@ int ObjectRef::l_set_attribute(lua_State *L) "Deprecated call to set_attribute, use MetaDataRef methods instead."); ObjectRef *ref = checkobject(L, 1); - PlayerSAO* co = getplayersao(ref); - if (co == NULL) + PlayerSAO* playersao = getplayersao(ref); + if (playersao == nullptr) return 0; std::string attr = luaL_checkstring(L, 2); if (lua_isnil(L, 3)) { - co->getMeta().removeString(attr); + playersao->getMeta().removeString(attr); } else { std::string value = luaL_checkstring(L, 3); - co->getMeta().setString(attr, value); + playersao->getMeta().setString(attr, value); } return 1; } @@ -1340,14 +1255,14 @@ int ObjectRef::l_get_attribute(lua_State *L) "Deprecated call to get_attribute, use MetaDataRef methods instead."); ObjectRef *ref = checkobject(L, 1); - PlayerSAO* co = getplayersao(ref); - if (co == NULL) + PlayerSAO* playersao = getplayersao(ref); + if (playersao == nullptr) return 0; std::string attr = luaL_checkstring(L, 2); std::string value; - if (co->getMeta().getStringToRef(attr, value)) { + if (playersao->getMeta().getStringToRef(attr, value)) { lua_pushstring(L, value.c_str()); return 1; } @@ -1360,11 +1275,11 @@ int ObjectRef::l_get_attribute(lua_State *L) int ObjectRef::l_get_meta(lua_State *L) { ObjectRef *ref = checkobject(L, 1); - PlayerSAO *co = getplayersao(ref); - if (co == NULL) + PlayerSAO *playersao = getplayersao(ref); + if (playersao == nullptr) return 0; - PlayerMetaRef::create(L, &co->getMeta()); + PlayerMetaRef::create(L, &playersao->getMeta()); return 1; } @@ -1375,7 +1290,9 @@ int ObjectRef::l_set_inventory_formspec(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - if (player == NULL) return 0; + if (player == nullptr) + return 0; + std::string formspec = luaL_checkstring(L, 2); player->inventory_formspec = formspec; @@ -1390,9 +1307,11 @@ int ObjectRef::l_get_inventory_formspec(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - if (player == NULL) return 0; + if (player == nullptr) + return 0; std::string formspec = player->inventory_formspec; + lua_pushlstring(L, formspec.c_str(), formspec.size()); return 1; } @@ -1403,7 +1322,7 @@ int ObjectRef::l_set_formspec_prepend(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - if (player == NULL) + if (player == nullptr) return 0; std::string formspec = luaL_checkstring(L, 2); @@ -1414,16 +1333,17 @@ int ObjectRef::l_set_formspec_prepend(lua_State *L) return 1; } -// get_formspec_prepend(self) -> formspec +// get_formspec_prepend(self) int ObjectRef::l_get_formspec_prepend(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - if (player == NULL) + if (player == nullptr) return 0; std::string formspec = player->formspec_prepend; + lua_pushlstring(L, formspec.c_str(), formspec.size()); return 1; } @@ -1434,7 +1354,7 @@ int ObjectRef::l_get_player_control(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - if (player == NULL) { + if (player == nullptr) { lua_pushlstring(L, "", 0); return 1; } @@ -1455,9 +1375,14 @@ int ObjectRef::l_get_player_control(lua_State *L) lua_setfield(L, -2, "aux1"); lua_pushboolean(L, control.sneak); lua_setfield(L, -2, "sneak"); - lua_pushboolean(L, control.LMB); + lua_pushboolean(L, control.dig); + lua_setfield(L, -2, "dig"); + lua_pushboolean(L, control.place); + lua_setfield(L, -2, "place"); + // Legacy fields to ensure mod compatibility + lua_pushboolean(L, control.dig); lua_setfield(L, -2, "LMB"); - lua_pushboolean(L, control.RMB); + lua_pushboolean(L, control.place); lua_setfield(L, -2, "RMB"); lua_pushboolean(L, control.zoom); lua_setfield(L, -2, "zoom"); @@ -1470,22 +1395,87 @@ int ObjectRef::l_get_player_control_bits(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - if (player == NULL) { + if (player == nullptr) { lua_pushlstring(L, "", 0); return 1; } - // Do it + lua_pushnumber(L, player->keyPressed); return 1; } -// hud_add(self, form) +// set_physics_override(self, override_table) +int ObjectRef::l_set_physics_override(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + PlayerSAO *playersao = (PlayerSAO *) getobject(ref); + if (playersao == nullptr) + return 0; + + if (lua_istable(L, 2)) { + bool modified = false; + modified |= getfloatfield(L, 2, "speed", playersao->m_physics_override_speed); + modified |= getfloatfield(L, 2, "jump", playersao->m_physics_override_jump); + modified |= getfloatfield(L, 2, "gravity", playersao->m_physics_override_gravity); + modified |= getboolfield(L, 2, "sneak", playersao->m_physics_override_sneak); + modified |= getboolfield(L, 2, "sneak_glitch", playersao->m_physics_override_sneak_glitch); + modified |= getboolfield(L, 2, "new_move", playersao->m_physics_override_new_move); + if (modified) + playersao->m_physics_override_sent = false; + } else { + // old, non-table format + // TODO: Remove this code after version 5.4.0 + log_deprecated(L, "Deprecated use of set_physics_override(num, num, num)"); + + if (!lua_isnil(L, 2)) { + playersao->m_physics_override_speed = lua_tonumber(L, 2); + playersao->m_physics_override_sent = false; + } + if (!lua_isnil(L, 3)) { + playersao->m_physics_override_jump = lua_tonumber(L, 3); + playersao->m_physics_override_sent = false; + } + if (!lua_isnil(L, 4)) { + playersao->m_physics_override_gravity = lua_tonumber(L, 4); + playersao->m_physics_override_sent = false; + } + } + return 0; +} + +// get_physics_override(self) +int ObjectRef::l_get_physics_override(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + PlayerSAO *playersao = (PlayerSAO *)getobject(ref); + if (playersao == nullptr) + return 0; + + lua_newtable(L); + lua_pushnumber(L, playersao->m_physics_override_speed); + lua_setfield(L, -2, "speed"); + lua_pushnumber(L, playersao->m_physics_override_jump); + lua_setfield(L, -2, "jump"); + lua_pushnumber(L, playersao->m_physics_override_gravity); + lua_setfield(L, -2, "gravity"); + lua_pushboolean(L, playersao->m_physics_override_sneak); + lua_setfield(L, -2, "sneak"); + lua_pushboolean(L, playersao->m_physics_override_sneak_glitch); + lua_setfield(L, -2, "sneak_glitch"); + lua_pushboolean(L, playersao->m_physics_override_new_move); + lua_setfield(L, -2, "new_move"); + return 1; +} + +// hud_add(self, hud) int ObjectRef::l_hud_add(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - if (player == NULL) + if (player == nullptr) return 0; HudElement *elem = new HudElement; @@ -1507,12 +1497,10 @@ int ObjectRef::l_hud_remove(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - if (player == NULL) + if (player == nullptr) return 0; - u32 id = -1; - if (!lua_isnil(L, 2)) - id = lua_tonumber(L, 2); + u32 id = luaL_checkint(L, 2); if (!getServer(L)->hudRemove(player, id)) return 0; @@ -1527,17 +1515,17 @@ int ObjectRef::l_hud_change(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - if (player == NULL) + if (player == nullptr) return 0; - u32 id = lua_isnumber(L, 2) ? lua_tonumber(L, 2) : -1; + u32 id = luaL_checkint(L, 2); - HudElement *e = player->getHud(id); - if (!e) + HudElement *elem = player->getHud(id); + if (elem == nullptr) return 0; - void *value = NULL; - HudElementStat stat = read_hud_change(L, e, &value); + void *value = nullptr; + HudElementStat stat = read_hud_change(L, elem, &value); getServer(L)->hudChange(player, id, stat, value); @@ -1551,15 +1539,16 @@ int ObjectRef::l_hud_get(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - if (player == NULL) + if (player == nullptr) return 0; - u32 id = lua_tonumber(L, -1); + u32 id = luaL_checkint(L, 2); - HudElement *e = player->getHud(id); - if (!e) + HudElement *elem = player->getHud(id); + if (elem == nullptr) return 0; - push_hud_element(L, e); + + push_hud_element(L, elem); return 1; } @@ -1569,7 +1558,7 @@ int ObjectRef::l_hud_set_flags(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - if (player == NULL) + if (player == nullptr) return 0; u32 flags = 0; @@ -1590,12 +1579,13 @@ int ObjectRef::l_hud_set_flags(lua_State *L) return 1; } +// hud_get_flags(self) int ObjectRef::l_hud_get_flags(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - if (player == NULL) + if (player == nullptr) return 0; lua_newtable(L); @@ -1613,7 +1603,6 @@ int ObjectRef::l_hud_get_flags(lua_State *L) lua_setfield(L, -2, "minimap"); lua_pushboolean(L, player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE); lua_setfield(L, -2, "minimap_radar"); - return 1; } @@ -1623,10 +1612,10 @@ int ObjectRef::l_hud_set_hotbar_itemcount(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - if (player == NULL) + if (player == nullptr) return 0; - s32 hotbar_itemcount = lua_tonumber(L, 2); + s32 hotbar_itemcount = luaL_checkint(L, 2); if (!getServer(L)->hudSetHotbarItemcount(player, hotbar_itemcount)) return 0; @@ -1641,7 +1630,7 @@ int ObjectRef::l_hud_get_hotbar_itemcount(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - if (player == NULL) + if (player == nullptr) return 0; lua_pushnumber(L, player->getHotbarItemcount()); @@ -1654,7 +1643,7 @@ int ObjectRef::l_hud_set_hotbar_image(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - if (player == NULL) + if (player == nullptr) return 0; std::string name = readParam<std::string>(L, 2); @@ -1669,10 +1658,11 @@ int ObjectRef::l_hud_get_hotbar_image(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - if (player == NULL) + if (player == nullptr) return 0; const std::string &name = player->getHotbarImage(); + lua_pushlstring(L, name.c_str(), name.size()); return 1; } @@ -1683,7 +1673,7 @@ int ObjectRef::l_hud_set_hotbar_selected_image(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - if (player == NULL) + if (player == nullptr) return 0; std::string name = readParam<std::string>(L, 2); @@ -1698,44 +1688,45 @@ int ObjectRef::l_hud_get_hotbar_selected_image(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - if (player == NULL) + if (player == nullptr) return 0; const std::string &name = player->getHotbarSelectedImage(); + lua_pushlstring(L, name.c_str(), name.size()); return 1; } -// set_sky(self, {base_color=, type=, textures=, clouds=, sky_colors={}}) +// set_sky(self, sky_parameters) int ObjectRef::l_set_sky(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - if (!player) + if (player == nullptr) return 0; + SkyboxParams sky_params = player->getSkyParams(); bool is_colorspec = is_color_table(L, 2); - SkyboxParams skybox_params = player->getSkyParams(); if (lua_istable(L, 2) && !is_colorspec) { lua_getfield(L, 2, "base_color"); if (!lua_isnil(L, -1)) - read_color(L, -1, &skybox_params.bgcolor); + read_color(L, -1, &sky_params.bgcolor); lua_pop(L, 1); lua_getfield(L, 2, "type"); if (!lua_isnil(L, -1)) - skybox_params.type = luaL_checkstring(L, -1); + sky_params.type = luaL_checkstring(L, -1); lua_pop(L, 1); lua_getfield(L, 2, "textures"); - skybox_params.textures.clear(); - if (lua_istable(L, -1) && skybox_params.type == "skybox") { + sky_params.textures.clear(); + if (lua_istable(L, -1) && sky_params.type == "skybox") { lua_pushnil(L); while (lua_next(L, -2) != 0) { // Key is at index -2 and value at index -1 - skybox_params.textures.emplace_back(readParam<std::string>(L, -1)); + sky_params.textures.emplace_back(readParam<std::string>(L, -1)); // Removes the value, but keeps the key for iteration lua_pop(L, 1); } @@ -1748,56 +1739,56 @@ int ObjectRef::l_set_sky(lua_State *L) using "regular" or "plain" skybox modes as textures aren't needed. */ - if (skybox_params.textures.size() != 6 && skybox_params.textures.size() > 0) + if (sky_params.textures.size() != 6 && sky_params.textures.size() > 0) throw LuaError("Skybox expects 6 textures!"); - skybox_params.clouds = getboolfield_default(L, 2, - "clouds", skybox_params.clouds); + sky_params.clouds = getboolfield_default(L, 2, + "clouds", sky_params.clouds); lua_getfield(L, 2, "sky_color"); if (lua_istable(L, -1)) { lua_getfield(L, -1, "day_sky"); - read_color(L, -1, &skybox_params.sky_color.day_sky); + read_color(L, -1, &sky_params.sky_color.day_sky); lua_pop(L, 1); lua_getfield(L, -1, "day_horizon"); - read_color(L, -1, &skybox_params.sky_color.day_horizon); + read_color(L, -1, &sky_params.sky_color.day_horizon); lua_pop(L, 1); lua_getfield(L, -1, "dawn_sky"); - read_color(L, -1, &skybox_params.sky_color.dawn_sky); + read_color(L, -1, &sky_params.sky_color.dawn_sky); lua_pop(L, 1); lua_getfield(L, -1, "dawn_horizon"); - read_color(L, -1, &skybox_params.sky_color.dawn_horizon); + read_color(L, -1, &sky_params.sky_color.dawn_horizon); lua_pop(L, 1); lua_getfield(L, -1, "night_sky"); - read_color(L, -1, &skybox_params.sky_color.night_sky); + read_color(L, -1, &sky_params.sky_color.night_sky); lua_pop(L, 1); lua_getfield(L, -1, "night_horizon"); - read_color(L, -1, &skybox_params.sky_color.night_horizon); + read_color(L, -1, &sky_params.sky_color.night_horizon); lua_pop(L, 1); lua_getfield(L, -1, "indoors"); - read_color(L, -1, &skybox_params.sky_color.indoors); + read_color(L, -1, &sky_params.sky_color.indoors); lua_pop(L, 1); // Prevent flickering clouds at dawn/dusk: - skybox_params.fog_sun_tint = video::SColor(255, 255, 255, 255); + sky_params.fog_sun_tint = video::SColor(255, 255, 255, 255); lua_getfield(L, -1, "fog_sun_tint"); - read_color(L, -1, &skybox_params.fog_sun_tint); + read_color(L, -1, &sky_params.fog_sun_tint); lua_pop(L, 1); - skybox_params.fog_moon_tint = video::SColor(255, 255, 255, 255); + sky_params.fog_moon_tint = video::SColor(255, 255, 255, 255); lua_getfield(L, -1, "fog_moon_tint"); - read_color(L, -1, &skybox_params.fog_moon_tint); + read_color(L, -1, &sky_params.fog_moon_tint); lua_pop(L, 1); lua_getfield(L, -1, "fog_tint_type"); if (!lua_isnil(L, -1)) - skybox_params.fog_tint_type = luaL_checkstring(L, -1); + sky_params.fog_tint_type = luaL_checkstring(L, -1); lua_pop(L, 1); // Because we need to leave the "sky_color" table. @@ -1813,14 +1804,14 @@ int ObjectRef::l_set_sky(lua_State *L) StarParams star_params = player->getStarParams(); // Prevent erroneous background colors - skybox_params.bgcolor = video::SColor(255, 255, 255, 255); - read_color(L, 2, &skybox_params.bgcolor); + sky_params.bgcolor = video::SColor(255, 255, 255, 255); + read_color(L, 2, &sky_params.bgcolor); - skybox_params.type = luaL_checkstring(L, 3); + sky_params.type = luaL_checkstring(L, 3); // Preserve old behaviour of the sun, moon and stars // when using the old set_sky call. - if (skybox_params.type == "regular") { + if (sky_params.type == "regular") { sun_params.visible = true; sun_params.sunrise_visible = true; moon_params.visible = true; @@ -1832,31 +1823,31 @@ int ObjectRef::l_set_sky(lua_State *L) star_params.visible = false; } - skybox_params.textures.clear(); + sky_params.textures.clear(); if (lua_istable(L, 4)) { lua_pushnil(L); while (lua_next(L, 4) != 0) { // Key at index -2, and value at index -1 if (lua_isstring(L, -1)) - skybox_params.textures.emplace_back(readParam<std::string>(L, -1)); + sky_params.textures.emplace_back(readParam<std::string>(L, -1)); else - skybox_params.textures.emplace_back(""); + sky_params.textures.emplace_back(""); // Remove the value, keep the key for the next iteration lua_pop(L, 1); } } - if (skybox_params.type == "skybox" && skybox_params.textures.size() != 6) + if (sky_params.type == "skybox" && sky_params.textures.size() != 6) throw LuaError("Skybox expects 6 textures."); - skybox_params.clouds = true; + sky_params.clouds = true; if (lua_isboolean(L, 5)) - skybox_params.clouds = readParam<bool>(L, 5); + sky_params.clouds = readParam<bool>(L, 5); getServer(L)->setSun(player, sun_params); getServer(L)->setMoon(player, moon_params); getServer(L)->setStars(player, star_params); } - getServer(L)->setSky(player, skybox_params); + getServer(L)->setSky(player, sky_params); lua_pushboolean(L, true); return 1; } @@ -1867,18 +1858,17 @@ int ObjectRef::l_get_sky(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - - if (!player) + if (player == nullptr) return 0; - SkyboxParams skybox_params; - skybox_params = player->getSkyParams(); + + SkyboxParams skybox_params = player->getSkyParams(); push_ARGB8(L, skybox_params.bgcolor); lua_pushlstring(L, skybox_params.type.c_str(), skybox_params.type.size()); lua_newtable(L); s16 i = 1; - for (const std::string& texture : skybox_params.textures) { + for (const std::string &texture : skybox_params.textures) { lua_pushlstring(L, texture.c_str(), texture.size()); lua_rawseti(L, -2, i++); } @@ -1892,11 +1882,10 @@ int ObjectRef::l_get_sky_color(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - - if (!player) + if (player == nullptr) return 0; - const SkyboxParams& skybox_params = player->getSkyParams(); + const SkyboxParams &skybox_params = player->getSkyParams(); lua_newtable(L); if (skybox_params.type == "regular") { @@ -1924,18 +1913,16 @@ int ObjectRef::l_get_sky_color(lua_State *L) return 1; } -// set_sun(self, {visible, texture=, tonemap=, sunrise=, rotation=, scale=}) +// set_sun(self, sun_parameters) int ObjectRef::l_set_sun(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - if (!player) - return 0; - - if (!lua_istable(L, 2)) + if (player == nullptr) return 0; + luaL_checktype(L, 2, LUA_TTABLE); SunParams sun_params = player->getSunParams(); sun_params.visible = getboolfield_default(L, 2, @@ -1962,8 +1949,9 @@ int ObjectRef::l_get_sun(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - if (!player) + if (player == nullptr) return 0; + const SunParams &sun_params = player->getSunParams(); lua_newtable(L); @@ -1979,21 +1967,19 @@ int ObjectRef::l_get_sun(lua_State *L) lua_setfield(L, -2, "sunrise_visible"); lua_pushnumber(L, sun_params.scale); lua_setfield(L, -2, "scale"); - return 1; } -// set_moon(self, {visible, texture=, tonemap=, sunrise=, rotation=, scale=}) +// set_moon(self, moon_parameters) int ObjectRef::l_set_moon(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - if (!player) - return 0; - if (!lua_istable(L, 2)) + if (player == nullptr) return 0; + luaL_checktype(L, 2, LUA_TTABLE); MoonParams moon_params = player->getMoonParams(); moon_params.visible = getboolfield_default(L, 2, @@ -2016,8 +2002,9 @@ int ObjectRef::l_get_moon(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - if (!player) + if (player == nullptr) return 0; + const MoonParams &moon_params = player->getMoonParams(); lua_newtable(L); @@ -2029,21 +2016,19 @@ int ObjectRef::l_get_moon(lua_State *L) lua_setfield(L, -2, "tonemap"); lua_pushnumber(L, moon_params.scale); lua_setfield(L, -2, "scale"); - return 1; } -// set_stars(self, {visible, count=, starcolor=, rotation=, scale=}) +// set_stars(self, star_parameters) int ObjectRef::l_set_stars(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - if (!player) - return 0; - if (!lua_istable(L, 2)) + if (player == nullptr) return 0; + luaL_checktype(L, 2, LUA_TTABLE); StarParams star_params = player->getStarParams(); star_params.visible = getboolfield_default(L, 2, @@ -2070,8 +2055,9 @@ int ObjectRef::l_get_stars(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - if (!player) + if (player == nullptr) return 0; + const StarParams &star_params = player->getStarParams(); lua_newtable(L); @@ -2083,21 +2069,19 @@ int ObjectRef::l_get_stars(lua_State *L) lua_setfield(L, -2, "star_color"); lua_pushnumber(L, star_params.scale); lua_setfield(L, -2, "scale"); - return 1; } -// set_clouds(self, {density=, color=, ambient=, height=, thickness=, speed=}) +// set_clouds(self, cloud_parameters) int ObjectRef::l_set_clouds(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - if (!player) - return 0; - if (!lua_istable(L, 2)) + if (player == nullptr) return 0; + luaL_checktype(L, 2, LUA_TTABLE); CloudParams cloud_params = player->getCloudParams(); cloud_params.density = getfloatfield_default(L, 2, "density", cloud_params.density); @@ -2133,8 +2117,9 @@ int ObjectRef::l_get_clouds(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - if (!player) + if (player == nullptr) return 0; + const CloudParams &cloud_params = player->getCloudParams(); lua_newtable(L); @@ -2154,25 +2139,27 @@ int ObjectRef::l_get_clouds(lua_State *L) lua_pushnumber(L, cloud_params.speed.Y); lua_setfield(L, -2, "y"); lua_setfield(L, -2, "speed"); - return 1; } -// override_day_night_ratio(self, brightness=0...1) +// override_day_night_ratio(self, ratio) int ObjectRef::l_override_day_night_ratio(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - if (player == NULL) + if (player == nullptr) return 0; bool do_override = false; float ratio = 0.0f; + if (!lua_isnil(L, 2)) { do_override = true; ratio = readParam<float>(L, 2); + luaL_argcheck(L, ratio >= 0.0f && ratio <= 1.0f, 1, + "value must be between 0 and 1"); } getServer(L)->overrideDayNightRatio(player, do_override, ratio); @@ -2186,7 +2173,7 @@ int ObjectRef::l_get_day_night_ratio(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); RemotePlayer *player = getplayer(ref); - if (player == NULL) + if (player == nullptr) return 0; bool do_override; @@ -2201,27 +2188,77 @@ int ObjectRef::l_get_day_night_ratio(lua_State *L) return 1; } -ObjectRef::ObjectRef(ServerActiveObject *object): - m_object(object) +// set_minimap_modes(self, modes, selected_mode) +int ObjectRef::l_set_minimap_modes(lua_State *L) { - //infostream<<"ObjectRef created for id="<<m_object->getId()<<std::endl; + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + RemotePlayer *player = getplayer(ref); + if (player == nullptr) + return 0; + + luaL_checktype(L, 2, LUA_TTABLE); + std::vector<MinimapMode> modes; + s16 selected_mode = luaL_checkint(L, 3); + + lua_pushnil(L); + while (lua_next(L, 2) != 0) { + /* key is at index -2, value is at index -1 */ + if (lua_istable(L, -1)) { + bool ok = true; + MinimapMode mode; + std::string type = getstringfield_default(L, -1, "type", ""); + if (type == "off") + mode.type = MINIMAP_TYPE_OFF; + else if (type == "surface") + mode.type = MINIMAP_TYPE_SURFACE; + else if (type == "radar") + mode.type = MINIMAP_TYPE_RADAR; + else if (type == "texture") { + mode.type = MINIMAP_TYPE_TEXTURE; + mode.texture = getstringfield_default(L, -1, "texture", ""); + mode.scale = getintfield_default(L, -1, "scale", 1); + } else { + warningstream << "Minimap mode of unknown type \"" << type.c_str() + << "\" ignored.\n" << std::endl; + ok = false; + } + + if (ok) { + mode.label = getstringfield_default(L, -1, "label", ""); + // Size is limited to 512. Performance gets poor if size too large, and + // segfaults have been experienced. + mode.size = rangelim(getintfield_default(L, -1, "size", 0), 1, 512); + modes.push_back(mode); + } + } + /* removes 'value'; keeps 'key' for next iteration */ + lua_pop(L, 1); + } + lua_pop(L, 1); // Remove key + + getServer(L)->SendMinimapModes(player->getPeerId(), modes, selected_mode); + return 0; } +ObjectRef::ObjectRef(ServerActiveObject *object): + m_object(object) +{} + // Creates an ObjectRef and leaves it on top of stack // Not callable from Lua; all references are created on the C side. void ObjectRef::create(lua_State *L, ServerActiveObject *object) { - ObjectRef *o = new ObjectRef(object); - //infostream<<"ObjectRef::create: o="<<o<<std::endl; - *(void **)(lua_newuserdata(L, sizeof(void *))) = o; + ObjectRef *obj = new ObjectRef(object); + *(void **)(lua_newuserdata(L, sizeof(void *))) = obj; luaL_getmetatable(L, className); lua_setmetatable(L, -2); } void ObjectRef::set_null(lua_State *L) { - ObjectRef *o = checkobject(L, -1); - o->m_object = NULL; + ObjectRef *obj = checkobject(L, -1); + obj->m_object = nullptr; } void ObjectRef::Register(lua_State *L) @@ -2245,12 +2282,8 @@ void ObjectRef::Register(lua_State *L) lua_pop(L, 1); // drop metatable - markAliasDeprecated(methods); luaL_openlib(L, 0, methods, 0); // fill methodtable lua_pop(L, 1); // drop methodtable - - // Cannot be created from Lua - //lua_register(L, className, create_object); } const char ObjectRef::className[] = "ObjectRef"; @@ -2278,15 +2311,19 @@ luaL_Reg ObjectRef::methods[] = { luamethod(ObjectRef, get_bone_position), luamethod(ObjectRef, set_attach), luamethod(ObjectRef, get_attach), + luamethod(ObjectRef, get_children), luamethod(ObjectRef, set_detach), luamethod(ObjectRef, set_properties), luamethod(ObjectRef, get_properties), luamethod(ObjectRef, set_nametag_attributes), luamethod(ObjectRef, get_nametag_attributes), - // LuaEntitySAO-only + luamethod_aliased(ObjectRef, set_velocity, setvelocity), - luamethod(ObjectRef, add_velocity), + luamethod_aliased(ObjectRef, add_velocity, add_player_velocity), luamethod_aliased(ObjectRef, get_velocity, getvelocity), + luamethod_dep(ObjectRef, get_velocity, get_player_velocity), + + // LuaEntitySAO-only luamethod_aliased(ObjectRef, set_acceleration, setacceleration), luamethod_aliased(ObjectRef, get_acceleration, getacceleration), luamethod_aliased(ObjectRef, set_yaw, setyaw), @@ -2294,15 +2331,14 @@ luaL_Reg ObjectRef::methods[] = { luamethod(ObjectRef, set_rotation), luamethod(ObjectRef, get_rotation), luamethod_aliased(ObjectRef, set_texture_mod, settexturemod), + luamethod(ObjectRef, get_texture_mod), luamethod_aliased(ObjectRef, set_sprite, setsprite), luamethod(ObjectRef, get_entity_name), luamethod(ObjectRef, get_luaentity), + // Player-only luamethod(ObjectRef, is_player), - luamethod(ObjectRef, is_player_connected), luamethod(ObjectRef, get_player_name), - luamethod(ObjectRef, get_player_velocity), - luamethod(ObjectRef, add_player_velocity), luamethod(ObjectRef, get_look_dir), luamethod(ObjectRef, get_look_pitch), luamethod(ObjectRef, get_look_yaw), @@ -2357,5 +2393,6 @@ luaL_Reg ObjectRef::methods[] = { luamethod(ObjectRef, set_eye_offset), luamethod(ObjectRef, get_eye_offset), luamethod(ObjectRef, send_mapblock), + luamethod(ObjectRef, set_minimap_modes), {0,0} }; diff --git a/src/script/lua_api/l_object.h b/src/script/lua_api/l_object.h index a75c59fd9..db3a3a7cf 100644 --- a/src/script/lua_api/l_object.h +++ b/src/script/lua_api/l_object.h @@ -69,29 +69,24 @@ private: static int l_remove(lua_State *L); // get_pos(self) - // returns: {x=num, y=num, z=num} static int l_get_pos(lua_State *L); // set_pos(self, pos) static int l_set_pos(lua_State *L); - // move_to(self, pos, continuous=false) + // move_to(self, pos, continuous) static int l_move_to(lua_State *L); // punch(self, puncher, time_from_last_punch, tool_capabilities, dir) static int l_punch(lua_State *L); - // right_click(self, clicker); clicker = an another ObjectRef + // right_click(self, clicker) static int l_right_click(lua_State *L); - // set_hp(self, hp) - // hp = number of hitpoints (2 * number of hearts) - // returns: nil + // set_hp(self, hp, reason) static int l_set_hp(lua_State *L); // get_hp(self) - // returns: number of hitpoints (2 * number of hearts) - // 0 if not applicable to this type of object static int l_get_hp(lua_State *L); // get_inventory(self) @@ -106,7 +101,7 @@ private: // get_wielded_item(self) static int l_get_wielded_item(lua_State *L); - // set_wielded_item(self, itemstack or itemstring or table or nil) + // set_wielded_item(self, item) static int l_set_wielded_item(lua_State *L); // set_armor_groups(self, groups) @@ -115,8 +110,7 @@ private: // get_armor_groups(self) static int l_get_armor_groups(lua_State *L); - // set_physics_override(self, physics_override_speed, physics_override_jump, - // physics_override_gravity, sneak, sneak_glitch, new_move) + // set_physics_override(self, override_table) static int l_set_physics_override(lua_State *L); // get_physics_override(self) @@ -131,7 +125,7 @@ private: // get_animation(self) static int l_get_animation(lua_State *L); - // set_bone_position(self, std::string bone, v3f position, v3f rotation) + // set_bone_position(self, bone, position, rotation) static int l_set_bone_position(lua_State *L); // get_bone_position(self, bone) @@ -143,6 +137,9 @@ private: // get_attach(self) static int l_get_attach(lua_State *L); + // get_children(self) + static int l_get_children(lua_State *L); + // set_detach(self) static int l_set_detach(lua_State *L); @@ -157,28 +154,28 @@ private: /* LuaEntitySAO-only */ - // set_velocity(self, {x=num, y=num, z=num}) + // set_velocity(self, velocity) static int l_set_velocity(lua_State *L); - // add_velocity(self, {x=num, y=num, z=num}) + // add_velocity(self, velocity) static int l_add_velocity(lua_State *L); // get_velocity(self) static int l_get_velocity(lua_State *L); - // set_acceleration(self, {x=num, y=num, z=num}) + // set_acceleration(self, acceleration) static int l_set_acceleration(lua_State *L); // get_acceleration(self) static int l_get_acceleration(lua_State *L); - // set_rotation(self, {x=num, y=num, z=num}) + // set_rotation(self, rotation) static int l_set_rotation(lua_State *L); // get_rotation(self) static int l_get_rotation(lua_State *L); - // set_yaw(self, radians) + // set_yaw(self, yaw) static int l_set_yaw(lua_State *L); // get_yaw(self) @@ -190,8 +187,7 @@ private: // l_get_texture_mod(self) static int l_get_texture_mod(lua_State *L); - // set_sprite(self, p={x=0,y=0}, num_frames=1, framelength=0.2, - // select_horiz_by_yawpitch=false) + // set_sprite(self, start_frame, num_frames, framelength, select_x_by_camera) static int l_set_sprite(lua_State *L); // DEPRECATED @@ -203,18 +199,9 @@ private: /* Player-only */ - // is_player_connected(self) - static int l_is_player_connected(lua_State *L); - // get_player_name(self) static int l_get_player_name(lua_State *L); - // get_player_velocity(self) - static int l_get_player_velocity(lua_State *L); - - // add_player_velocity(self, {x=num, y=num, z=num}) - static int l_add_player_velocity(lua_State *L); - // get_fov(self) static int l_get_fov(lua_State *L); @@ -235,7 +222,7 @@ private: // get_look_yaw2(self) static int l_get_look_horizontal(lua_State *L); - // set_fov(self, degrees, is_multiplier) + // set_fov(self, degrees, is_multiplier, transition_time) static int l_set_fov(lua_State *L); // set_look_vertical(self, radians) @@ -258,9 +245,11 @@ private: // get_breath(self, breath) static int l_get_breath(lua_State *L); + // DEPRECATED // set_attribute(self, attribute, value) static int l_set_attribute(lua_State *L); + // DEPRECATED // get_attribute(self, attribute) static int l_get_attribute(lua_State *L); @@ -270,13 +259,13 @@ private: // set_inventory_formspec(self, formspec) static int l_set_inventory_formspec(lua_State *L); - // get_inventory_formspec(self) -> formspec + // get_inventory_formspec(self) static int l_get_inventory_formspec(lua_State *L); // set_formspec_prepend(self, formspec) static int l_set_formspec_prepend(lua_State *L); - // get_formspec_prepend(self) -> formspec + // get_formspec_prepend(self) static int l_get_formspec_prepend(lua_State *L); // get_player_control(self) @@ -324,7 +313,7 @@ private: // hud_get_hotbar_selected_image(self) static int l_hud_get_hotbar_selected_image(lua_State *L); - // set_sky({base_color=, type=, textures=, clouds=, sky_colors={}}) + // set_sky(self, sky_parameters) static int l_set_sky(lua_State *L); // get_sky(self) @@ -333,25 +322,25 @@ private: // get_sky_color(self) static int l_get_sky_color(lua_State* L); - // set_sun(self, {visible, texture=, tonemap=, sunrise=, rotation=, scale=}) + // set_sun(self, sun_parameters) static int l_set_sun(lua_State *L); // get_sun(self) static int l_get_sun(lua_State *L); - // set_moon(self, {visible, texture=, tonemap=, rotation, scale=}) + // set_moon(self, moon_parameters) static int l_set_moon(lua_State *L); // get_moon(self) static int l_get_moon(lua_State *L); - // set_stars(self, {visible, count=, starcolor=, rotation, scale=}) + // set_stars(self, star_parameters) static int l_set_stars(lua_State *L); // get_stars(self) static int l_get_stars(lua_State *L); - // set_clouds(self, {density=, color=, ambient=, height=, thickness=, speed=}) + // set_clouds(self, cloud_parameters) static int l_set_clouds(lua_State *L); // get_clouds(self) @@ -363,13 +352,13 @@ private: // get_day_night_ratio(self) static int l_get_day_night_ratio(lua_State *L); - // set_local_animation(self, {stand/idle}, {walk}, {dig}, {walk+dig}, frame_speed) + // set_local_animation(self, idle, walk, dig, walk_while_dig, frame_speed) static int l_set_local_animation(lua_State *L); // get_local_animation(self) static int l_get_local_animation(lua_State *L); - // set_eye_offset(self, v3f first pv, v3f third pv) + // set_eye_offset(self, firstperson, thirdperson) static int l_set_eye_offset(lua_State *L); // get_eye_offset(self) @@ -383,4 +372,7 @@ private: // send_mapblock(pos) static int l_send_mapblock(lua_State *L); + + // set_minimap_modes(self, modes, wanted_mode) + static int l_set_minimap_modes(lua_State *L); }; diff --git a/src/server.cpp b/src/server.cpp index fe2bb3840..8f6257afe 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -213,7 +213,8 @@ Server::Server( bool simple_singleplayer_mode, Address bind_addr, bool dedicated, - ChatInterface *iface + ChatInterface *iface, + std::string *on_shutdown_errmsg ): m_bind_addr(bind_addr), m_path_world(path_world), @@ -232,6 +233,7 @@ Server::Server( m_thread(new ServerThread(this)), m_clients(m_con), m_admin_chat(iface), + m_on_shutdown_errmsg(on_shutdown_errmsg), m_modchannel_mgr(new ModChannelMgr()) { if (m_path_world.empty()) @@ -314,7 +316,18 @@ Server::~Server() // Execute script shutdown hooks infostream << "Executing shutdown hooks" << std::endl; - m_script->on_shutdown(); + try { + m_script->on_shutdown(); + } catch (ModError &e) { + errorstream << "ModError: " << e.what() << std::endl; + if (m_on_shutdown_errmsg) { + if (m_on_shutdown_errmsg->empty()) { + *m_on_shutdown_errmsg = std::string("ModError: ") + e.what(); + } else { + *m_on_shutdown_errmsg += std::string("\nModError: ") + e.what(); + } + } + } infostream << "Server: Saving environment metadata" << std::endl; m_env->saveMeta(); @@ -356,8 +369,13 @@ void Server::init() infostream << "- game: " << m_gamespec.path << std::endl; // Create world if it doesn't exist - if (!loadGameConfAndInitWorld(m_path_world, m_gamespec)) - throw ServerError("Failed to initialize world"); + try { + loadGameConfAndInitWorld(m_path_world, + fs::GetFilenameFromPath(m_path_world.c_str()), + m_gamespec, false); + } catch (const BaseException &e) { + throw ServerError(std::string("Failed to initialize world: ") + e.what()); + } // Create emerge manager m_emerge = new EmergeManager(this); @@ -635,7 +653,12 @@ void Server::AsyncRunStep(bool initial_step) } m_clients.step(dtime); - m_lag_gauge->increment((m_lag_gauge->get() > dtime ? -1 : 1) * dtime/100); + // increase/decrease lag gauge gradually + if (m_lag_gauge->get() > dtime) { + m_lag_gauge->decrement(dtime/100); + } else { + m_lag_gauge->increment(dtime/100); + } #if USE_CURL // send masterserver announce { @@ -779,7 +802,7 @@ void Server::AsyncRunStep(bool initial_step) // u16 id // std::string data buffer.append(idbuf, sizeof(idbuf)); - buffer.append(serializeString(aom.datastring)); + buffer.append(serializeString16(aom.datastring)); } } /* @@ -1970,7 +1993,7 @@ void Server::SendActiveObjectRemoveAdd(RemoteClient *client, PlayerSAO *playersa writeU8((u8*)buf, type); data.append(buf, 1); - data.append(serializeLongString( + data.append(serializeString32( obj->getClientInitializationData(client->net_proto_version))); // Add to known objects @@ -2315,7 +2338,7 @@ void Server::SendBlockNoLock(session_t peer_id, MapBlock *block, u8 ver, block->serializeNetworkSpecific(os); std::string s = os.str(); - NetworkPacket pkt(TOCLIENT_BLOCKDATA, 2 + 2 + 2 + 2 + s.size(), peer_id); + NetworkPacket pkt(TOCLIENT_BLOCKDATA, 2 + 2 + 2 + s.size(), peer_id); pkt << block->getPos(); pkt.putRawString(s.c_str(), s.size()); @@ -2433,31 +2456,14 @@ bool Server::addMediaFile(const std::string &filename, // Ok, attempt to load the file and add to cache // Read data - std::ifstream fis(filepath.c_str(), std::ios_base::binary); - if (!fis.good()) { - errorstream << "Server::addMediaFile(): Could not open \"" - << filename << "\" for reading" << std::endl; - return false; - } std::string filedata; - bool bad = false; - for (;;) { - char buf[1024]; - fis.read(buf, sizeof(buf)); - std::streamsize len = fis.gcount(); - filedata.append(buf, len); - if (fis.eof()) - break; - if (!fis.good()) { - bad = true; - break; - } - } - if (bad) { - errorstream << "Server::addMediaFile(): Failed to read \"" - << filename << "\"" << std::endl; + if (!fs::ReadFile(filepath, filedata)) { + errorstream << "Server::addMediaFile(): Failed to open \"" + << filename << "\" for reading" << std::endl; return false; - } else if (filedata.empty()) { + } + + if (filedata.empty()) { errorstream << "Server::addMediaFile(): Empty file \"" << filepath << "\"" << std::endl; return false; @@ -2489,19 +2495,25 @@ void Server::fillMediaCache() // Collect all media file paths std::vector<std::string> paths; - m_modmgr->getModsMediaPaths(paths); - fs::GetRecursiveDirs(paths, m_gamespec.path + DIR_DELIM + "textures"); + // The paths are ordered in descending priority 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); // Collect media file information from paths into cache for (const std::string &mediapath : paths) { std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath); for (const fs::DirListNode &dln : dirlist) { - if (dln.dir) // Ignore dirs + if (dln.dir) // Ignore dirs (already in paths) + continue; + + const std::string &filename = dln.name; + if (m_media.find(filename) != m_media.end()) // Do not override continue; + std::string filepath = mediapath; - filepath.append(DIR_DELIM).append(dln.name); - addMediaFile(dln.name, filepath); + filepath.append(DIR_DELIM).append(filename); + addMediaFile(filename, filepath); } } @@ -2650,6 +2662,23 @@ void Server::sendRequestedMedia(session_t peer_id, } } +void Server::SendMinimapModes(session_t peer_id, + std::vector<MinimapMode> &modes, size_t wanted_mode) +{ + RemotePlayer *player = m_env->getPlayer(peer_id); + assert(player); + if (player->getPeerId() == PEER_ID_INEXISTENT) + return; + + NetworkPacket pkt(TOCLIENT_MINIMAP_MODES, 0, peer_id); + pkt << (u16)modes.size() << (u16)wanted_mode; + + for (auto &mode : modes) + pkt << (u16)mode.type << mode.label << mode.size << mode.texture << mode.scale; + + Send(&pkt); +} + void Server::sendDetachedInventory(Inventory *inventory, const std::string &name, session_t peer_id) { NetworkPacket pkt(TOCLIENT_DETACHED_INVENTORY, 0, peer_id); @@ -3866,19 +3895,27 @@ void Server::broadcastModChannelMessage(const std::string &channel, } } -void Server::loadTranslationLanguage(const std::string &lang_code) +Translations *Server::getTranslationLanguage(const std::string &lang_code) { - if (g_server_translations->count(lang_code)) - return; // Already loaded + if (lang_code.empty()) + return nullptr; + + auto it = server_translations.find(lang_code); + if (it != server_translations.end()) + return &it->second; // Already loaded + + // [] will create an entry + auto *translations = &server_translations[lang_code]; std::string suffix = "." + lang_code + ".tr"; for (const auto &i : m_media) { if (str_ends_with(i.first, suffix)) { - std::ifstream t(i.second.path); - std::string data((std::istreambuf_iterator<char>(t)), - std::istreambuf_iterator<char>()); - - (*g_server_translations)[lang_code].loadTranslation(data); + std::string data; + if (fs::ReadFile(i.second.path, data)) { + translations->loadTranslation(data); + } } } + + return translations; } diff --git a/src/server.h b/src/server.h index f44716531..4b3ac5cf7 100644 --- a/src/server.h +++ b/src/server.h @@ -38,6 +38,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "serverenvironment.h" #include "clientiface.h" #include "chatmessage.h" +#include "translation.h" #include <string> #include <list> #include <map> @@ -117,6 +118,14 @@ struct ServerPlayingSound std::unordered_set<session_t> clients; // peer ids }; +struct MinimapMode { + MinimapType type = MINIMAP_TYPE_OFF; + std::string label; + u16 size = 0; + std::string texture; + u16 scale = 1; +}; + class Server : public con::PeerHandler, public MapEventReceiver, public IGameDef { @@ -131,7 +140,8 @@ public: bool simple_singleplayer_mode, Address bind_addr, bool dedicated, - ChatInterface *iface = nullptr + ChatInterface *iface = nullptr, + std::string *on_shutdown_errmsg = nullptr ); ~Server(); DISABLE_CLASS_COPY(Server); @@ -329,6 +339,10 @@ public: void SendPlayerSpeed(session_t peer_id, const v3f &added_vel); void SendPlayerFov(session_t peer_id); + void SendMinimapModes(session_t peer_id, + std::vector<MinimapMode> &modes, + size_t wanted_mode); + void sendDetachedInventories(session_t peer_id, bool incremental); virtual bool registerModStorage(ModMetadata *storage); @@ -342,8 +356,8 @@ public: // Send block to specific player only bool SendBlock(session_t peer_id, const v3s16 &blockpos); - // Load translations for a language - void loadTranslationLanguage(const std::string &lang_code); + // Get or load translations for a language + Translations *getTranslationLanguage(const std::string &lang_code); // Bind address Address m_bind_addr; @@ -556,6 +570,8 @@ private: // Mods std::unique_ptr<ServerModManager> m_modmgr; + std::unordered_map<std::string, Translations> server_translations; + /* Threads */ @@ -596,6 +612,10 @@ private: ChatInterface *m_admin_chat; std::string m_admin_nick; + // if a mod-error occurs in the on_shutdown callback, the error message will + // be written into this + std::string *const m_on_shutdown_errmsg; + /* Map edit event queue. Automatically receives all map edits. The constructor of this class registers us to receive them through diff --git a/src/server/luaentity_sao.cpp b/src/server/luaentity_sao.cpp index d504c42ca..b39797531 100644 --- a/src/server/luaentity_sao.cpp +++ b/src/server/luaentity_sao.cpp @@ -42,8 +42,8 @@ LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos, const std::string &d u8 version2 = 0; u8 version = readU8(is); - name = deSerializeString(is); - state = deSerializeLongString(is); + name = deSerializeString16(is); + state = deSerializeString32(is); if (version < 1) break; @@ -225,7 +225,7 @@ std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version) // PROTOCOL_VERSION >= 37 writeU8(os, 1); // version - os << serializeString(""); // name + os << serializeString16(""); // name writeU8(os, 0); // is_player writeU16(os, getId()); //id writeV3F32(os, m_base_position); @@ -233,26 +233,26 @@ std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version) writeU16(os, m_hp); std::ostringstream msg_os(std::ios::binary); - msg_os << serializeLongString(getPropertyPacket()); // message 1 - msg_os << serializeLongString(generateUpdateArmorGroupsCommand()); // 2 - msg_os << serializeLongString(generateUpdateAnimationCommand()); // 3 + msg_os << serializeString32(getPropertyPacket()); // message 1 + msg_os << serializeString32(generateUpdateArmorGroupsCommand()); // 2 + msg_os << serializeString32(generateUpdateAnimationCommand()); // 3 for (const auto &bone_pos : m_bone_position) { - msg_os << serializeLongString(generateUpdateBonePositionCommand( - bone_pos.first, bone_pos.second.X, bone_pos.second.Y)); // m_bone_position.size + msg_os << serializeString32(generateUpdateBonePositionCommand( + bone_pos.first, bone_pos.second.X, bone_pos.second.Y)); // 3 + N } - msg_os << serializeLongString(generateUpdateAttachmentCommand()); // 4 + msg_os << serializeString32(generateUpdateAttachmentCommand()); // 4 + m_bone_position.size int message_count = 4 + m_bone_position.size(); for (const auto &id : getAttachmentChildIds()) { if (ServerActiveObject *obj = m_env->getActiveObject(id)) { message_count++; - msg_os << serializeLongString(obj->generateUpdateInfantCommand( + msg_os << serializeString32(obj->generateUpdateInfantCommand( id, protocol_version)); } } - msg_os << serializeLongString(generateSetTextureModCommand()); + msg_os << serializeString32(generateSetTextureModCommand()); message_count++; writeU8(os, message_count); @@ -270,14 +270,14 @@ void LuaEntitySAO::getStaticData(std::string *result) const // version must be 1 to keep backwards-compatibility. See version2 writeU8(os, 1); // name - os<<serializeString(m_init_name); + os<<serializeString16(m_init_name); // state if(m_registered){ std::string state = m_env->getScriptIface()-> luaentity_GetStaticdata(m_id); - os<<serializeLongString(state); + os<<serializeString32(state); } else { - os<<serializeLongString(m_init_state); + os<<serializeString32(m_init_state); } writeU16(os, m_hp); writeV3F1000(os, m_velocity); @@ -436,7 +436,7 @@ std::string LuaEntitySAO::generateSetTextureModCommand() const // command writeU8(os, AO_CMD_SET_TEXTURE_MOD); // parameters - os << serializeString(m_current_texture_modifier); + os << serializeString16(m_current_texture_modifier); return os.str(); } diff --git a/src/server/luaentity_sao.h b/src/server/luaentity_sao.h index 2520c8f5d..e060aa06d 100644 --- a/src/server/luaentity_sao.h +++ b/src/server/luaentity_sao.h @@ -42,6 +42,7 @@ public: void step(float dtime, bool send_recommended); std::string getClientInitializationData(u16 protocol_version); bool isStaticAllowed() const { return m_prop.static_save; } + bool shouldUnload() const { return true; } void getStaticData(std::string *result) const; u16 punch(v3f dir, const ToolCapabilities *toolcap = nullptr, ServerActiveObject *puncher = nullptr, diff --git a/src/server/mods.cpp b/src/server/mods.cpp index 6ac530739..cf1467648 100644 --- a/src/server/mods.cpp +++ b/src/server/mods.cpp @@ -99,10 +99,10 @@ 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) { - paths.push_back(spec.path + DIR_DELIM + "textures"); - paths.push_back(spec.path + DIR_DELIM + "sounds"); - paths.push_back(spec.path + DIR_DELIM + "media"); - paths.push_back(spec.path + DIR_DELIM + "models"); - paths.push_back(spec.path + DIR_DELIM + "locale"); + fs::GetRecursiveDirs(paths, spec.path + DIR_DELIM + "textures"); + fs::GetRecursiveDirs(paths, spec.path + DIR_DELIM + "sounds"); + fs::GetRecursiveDirs(paths, spec.path + DIR_DELIM + "media"); + fs::GetRecursiveDirs(paths, spec.path + DIR_DELIM + "models"); + fs::GetRecursiveDirs(paths, spec.path + DIR_DELIM + "locale"); } } diff --git a/src/server/player_sao.cpp b/src/server/player_sao.cpp index 3d9f08bfa..62515d1c9 100644 --- a/src/server/player_sao.cpp +++ b/src/server/player_sao.cpp @@ -55,6 +55,7 @@ PlayerSAO::PlayerSAO(ServerEnvironment *env_, RemotePlayer *player_, session_t p m_prop.backface_culling = false; m_prop.makes_footstep_sound = true; m_prop.stepheight = PLAYER_DEFAULT_STEPHEIGHT * BS; + m_prop.show_on_minimap = true; m_hp = m_prop.hp_max; m_breath = m_prop.breath_max; // Disable zoom in survival mode using a value of 0 @@ -109,7 +110,7 @@ std::string PlayerSAO::getClientInitializationData(u16 protocol_version) // Protocol >= 15 writeU8(os, 1); // version - os << serializeString(m_player->getName()); // name + os << serializeString16(m_player->getName()); // name writeU8(os, 1); // is_player writeS16(os, getId()); // id writeV3F32(os, m_base_position); @@ -117,22 +118,22 @@ std::string PlayerSAO::getClientInitializationData(u16 protocol_version) writeU16(os, getHP()); std::ostringstream msg_os(std::ios::binary); - msg_os << serializeLongString(getPropertyPacket()); // message 1 - msg_os << serializeLongString(generateUpdateArmorGroupsCommand()); // 2 - msg_os << serializeLongString(generateUpdateAnimationCommand()); // 3 + msg_os << serializeString32(getPropertyPacket()); // message 1 + msg_os << serializeString32(generateUpdateArmorGroupsCommand()); // 2 + msg_os << serializeString32(generateUpdateAnimationCommand()); // 3 for (const auto &bone_pos : m_bone_position) { - msg_os << serializeLongString(generateUpdateBonePositionCommand( - bone_pos.first, bone_pos.second.X, bone_pos.second.Y)); // m_bone_position.size + msg_os << serializeString32(generateUpdateBonePositionCommand( + bone_pos.first, bone_pos.second.X, bone_pos.second.Y)); // 3 + N } - msg_os << serializeLongString(generateUpdateAttachmentCommand()); // 4 - msg_os << serializeLongString(generateUpdatePhysicsOverrideCommand()); // 5 + msg_os << serializeString32(generateUpdateAttachmentCommand()); // 4 + m_bone_position.size + msg_os << serializeString32(generateUpdatePhysicsOverrideCommand()); // 5 + m_bone_position.size int message_count = 5 + m_bone_position.size(); for (const auto &id : getAttachmentChildIds()) { if (ServerActiveObject *obj = m_env->getActiveObject(id)) { message_count++; - msg_os << serializeLongString(obj->generateUpdateInfantCommand( + msg_os << serializeString32(obj->generateUpdateInfantCommand( id, protocol_version)); } } @@ -457,20 +458,25 @@ u16 PlayerSAO::punch(v3f dir, void PlayerSAO::setHP(s32 hp, const PlayerHPChangeReason &reason) { - s32 oldhp = m_hp; + if (hp == (s32)m_hp) + return; // Nothing to do - //hp = rangelim(hp, 0, m_prop.hp_max); + if (m_hp <= 0 && hp < (s32)m_hp) + return; // Cannot take more damage - if (oldhp != hp) { - s32 hp_change = m_env->getScriptIface()->on_player_hpchange(this, hp - oldhp, reason); + { + s32 hp_change = m_env->getScriptIface()->on_player_hpchange(this, hp - m_hp, reason); if (hp_change == 0) return; - hp = rangelim(oldhp + hp_change, 0, m_prop.hp_max); + hp = m_hp + hp_change; } + s32 oldhp = m_hp; + hp = rangelim(hp, 0, m_prop.hp_max); + if (hp < oldhp && isImmortal()) - return; + return; // Do not allow immortal players to be damaged m_hp = hp; @@ -558,11 +564,35 @@ void PlayerSAO::setMaxSpeedOverride(const v3f &vel) bool PlayerSAO::checkMovementCheat() { - if (isAttached() || m_is_singleplayer || + if (m_is_singleplayer || 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/player_sao.h b/src/server/player_sao.h index 8571bd4f9..3e178d4fc 100644 --- a/src/server/player_sao.h +++ b/src/server/player_sao.h @@ -83,6 +83,7 @@ public: void addedToEnvironment(u32 dtime_s); void removingFromEnvironment(); bool isStaticAllowed() const { return false; } + bool shouldUnload() const { return false; } std::string getClientInitializationData(u16 protocol_version); void getStaticData(std::string *result) const; void step(float dtime, bool send_recommended); diff --git a/src/server/serveractiveobject.cpp b/src/server/serveractiveobject.cpp index 3341dc008..8cb59b2d6 100644 --- a/src/server/serveractiveobject.cpp +++ b/src/server/serveractiveobject.cpp @@ -61,7 +61,7 @@ std::string ServerActiveObject::generateUpdateInfantCommand(u16 infant_id, u16 p // Clients since 4aa9a66 so no longer need this data // Version 38 is the first bump after that commit. // See also: ClientEnvironment::addActiveObject - os << serializeLongString(getClientInitializationData(protocol_version)); + os << serializeString32(getClientInitializationData(protocol_version)); } return os.str(); } diff --git a/src/server/serveractiveobject.h b/src/server/serveractiveobject.h index 927009aef..2764d159e 100644 --- a/src/server/serveractiveobject.h +++ b/src/server/serveractiveobject.h @@ -125,6 +125,7 @@ public: assert(isStaticAllowed()); *result = ""; } + /* Return false in here to never save and instead remove object on unload. getStaticData() will not be called in that case. @@ -132,6 +133,14 @@ public: virtual bool isStaticAllowed() const {return true;} + /* + Return false here to never unload the object. + isStaticAllowed && shouldUnload -> unload when out of active block range + !isStaticAllowed && shouldUnload -> unload when block is unloaded + */ + virtual bool shouldUnload() const + { return true; } + // Returns tool wear virtual u16 punch(v3f dir, const ToolCapabilities *toolcap = nullptr, diff --git a/src/server/unit_sao.cpp b/src/server/unit_sao.cpp index ef0e87f2c..2371640ca 100644 --- a/src/server/unit_sao.cpp +++ b/src/server/unit_sao.cpp @@ -121,8 +121,8 @@ void UnitSAO::sendOutdatedData() } // clang-format on -void UnitSAO::setAttachment( - int parent_id, const std::string &bone, v3f position, v3f rotation) +void UnitSAO::setAttachment(int parent_id, const std::string &bone, v3f position, + v3f rotation, bool force_visible) { // Attachments need to be handled on both the server and client. // If we just attach on the server, we can only copy the position of the parent. @@ -137,6 +137,7 @@ void UnitSAO::setAttachment( m_attachment_bone = bone; m_attachment_position = position; m_attachment_rotation = rotation; + m_force_visible = force_visible; m_attachment_sent = false; if (parent_id != old_parent) { @@ -145,13 +146,14 @@ void UnitSAO::setAttachment( } } -void UnitSAO::getAttachment( - int *parent_id, std::string *bone, v3f *position, v3f *rotation) const +void UnitSAO::getAttachment(int *parent_id, std::string *bone, v3f *position, + v3f *rotation, bool *force_visible) const { *parent_id = m_attachment_parent_id; *bone = m_attachment_bone; *position = m_attachment_position; *rotation = m_attachment_rotation; + *force_visible = m_force_visible; } void UnitSAO::clearChildAttachments() @@ -159,7 +161,7 @@ void UnitSAO::clearChildAttachments() for (int child_id : m_attachment_child_ids) { // Child can be NULL if it was deleted earlier if (ServerActiveObject *child = m_env->getActiveObject(child_id)) - child->setAttachment(0, "", v3f(0, 0, 0), v3f(0, 0, 0)); + child->setAttachment(0, "", v3f(0, 0, 0), v3f(0, 0, 0), false); } m_attachment_child_ids.clear(); } @@ -169,9 +171,9 @@ void UnitSAO::clearParentAttachment() ServerActiveObject *parent = nullptr; if (m_attachment_parent_id) { parent = m_env->getActiveObject(m_attachment_parent_id); - setAttachment(0, "", m_attachment_position, m_attachment_rotation); + setAttachment(0, "", m_attachment_position, m_attachment_rotation, false); } else { - setAttachment(0, "", v3f(0, 0, 0), v3f(0, 0, 0)); + setAttachment(0, "", v3f(0, 0, 0), v3f(0, 0, 0), false); } // Do it if (parent) @@ -242,9 +244,10 @@ std::string UnitSAO::generateUpdateAttachmentCommand() const writeU8(os, AO_CMD_ATTACH_TO); // parameters writeS16(os, m_attachment_parent_id); - os << serializeString(m_attachment_bone); + os << serializeString16(m_attachment_bone); writeV3F32(os, m_attachment_position); writeV3F32(os, m_attachment_rotation); + writeU8(os, m_force_visible); return os.str(); } @@ -255,7 +258,7 @@ std::string UnitSAO::generateUpdateBonePositionCommand( // command writeU8(os, AO_CMD_SET_BONE_POSITION); // parameters - os << serializeString(bone); + os << serializeString16(bone); writeV3F32(os, position); writeV3F32(os, rotation); return os.str(); @@ -291,7 +294,7 @@ std::string UnitSAO::generateUpdateArmorGroupsCommand() const writeU8(os, AO_CMD_UPDATE_ARMOR_GROUPS); writeU16(os, m_armor_groups.size()); for (const auto &armor_group : m_armor_groups) { - os << serializeString(armor_group.first); + os << serializeString16(armor_group.first); writeS16(os, armor_group.second); } return os.str(); diff --git a/src/server/unit_sao.h b/src/server/unit_sao.h index 3cb7f0ad5..a21e055c5 100644 --- a/src/server/unit_sao.h +++ b/src/server/unit_sao.h @@ -64,9 +64,9 @@ public: ServerActiveObject *getParent() const; inline bool isAttached() const { return getParent(); } void setAttachment(int parent_id, const std::string &bone, v3f position, - v3f rotation); + v3f rotation, bool force_visible); void getAttachment(int *parent_id, std::string *bone, v3f *position, - v3f *rotation) const; + v3f *rotation, bool *force_visible) const; void clearChildAttachments(); void clearParentAttachment(); void addAttachmentChild(int child_id); @@ -133,4 +133,5 @@ private: v3f m_attachment_position; v3f m_attachment_rotation; bool m_attachment_sent = false; + bool m_force_visible = false; }; diff --git a/src/serverenvironment.cpp b/src/serverenvironment.cpp index ad2ffc9a4..d044b003d 100644 --- a/src/serverenvironment.cpp +++ b/src/serverenvironment.cpp @@ -1066,6 +1066,91 @@ bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n) return true; } +u8 ServerEnvironment::findSunlight(v3s16 pos) const +{ + // Directions for neighbouring nodes with specified order + static const v3s16 dirs[] = { + v3s16(-1, 0, 0), v3s16(1, 0, 0), v3s16(0, 0, -1), v3s16(0, 0, 1), + v3s16(0, -1, 0), v3s16(0, 1, 0) + }; + + const NodeDefManager *ndef = m_server->ndef(); + + // found_light remembers the highest known sunlight value at pos + u8 found_light = 0; + + struct stack_entry { + v3s16 pos; + s16 dist; + }; + std::stack<stack_entry> stack; + stack.push({pos, 0}); + + std::unordered_map<s64, s8> dists; + dists[MapDatabase::getBlockAsInteger(pos)] = 0; + + while (!stack.empty()) { + struct stack_entry e = stack.top(); + stack.pop(); + + v3s16 currentPos = e.pos; + s8 dist = e.dist + 1; + + for (const v3s16& off : dirs) { + v3s16 neighborPos = currentPos + off; + s64 neighborHash = MapDatabase::getBlockAsInteger(neighborPos); + + // Do not walk neighborPos multiple times unless the distance to the start + // position is shorter + auto it = dists.find(neighborHash); + if (it != dists.end() && dist >= it->second) + continue; + + // Position to walk + bool is_position_ok; + MapNode node = m_map->getNode(neighborPos, &is_position_ok); + if (!is_position_ok) { + // This happens very rarely because the map at currentPos is loaded + m_map->emergeBlock(neighborPos, false); + node = m_map->getNode(neighborPos, &is_position_ok); + if (!is_position_ok) + continue; // not generated + } + + const ContentFeatures &def = ndef->get(node); + if (!def.sunlight_propagates) { + // Do not test propagation here again + dists[neighborHash] = -1; + continue; + } + + // Sunlight could have come from here + dists[neighborHash] = dist; + u8 daylight = node.param1 & 0x0f; + + // In the special case where sunlight shines from above and thus + // does not decrease with upwards distance, daylight is always + // bigger than nightlight, which never reaches 15 + int possible_finlight = daylight - dist; + if (possible_finlight <= found_light) { + // Light from here cannot make a brighter light at currentPos than + // found_light + continue; + } + + u8 nightlight = node.param1 >> 4; + if (daylight > nightlight) { + // Found a valid daylight + found_light = possible_finlight; + } else { + // Sunlight may be darker, so walk the neighbours + stack.push({neighborPos, dist}); + } + } + } + return found_light; +} + void ServerEnvironment::clearObjects(ClearObjectsMode mode) { infostream << "ServerEnvironment::clearObjects(): " @@ -1354,8 +1439,8 @@ void ServerEnvironment::step(float dtime) std::shuffle(output.begin(), output.end(), m_rgen); int i = 0; - // The time budget for ABMs is 20%. - u32 max_time_ms = m_cache_abm_interval * 1000 / 5; + // determine the time budget for ABMs + u32 max_time_ms = m_cache_abm_interval * 1000 * m_cache_abm_time_budget; for (const v3s16 &p : output) { MapBlock *block = m_map->getBlockNoCreateNoEx(p); if (!block) @@ -1887,8 +1972,8 @@ void ServerEnvironment::deactivateFarObjects(bool _force_delete) // force_delete might be overriden per object bool force_delete = _force_delete; - // Do not deactivate if static data creation not allowed - if (!force_delete && !obj->isStaticAllowed()) + // Do not deactivate if disallowed + if (!force_delete && !obj->shouldUnload()) return false; // removeRemovedObjects() is responsible for these @@ -1917,7 +2002,10 @@ void ServerEnvironment::deactivateFarObjects(bool _force_delete) } // If block is still active, don't remove - if (!force_delete && m_active_blocks.contains(blockpos_o)) + bool still_active = obj->isStaticAllowed() ? + m_active_blocks.contains(blockpos_o) : + getMap().getBlockNoCreateNoEx(blockpos_o) != nullptr; + if (!force_delete && still_active) return false; verbosestream << "ServerEnvironment::deactivateFarObjects(): " diff --git a/src/serverenvironment.h b/src/serverenvironment.h index af742e290..cfd5b8f3e 100644 --- a/src/serverenvironment.h +++ b/src/serverenvironment.h @@ -322,6 +322,9 @@ public: bool removeNode(v3s16 p); bool swapNode(v3s16 p, const MapNode &n); + // Find the daylight value at pos with a Depth First Search + u8 findSunlight(v3s16 pos) const; + // Find all active objects inside a radius around a point void getObjectsInsideRadius(std::vector<ServerActiveObject *> &objects, const v3f &pos, float radius, std::function<bool(ServerActiveObject *obj)> include_obj_cb) diff --git a/src/serverlist.cpp b/src/serverlist.cpp index 18264e933..80a8c2f1a 100644 --- a/src/serverlist.cpp +++ b/src/serverlist.cpp @@ -52,15 +52,7 @@ std::vector<ServerListSpec> getLocal() { std::string path = ServerList::getFilePath(); std::string liststring; - if (fs::PathExists(path)) { - std::ifstream istream(path.c_str()); - if (istream.is_open()) { - std::ostringstream ostream; - ostream << istream.rdbuf(); - liststring = ostream.str(); - istream.close(); - } - } + fs::ReadFile(path, liststring); return deSerialize(liststring); } @@ -253,19 +245,26 @@ void sendAnnounce(AnnounceAction action, for (const ModSpec &mod : mods) { server["mods"].append(mod.name); } - actionstream << "Announcing to " << g_settings->get("serverlist_url") << std::endl; } else if (action == AA_UPDATE) { if (lag) server["lag"] = lag; } + if (action == AA_START) { + actionstream << "Announcing " << aa_names[action] << " to " << + g_settings->get("serverlist_url") << std::endl; + } else { + infostream << "Announcing " << aa_names[action] << " to " << + g_settings->get("serverlist_url") << std::endl; + } + HTTPFetchRequest fetch_request; fetch_request.url = g_settings->get("serverlist_url") + std::string("/announce"); - fetch_request.post_fields["json"] = fastWriteJson(server); + fetch_request.method = HTTP_POST; + fetch_request.fields["json"] = fastWriteJson(server); fetch_request.multipart = true; httpfetch_async(fetch_request); } #endif } // namespace ServerList - diff --git a/src/settings.cpp b/src/settings.cpp index 55404319e..f30ef34e9 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -45,7 +45,13 @@ Settings::~Settings() Settings & Settings::operator += (const Settings &other) { - update(other); + if (&other == this) + return *this; + + MutexAutoLock lock(m_mutex); + MutexAutoLock lock2(other.m_mutex); + + updateNoLock(other); return *this; } @@ -512,25 +518,6 @@ u32 Settings::getFlagStr(const std::string &name, const FlagDesc *flagdesc, return flags; } -// N.B. if getStruct() is used to read a non-POD aggregate type, -// the behavior is undefined. -bool Settings::getStruct(const std::string &name, const std::string &format, - void *out, size_t olen) const -{ - std::string valstr; - - try { - valstr = get(name); - } catch (SettingNotFoundException &e) { - return false; - } - - if (!deSerializeStringToStruct(valstr, format, out, olen)) - return false; - - return true; -} - bool Settings::getNoiseParams(const std::string &name, NoiseParams &np) const { @@ -546,6 +533,7 @@ bool Settings::getNoiseParamsFromValue(const std::string &name, if (!getNoEx(name, value)) return false; + // Format: f32,f32,(f32,f32,f32),s32,s32,f32[,f32] Strfnd f(value); np.offset = stof(f.next(",")); @@ -615,28 +603,6 @@ std::vector<std::string> Settings::getNames() const * Getters that don't throw exceptions * ***************************************/ -bool Settings::getEntryNoEx(const std::string &name, SettingsEntry &val) const -{ - try { - val = getEntry(name); - return true; - } catch (SettingNotFoundException &e) { - return false; - } -} - - -bool Settings::getEntryDefaultNoEx(const std::string &name, SettingsEntry &val) const -{ - try { - val = getEntryDefault(name); - return true; - } catch (SettingNotFoundException &e) { - return false; - } -} - - bool Settings::getGroupNoEx(const std::string &name, Settings *&val) const { try { @@ -822,15 +788,23 @@ bool Settings::setDefault(const std::string &name, const std::string &value) } -bool Settings::setGroup(const std::string &name, Settings *group) +bool Settings::setGroup(const std::string &name, const Settings &group) { - return setEntry(name, &group, true, false); + // Settings must own the group pointer + // avoid double-free by copying the source + Settings *copy = new Settings(); + *copy = group; + return setEntry(name, ©, true, false); } -bool Settings::setGroupDefault(const std::string &name, Settings *group) +bool Settings::setGroupDefault(const std::string &name, const Settings &group) { - return setEntry(name, &group, true, true); + // Settings must own the group pointer + // avoid double-free by copying the source + Settings *copy = new Settings(); + *copy = group; + return setEntry(name, ©, true, true); } @@ -900,17 +874,6 @@ bool Settings::setFlagStr(const std::string &name, u32 flags, } -bool Settings::setStruct(const std::string &name, const std::string &format, - void *value) -{ - std::string structstr; - if (!serializeStructToString(&structstr, format, value)) - return false; - - return set(name, structstr); -} - - bool Settings::setNoiseParams(const std::string &name, const NoiseParams &np, bool set_default) { @@ -961,32 +924,6 @@ void Settings::clearDefaults() clearDefaultsNoLock(); } -void Settings::updateValue(const Settings &other, const std::string &name) -{ - if (&other == this) - return; - - MutexAutoLock lock(m_mutex); - - try { - m_settings[name] = other.get(name); - } catch (SettingNotFoundException &e) { - } -} - - -void Settings::update(const Settings &other) -{ - if (&other == this) - return; - - MutexAutoLock lock(m_mutex); - MutexAutoLock lock2(other.m_mutex); - - updateNoLock(other); -} - - SettingsParseEvent Settings::parseConfigObject(const std::string &line, const std::string &end, std::string &name, std::string &value) { @@ -1052,7 +989,7 @@ void Settings::overrideDefaults(Settings *other) { for (const auto &setting : other->m_settings) { if (setting.second.is_group) { - setGroupDefault(setting.first, setting.second.group); + setGroupDefault(setting.first, *setting.second.group); continue; } const FlagDesc *flagdesc = getFlagDescFallback(setting.first); @@ -1102,6 +1039,19 @@ void Settings::deregisterChangedCallback(const std::string &name, } } +void Settings::removeSecureSettings() +{ + for (const auto &name : getNames()) { + if (name.compare(0, 7, "secure.") != 0) + continue; + + errorstream << "Secure setting " << name + << " isn't allowed, so was ignored." + << std::endl; + remove(name); + } +} + void Settings::doCallbacks(const std::string &name) const { MutexAutoLock lock(m_callback_mutex); diff --git a/src/settings.h b/src/settings.h index 0c9a155db..6db2f9481 100644 --- a/src/settings.h +++ b/src/settings.h @@ -113,23 +113,10 @@ public: bool parseConfigLines(std::istream &is, const std::string &end = ""); void writeLines(std::ostream &os, u32 tab_depth=0) const; - SettingsParseEvent parseConfigObject(const std::string &line, - const std::string &end, std::string &name, std::string &value); - bool updateConfigObject(std::istream &is, std::ostream &os, - const std::string &end, u32 tab_depth=0); - - static bool checkNameValid(const std::string &name); - static bool checkValueValid(const std::string &value); - static std::string getMultiline(std::istream &is, size_t *num_lines=NULL); - static void printEntry(std::ostream &os, const std::string &name, - const SettingsEntry &entry, u32 tab_depth=0); - /*********** * Getters * ***********/ - const SettingsEntry &getEntry(const std::string &name) const; - const SettingsEntry &getEntryDefault(const std::string &name) const; Settings *getGroup(const std::string &name) const; const std::string &get(const std::string &name) const; const std::string &getDefault(const std::string &name) const; @@ -144,10 +131,6 @@ public: v3f getV3F(const std::string &name) const; u32 getFlagStr(const std::string &name, const FlagDesc *flagdesc, u32 *flagmask) const; - // N.B. if getStruct() is used to read a non-POD aggregate type, - // the behavior is undefined. - bool getStruct(const std::string &name, const std::string &format, - void *out, size_t olen) const; bool getNoiseParams(const std::string &name, NoiseParams &np) const; bool getNoiseParamsFromValue(const std::string &name, NoiseParams &np) const; bool getNoiseParamsFromGroup(const std::string &name, NoiseParams &np) const; @@ -161,8 +144,6 @@ public: * Getters that don't throw exceptions * ***************************************/ - bool getEntryNoEx(const std::string &name, SettingsEntry &val) const; - bool getEntryDefaultNoEx(const std::string &name, SettingsEntry &val) const; bool getGroupNoEx(const std::string &name, Settings *&val) const; bool getNoEx(const std::string &name, std::string &val) const; bool getDefaultNoEx(const std::string &name, std::string &val) const; @@ -192,8 +173,8 @@ public: bool set_group, bool set_default); bool set(const std::string &name, const std::string &value); bool setDefault(const std::string &name, const std::string &value); - bool setGroup(const std::string &name, Settings *group); - bool setGroupDefault(const std::string &name, Settings *group); + bool setGroup(const std::string &name, const Settings &group); + bool setGroupDefault(const std::string &name, const Settings &group); bool setBool(const std::string &name, bool value); bool setS16(const std::string &name, s16 value); bool setU16(const std::string &name, u16 value); @@ -206,16 +187,11 @@ public: const FlagDesc *flagdesc = nullptr, u32 flagmask = U32_MAX); bool setNoiseParams(const std::string &name, const NoiseParams &np, bool set_default=false); - // N.B. if setStruct() is used to write a non-POD aggregate type, - // the behavior is undefined. - bool setStruct(const std::string &name, const std::string &format, void *value); // remove a setting bool remove(const std::string &name); void clear(); void clearDefaults(); - void updateValue(const Settings &other, const std::string &name); - void update(const Settings &other); /************** * Miscellany * @@ -231,7 +207,34 @@ public: void deregisterChangedCallback(const std::string &name, SettingsChangedCallback cbf, void *userdata = NULL); + void removeSecureSettings(); + private: + /*********************** + * Reading and writing * + ***********************/ + + SettingsParseEvent parseConfigObject(const std::string &line, + const std::string &end, std::string &name, std::string &value); + bool updateConfigObject(std::istream &is, std::ostream &os, + const std::string &end, u32 tab_depth=0); + + static bool checkNameValid(const std::string &name); + static bool checkValueValid(const std::string &value); + static std::string getMultiline(std::istream &is, size_t *num_lines=NULL); + static void printEntry(std::ostream &os, const std::string &name, + const SettingsEntry &entry, u32 tab_depth=0); + + /*********** + * Getters * + ***********/ + + const SettingsEntry &getEntry(const std::string &name) const; + const SettingsEntry &getEntryDefault(const std::string &name) const; + + // Allow TestSettings to run sanity checks using private functions. + friend class TestSettings; + void updateNoLock(const Settings &other); void clearNoLock(); void clearDefaultsNoLock(); diff --git a/src/settings_translation_file.cpp b/src/settings_translation_file.cpp index febfbb9d3..0cd772337 100644 --- a/src/settings_translation_file.cpp +++ b/src/settings_translation_file.cpp @@ -242,13 +242,7 @@ fake_function() { gettext("Enables Hable's 'Uncharted 2' filmic tone mapping.\nSimulates the tone curve of photographic film and how this approximates the\nappearance of high dynamic range images. Mid-range contrast is slightly\nenhanced, highlights and shadows are gradually compressed."); gettext("Bumpmapping"); gettext("Bumpmapping"); - gettext("Enables bumpmapping for textures. Normalmaps need to be supplied by the texture pack\nor need to be auto-generated.\nRequires shaders to be enabled."); - gettext("Generate normalmaps"); - gettext("Enables on the fly normalmap generation (Emboss effect).\nRequires bumpmapping to be enabled."); - gettext("Normalmaps strength"); - gettext("Strength of generated normalmaps."); - gettext("Normalmaps sampling"); - gettext("Defines sampling step of texture.\nA higher value results in smoother normal maps."); + gettext("Enables bumpmapping for textures. Normalmaps need to be supplied by the texture pack.\nRequires shaders to be enabled."); gettext("Parallax Occlusion"); gettext("Parallax occlusion"); gettext("Enables parallax occlusion mapping.\nRequires shaders to be enabled."); diff --git a/src/sound.h b/src/sound.h index 6cbd55e8f..6f7b0a1af 100644 --- a/src/sound.h +++ b/src/sound.h @@ -39,7 +39,7 @@ struct SimpleSoundSpec // keep in sync with item definitions void serialize(std::ostream &os, u8 cf_version) const { - os << serializeString(name); + os << serializeString16(name); writeF32(os, gain); writeF32(os, pitch); writeF32(os, fade); @@ -49,7 +49,7 @@ struct SimpleSoundSpec void deSerialize(std::istream &is, u8 cf_version) { - name = deSerializeString(is); + name = deSerializeString16(is); gain = readF32(is); pitch = readF32(is); fade = readF32(is); diff --git a/src/staticobject.cpp b/src/staticobject.cpp index 5ccb7baf5..86e455b9f 100644 --- a/src/staticobject.cpp +++ b/src/staticobject.cpp @@ -35,7 +35,7 @@ void StaticObject::serialize(std::ostream &os) // pos writeV3F1000(os, pos); // data - os<<serializeString(data); + os<<serializeString16(data); } void StaticObject::deSerialize(std::istream &is, u8 version) { @@ -44,7 +44,7 @@ void StaticObject::deSerialize(std::istream &is, u8 version) // pos pos = readV3F1000(is); // data - data = deSerializeString(is); + data = deSerializeString16(is); } void StaticObjectList::serialize(std::ostream &os) diff --git a/src/texture_override.cpp b/src/texture_override.cpp index 10d129b87..effdb0efd 100644 --- a/src/texture_override.cpp +++ b/src/texture_override.cpp @@ -24,6 +24,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include <algorithm> #include <fstream> +#define override_cast static_cast<override_t> + TextureOverrideSource::TextureOverrideSource(std::string filepath) { std::ifstream infile(filepath.c_str()); @@ -56,25 +58,37 @@ TextureOverrideSource::TextureOverrideSource(std::string filepath) std::vector<std::string> targets = str_split(splitted[1], ','); for (const std::string &target : targets) { if (target == "top") - texture_override.target |= static_cast<u8>(OverrideTarget::TOP); + texture_override.target |= override_cast(OverrideTarget::TOP); else if (target == "bottom") - texture_override.target |= static_cast<u8>(OverrideTarget::BOTTOM); + texture_override.target |= override_cast(OverrideTarget::BOTTOM); else if (target == "left") - texture_override.target |= static_cast<u8>(OverrideTarget::LEFT); + texture_override.target |= override_cast(OverrideTarget::LEFT); else if (target == "right") - texture_override.target |= static_cast<u8>(OverrideTarget::RIGHT); + texture_override.target |= override_cast(OverrideTarget::RIGHT); else if (target == "front") - texture_override.target |= static_cast<u8>(OverrideTarget::FRONT); + texture_override.target |= override_cast(OverrideTarget::FRONT); else if (target == "back") - texture_override.target |= static_cast<u8>(OverrideTarget::BACK); + texture_override.target |= override_cast(OverrideTarget::BACK); else if (target == "inventory") - texture_override.target |= static_cast<u8>(OverrideTarget::INVENTORY); + texture_override.target |= override_cast(OverrideTarget::INVENTORY); else if (target == "wield") - texture_override.target |= static_cast<u8>(OverrideTarget::WIELD); + texture_override.target |= override_cast(OverrideTarget::WIELD); + else if (target == "special1") + texture_override.target |= override_cast(OverrideTarget::SPECIAL_1); + else if (target == "special2") + texture_override.target |= override_cast(OverrideTarget::SPECIAL_2); + else if (target == "special3") + texture_override.target |= override_cast(OverrideTarget::SPECIAL_3); + else if (target == "special4") + texture_override.target |= override_cast(OverrideTarget::SPECIAL_4); + else if (target == "special5") + texture_override.target |= override_cast(OverrideTarget::SPECIAL_5); + else if (target == "special6") + texture_override.target |= override_cast(OverrideTarget::SPECIAL_6); else if (target == "sides") - texture_override.target |= static_cast<u8>(OverrideTarget::SIDES); + texture_override.target |= override_cast(OverrideTarget::SIDES); else if (target == "all" || target == "*") - texture_override.target |= static_cast<u8>(OverrideTarget::ALL_FACES); + texture_override.target |= override_cast(OverrideTarget::ALL_FACES); else { // Report invalid target warningstream << filepath << ":" << line_index @@ -85,7 +99,7 @@ TextureOverrideSource::TextureOverrideSource(std::string filepath) } // If there are no valid targets, skip adding this override - if (texture_override.target == static_cast<u8>(OverrideTarget::INVALID)) { + if (texture_override.target == override_cast(OverrideTarget::INVALID)) { continue; } @@ -112,7 +126,7 @@ std::vector<TextureOverride> TextureOverrideSource::getNodeTileOverrides() std::vector<TextureOverride> found_overrides; for (const TextureOverride &texture_override : m_overrides) { - if (texture_override.hasTarget(OverrideTarget::ALL_FACES)) + if (texture_override.hasTarget(OverrideTarget::NODE_TARGETS)) found_overrides.push_back(texture_override); } diff --git a/src/texture_override.h b/src/texture_override.h index db03bd014..bdc95e732 100644 --- a/src/texture_override.h +++ b/src/texture_override.h @@ -23,8 +23,10 @@ with this program; if not, write to the Free Software Foundation, Inc., #include <string> #include <vector> +typedef u16 override_t; + //! Bitmask enum specifying what a texture override should apply to -enum class OverrideTarget : u8 +enum class OverrideTarget : override_t { INVALID = 0, TOP = 1 << 0, @@ -35,23 +37,33 @@ enum class OverrideTarget : u8 BACK = 1 << 5, INVENTORY = 1 << 6, WIELD = 1 << 7, + SPECIAL_1 = 1 << 8, + SPECIAL_2 = 1 << 9, + SPECIAL_3 = 1 << 10, + SPECIAL_4 = 1 << 11, + SPECIAL_5 = 1 << 12, + SPECIAL_6 = 1 << 13, + // clang-format off SIDES = LEFT | RIGHT | FRONT | BACK, ALL_FACES = TOP | BOTTOM | SIDES, + ALL_SPECIAL = SPECIAL_1 | SPECIAL_2 | SPECIAL_3 | SPECIAL_4 | SPECIAL_5 | SPECIAL_6, + NODE_TARGETS = ALL_FACES | ALL_SPECIAL, ITEM_TARGETS = INVENTORY | WIELD, + // clang-format on }; struct TextureOverride { std::string id; std::string texture; - u8 target; + override_t target; // Helper function for checking if an OverrideTarget is found in // a TextureOverride without casting inline bool hasTarget(OverrideTarget overrideTarget) const { - return (target & static_cast<u8>(overrideTarget)) != 0; + return (target & static_cast<override_t>(overrideTarget)) != 0; } }; diff --git a/src/threading/thread.cpp b/src/threading/thread.cpp index e0f808c4d..5cfc60995 100644 --- a/src/threading/thread.cpp +++ b/src/threading/thread.cpp @@ -33,6 +33,8 @@ DEALINGS IN THE SOFTWARE. #include <sys/prctl.h> #elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) #include <pthread_np.h> +#elif defined(__NetBSD__) + #include <sched.h> #elif defined(_MSC_VER) struct THREADNAME_INFO { DWORD dwType; // Must be 0x1000 @@ -71,7 +73,28 @@ Thread::Thread(const std::string &name) : Thread::~Thread() { - kill(); + // kill the thread if running + if (!m_running) { + wait(); + } else { + + m_running = false; + +#if defined(_WIN32) + // See https://msdn.microsoft.com/en-us/library/hh920601.aspx#thread__native_handle_method + TerminateThread((HANDLE) m_thread_obj->native_handle(), 0); + CloseHandle((HANDLE) m_thread_obj->native_handle()); +#else + // We need to pthread_kill instead on Android since NDKv5's pthread + // implementation is incomplete. +# ifdef __ANDROID__ + pthread_kill(getThreadHandle(), SIGKILL); +# else + pthread_cancel(getThreadHandle()); +# endif + wait(); +#endif + } // Make sure start finished mutex is unlocked before it's destroyed if (m_start_finished_mutex.try_lock()) @@ -136,37 +159,6 @@ bool Thread::wait() } -bool Thread::kill() -{ - if (!m_running) { - wait(); - return false; - } - - m_running = false; - -#if defined(_WIN32) - // See https://msdn.microsoft.com/en-us/library/hh920601.aspx#thread__native_handle_method - TerminateThread((HANDLE) m_thread_obj->native_handle(), 0); - CloseHandle((HANDLE) m_thread_obj->native_handle()); -#else - // We need to pthread_kill instead on Android since NDKv5's pthread - // implementation is incomplete. -# ifdef __ANDROID__ - pthread_kill(getThreadHandle(), SIGKILL); -# else - pthread_cancel(getThreadHandle()); -# endif - wait(); -#endif - - m_retval = nullptr; - m_joinable = false; - m_request_stop = false; - - return true; -} - bool Thread::getReturnValue(void **ret) { @@ -219,12 +211,16 @@ void Thread::setName(const std::string &name) #elif defined(__NetBSD__) - pthread_setname_np(pthread_self(), name.c_str()); + pthread_setname_np(pthread_self(), "%s", const_cast<char*>(name.c_str())); #elif defined(__APPLE__) pthread_setname_np(name.c_str()); +#elif defined(__HAIKU__) + + rename_thread(find_thread(NULL), name.c_str()); + #elif defined(_MSC_VER) // Windows itself doesn't support thread names, @@ -281,7 +277,14 @@ bool Thread::bindToProcessor(unsigned int proc_number) CPU_SET(proc_number, &cpuset); return pthread_setaffinity_np(getThreadHandle(), sizeof(cpuset), &cpuset) == 0; +#elif defined(__NetBSD__) + cpuset_t *cpuset = cpuset_create(); + if (cpuset == NULL) + return false; + int r = pthread_setaffinity_np(getThreadHandle(), cpuset_size(cpuset), cpuset); + cpuset_destroy(cpuset); + return r == 0; #elif defined(__sun) || defined(sun) return processor_bind(P_LWPID, P_MYID, proc_number, NULL) == 0 diff --git a/src/threading/thread.h b/src/threading/thread.h index cea92226f..45fb171da 100644 --- a/src/threading/thread.h +++ b/src/threading/thread.h @@ -36,6 +36,10 @@ DEALINGS IN THE SOFTWARE. #include <sys/thread.h> // for tid_t #endif +#ifdef __HAIKU__ + #include <kernel/OS.h> +#endif + /* * On platforms using pthreads, these five priority classes correlate to * even divisions between the minimum and maximum reported thread priority. @@ -71,14 +75,6 @@ public: bool stop(); /* - * Immediately terminates the thread. - * This should be used with extreme caution, as the thread will not have - * any opportunity to release resources it may be holding (such as memory - * or locks). - */ - bool kill(); - - /* * Waits for thread to finish. * Note: This does not stop a thread, you have to do this on your own. * Returns false immediately if the thread is not started or has been waited diff --git a/src/tool.cpp b/src/tool.cpp index 22e41d28e..90f4f9c12 100644 --- a/src/tool.cpp +++ b/src/tool.cpp @@ -66,7 +66,7 @@ void ToolCapabilities::serialize(std::ostream &os, u16 protocol_version) const for (const auto &groupcap : groupcaps) { const std::string *name = &groupcap.first; const ToolGroupCap *cap = &groupcap.second; - os << serializeString(*name); + os << serializeString16(*name); writeS16(os, cap->uses); writeS16(os, cap->maxlevel); writeU32(os, cap->times.size()); @@ -79,7 +79,7 @@ void ToolCapabilities::serialize(std::ostream &os, u16 protocol_version) const writeU32(os, damageGroups.size()); for (const auto &damageGroup : damageGroups) { - os << serializeString(damageGroup.first); + os << serializeString16(damageGroup.first); writeS16(os, damageGroup.second); } @@ -98,7 +98,7 @@ void ToolCapabilities::deSerialize(std::istream &is) groupcaps.clear(); u32 groupcaps_size = readU32(is); for (u32 i = 0; i < groupcaps_size; i++) { - std::string name = deSerializeString(is); + std::string name = deSerializeString16(is); ToolGroupCap cap; cap.uses = readS16(is); cap.maxlevel = readS16(is); @@ -113,7 +113,7 @@ void ToolCapabilities::deSerialize(std::istream &is) u32 damage_groups_size = readU32(is); for (u32 i = 0; i < damage_groups_size; i++) { - std::string name = deSerializeString(is); + std::string name = deSerializeString16(is); s16 rating = readS16(is); damageGroups[name] = rating; } diff --git a/src/translation.cpp b/src/translation.cpp index 8bbaee0a3..82e813a5d 100644 --- a/src/translation.cpp +++ b/src/translation.cpp @@ -29,14 +29,6 @@ Translations client_translations; Translations *g_client_translations = &client_translations; #endif -// Per language server translations -std::unordered_map<std::string,Translations> server_translations; -std::unordered_map<std::string,Translations> *g_server_translations = &server_translations; - -Translations::~Translations() -{ - clear(); -} void Translations::clear() { diff --git a/src/translation.h b/src/translation.h index 71423b15e..f1a336fca 100644 --- a/src/translation.h +++ b/src/translation.h @@ -23,7 +23,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include <string> class Translations; -extern std::unordered_map<std::string, Translations> *g_server_translations; #ifndef SERVER extern Translations *g_client_translations; #endif @@ -31,10 +30,6 @@ extern Translations *g_client_translations; class Translations { public: - Translations() = default; - - ~Translations(); - void loadTranslation(const std::string &data); void clear(); const std::wstring &getTranslation( diff --git a/src/unittest/test_map_settings_manager.cpp b/src/unittest/test_map_settings_manager.cpp index 0a5c971b9..3d642a9b4 100644 --- a/src/unittest/test_map_settings_manager.cpp +++ b/src/unittest/test_map_settings_manager.cpp @@ -65,31 +65,6 @@ void check_noise_params(const NoiseParams *np1, const NoiseParams *np2) } -std::string read_file_to_string(const std::string &filepath) -{ - std::string buf; - FILE *f = fopen(filepath.c_str(), "rb"); - if (!f) - return ""; - - fseek(f, 0, SEEK_END); - - long filesize = ftell(f); - if (filesize == -1) { - fclose(f); - return ""; - } - rewind(f); - - buf.resize(filesize); - - UASSERTEQ(size_t, fread(&buf[0], 1, filesize, f), 1); - - fclose(f); - return buf; -} - - void TestMapSettingsManager::makeUserConfig(Settings *conf) { conf->set("mg_name", "v7"); @@ -199,7 +174,8 @@ void TestMapSettingsManager::testMapSettingsManager() }; SHA1 ctx; - std::string metafile_contents = read_file_to_string(test_mapmeta_path); + std::string metafile_contents; + UASSERT(fs::ReadFile(test_mapmeta_path, metafile_contents)); ctx.addBytes(&metafile_contents[0], metafile_contents.size()); unsigned char *sha1_result = ctx.getDigest(); int resultdiff = memcmp(sha1_result, expected_contents_hash, 20); diff --git a/src/unittest/test_serialization.cpp b/src/unittest/test_serialization.cpp index 51e28f144..660d77d02 100644 --- a/src/unittest/test_serialization.cpp +++ b/src/unittest/test_serialization.cpp @@ -32,25 +32,19 @@ public: void buildTestStrings(); void testSerializeString(); - void testSerializeWideString(); void testSerializeLongString(); void testSerializeJsonString(); - void testSerializeHex(); void testDeSerializeString(); - void testDeSerializeWideString(); void testDeSerializeLongString(); void testStreamRead(); void testStreamWrite(); - void testVecPut(); - void testStringLengthLimits(); - void testBufReader(); void testFloatFormat(); std::string teststring2; std::wstring teststring2_w; std::string teststring2_w_encoded; - static const u8 test_serialized_data[12 * 13 - 8]; + static const u8 test_serialized_data[12 * 11 - 2]; }; static TestSerialization g_test_instance; @@ -61,17 +55,11 @@ void TestSerialization::runTests(IGameDef *gamedef) TEST(testSerializeString); TEST(testDeSerializeString); - TEST(testSerializeWideString); - TEST(testDeSerializeWideString); TEST(testSerializeLongString); TEST(testDeSerializeLongString); TEST(testSerializeJsonString); - TEST(testSerializeHex); TEST(testStreamRead); TEST(testStreamWrite); - TEST(testVecPut); - TEST(testStringLengthLimits); - TEST(testBufReader); TEST(testFloatFormat); } @@ -103,21 +91,21 @@ void TestSerialization::buildTestStrings() void TestSerialization::testSerializeString() { // Test blank string - UASSERT(serializeString("") == mkstr("\0\0")); + UASSERT(serializeString16("") == mkstr("\0\0")); // Test basic string - UASSERT(serializeString("Hello world!") == mkstr("\0\14Hello world!")); + UASSERT(serializeString16("Hello world!") == mkstr("\0\14Hello world!")); // Test character range - UASSERT(serializeString(teststring2) == mkstr("\1\0") + teststring2); + UASSERT(serializeString16(teststring2) == mkstr("\1\0") + teststring2); } void TestSerialization::testDeSerializeString() { // Test deserialize { - std::istringstream is(serializeString(teststring2), std::ios::binary); - UASSERT(deSerializeString(is) == teststring2); + std::istringstream is(serializeString16(teststring2), std::ios::binary); + UASSERT(deSerializeString16(is) == teststring2); UASSERT(!is.eof()); is.get(); UASSERT(is.eof()); @@ -126,78 +114,34 @@ void TestSerialization::testDeSerializeString() // Test deserialize an incomplete length specifier { std::istringstream is(mkstr("\x53"), std::ios::binary); - EXCEPTION_CHECK(SerializationError, deSerializeString(is)); + EXCEPTION_CHECK(SerializationError, deSerializeString16(is)); } // Test deserialize a string with incomplete data { std::istringstream is(mkstr("\x00\x55 abcdefg"), std::ios::binary); - EXCEPTION_CHECK(SerializationError, deSerializeString(is)); - } -} - -void TestSerialization::testSerializeWideString() -{ - // Test blank string - UASSERT(serializeWideString(L"") == mkstr("\0\0")); - - // Test basic string - UASSERT(serializeWideString(utf8_to_wide("Hello world!")) == - mkstr("\0\14\0H\0e\0l\0l\0o\0 \0w\0o\0r\0l\0d\0!")); - - // Test character range - UASSERT(serializeWideString(teststring2_w) == - mkstr("\1\0") + teststring2_w_encoded); -} - -void TestSerialization::testDeSerializeWideString() -{ - // Test deserialize - { - std::istringstream is(serializeWideString(teststring2_w), std::ios::binary); - UASSERT(deSerializeWideString(is) == teststring2_w); - UASSERT(!is.eof()); - is.get(); - UASSERT(is.eof()); - } - - // Test deserialize an incomplete length specifier - { - std::istringstream is(mkstr("\x53"), std::ios::binary); - EXCEPTION_CHECK(SerializationError, deSerializeWideString(is)); - } - - // Test deserialize a string with an incomplete character - { - std::istringstream is(mkstr("\x00\x07\0a\0b\0c\0d\0e\0f\0"), std::ios::binary); - EXCEPTION_CHECK(SerializationError, deSerializeWideString(is)); - } - - // Test deserialize a string with incomplete data - { - std::istringstream is(mkstr("\x00\x08\0a\0b\0c\0d\0e\0f"), std::ios::binary); - EXCEPTION_CHECK(SerializationError, deSerializeWideString(is)); + EXCEPTION_CHECK(SerializationError, deSerializeString16(is)); } } void TestSerialization::testSerializeLongString() { // Test blank string - UASSERT(serializeLongString("") == mkstr("\0\0\0\0")); + UASSERT(serializeString32("") == mkstr("\0\0\0\0")); // Test basic string - UASSERT(serializeLongString("Hello world!") == mkstr("\0\0\0\14Hello world!")); + UASSERT(serializeString32("Hello world!") == mkstr("\0\0\0\14Hello world!")); // Test character range - UASSERT(serializeLongString(teststring2) == mkstr("\0\0\1\0") + teststring2); + UASSERT(serializeString32(teststring2) == mkstr("\0\0\1\0") + teststring2); } void TestSerialization::testDeSerializeLongString() { // Test deserialize { - std::istringstream is(serializeLongString(teststring2), std::ios::binary); - UASSERT(deSerializeLongString(is) == teststring2); + std::istringstream is(serializeString32(teststring2), std::ios::binary); + UASSERT(deSerializeString32(is) == teststring2); UASSERT(!is.eof()); is.get(); UASSERT(is.eof()); @@ -206,19 +150,19 @@ void TestSerialization::testDeSerializeLongString() // Test deserialize an incomplete length specifier { std::istringstream is(mkstr("\x53"), std::ios::binary); - EXCEPTION_CHECK(SerializationError, deSerializeLongString(is)); + EXCEPTION_CHECK(SerializationError, deSerializeString32(is)); } // Test deserialize a string with incomplete data { std::istringstream is(mkstr("\x00\x00\x00\x05 abc"), std::ios::binary); - EXCEPTION_CHECK(SerializationError, deSerializeLongString(is)); + EXCEPTION_CHECK(SerializationError, deSerializeString32(is)); } // Test deserialize a string with a length too large { std::istringstream is(mkstr("\xFF\xFF\xFF\xFF blah"), std::ios::binary); - EXCEPTION_CHECK(SerializationError, deSerializeLongString(is)); + EXCEPTION_CHECK(SerializationError, deSerializeString32(is)); } } @@ -268,25 +212,6 @@ void TestSerialization::testSerializeJsonString() UASSERT(is.eof()); } -void TestSerialization::testSerializeHex() -{ - // Test blank string - UASSERT(serializeHexString("") == ""); - UASSERT(serializeHexString("", true) == ""); - - // Test basic string - UASSERT(serializeHexString("Hello world!") == - "48656c6c6f20776f726c6421"); - UASSERT(serializeHexString("Hello world!", true) == - "48 65 6c 6c 6f 20 77 6f 72 6c 64 21"); - - // Test binary string - UASSERT(serializeHexString(mkstr("\x00\x0a\xb0\x63\x1f\x00\xff")) == - "000ab0631f00ff"); - UASSERT(serializeHexString(mkstr("\x00\x0a\xb0\x63\x1f\x00\xff"), true) == - "00 0a b0 63 1f 00 ff"); -} - void TestSerialization::testStreamRead() { @@ -310,19 +235,17 @@ void TestSerialization::testStreamRead() UASSERT(readF1000(is) == F1000_MIN); UASSERT(readF1000(is) == F1000_MAX); - UASSERT(deSerializeString(is) == "foobar!"); + UASSERT(deSerializeString16(is) == "foobar!"); UASSERT(readV2S16(is) == v2s16(500, 500)); UASSERT(readV3S16(is) == v3s16(4207, 604, -30)); UASSERT(readV2S32(is) == v2s32(1920, 1080)); UASSERT(readV3S32(is) == v3s32(-400, 6400054, 290549855)); - UASSERT(deSerializeWideString(is) == L"\x02~woof~\x5455"); - UASSERT(readV3F1000(is) == v3f(500, 10024.2f, -192.54f)); UASSERT(readARGB8(is) == video::SColor(255, 128, 50, 128)); - UASSERT(deSerializeLongString(is) == "some longer string here"); + UASSERT(deSerializeString32(is) == "some longer string here"); UASSERT(is.rdbuf()->in_avail() == 2); UASSERT(readU16(is) == 0xF00D); @@ -350,7 +273,7 @@ void TestSerialization::testStreamWrite() writeF1000(os, F1000_MIN); writeF1000(os, F1000_MAX); - os << serializeString("foobar!"); + os << serializeString16("foobar!"); data = os.str(); UASSERT(data.size() < sizeof(test_serialized_data)); @@ -361,12 +284,10 @@ void TestSerialization::testStreamWrite() writeV2S32(os, v2s32(1920, 1080)); writeV3S32(os, v3s32(-400, 6400054, 290549855)); - os << serializeWideString(L"\x02~woof~\x5455"); - writeV3F1000(os, v3f(500, 10024.2f, -192.54f)); writeARGB8(os, video::SColor(255, 128, 50, 128)); - os << serializeLongString("some longer string here"); + os << serializeString32("some longer string here"); writeU16(os, 0xF00D); @@ -376,255 +297,6 @@ void TestSerialization::testStreamWrite() } -void TestSerialization::testVecPut() -{ - std::vector<u8> buf; - - putU8(&buf, 0x11); - putU16(&buf, 0x2233); - putU32(&buf, 0x44556677); - putU64(&buf, 0x8899AABBCCDDEEFFLL); - - putS8(&buf, -128); - putS16(&buf, 30000); - putS32(&buf, -6); - putS64(&buf, -43); - - putF1000(&buf, 53.53467f); - putF1000(&buf, -300000.32f); - putF1000(&buf, F1000_MIN); - putF1000(&buf, F1000_MAX); - - putString(&buf, "foobar!"); - - putV2S16(&buf, v2s16(500, 500)); - putV3S16(&buf, v3s16(4207, 604, -30)); - putV2S32(&buf, v2s32(1920, 1080)); - putV3S32(&buf, v3s32(-400, 6400054, 290549855)); - - putWideString(&buf, L"\x02~woof~\x5455"); - - putV3F1000(&buf, v3f(500, 10024.2f, -192.54f)); - putARGB8(&buf, video::SColor(255, 128, 50, 128)); - - putLongString(&buf, "some longer string here"); - - putU16(&buf, 0xF00D); - - UASSERT(buf.size() == sizeof(test_serialized_data)); - UASSERT(!memcmp(&buf[0], test_serialized_data, sizeof(test_serialized_data))); -} - - -void TestSerialization::testStringLengthLimits() -{ - std::vector<u8> buf; - std::string too_long(STRING_MAX_LEN + 1, 'A'); - std::string way_too_large(LONG_STRING_MAX_LEN + 1, 'B'); - std::wstring too_long_wide(WIDE_STRING_MAX_LEN + 1, L'C'); - - EXCEPTION_CHECK(SerializationError, putString(&buf, too_long)); - - putLongString(&buf, too_long); - too_long.resize(too_long.size() - 1); - putString(&buf, too_long); - - EXCEPTION_CHECK(SerializationError, putWideString(&buf, too_long_wide)); - too_long_wide.resize(too_long_wide.size() - 1); - putWideString(&buf, too_long_wide); -} - - -void TestSerialization::testBufReader() -{ - u8 u8_data; - u16 u16_data; - u32 u32_data; - u64 u64_data; - s8 s8_data; - s16 s16_data; - s32 s32_data; - s64 s64_data; - f32 f32_data, f32_data2, f32_data3, f32_data4; - video::SColor scolor_data; - v2s16 v2s16_data; - v3s16 v3s16_data; - v2s32 v2s32_data; - v3s32 v3s32_data; - v3f v3f_data; - std::string string_data; - std::wstring widestring_data; - std::string longstring_data; - u8 raw_data[10] = {0}; - - BufReader buf(test_serialized_data, sizeof(test_serialized_data)); - - // Try reading data like normal - UASSERT(buf.getU8() == 0x11); - UASSERT(buf.getU16() == 0x2233); - UASSERT(buf.getU32() == 0x44556677); - UASSERT(buf.getU64() == 0x8899AABBCCDDEEFFLL); - UASSERT(buf.getS8() == -128); - UASSERT(buf.getS16() == 30000); - UASSERT(buf.getS32() == -6); - UASSERT(buf.getS64() == -43); - UASSERT(buf.getF1000() == 53.534f); - UASSERT(buf.getF1000() == -300000.32f); - UASSERT(buf.getF1000() == F1000_MIN); - UASSERT(buf.getF1000() == F1000_MAX); - UASSERT(buf.getString() == "foobar!"); - UASSERT(buf.getV2S16() == v2s16(500, 500)); - UASSERT(buf.getV3S16() == v3s16(4207, 604, -30)); - UASSERT(buf.getV2S32() == v2s32(1920, 1080)); - UASSERT(buf.getV3S32() == v3s32(-400, 6400054, 290549855)); - UASSERT(buf.getWideString() == L"\x02~woof~\x5455"); - UASSERT(buf.getV3F1000() == v3f(500, 10024.2f, -192.54f)); - UASSERT(buf.getARGB8() == video::SColor(255, 128, 50, 128)); - UASSERT(buf.getLongString() == "some longer string here"); - - // Verify the offset and data is unchanged after a failed read - size_t orig_pos = buf.pos; - u32_data = 0; - UASSERT(buf.getU32NoEx(&u32_data) == false); - UASSERT(buf.pos == orig_pos); - UASSERT(u32_data == 0); - - // Now try the same for a failed string read - UASSERT(buf.getStringNoEx(&string_data) == false); - UASSERT(buf.pos == orig_pos); - UASSERT(string_data == ""); - - // Now try the same for a failed string read - UASSERT(buf.getWideStringNoEx(&widestring_data) == false); - UASSERT(buf.pos == orig_pos); - UASSERT(widestring_data == L""); - - UASSERT(buf.getU16() == 0xF00D); - - UASSERT(buf.remaining() == 0); - - // Check to make sure these each blow exceptions as they're supposed to - EXCEPTION_CHECK(SerializationError, buf.getU8()); - EXCEPTION_CHECK(SerializationError, buf.getU16()); - EXCEPTION_CHECK(SerializationError, buf.getU32()); - EXCEPTION_CHECK(SerializationError, buf.getU64()); - - EXCEPTION_CHECK(SerializationError, buf.getS8()); - EXCEPTION_CHECK(SerializationError, buf.getS16()); - EXCEPTION_CHECK(SerializationError, buf.getS32()); - EXCEPTION_CHECK(SerializationError, buf.getS64()); - - EXCEPTION_CHECK(SerializationError, buf.getF1000()); - EXCEPTION_CHECK(SerializationError, buf.getARGB8()); - - EXCEPTION_CHECK(SerializationError, buf.getV2S16()); - EXCEPTION_CHECK(SerializationError, buf.getV3S16()); - EXCEPTION_CHECK(SerializationError, buf.getV2S32()); - EXCEPTION_CHECK(SerializationError, buf.getV3S32()); - EXCEPTION_CHECK(SerializationError, buf.getV3F1000()); - - EXCEPTION_CHECK(SerializationError, buf.getString()); - EXCEPTION_CHECK(SerializationError, buf.getWideString()); - EXCEPTION_CHECK(SerializationError, buf.getLongString()); - EXCEPTION_CHECK(SerializationError, - buf.getRawData(raw_data, sizeof(raw_data))); - - // See if we can skip backwards - buf.pos = 5; - UASSERT(buf.getRawDataNoEx(raw_data, 3) == true); - UASSERT(raw_data[0] == 0x66); - UASSERT(raw_data[1] == 0x77); - UASSERT(raw_data[2] == 0x88); - - UASSERT(buf.getU32() == 0x99AABBCC); - UASSERT(buf.pos == 12); - - // Now let's try it all over again using the NoEx variants - buf.pos = 0; - - UASSERT(buf.getU8NoEx(&u8_data)); - UASSERT(buf.getU16NoEx(&u16_data)); - UASSERT(buf.getU32NoEx(&u32_data)); - UASSERT(buf.getU64NoEx(&u64_data)); - - UASSERT(buf.getS8NoEx(&s8_data)); - UASSERT(buf.getS16NoEx(&s16_data)); - UASSERT(buf.getS32NoEx(&s32_data)); - UASSERT(buf.getS64NoEx(&s64_data)); - - UASSERT(buf.getF1000NoEx(&f32_data)); - UASSERT(buf.getF1000NoEx(&f32_data2)); - UASSERT(buf.getF1000NoEx(&f32_data3)); - UASSERT(buf.getF1000NoEx(&f32_data4)); - - UASSERT(buf.getStringNoEx(&string_data)); - UASSERT(buf.getV2S16NoEx(&v2s16_data)); - UASSERT(buf.getV3S16NoEx(&v3s16_data)); - UASSERT(buf.getV2S32NoEx(&v2s32_data)); - UASSERT(buf.getV3S32NoEx(&v3s32_data)); - UASSERT(buf.getWideStringNoEx(&widestring_data)); - UASSERT(buf.getV3F1000NoEx(&v3f_data)); - UASSERT(buf.getARGB8NoEx(&scolor_data)); - - UASSERT(buf.getLongStringNoEx(&longstring_data)); - - // and make sure we got the correct data - UASSERT(u8_data == 0x11); - UASSERT(u16_data == 0x2233); - UASSERT(u32_data == 0x44556677); - UASSERT(u64_data == 0x8899AABBCCDDEEFFLL); - UASSERT(s8_data == -128); - UASSERT(s16_data == 30000); - UASSERT(s32_data == -6); - UASSERT(s64_data == -43); - UASSERT(f32_data == 53.534f); - UASSERT(f32_data2 == -300000.32f); - UASSERT(f32_data3 == F1000_MIN); - UASSERT(f32_data4 == F1000_MAX); - UASSERT(string_data == "foobar!"); - UASSERT(v2s16_data == v2s16(500, 500)); - UASSERT(v3s16_data == v3s16(4207, 604, -30)); - UASSERT(v2s32_data == v2s32(1920, 1080)); - UASSERT(v3s32_data == v3s32(-400, 6400054, 290549855)); - UASSERT(widestring_data == L"\x02~woof~\x5455"); - UASSERT(v3f_data == v3f(500, 10024.2f, -192.54f)); - UASSERT(scolor_data == video::SColor(255, 128, 50, 128)); - UASSERT(longstring_data == "some longer string here"); - - UASSERT(buf.remaining() == 2); - UASSERT(buf.getRawDataNoEx(raw_data, 3) == false); - UASSERT(buf.remaining() == 2); - UASSERT(buf.getRawDataNoEx(raw_data, 2) == true); - UASSERT(raw_data[0] == 0xF0); - UASSERT(raw_data[1] == 0x0D); - UASSERT(buf.remaining() == 0); - - // Make sure no more available data causes a failure - UASSERT(!buf.getU8NoEx(&u8_data)); - UASSERT(!buf.getU16NoEx(&u16_data)); - UASSERT(!buf.getU32NoEx(&u32_data)); - UASSERT(!buf.getU64NoEx(&u64_data)); - - UASSERT(!buf.getS8NoEx(&s8_data)); - UASSERT(!buf.getS16NoEx(&s16_data)); - UASSERT(!buf.getS32NoEx(&s32_data)); - UASSERT(!buf.getS64NoEx(&s64_data)); - - UASSERT(!buf.getF1000NoEx(&f32_data)); - UASSERT(!buf.getARGB8NoEx(&scolor_data)); - - UASSERT(!buf.getV2S16NoEx(&v2s16_data)); - UASSERT(!buf.getV3S16NoEx(&v3s16_data)); - UASSERT(!buf.getV2S32NoEx(&v2s32_data)); - UASSERT(!buf.getV3S32NoEx(&v3s32_data)); - UASSERT(!buf.getV3F1000NoEx(&v3f_data)); - - UASSERT(!buf.getStringNoEx(&string_data)); - UASSERT(!buf.getWideStringNoEx(&widestring_data)); - UASSERT(!buf.getLongStringNoEx(&longstring_data)); - UASSERT(!buf.getRawDataNoEx(raw_data, sizeof(raw_data))); -} - void TestSerialization::testFloatFormat() { FloatType type = getFloatSerializationType(); @@ -708,7 +380,7 @@ void TestSerialization::testFloatFormat() UASSERT(test_single(i)); } -const u8 TestSerialization::test_serialized_data[12 * 13 - 8] = { +const u8 TestSerialization::test_serialized_data[12 * 11 - 2] = { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x80, 0x75, 0x30, 0xff, 0xff, 0xff, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xd5, 0x00, 0x00, 0xd1, 0x1e, 0xee, 0x1e, @@ -716,9 +388,7 @@ const u8 TestSerialization::test_serialized_data[12 * 13 - 8] = { 0x66, 0x6f, 0x6f, 0x62, 0x61, 0x72, 0x21, 0x01, 0xf4, 0x01, 0xf4, 0x10, 0x6f, 0x02, 0x5c, 0xff, 0xe2, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x04, 0x38, 0xff, 0xff, 0xfe, 0x70, 0x00, 0x61, 0xa8, 0x36, 0x11, 0x51, 0x70, - 0x5f, 0x00, 0x08, 0x00, - 0x02, 0x00, 0x7e, 0x00, 'w', 0x00, 'o', 0x00, 'o', 0x00, 'f', 0x00, // \x02~woof~\x5455 - 0x7e, 0x54, 0x55, 0x00, 0x07, 0xa1, 0x20, 0x00, 0x98, 0xf5, 0x08, 0xff, + 0x5f, 0x00, 0x07, 0xa1, 0x20, 0x00, 0x98, 0xf5, 0x08, 0xff, 0xfd, 0x0f, 0xe4, 0xff, 0x80, 0x32, 0x80, 0x00, 0x00, 0x00, 0x17, 0x73, 0x6f, 0x6d, 0x65, 0x20, 0x6c, 0x6f, 0x6e, 0x67, 0x65, 0x72, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x68, 0x65, 0x72, 0x65, 0xF0, 0x0D, diff --git a/src/unittest/test_servermodmanager.cpp b/src/unittest/test_servermodmanager.cpp index 799936757..e3edb0c32 100644 --- a/src/unittest/test_servermodmanager.cpp +++ b/src/unittest/test_servermodmanager.cpp @@ -169,6 +169,4 @@ void TestServerModManager::testGetModMediaPaths() std::vector<std::string> result; sm.getModsMediaPaths(result); UASSERTEQ(bool, result.empty(), false); - // We should have 5 folders for each mod (textures, media, locale, model, sounds) - UASSERTEQ(unsigned long, result.size() % 5, 0); } diff --git a/src/unittest/test_settings.cpp b/src/unittest/test_settings.cpp index aa56f3e06..f91ba5b67 100644 --- a/src/unittest/test_settings.cpp +++ b/src/unittest/test_settings.cpp @@ -149,15 +149,15 @@ void TestSettings::testAllSettings() UASSERT(group->getS16("a") == 5); UASSERT(fabs(group->getFloat("bb") - 2.5) < 0.001); - Settings *group3 = new Settings; - group3->set("cat", "meow"); - group3->set("dog", "woof"); - - Settings *group2 = new Settings; - group2->setS16("num_apples", 4); - group2->setS16("num_oranges", 53); - group2->setGroup("animals", group3); - group2->set("animals", "cute"); //destroys group 3 + Settings group3; + group3.set("cat", "meow"); + group3.set("dog", "woof"); + + Settings group2; + group2.setS16("num_apples", 4); + group2.setS16("num_oranges", 53); + group2.setGroup("animals", group3); + group2.set("animals", "cute"); //destroys group 3 s.setGroup("groupy_thing", group2); // Test set failure conditions diff --git a/src/unittest/test_threading.cpp b/src/unittest/test_threading.cpp index 8d4d814fd..65ef7c02d 100644 --- a/src/unittest/test_threading.cpp +++ b/src/unittest/test_threading.cpp @@ -31,7 +31,6 @@ public: void runTests(IGameDef *gamedef); void testStartStopWait(); - void testThreadKill(); void testAtomicSemaphoreThread(); }; @@ -40,7 +39,6 @@ static TestThreading g_test_instance; void TestThreading::runTests(IGameDef *gamedef) { TEST(testStartStopWait); - TEST(testThreadKill); TEST(testAtomicSemaphoreThread); } @@ -111,29 +109,6 @@ void TestThreading::testStartStopWait() } -void TestThreading::testThreadKill() -{ - SimpleTestThread *thread = new SimpleTestThread(300); - - UASSERT(thread->start() == true); - - // kill()ing is quite violent, so let's make sure our victim is sleeping - // before we do this... so we don't corrupt the rest of the program's state - sleep_ms(100); - UASSERT(thread->kill() == true); - - // The state of the thread object should be reset if all went well - UASSERT(thread->isRunning() == false); - UASSERT(thread->start() == true); - UASSERT(thread->stop() == true); - UASSERT(thread->wait() == true); - - // kill() after already waiting should fail. - UASSERT(thread->kill() == false); - - delete thread; -} - class AtomicTestThread : public Thread { public: diff --git a/src/util/base64.cpp b/src/util/base64.cpp index c75f98598..6e1584410 100644 --- a/src/util/base64.cpp +++ b/src/util/base64.cpp @@ -34,8 +34,9 @@ static const std::string base64_chars = "0123456789+/"; -static inline bool is_base64(unsigned char c) { - return (isalnum(c) || (c == '+') || (c == '/')); +static inline bool is_base64(unsigned char c) +{ + return isalnum(c) || c == '+' || c == '/' || c == '='; } bool base64_is_valid(std::string const& s) diff --git a/src/util/md32_common.h b/src/util/md32_common.h index a4c2099c9..2c050b72a 100644 --- a/src/util/md32_common.h +++ b/src/util/md32_common.h @@ -301,7 +301,7 @@ int HASH_UPDATE(HASH_CTX *c, const void *data_, size_t len) { - const unsigned char *data = data_; + const unsigned char *data = (const unsigned char *)data_; unsigned char *p; HASH_LONG l; size_t n; diff --git a/src/util/serialize.cpp b/src/util/serialize.cpp index 5b276668d..d770101f2 100644 --- a/src/util/serialize.cpp +++ b/src/util/serialize.cpp @@ -30,86 +30,18 @@ with this program; if not, write to the Free Software Foundation, Inc., FloatType g_serialize_f32_type = FLOATTYPE_UNKNOWN; -//// -//// BufReader -//// - -bool BufReader::getStringNoEx(std::string *val) -{ - u16 num_chars; - if (!getU16NoEx(&num_chars)) - return false; - - if (pos + num_chars > size) { - pos -= sizeof(num_chars); - return false; - } - - val->assign((const char *)data + pos, num_chars); - pos += num_chars; - - return true; -} - -bool BufReader::getWideStringNoEx(std::wstring *val) -{ - u16 num_chars; - if (!getU16NoEx(&num_chars)) - return false; - - if (pos + num_chars * 2 > size) { - pos -= sizeof(num_chars); - return false; - } - - for (size_t i = 0; i != num_chars; i++) { - val->push_back(readU16(data + pos)); - pos += 2; - } - - return true; -} - -bool BufReader::getLongStringNoEx(std::string *val) -{ - u32 num_chars; - if (!getU32NoEx(&num_chars)) - return false; - - if (pos + num_chars > size) { - pos -= sizeof(num_chars); - return false; - } - - val->assign((const char *)data + pos, num_chars); - pos += num_chars; - - return true; -} - -bool BufReader::getRawDataNoEx(void *val, size_t len) -{ - if (pos + len > size) - return false; - - memcpy(val, data + pos, len); - pos += len; - - return true; -} - //// //// String //// -std::string serializeString(const std::string &plain) +std::string serializeString16(const std::string &plain) { std::string s; char buf[2]; if (plain.size() > STRING_MAX_LEN) - throw SerializationError("String too long for serializeString"); + throw SerializationError("String too long for serializeString16"); s.reserve(2 + plain.size()); writeU16((u8 *)&buf[0], plain.size()); @@ -119,14 +51,14 @@ std::string serializeString(const std::string &plain) return s; } -std::string deSerializeString(std::istream &is) +std::string deSerializeString16(std::istream &is) { std::string s; char buf[2]; is.read(buf, 2); if (is.gcount() != 2) - throw SerializationError("deSerializeString: size not read"); + throw SerializationError("deSerializeString16: size not read"); u16 s_size = readU16((u8 *)buf); if (s_size == 0) @@ -135,66 +67,17 @@ std::string deSerializeString(std::istream &is) s.resize(s_size); is.read(&s[0], s_size); if (is.gcount() != s_size) - throw SerializationError("deSerializeString: couldn't read all chars"); + throw SerializationError("deSerializeString16: couldn't read all chars"); return s; } -//// -//// Wide String -//// - -std::string serializeWideString(const std::wstring &plain) -{ - std::string s; - char buf[2]; - - if (plain.size() > WIDE_STRING_MAX_LEN) - throw SerializationError("String too long for serializeWideString"); - s.reserve(2 + 2 * plain.size()); - - writeU16((u8 *)buf, plain.size()); - s.append(buf, 2); - - for (wchar_t i : plain) { - writeU16((u8 *)buf, i); - s.append(buf, 2); - } - return s; -} - -std::wstring deSerializeWideString(std::istream &is) -{ - std::wstring s; - char buf[2]; - - is.read(buf, 2); - if (is.gcount() != 2) - throw SerializationError("deSerializeWideString: size not read"); - - u16 s_size = readU16((u8 *)buf); - if (s_size == 0) - return s; - - s.reserve(s_size); - for (u32 i = 0; i < s_size; i++) { - is.read(&buf[0], 2); - if (is.gcount() != 2) { - throw SerializationError( - "deSerializeWideString: couldn't read all chars"); - } - - wchar_t c16 = readU16((u8 *)buf); - s.append(&c16, 1); - } - return s; -} //// //// Long String //// -std::string serializeLongString(const std::string &plain) +std::string serializeString32(const std::string &plain) { std::string s; char buf[4]; @@ -209,7 +92,7 @@ std::string serializeLongString(const std::string &plain) return s; } -std::string deSerializeLongString(std::istream &is) +std::string deSerializeString32(std::istream &is) { std::string s; char buf[4]; @@ -404,284 +287,3 @@ std::string deSerializeJsonStringIfNeeded(std::istream &is) return tmp_os.str(); } -//// -//// String/Struct conversions -//// - -bool deSerializeStringToStruct(std::string valstr, - std::string format, void *out, size_t olen) -{ - size_t len = olen; - std::vector<std::string *> strs_alloced; - std::string *str; - char *f, *snext; - size_t pos; - - char *s = &valstr[0]; - char *buf = new char[len]; - char *bufpos = buf; - - char *fmtpos, *fmt = &format[0]; - while ((f = strtok_r(fmt, ",", &fmtpos)) && s) { - fmt = nullptr; - - bool is_unsigned = false; - int width = 0; - char valtype = *f; - - width = (int)strtol(f + 1, &f, 10); - if (width && valtype == 's') - valtype = 'i'; - - switch (valtype) { - case 'u': - is_unsigned = true; - /* FALLTHROUGH */ - case 'i': - if (width == 16) { - bufpos += PADDING(bufpos, u16); - if ((bufpos - buf) + sizeof(u16) <= len) { - if (is_unsigned) - *(u16 *)bufpos = (u16)strtoul(s, &s, 10); - else - *(s16 *)bufpos = (s16)strtol(s, &s, 10); - } - bufpos += sizeof(u16); - } else if (width == 32) { - bufpos += PADDING(bufpos, u32); - if ((bufpos - buf) + sizeof(u32) <= len) { - if (is_unsigned) - *(u32 *)bufpos = (u32)strtoul(s, &s, 10); - else - *(s32 *)bufpos = (s32)strtol(s, &s, 10); - } - bufpos += sizeof(u32); - } else if (width == 64) { - bufpos += PADDING(bufpos, u64); - if ((bufpos - buf) + sizeof(u64) <= len) { - if (is_unsigned) - *(u64 *)bufpos = (u64)strtoull(s, &s, 10); - else - *(s64 *)bufpos = (s64)strtoll(s, &s, 10); - } - bufpos += sizeof(u64); - } - s = strchr(s, ','); - break; - case 'b': - snext = strchr(s, ','); - if (snext) - *snext++ = 0; - - bufpos += PADDING(bufpos, bool); - if ((bufpos - buf) + sizeof(bool) <= len) - *(bool *)bufpos = is_yes(std::string(s)); - bufpos += sizeof(bool); - - s = snext; - break; - case 'f': - bufpos += PADDING(bufpos, float); - if ((bufpos - buf) + sizeof(float) <= len) - *(float *)bufpos = strtof(s, &s); - bufpos += sizeof(float); - - s = strchr(s, ','); - break; - case 's': - while (*s == ' ' || *s == '\t') - s++; - if (*s++ != '"') //error, expected string - goto fail; - snext = s; - - while (snext[0] && !(snext[-1] != '\\' && snext[0] == '"')) - snext++; - *snext++ = 0; - - bufpos += PADDING(bufpos, std::string *); - - str = new std::string(s); - pos = 0; - while ((pos = str->find("\\\"", pos)) != std::string::npos) - str->erase(pos, 1); - - if ((bufpos - buf) + sizeof(std::string *) <= len) - *(std::string **)bufpos = str; - bufpos += sizeof(std::string *); - strs_alloced.push_back(str); - - s = *snext ? snext + 1 : nullptr; - break; - case 'v': - while (*s == ' ' || *s == '\t') - s++; - if (*s++ != '(') //error, expected vector - goto fail; - - if (width == 2) { - bufpos += PADDING(bufpos, v2f); - - if ((bufpos - buf) + sizeof(v2f) <= len) { - v2f *v = (v2f *)bufpos; - v->X = strtof(s, &s); - s++; - v->Y = strtof(s, &s); - } - - bufpos += sizeof(v2f); - } else if (width == 3) { - bufpos += PADDING(bufpos, v3f); - if ((bufpos - buf) + sizeof(v3f) <= len) { - v3f *v = (v3f *)bufpos; - v->X = strtof(s, &s); - s++; - v->Y = strtof(s, &s); - s++; - v->Z = strtof(s, &s); - } - - bufpos += sizeof(v3f); - } - s = strchr(s, ','); - break; - default: //error, invalid format specifier - goto fail; - } - - if (s && *s == ',') - s++; - - if ((size_t)(bufpos - buf) > len) //error, buffer too small - goto fail; - } - - if (f && *f) { //error, mismatched number of fields and values -fail: - for (size_t i = 0; i != strs_alloced.size(); i++) - delete strs_alloced[i]; - delete[] buf; - return false; - } - - memcpy(out, buf, olen); - delete[] buf; - return true; -} - -// Casts *buf to a signed or unsigned fixed-width integer of 'w' width -#define SIGN_CAST(w, buf) (is_unsigned ? *((u##w *) buf) : *((s##w *) buf)) - -bool serializeStructToString(std::string *out, - std::string format, void *value) -{ - std::ostringstream os; - std::string str; - char *f; - size_t strpos; - - char *bufpos = (char *) value; - char *fmtpos, *fmt = &format[0]; - while ((f = strtok_r(fmt, ",", &fmtpos))) { - fmt = nullptr; - bool is_unsigned = false; - int width = 0; - char valtype = *f; - - width = (int)strtol(f + 1, &f, 10); - if (width && valtype == 's') - valtype = 'i'; - - switch (valtype) { - case 'u': - is_unsigned = true; - /* FALLTHROUGH */ - case 'i': - if (width == 16) { - bufpos += PADDING(bufpos, u16); - os << SIGN_CAST(16, bufpos); - bufpos += sizeof(u16); - } else if (width == 32) { - bufpos += PADDING(bufpos, u32); - os << SIGN_CAST(32, bufpos); - bufpos += sizeof(u32); - } else if (width == 64) { - bufpos += PADDING(bufpos, u64); - os << SIGN_CAST(64, bufpos); - bufpos += sizeof(u64); - } - break; - case 'b': - bufpos += PADDING(bufpos, bool); - os << std::boolalpha << *((bool *) bufpos); - bufpos += sizeof(bool); - break; - case 'f': - bufpos += PADDING(bufpos, float); - os << *((float *) bufpos); - bufpos += sizeof(float); - break; - case 's': - bufpos += PADDING(bufpos, std::string *); - str = **((std::string **) bufpos); - - strpos = 0; - while ((strpos = str.find('"', strpos)) != std::string::npos) { - str.insert(strpos, 1, '\\'); - strpos += 2; - } - - os << str; - bufpos += sizeof(std::string *); - break; - case 'v': - if (width == 2) { - bufpos += PADDING(bufpos, v2f); - v2f *v = (v2f *) bufpos; - os << '(' << v->X << ", " << v->Y << ')'; - bufpos += sizeof(v2f); - } else { - bufpos += PADDING(bufpos, v3f); - v3f *v = (v3f *) bufpos; - os << '(' << v->X << ", " << v->Y << ", " << v->Z << ')'; - bufpos += sizeof(v3f); - } - break; - default: - return false; - } - os << ", "; - } - *out = os.str(); - - // Trim off the trailing comma and space - if (out->size() >= 2) - out->resize(out->size() - 2); - - return true; -} - -#undef SIGN_CAST - -//// -//// Other -//// - -std::string serializeHexString(const std::string &data, bool insert_spaces) -{ - std::string result; - result.reserve(data.size() * (2 + insert_spaces)); - - static const char hex_chars[] = "0123456789abcdef"; - - const size_t len = data.size(); - for (size_t i = 0; i != len; i++) { - u8 byte = data[i]; - result.push_back(hex_chars[(byte >> 4) & 0x0F]); - result.push_back(hex_chars[(byte >> 0) & 0x0F]); - if (insert_spaces && i != len - 1) - result.push_back(' '); - } - - return result; -} diff --git a/src/util/serialize.h b/src/util/serialize.h index a4b5a234a..b3ec28eab 100644 --- a/src/util/serialize.h +++ b/src/util/serialize.h @@ -440,22 +440,16 @@ MAKE_STREAM_WRITE_FXN(video::SColor, ARGB8, 4); //// // Creates a string with the length as the first two bytes -std::string serializeString(const std::string &plain); - -// Creates a string with the length as the first two bytes from wide string -std::string serializeWideString(const std::wstring &plain); +std::string serializeString16(const std::string &plain); // Reads a string with the length as the first two bytes -std::string deSerializeString(std::istream &is); - -// Reads a wide string with the length as the first two bytes -std::wstring deSerializeWideString(std::istream &is); +std::string deSerializeString16(std::istream &is); // Creates a string with the length as the first four bytes -std::string serializeLongString(const std::string &plain); +std::string serializeString32(const std::string &plain); // Reads a string with the length as the first four bytes -std::string deSerializeLongString(std::istream &is); +std::string deSerializeString32(std::istream &is); // Creates a string encoded in JSON format (almost equivalent to a C string literal) std::string serializeJsonString(const std::string &plain); @@ -469,239 +463,3 @@ std::string serializeJsonStringIfNeeded(const std::string &s); // Parses a string serialized by serializeJsonStringIfNeeded. std::string deSerializeJsonStringIfNeeded(std::istream &is); - -// Creates a string consisting of the hexadecimal representation of `data` -std::string serializeHexString(const std::string &data, bool insert_spaces=false); - -// Creates a string containing comma delimited values of a struct whose layout is -// described by the parameter format -bool serializeStructToString(std::string *out, - std::string format, void *value); - -// Reads a comma delimited string of values into a struct whose layout is -// decribed by the parameter format -bool deSerializeStringToStruct(std::string valstr, - std::string format, void *out, size_t olen); - -//// -//// BufReader -//// - -#define MAKE_BUFREADER_GETNOEX_FXN(T, N, S) \ - inline bool get ## N ## NoEx(T *val) \ - { \ - if (pos + S > size) \ - return false; \ - *val = read ## N(data + pos); \ - pos += S; \ - return true; \ - } - -#define MAKE_BUFREADER_GET_FXN(T, N) \ - inline T get ## N() \ - { \ - T val; \ - if (!get ## N ## NoEx(&val)) \ - throw SerializationError("Attempted read past end of data"); \ - return val; \ - } - -class BufReader { -public: - BufReader(const u8 *data_, size_t size_) : - data(data_), - size(size_) - { - } - - MAKE_BUFREADER_GETNOEX_FXN(u8, U8, 1); - MAKE_BUFREADER_GETNOEX_FXN(u16, U16, 2); - MAKE_BUFREADER_GETNOEX_FXN(u32, U32, 4); - MAKE_BUFREADER_GETNOEX_FXN(u64, U64, 8); - MAKE_BUFREADER_GETNOEX_FXN(s8, S8, 1); - MAKE_BUFREADER_GETNOEX_FXN(s16, S16, 2); - MAKE_BUFREADER_GETNOEX_FXN(s32, S32, 4); - MAKE_BUFREADER_GETNOEX_FXN(s64, S64, 8); - MAKE_BUFREADER_GETNOEX_FXN(f32, F1000, 4); - MAKE_BUFREADER_GETNOEX_FXN(v2s16, V2S16, 4); - MAKE_BUFREADER_GETNOEX_FXN(v3s16, V3S16, 6); - MAKE_BUFREADER_GETNOEX_FXN(v2s32, V2S32, 8); - MAKE_BUFREADER_GETNOEX_FXN(v3s32, V3S32, 12); - MAKE_BUFREADER_GETNOEX_FXN(v3f, V3F1000, 12); - MAKE_BUFREADER_GETNOEX_FXN(video::SColor, ARGB8, 4); - - bool getStringNoEx(std::string *val); - bool getWideStringNoEx(std::wstring *val); - bool getLongStringNoEx(std::string *val); - bool getRawDataNoEx(void *data, size_t len); - - MAKE_BUFREADER_GET_FXN(u8, U8); - MAKE_BUFREADER_GET_FXN(u16, U16); - MAKE_BUFREADER_GET_FXN(u32, U32); - MAKE_BUFREADER_GET_FXN(u64, U64); - MAKE_BUFREADER_GET_FXN(s8, S8); - MAKE_BUFREADER_GET_FXN(s16, S16); - MAKE_BUFREADER_GET_FXN(s32, S32); - MAKE_BUFREADER_GET_FXN(s64, S64); - MAKE_BUFREADER_GET_FXN(f32, F1000); - MAKE_BUFREADER_GET_FXN(v2s16, V2S16); - MAKE_BUFREADER_GET_FXN(v3s16, V3S16); - MAKE_BUFREADER_GET_FXN(v2s32, V2S32); - MAKE_BUFREADER_GET_FXN(v3s32, V3S32); - MAKE_BUFREADER_GET_FXN(v3f, V3F1000); - MAKE_BUFREADER_GET_FXN(video::SColor, ARGB8); - MAKE_BUFREADER_GET_FXN(std::string, String); - MAKE_BUFREADER_GET_FXN(std::wstring, WideString); - MAKE_BUFREADER_GET_FXN(std::string, LongString); - - inline void getRawData(void *val, size_t len) - { - if (!getRawDataNoEx(val, len)) - throw SerializationError("Attempted read past end of data"); - } - - inline size_t remaining() - { - assert(pos <= size); - return size - pos; - } - - const u8 *data; - size_t size; - size_t pos = 0; -}; - -#undef MAKE_BUFREADER_GET_FXN -#undef MAKE_BUFREADER_GETNOEX_FXN - - -//// -//// Vector-based write routines -//// - -inline void putU8(std::vector<u8> *dest, u8 val) -{ - dest->push_back((val >> 0) & 0xFF); -} - -inline void putU16(std::vector<u8> *dest, u16 val) -{ - dest->push_back((val >> 8) & 0xFF); - dest->push_back((val >> 0) & 0xFF); -} - -inline void putU32(std::vector<u8> *dest, u32 val) -{ - dest->push_back((val >> 24) & 0xFF); - dest->push_back((val >> 16) & 0xFF); - dest->push_back((val >> 8) & 0xFF); - dest->push_back((val >> 0) & 0xFF); -} - -inline void putU64(std::vector<u8> *dest, u64 val) -{ - dest->push_back((val >> 56) & 0xFF); - dest->push_back((val >> 48) & 0xFF); - dest->push_back((val >> 40) & 0xFF); - dest->push_back((val >> 32) & 0xFF); - dest->push_back((val >> 24) & 0xFF); - dest->push_back((val >> 16) & 0xFF); - dest->push_back((val >> 8) & 0xFF); - dest->push_back((val >> 0) & 0xFF); -} - -inline void putS8(std::vector<u8> *dest, s8 val) -{ - putU8(dest, val); -} - -inline void putS16(std::vector<u8> *dest, s16 val) -{ - putU16(dest, val); -} - -inline void putS32(std::vector<u8> *dest, s32 val) -{ - putU32(dest, val); -} - -inline void putS64(std::vector<u8> *dest, s64 val) -{ - putU64(dest, val); -} - -inline void putF1000(std::vector<u8> *dest, f32 val) -{ - putS32(dest, val * FIXEDPOINT_FACTOR); -} - -inline void putV2S16(std::vector<u8> *dest, v2s16 val) -{ - putS16(dest, val.X); - putS16(dest, val.Y); -} - -inline void putV3S16(std::vector<u8> *dest, v3s16 val) -{ - putS16(dest, val.X); - putS16(dest, val.Y); - putS16(dest, val.Z); -} - -inline void putV2S32(std::vector<u8> *dest, v2s32 val) -{ - putS32(dest, val.X); - putS32(dest, val.Y); -} - -inline void putV3S32(std::vector<u8> *dest, v3s32 val) -{ - putS32(dest, val.X); - putS32(dest, val.Y); - putS32(dest, val.Z); -} - -inline void putV3F1000(std::vector<u8> *dest, v3f val) -{ - putF1000(dest, val.X); - putF1000(dest, val.Y); - putF1000(dest, val.Z); -} - -inline void putARGB8(std::vector<u8> *dest, video::SColor val) -{ - putU32(dest, val.color); -} - -inline void putString(std::vector<u8> *dest, const std::string &val) -{ - if (val.size() > STRING_MAX_LEN) - throw SerializationError("String too long"); - - putU16(dest, val.size()); - dest->insert(dest->end(), val.begin(), val.end()); -} - -inline void putWideString(std::vector<u8> *dest, const std::wstring &val) -{ - if (val.size() > WIDE_STRING_MAX_LEN) - throw SerializationError("String too long"); - - putU16(dest, val.size()); - for (size_t i = 0; i != val.size(); i++) - putU16(dest, val[i]); -} - -inline void putLongString(std::vector<u8> *dest, const std::string &val) -{ - if (val.size() > LONG_STRING_MAX_LEN) - throw SerializationError("String too long"); - - putU32(dest, val.size()); - dest->insert(dest->end(), val.begin(), val.end()); -} - -inline void putRawData(std::vector<u8> *dest, const void *src, size_t len) -{ - dest->insert(dest->end(), (u8 *)src, (u8 *)src + len); -} diff --git a/src/util/sha256.c b/src/util/sha256.c index 4241f31f3..5c8266f9a 100644 --- a/src/util/sha256.c +++ b/src/util/sha256.c @@ -18,13 +18,13 @@ const char SHA256_version[] = "SHA-256" OPENSSL_VERSION_PTEXT; unsigned static char cleanse_ctr = 0; static void OPENSSL_cleanse(void *ptr, size_t len) { - unsigned char *p = ptr; + unsigned char *p = (unsigned char *)ptr; size_t loop = len, ctr = cleanse_ctr; while (loop--) { *(p++) = (unsigned char)ctr; ctr += (17 + ((size_t)p & 0xF)); } - p = memchr(ptr, (unsigned char)ctr, len); + p = (unsigned char *)memchr(ptr, (unsigned char)ctr, len); if (p) ctr += (63 + (size_t)p); cleanse_ctr = (unsigned char)ctr; @@ -262,7 +262,7 @@ static void sha256_block_data_order(SHA256_CTX *ctx, const void *in, unsigned MD32_REG_T a, b, c, d, e, f, g, h, s0, s1, T1; SHA_LONG X[16]; int i; - const unsigned char *data = in; + const unsigned char *data = (const unsigned char *)in; const union { long one; char little; diff --git a/src/util/string.cpp b/src/util/string.cpp index 6e1db798c..8381a29c5 100644 --- a/src/util/string.cpp +++ b/src/util/string.cpp @@ -27,6 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "translation.h" #include <algorithm> +#include <array> #include <sstream> #include <iomanip> #include <map> @@ -889,3 +890,70 @@ std::wstring translate_string(const std::wstring &s) return translate_string(s, g_client_translations); #endif } + +static const std::array<std::wstring, 22> disallowed_dir_names = { + // Problematic filenames from here: + // https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file#file-and-directory-names + L"CON", + L"PRN", + L"AUX", + L"NUL", + L"COM1", + L"COM2", + L"COM3", + L"COM4", + L"COM5", + L"COM6", + L"COM7", + L"COM8", + L"COM9", + L"LPT1", + L"LPT2", + L"LPT3", + L"LPT4", + L"LPT5", + L"LPT6", + L"LPT7", + L"LPT8", + L"LPT9", +}; + +/** + * List of characters that are blacklisted from created directories + */ +static const std::wstring disallowed_path_chars = L"<>:\"/\\|?*."; + +/** + * Sanitize the name of a new directory. This consists of two stages: + * 1. Check for 'reserved filenames' that can't be used on some filesystems + * and add a prefix to them + * 2. Remove 'unsafe' characters from the name by replacing them with '_' + */ +std::string sanitizeDirName(const std::string &str, const std::string &optional_prefix) +{ + std::wstring safe_name = utf8_to_wide(str); + + for (std::wstring disallowed_name : disallowed_dir_names) { + if (str_equal(safe_name, disallowed_name, true)) { + safe_name = utf8_to_wide(optional_prefix) + safe_name; + break; + } + } + + for (unsigned long i = 0; i < safe_name.length(); i++) { + bool is_valid = true; + + // Unlikely, but control characters should always be blacklisted + if (safe_name[i] < 32) { + is_valid = false; + } else if (safe_name[i] < 128) { + is_valid = disallowed_path_chars.find_first_of(safe_name[i]) + == std::wstring::npos; + } + + if (!is_valid) + safe_name[i] = '_'; + } + + return wide_to_utf8(safe_name); +} diff --git a/src/util/string.h b/src/util/string.h index 185fb55e2..6fd11fadc 100644 --- a/src/util/string.h +++ b/src/util/string.h @@ -746,3 +746,11 @@ inline irr::core::stringw utf8_to_stringw(const std::string &input) std::wstring str = utf8_to_wide(input); return irr::core::stringw(str.c_str()); } + +/** + * Sanitize the name of a new directory. This consists of two stages: + * 1. Check for 'reserved filenames' that can't be used on some filesystems + * and prefix them + * 2. Remove 'unsafe' characters from the name by replacing them with '_' + */ +std::string sanitizeDirName(const std::string &str, const std::string &optional_prefix); |